3ea180c1ef
This commit simplifies worker startup coordination and addresses Windows-specific issues: **Lock Removal**: - Removed entire file-based locking system (~100 lines) - Replaced with health-check-first approach - Port binding provides natural mutual exclusion - multiple spawns fail cleanly **Windows Stability**: - Removed all AbortSignal.timeout() calls to reduce Bun libuv assertion errors - Added 500ms shutdown delays on Windows to prevent zombie ports - Worker service has its own timeouts, so client-side timeouts are redundant **Package.json Updates**: - Updated worker scripts to use worker-service.cjs directly - Removed references to deleted worker-cli.js and worker-wrapper.cjs **Key Changes**: - src/services/worker-service.ts: Lock removal, shutdown delays, simplified start logic - src/hooks/*.ts: Removed AbortSignal.timeout from all HTTP requests - src/shared/worker-utils.ts: Removed AbortSignal.timeout from health checks - package.json: Updated worker:* scripts Resolves startup hangs, reduces assertion errors, and prevents zombie port issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
import { stdin } from 'process';
|
|
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
|
|
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
|
import { getProjectName } from '../utils/project-name.js';
|
|
import { logger } from '../utils/logger.js';
|
|
|
|
export interface UserPromptSubmitInput {
|
|
session_id: string;
|
|
cwd: string;
|
|
prompt: string;
|
|
}
|
|
|
|
|
|
/**
|
|
* New Hook Main Logic
|
|
*/
|
|
async function newHook(input?: UserPromptSubmitInput): Promise<void> {
|
|
// Ensure worker is running before any other logic
|
|
await ensureWorkerRunning();
|
|
|
|
if (!input) {
|
|
throw new Error('newHook requires input');
|
|
}
|
|
|
|
const { session_id, cwd, prompt } = input;
|
|
const project = getProjectName(cwd);
|
|
|
|
logger.info('HOOK', 'new-hook: Received hook input', { session_id, has_prompt: !!prompt, cwd });
|
|
|
|
const port = getWorkerPort();
|
|
|
|
logger.info('HOOK', 'new-hook: Calling /api/sessions/init', { contentSessionId: session_id, project, prompt_length: prompt?.length });
|
|
|
|
// Initialize session via HTTP - handles DB operations and privacy checks
|
|
const initResponse = await fetch(`http://127.0.0.1:${port}/api/sessions/init`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
contentSessionId: session_id,
|
|
project,
|
|
prompt
|
|
})
|
|
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
|
});
|
|
|
|
if (!initResponse.ok) {
|
|
throw new Error(`Session initialization failed: ${initResponse.status}`);
|
|
}
|
|
|
|
const initResult = await initResponse.json();
|
|
const sessionDbId = initResult.sessionDbId;
|
|
const promptNumber = initResult.promptNumber;
|
|
|
|
logger.info('HOOK', 'new-hook: Received from /api/sessions/init', { sessionDbId, promptNumber, skipped: initResult.skipped });
|
|
|
|
// Check if prompt was entirely private (worker performs privacy check)
|
|
if (initResult.skipped && initResult.reason === 'private') {
|
|
logger.info('HOOK', `new-hook: Session ${sessionDbId}, prompt #${promptNumber} (fully private - skipped)`);
|
|
console.log(STANDARD_HOOK_RESPONSE);
|
|
return;
|
|
}
|
|
|
|
logger.info('HOOK', `new-hook: Session ${sessionDbId}, prompt #${promptNumber}`);
|
|
|
|
// Strip leading slash from commands for memory agent
|
|
// /review 101 → review 101 (more semantic for observations)
|
|
const cleanedPrompt = prompt.startsWith('/') ? prompt.substring(1) : prompt;
|
|
|
|
logger.info('HOOK', 'new-hook: Calling /sessions/{sessionDbId}/init', { sessionDbId, promptNumber, userPrompt_length: cleanedPrompt?.length });
|
|
|
|
// Initialize SDK agent session via HTTP (starts the agent!)
|
|
const response = await fetch(`http://127.0.0.1:${port}/sessions/${sessionDbId}/init`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ userPrompt: cleanedPrompt, promptNumber })
|
|
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`SDK agent start failed: ${response.status}`);
|
|
}
|
|
|
|
console.log(STANDARD_HOOK_RESPONSE);
|
|
}
|
|
|
|
// Entry Point
|
|
let input = '';
|
|
stdin.on('data', (chunk) => input += chunk);
|
|
stdin.on('end', async () => {
|
|
let parsed: UserPromptSubmitInput | undefined;
|
|
try {
|
|
parsed = input ? JSON.parse(input) : undefined;
|
|
} catch (error) {
|
|
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
|
|
}
|
|
await newHook(parsed);
|
|
});
|