Make ensureWorkerRunning async with health checks

Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-05 02:38:47 +00:00
parent f8695516a4
commit c506390007
9 changed files with 149 additions and 104 deletions
+8 -7
View File
@@ -126,9 +126,9 @@ function getObservations(db: SessionStore, sessionIds: string[]): Observation[]
/**
* Context Hook Main Logic
*/
function contextHook(input?: SessionStartInput, useColors: boolean = false, useIndexView: boolean = false): string {
async function contextHook(input?: SessionStartInput, useColors: boolean = false, useIndexView: boolean = false): Promise<string> {
// Ensure worker is running
ensureWorkerRunning();
await ensureWorkerRunning();
const cwd = input?.cwd ?? process.cwd();
const project = cwd ? path.basename(cwd) : 'unknown-project';
@@ -424,16 +424,17 @@ const forceColors = process.argv.includes('--colors'); // Add this line
if (stdin.isTTY || forceColors) { // Modify this line to include forceColors
// Running manually from terminal - print formatted output with colors
const contextOutput = contextHook(undefined, true, useIndexView);
console.log(contextOutput);
process.exit(0);
contextHook(undefined, true, useIndexView).then(contextOutput => {
console.log(contextOutput);
process.exit(0);
});
} else {
// Running from hook - wrap in hookSpecificOutput JSON format
let input = '';
stdin.on('data', (chunk) => input += chunk);
stdin.on('end', () => {
stdin.on('end', async () => {
const parsed = input.trim() ? JSON.parse(input) : undefined;
const contextOutput = contextHook(parsed, false, useIndexView);
const contextOutput = await contextHook(parsed, false, useIndexView);
const result = {
hookSpecificOutput: {
hookEventName: "SessionStart",
+1 -1
View File
@@ -28,7 +28,7 @@ async function newHook(input?: UserPromptSubmitInput): Promise<void> {
const project = path.basename(cwd);
// Ensure worker is running
ensureWorkerRunning();
await ensureWorkerRunning();
const db = new SessionStore();
+1 -1
View File
@@ -39,7 +39,7 @@ async function saveHook(input?: PostToolUseInput): Promise<void> {
}
// Ensure worker is running
ensureWorkerRunning();
await ensureWorkerRunning();
const db = new SessionStore();
+1 -1
View File
@@ -26,7 +26,7 @@ async function summaryHook(input?: StopInput): Promise<void> {
const { session_id } = input;
// Ensure worker is running
ensureWorkerRunning();
await ensureWorkerRunning();
const db = new SessionStore();
+63 -19
View File
@@ -4,17 +4,53 @@ import { getPackageRoot } from "./paths.js";
const FIXED_PORT = parseInt(process.env.CLAUDE_MEM_WORKER_PORT || "37777", 10);
/**
* Check if worker is responsive by trying the health endpoint
*/
async function isWorkerHealthy(timeoutMs: number = 3000): Promise<boolean> {
try {
const response = await fetch(`http://127.0.0.1:${FIXED_PORT}/health`, {
signal: AbortSignal.timeout(timeoutMs)
});
return response.ok;
} catch {
return false;
}
}
/**
* Wait for worker to become healthy
*/
async function waitForWorkerHealth(maxWaitMs: number = 10000): Promise<boolean> {
const start = Date.now();
const checkInterval = 100; // Check every 100ms
while (Date.now() - start < maxWaitMs) {
if (await isWorkerHealthy(1000)) {
return true;
}
// Wait before next check
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
return false;
}
/**
* Ensure worker service is running
* Checks if worker is already running before attempting to start
* This prevents unnecessary restarts that could interrupt mid-action processing
*/
export function ensureWorkerRunning(): void {
export async function ensureWorkerRunning(): Promise<void> {
// First, check if worker is already healthy
if (await isWorkerHealthy(1000)) {
return; // Worker is already running and responsive
}
const packageRoot = getPackageRoot();
const pm2Path = path.join(packageRoot, "node_modules", ".bin", "pm2");
const ecosystemPath = path.join(packageRoot, "ecosystem.config.cjs");
// Check if worker is already running
// Check PM2 status to see if worker process exists
const checkProcess = spawn(pm2Path, ["list", "--no-color"], {
cwd: packageRoot,
stdio: ["ignore", "pipe", "ignore"],
@@ -25,24 +61,32 @@ export function ensureWorkerRunning(): void {
output += data.toString();
});
checkProcess.on("close", (code) => {
// Check if 'claude-mem-worker' is in the PM2 list output and is 'online'
const isRunning = output.includes("claude-mem-worker") && output.includes("online");
if (!isRunning) {
// Only start if not already running
spawn(pm2Path, ["start", ecosystemPath], {
cwd: packageRoot,
stdio: "ignore",
});
// Simple wait - no complex health checks needed
const start = Date.now();
while (Date.now() - start < 200) {
// Busy wait
}
}
// Wait for PM2 list to complete
await new Promise<void>((resolve) => {
checkProcess.on("close", () => resolve());
});
// Check if 'claude-mem-worker' is in the PM2 list output and is 'online'
const isRunning = output.includes("claude-mem-worker") && output.includes("online");
if (!isRunning) {
// Start the worker
const startProcess = spawn(pm2Path, ["start", ecosystemPath], {
cwd: packageRoot,
stdio: "ignore",
});
// Wait for PM2 start command to complete
await new Promise<void>((resolve) => {
startProcess.on("close", () => resolve());
});
}
// Wait for worker to become healthy (either just started or was starting)
const healthy = await waitForWorkerHealth(10000);
if (!healthy) {
throw new Error("Worker failed to become healthy after starting");
}
}
/**