refactor: extract magic numbers to named constants

Low severity code quality improvements:
- session.ts: Add timing constants (SCREEN_STARTUP_DELAY_MS, etc.)
- screen-manager.ts: Add EXEC_TIMEOUT_MS, CR_MAX_ATTEMPTS, etc.
- subagent-watcher.ts: Add display length constants
- server.ts: Add STATS_COLLECTION_INTERVAL_MS, SESSION_LIMIT_WAIT_MS, etc.
- spawn-orchestrator.ts: Add budget ratio constants
- respawn-controller.ts: Use pre-compiled ANSI_ESCAPE_PATTERN
- state-store.ts: Use ES6 import for unlinkSync instead of dynamic require
- app.js: Add notification timing constants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-26 14:27:21 +01:00
co-authored by Claude Opus 4.5
parent 3c2e89f427
commit 428556e831
10 changed files with 168 additions and 70 deletions
+22 -4
View File
@@ -1,7 +1,25 @@
// Claudeman App - Tab-based Terminal UI
// ============================================================================
// Constants
// ============================================================================
// Default terminal scrollback (can be changed via settings)
const DEFAULT_SCROLLBACK = 5000;
// Timing constants
const STUCK_THRESHOLD_DEFAULT_MS = 600000; // 10 minutes - default for stuck detection
const GROUPING_TIMEOUT_MS = 5000; // 5 seconds - notification grouping window
const NOTIFICATION_LIST_CAP = 100; // Max notifications in list
const TITLE_FLASH_INTERVAL_MS = 1500; // Title flash rate
const BROWSER_NOTIF_RATE_LIMIT_MS = 3000; // Rate limit for browser notifications
const AUTO_CLOSE_NOTIFICATION_MS = 8000; // Auto-close browser notifications
const THROTTLE_DELAY_MS = 100; // General UI throttle delay
const TERMINAL_CHUNK_SIZE = 64 * 1024; // 64KB chunks for terminal data
const TERMINAL_TAIL_SIZE = 256 * 1024; // 256KB tail for initial load
const SYNC_WAIT_TIMEOUT_MS = 50; // Wait timeout for terminal sync
const STATS_POLLING_INTERVAL_MS = 2000; // System stats polling
// DEC mode 2026 - Synchronized Output
// Wrap terminal writes with these markers to prevent partial-frame flicker.
// Terminal buffers all output between markers and renders atomically.
@@ -98,7 +116,7 @@ class NotificationManager {
enabled: true,
browserNotifications: true,
audioAlerts: false,
stuckThresholdMs: 600000,
stuckThresholdMs: STUCK_THRESHOLD_DEFAULT_MS,
muteCritical: false,
muteWarning: false,
muteInfo: false,
@@ -140,7 +158,7 @@ class NotificationManager {
existing.notification.message = message;
existing.notification.timestamp = Date.now();
clearTimeout(existing.timeout);
existing.timeout = setTimeout(() => this.groupingMap.delete(groupKey), 5000);
existing.timeout = setTimeout(() => this.groupingMap.delete(groupKey), GROUPING_TIMEOUT_MS);
this.scheduleRender();
return;
}
@@ -163,7 +181,7 @@ class NotificationManager {
if (this.notifications.length > 100) this.notifications.pop();
// Track for grouping
const timeout = setTimeout(() => this.groupingMap.delete(groupKey), 5000);
const timeout = setTimeout(() => this.groupingMap.delete(groupKey), GROUPING_TIMEOUT_MS);
this.groupingMap.set(groupKey, { notification, timeout });
// Update unread
@@ -236,7 +254,7 @@ class NotificationManager {
document.title = this.titleFlashState
? `\u26A0\uFE0F (${this.unreadCount}) Claudeman`
: this.originalTitle;
}, 1500);
}, TITLE_FLASH_INTERVAL_MS);
// Set immediately
document.title = `\u26A0\uFE0F (${this.unreadCount}) Claudeman`;
}
+15 -7
View File
@@ -105,6 +105,14 @@ const MAX_TERMINAL_ROWS = 200;
const MAX_SESSION_NAME_LENGTH = 128;
// Maximum hook data size (prevents oversized SSE broadcasts)
const MAX_HOOK_DATA_SIZE = 8 * 1024;
// Stats collection interval (2 seconds)
const STATS_COLLECTION_INTERVAL_MS = 2000;
// Session limit wait time before retrying (5 seconds)
const SESSION_LIMIT_WAIT_MS = 5000;
// Pause between scheduled run iterations (2 seconds)
const ITERATION_PAUSE_MS = 2000;
// SSE batch flush threshold (number of items)
const BATCH_FLUSH_THRESHOLD = 1024;
// Pre-compiled regex for terminal buffer cleaning (avoids per-request compilation)
const CLAUDE_BANNER_PATTERN = /\x1b\[1mClaud/;
const CTRL_L_PATTERN = /\x0c/g;
@@ -1703,7 +1711,7 @@ export class WebServer extends EventEmitter {
// Start stats collection
this.app.post('/api/screens/stats/start', async () => {
this.screenManager.startStatsCollection(2000);
this.screenManager.startStatsCollection(STATS_COLLECTION_INTERVAL_MS);
return { success: true };
});
@@ -2700,7 +2708,7 @@ export class WebServer extends EventEmitter {
// Check session limit before creating new session
if (this.sessions.size >= MAX_CONCURRENT_SESSIONS) {
addLog(`Waiting: maximum concurrent sessions (${MAX_CONCURRENT_SESSIONS}) reached`);
await new Promise(r => setTimeout(r, 5000));
await new Promise(r => setTimeout(r, SESSION_LIMIT_WAIT_MS));
continue;
}
@@ -2733,7 +2741,7 @@ export class WebServer extends EventEmitter {
run.sessionId = null;
// Small pause between iterations
await new Promise(r => setTimeout(r, 2000));
await new Promise(r => setTimeout(r, ITERATION_PAUSE_MS));
} catch (err) {
addLog(`Error: ${getErrorMessage(err)}`);
this.broadcast('scheduled:updated', run);
@@ -2749,7 +2757,7 @@ export class WebServer extends EventEmitter {
}
// Continue despite errors
await new Promise(r => setTimeout(r, 5000));
await new Promise(r => setTimeout(r, SESSION_LIMIT_WAIT_MS));
}
}
@@ -2884,8 +2892,8 @@ export class WebServer extends EventEmitter {
const newBatch = existing + data;
this.terminalBatches.set(sessionId, newBatch);
// Flush immediately if batch is large (>1KB) for responsiveness
if (newBatch.length > 1024) {
// Flush immediately if batch is large for responsiveness
if (newBatch.length > BATCH_FLUSH_THRESHOLD) {
if (this.terminalBatchTimer) {
clearTimeout(this.terminalBatchTimer);
this.terminalBatchTimer = null;
@@ -3222,7 +3230,7 @@ export class WebServer extends EventEmitter {
}
// Start stats collection to show screen info
this.screenManager.startStatsCollection(2000);
this.screenManager.startStatsCollection(STATS_COLLECTION_INTERVAL_MS);
}
if (dead.length > 0) {