mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
chore: bump version to 0.1496
This commit is contained in:
@@ -26,7 +26,7 @@ When user says "COM":
|
|||||||
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
|
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
|
||||||
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
||||||
|
|
||||||
**Version**: 0.1495 (must match `package.json` for npm publish)
|
**Version**: 0.1496 (must match `package.json` for npm publish)
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudeman",
|
"name": "claudeman",
|
||||||
"version": "0.1495",
|
"version": "0.1496",
|
||||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -981,6 +981,8 @@ export class RalphTracker extends EventEmitter {
|
|||||||
this._todos.clear();
|
this._todos.clear();
|
||||||
this._completionPhraseCount.clear();
|
this._completionPhraseCount.clear();
|
||||||
this._taskNumberToContent.clear();
|
this._taskNumberToContent.clear();
|
||||||
|
this._todoStartTimes.clear();
|
||||||
|
this._alternateCompletionPhrases.clear();
|
||||||
this._lineBuffer = '';
|
this._lineBuffer = '';
|
||||||
// Reset all RALPH_STATUS block and circuit breaker state
|
// Reset all RALPH_STATUS block and circuit breaker state
|
||||||
this._statusBlockBuffer = [];
|
this._statusBlockBuffer = [];
|
||||||
@@ -2888,6 +2890,8 @@ export class RalphTracker extends EventEmitter {
|
|||||||
this._loopState = createInitialRalphTrackerState(); // This sets enabled: false
|
this._loopState = createInitialRalphTrackerState(); // This sets enabled: false
|
||||||
this._todos.clear();
|
this._todos.clear();
|
||||||
this._taskNumberToContent.clear();
|
this._taskNumberToContent.clear();
|
||||||
|
this._todoStartTimes.clear();
|
||||||
|
this._alternateCompletionPhrases.clear();
|
||||||
this._lineBuffer = '';
|
this._lineBuffer = '';
|
||||||
this._partialPromiseBuffer = '';
|
this._partialPromiseBuffer = '';
|
||||||
this._completionPhraseCount.clear();
|
this._completionPhraseCount.clear();
|
||||||
@@ -3838,6 +3842,8 @@ export class RalphTracker extends EventEmitter {
|
|||||||
this.stopIterationStallDetection();
|
this.stopIterationStallDetection();
|
||||||
this._todos.clear();
|
this._todos.clear();
|
||||||
this._taskNumberToContent.clear();
|
this._taskNumberToContent.clear();
|
||||||
|
this._todoStartTimes.clear();
|
||||||
|
this._alternateCompletionPhrases.clear();
|
||||||
this._completionPhraseCount.clear();
|
this._completionPhraseCount.clear();
|
||||||
this._planTasks.clear();
|
this._planTasks.clear();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
|||||||
@@ -1765,6 +1765,10 @@ export class RespawnController extends EventEmitter {
|
|||||||
clearInterval(this.stuckStateTimer);
|
clearInterval(this.stuckStateTimer);
|
||||||
this.stuckStateTimer = null;
|
this.stuckStateTimer = null;
|
||||||
}
|
}
|
||||||
|
if (this.detectionUpdateTimer) {
|
||||||
|
clearInterval(this.detectionUpdateTimer);
|
||||||
|
this.detectionUpdateTimer = null;
|
||||||
|
}
|
||||||
// Clear all tracked timers
|
// Clear all tracked timers
|
||||||
this.activeTimers.clear();
|
this.activeTimers.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -657,7 +657,7 @@ export class StateStore {
|
|||||||
const tempPath = this.ralphStatePath + '.tmp';
|
const tempPath = this.ralphStatePath + '.tmp';
|
||||||
let json: string;
|
let json: string;
|
||||||
try {
|
try {
|
||||||
json = JSON.stringify(data, null, 2);
|
json = JSON.stringify(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[StateStore] Failed to serialize Ralph state (circular reference or invalid data):', err);
|
console.error('[StateStore] Failed to serialize Ralph state (circular reference or invalid data):', err);
|
||||||
// Keep dirty flag true for retry - don't throw, let caller continue
|
// Keep dirty flag true for retry - don't throw, let caller continue
|
||||||
|
|||||||
+3
-10
@@ -1232,16 +1232,9 @@ export class SubagentWatcher extends EventEmitter {
|
|||||||
const agentCalls = this.pendingToolCalls.get(agentId)!;
|
const agentCalls = this.pendingToolCalls.get(agentId)!;
|
||||||
// Enforce size limit to prevent memory leak from rapid tool calls
|
// Enforce size limit to prevent memory leak from rapid tool calls
|
||||||
if (agentCalls.size >= MAX_PENDING_TOOL_CALLS) {
|
if (agentCalls.size >= MAX_PENDING_TOOL_CALLS) {
|
||||||
// Evict oldest entry by timestamp
|
// FIFO eviction: delete first (oldest) entry using Map insertion order
|
||||||
let oldestId: string | null = null;
|
const firstKey = agentCalls.keys().next().value;
|
||||||
let oldestTime = Infinity;
|
if (firstKey !== undefined) agentCalls.delete(firstKey);
|
||||||
for (const [id, call] of agentCalls) {
|
|
||||||
if (call.timestamp < oldestTime) {
|
|
||||||
oldestTime = call.timestamp;
|
|
||||||
oldestId = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldestId) agentCalls.delete(oldestId);
|
|
||||||
}
|
}
|
||||||
agentCalls.set(content.id, {
|
agentCalls.set(content.id, {
|
||||||
toolName: content.name,
|
toolName: content.name,
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ import { LRUMap } from './utils/lru-map.js';
|
|||||||
|
|
||||||
// ========== Constants ==========
|
// ========== Constants ==========
|
||||||
|
|
||||||
const POLL_INTERVAL_MS = 3000;
|
const POLL_INTERVAL_MS = 5000;
|
||||||
const MAX_CACHED_TEAMS = 50;
|
const MAX_CACHED_TEAMS = 50;
|
||||||
const MAX_CACHED_TASKS = 200;
|
const MAX_CACHED_TASKS = 200;
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ const WINDOW_DEFAULT_WIDTH_PX = 300;
|
|||||||
// xterm.js doesn't support DEC 2026 natively, so we implement buffering ourselves.
|
// xterm.js doesn't support DEC 2026 natively, so we implement buffering ourselves.
|
||||||
const DEC_SYNC_START = '\x1b[?2026h';
|
const DEC_SYNC_START = '\x1b[?2026h';
|
||||||
const DEC_SYNC_END = '\x1b[?2026l';
|
const DEC_SYNC_END = '\x1b[?2026l';
|
||||||
|
// Pre-compiled regex for stripping DEC 2026 markers (single pass instead of two replaceAll calls)
|
||||||
|
const DEC_SYNC_STRIP_RE = /\x1b\[\?2026[hl]/g;
|
||||||
|
|
||||||
// Built-in respawn configuration presets
|
// Built-in respawn configuration presets
|
||||||
const BUILTIN_RESPAWN_PRESETS = [
|
const BUILTIN_RESPAWN_PRESETS = [
|
||||||
@@ -1867,9 +1869,7 @@ class ClaudemanApp {
|
|||||||
|
|
||||||
// Strip any DEC 2026 markers that might be in the buffer
|
// Strip any DEC 2026 markers that might be in the buffer
|
||||||
// (from historical SSE data that was stored with markers)
|
// (from historical SSE data that was stored with markers)
|
||||||
const cleanBuffer = buffer
|
const cleanBuffer = buffer.replace(DEC_SYNC_STRIP_RE, '');
|
||||||
.replaceAll(DEC_SYNC_START, '')
|
|
||||||
.replaceAll(DEC_SYNC_END, '');
|
|
||||||
|
|
||||||
// For small buffers, write directly
|
// For small buffers, write directly
|
||||||
if (cleanBuffer.length <= chunkSize) {
|
if (cleanBuffer.length <= chunkSize) {
|
||||||
@@ -2157,9 +2157,7 @@ class ClaudemanApp {
|
|||||||
if (termData.terminalBuffer) {
|
if (termData.terminalBuffer) {
|
||||||
// Strip any DEC 2026 markers and write raw content
|
// Strip any DEC 2026 markers and write raw content
|
||||||
// (markers don't help here - this is a static buffer reload, not live Ink redraws)
|
// (markers don't help here - this is a static buffer reload, not live Ink redraws)
|
||||||
const cleanBuffer = termData.terminalBuffer
|
const cleanBuffer = termData.terminalBuffer.replace(DEC_SYNC_STRIP_RE, '');
|
||||||
.replaceAll(DEC_SYNC_START, '')
|
|
||||||
.replaceAll(DEC_SYNC_END, '');
|
|
||||||
// Use chunked write to avoid UI freeze with large buffers (can be 1-2MB)
|
// Use chunked write to avoid UI freeze with large buffers (can be 1-2MB)
|
||||||
await this.chunkedTerminalWrite(cleanBuffer);
|
await this.chunkedTerminalWrite(cleanBuffer);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-4
@@ -352,6 +352,10 @@ export class WebServer extends EventEmitter {
|
|||||||
private sseHealthCheckTimer: NodeJS.Timeout | null = null;
|
private sseHealthCheckTimer: NodeJS.Timeout | null = null;
|
||||||
// Flag to prevent new timers during shutdown
|
// Flag to prevent new timers during shutdown
|
||||||
private _isStopping: boolean = false;
|
private _isStopping: boolean = false;
|
||||||
|
// Cached light state for SSE init (avoids rebuilding on every reconnect)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
private cachedLightState: { data: Record<string, unknown>; timestamp: number } | null = null;
|
||||||
|
private static readonly LIGHT_STATE_CACHE_TTL_MS = 1000;
|
||||||
// Token recording for daily stats (track what's been recorded to avoid double-counting)
|
// Token recording for daily stats (track what's been recorded to avoid double-counting)
|
||||||
private lastRecordedTokens: Map<string, { input: number; output: number }> = new Map();
|
private lastRecordedTokens: Map<string, { input: number; output: number }> = new Map();
|
||||||
private tokenRecordingTimer: NodeJS.Timeout | null = null;
|
private tokenRecordingTimer: NodeJS.Timeout | null = null;
|
||||||
@@ -684,8 +688,9 @@ export class WebServer extends EventEmitter {
|
|||||||
this.persistSessionState(session);
|
this.persistSessionState(session);
|
||||||
await this.setupSessionListeners(session);
|
await this.setupSessionListeners(session);
|
||||||
|
|
||||||
this.broadcast('session:created', session.toDetailedState());
|
const detailedState = session.toDetailedState();
|
||||||
return { success: true, session: session.toDetailedState() };
|
this.broadcast('session:created', detailedState);
|
||||||
|
return { success: true, session: detailedState };
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rename a session
|
// Rename a session
|
||||||
@@ -4371,6 +4376,11 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
|
|||||||
* to prevent browser freezes. Terminal buffers are fetched on-demand.
|
* to prevent browser freezes. Terminal buffers are fetched on-demand.
|
||||||
*/
|
*/
|
||||||
private getLightState() {
|
private getLightState() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (this.cachedLightState && now - this.cachedLightState.timestamp < WebServer.LIGHT_STATE_CACHE_TTL_MS) {
|
||||||
|
return this.cachedLightState.data;
|
||||||
|
}
|
||||||
|
|
||||||
const respawnStatus: Record<string, ReturnType<RespawnController['getStatus']>> = {};
|
const respawnStatus: Record<string, ReturnType<RespawnController['getStatus']>> = {};
|
||||||
for (const [sessionId, controller] of this.respawnControllers) {
|
for (const [sessionId, controller] of this.respawnControllers) {
|
||||||
respawnStatus[sessionId] = controller.getStatus();
|
respawnStatus[sessionId] = controller.getStatus();
|
||||||
@@ -4385,15 +4395,18 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const result = {
|
||||||
version: APP_VERSION,
|
version: APP_VERSION,
|
||||||
sessions: this.getLightSessionsState(),
|
sessions: this.getLightSessionsState(),
|
||||||
scheduledRuns: Array.from(this.scheduledRuns.values()),
|
scheduledRuns: Array.from(this.scheduledRuns.values()),
|
||||||
respawnStatus,
|
respawnStatus,
|
||||||
globalStats: this.store.getAggregateStats(activeSessionTokens),
|
globalStats: this.store.getAggregateStats(activeSessionTokens),
|
||||||
subagents: subagentWatcher.getRecentSubagents(15), // 15 min to avoid stale agents
|
subagents: subagentWatcher.getRecentSubagents(15), // 15 min to avoid stale agents
|
||||||
timestamp: Date.now(),
|
timestamp: now,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.cachedLightState = { data: result, timestamp: now };
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendSSE(reply: FastifyReply, event: string, data: unknown): void {
|
private sendSSE(reply: FastifyReply, event: string, data: unknown): void {
|
||||||
@@ -4426,6 +4439,10 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private broadcast(event: string, data: unknown): void {
|
private broadcast(event: string, data: unknown): void {
|
||||||
|
// Invalidate light state cache on any state-changing broadcast
|
||||||
|
if (event.startsWith('session:') || event === 'respawn:') {
|
||||||
|
this.cachedLightState = null;
|
||||||
|
}
|
||||||
// Performance optimization: serialize JSON once for all clients
|
// Performance optimization: serialize JSON once for all clients
|
||||||
let message: string;
|
let message: string;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user