Compare commits

..

3 Commits

Author SHA1 Message Date
catlog22
d0ac3a5cd2 fix(csrf): prevent undefined token when session at max capacity
Root cause: generateToken() returned undefined when session already
had maxTokensPerSession (5) tokens, causing ERR_HTTP_INVALID_HEADER_VALUE.

Fix: Force generate token even when at capacity, ensuring we always
return a valid token string.

Related: v7.1.1 CLI process hang fix
2026-03-02 09:58:54 +08:00
catlog22
0939510e0d fix(lite-plan): handle empty exploration context when prior analysis exists
When analyze-with-file artifacts are passed to lite-plan, exploration is
skipped (hasPriorAnalysis=true) leaving manifest.explorations empty. The
agent prompt's exploration section rendered as blank, causing the planning
agent to lack context and fail to produce task JSON. Add conditional to
output a fallback hint directing the agent to use the Prior Analysis block
in the task description as primary planning context.
2026-03-02 00:04:32 +08:00
catlog22
deea92581b fix(cli): resolve process hang after CLI execution
Root cause: HTTP Keep-Alive connections kept event loop alive,
preventing process.exit() from executing even after CLI_EXECUTION_COMPLETED
event was sent.

Fix: Add `agent: false` and `Connection: close` header to HTTP requests
in notifyDashboard() and broadcastStreamEvent() functions.

- agent: false - Creates new Agent per request instead of global Keep-Alive Agent
- Connection: close - Tells server to close connection after response

Fixes issue where `ccw cli --tool gemini` would complete execution but
Bash command would hang indefinitely.
2026-03-01 23:34:31 +08:00
4 changed files with 32 additions and 6 deletions

View File

@@ -529,15 +529,17 @@ ${task_description}
## Multi-Angle Exploration Context
${manifest.explorations.map(exp => `### Exploration: ${exp.angle} (${exp.file})
${manifest.explorations.length > 0
? manifest.explorations.map(exp => `### Exploration: ${exp.angle} (${exp.file})
Path: ${exp.path}
Read this file for detailed ${exp.angle} analysis.`).join('\n\n')}
Read this file for detailed ${exp.angle} analysis.`).join('\n\n') + `
Total explorations: ${manifest.exploration_count}
Angles covered: ${manifest.explorations.map(e => e.angle).join(', ')}
Manifest: ${sessionFolder}/explorations-manifest.json
Manifest: ${sessionFolder}/explorations-manifest.json`
: `No exploration files. Task Description above contains "## Prior Analysis" with analysis summary, key files, and findings — use it as primary planning context.`}
## User Clarifications
${JSON.stringify(clarificationContext) || "None"}

View File

@@ -51,9 +51,11 @@ function notifyDashboard(data: Record<string, unknown>): void {
path: '/api/hook',
method: 'POST',
timeout: 2000, // 2 second timeout to prevent hanging
agent: false, // Disable Keep-Alive to allow process exit
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
'Content-Length': Buffer.byteLength(payload),
'Connection': 'close' // Ensure connection closes after response
}
});
@@ -93,9 +95,11 @@ function broadcastStreamEvent(eventType: string, payload: Record<string, unknown
path: '/api/hook',
method: 'POST',
timeout: 1000, // Short timeout for streaming
agent: false, // Disable Keep-Alive to allow process exit
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
'Content-Length': Buffer.byteLength(data),
'Connection': 'close' // Ensure connection closes after response
}
});

View File

@@ -56,6 +56,26 @@ export class CsrfTokenManager {
*/
generateToken(sessionId: string): string {
const tokens = this.generateTokens(sessionId, 1);
// If no slots available (session at max capacity), force generate anyway
// This ensures we always return a valid token
if (tokens.length === 0) {
const token = randomBytes(32).toString('hex');
const expiresAtMs = Date.now() + this.tokenTtlMs;
const record: CsrfTokenRecord = {
sessionId,
expiresAtMs,
used: false,
};
// Get or create session map
let sessionMap = this.sessionTokens.get(sessionId);
if (!sessionMap) {
sessionMap = new Map();
this.sessionTokens.set(sessionId, sessionMap);
}
sessionMap.set(token, record);
this.tokenToSession.set(token, sessionId);
return token;
}
return tokens[0];
}

View File

@@ -1,6 +1,6 @@
{
"name": "claude-code-workflow",
"version": "7.1.0",
"version": "7.1.1",
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
"type": "module",
"main": "ccw/dist/index.js",