mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-21 06:26:04 +00:00
fix(cursor): clean up PID file on startDaemon failure and improve daemon robustness
- Add removePidFile() in safeResolve on failure to prevent stale PIDs - Only send SIGKILL in stopDaemon if SIGTERM wait loop exhausted - Check isDaemonRunning before model fetch to avoid 5s timeout - Add port validation unit tests for startDaemon
This commit is contained in:
@@ -150,6 +150,7 @@ export async function startDaemon(
|
|||||||
if (resolved) return;
|
if (resolved) return;
|
||||||
resolved = true;
|
resolved = true;
|
||||||
if (checkTimeout) clearTimeout(checkTimeout);
|
if (checkTimeout) clearTimeout(checkTimeout);
|
||||||
|
if (!result.success) removePidFile();
|
||||||
resolve(result);
|
resolve(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -280,11 +281,13 @@ export async function stopDaemon(): Promise<{ success: boolean; error?: string }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escalate to SIGKILL if process still alive after SIGTERM attempts
|
// Escalate to SIGKILL only if SIGTERM wait loop exhausted
|
||||||
try {
|
if (attempts >= 10) {
|
||||||
process.kill(pid, 'SIGKILL');
|
try {
|
||||||
} catch {
|
process.kill(pid, 'SIGKILL');
|
||||||
// Already dead — good
|
} catch {
|
||||||
|
// Already dead — good
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removePidFile();
|
removePidFile();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import type { CursorModel } from './types';
|
import type { CursorModel } from './types';
|
||||||
|
import { isDaemonRunning } from './cursor-daemon';
|
||||||
|
|
||||||
/** Default daemon port */
|
/** Default daemon port */
|
||||||
export const DEFAULT_CURSOR_PORT = 4242;
|
export const DEFAULT_CURSOR_PORT = 4242;
|
||||||
@@ -142,8 +143,12 @@ export async function fetchModelsFromDaemon(port: number): Promise<CursorModel[]
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get available models (from daemon or defaults).
|
* Get available models (from daemon or defaults).
|
||||||
|
* Checks daemon health first to avoid 5s timeout when daemon is not running.
|
||||||
*/
|
*/
|
||||||
export async function getAvailableModels(port: number): Promise<CursorModel[]> {
|
export async function getAvailableModels(port: number): Promise<CursorModel[]> {
|
||||||
|
if (!(await isDaemonRunning(port))) {
|
||||||
|
return DEFAULT_CURSOR_MODELS;
|
||||||
|
}
|
||||||
return fetchModelsFromDaemon(port);
|
return fetchModelsFromDaemon(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
isDaemonRunning,
|
isDaemonRunning,
|
||||||
getDaemonStatus,
|
getDaemonStatus,
|
||||||
stopDaemon,
|
stopDaemon,
|
||||||
|
startDaemon,
|
||||||
} from '../../../src/cursor/cursor-daemon';
|
} from '../../../src/cursor/cursor-daemon';
|
||||||
|
|
||||||
// Test isolation
|
// Test isolation
|
||||||
@@ -116,6 +117,26 @@ describe('removePidFile', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('startDaemon', () => {
|
||||||
|
it('rejects invalid port (0)', async () => {
|
||||||
|
const result = await startDaemon({ port: 0, model: 'test' });
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.error).toContain('Invalid port');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid port (65536)', async () => {
|
||||||
|
const result = await startDaemon({ port: 65536, model: 'test' });
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.error).toContain('Invalid port');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects non-integer port', async () => {
|
||||||
|
const result = await startDaemon({ port: 3.14, model: 'test' });
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.error).toContain('Invalid port');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('isDaemonRunning', () => {
|
describe('isDaemonRunning', () => {
|
||||||
it('returns false when no daemon is running on port', async () => {
|
it('returns false when no daemon is running on port', async () => {
|
||||||
// Use a port that should not have anything running
|
// Use a port that should not have anything running
|
||||||
|
|||||||
Reference in New Issue
Block a user