mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 14:16:43 +00:00
fix(cursor): harden stopDaemon PID validation, tighten regex, add lifecycle test
- Validate PID belongs to cursor daemon via /proc before signaling - Tighten detectProvider regex to avoid over-matching o-prefixed models - Add integration test for daemon start→health→stop lifecycle - Add void cast on discarded handleHelp() return value - Update model catalog date comment
This commit is contained in:
@@ -60,7 +60,7 @@ export async function handleCursorCommand(args: string[]): Promise<number> {
|
||||
default:
|
||||
console.error(fail(`Unknown subcommand: ${subcommand}`));
|
||||
console.error('');
|
||||
handleHelp();
|
||||
void handleHelp(); // Print help but keep exit code 1
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,18 @@ export async function stopDaemon(): Promise<{ success: boolean; error?: string }
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify the PID belongs to our daemon before signaling
|
||||
try {
|
||||
const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf8');
|
||||
if (!cmdline.includes('cursor') && !cmdline.includes('/health')) {
|
||||
// PID was reused by an unrelated process
|
||||
removePidFile();
|
||||
return { success: true };
|
||||
}
|
||||
} catch {
|
||||
// /proc not available (macOS/Windows) or process gone — proceed with kill
|
||||
}
|
||||
|
||||
// Send SIGTERM to the process
|
||||
process.kill(pid, 'SIGTERM');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Cursor Model Catalog
|
||||
*
|
||||
* Manages available models from Cursor IDE.
|
||||
* Based on Cursor's supported models as of Feb 2025.
|
||||
* Based on Cursor's supported models catalog.
|
||||
*/
|
||||
|
||||
import * as http from 'http';
|
||||
@@ -165,7 +165,7 @@ export function getDefaultModel(): string {
|
||||
*/
|
||||
export function detectProvider(modelId: string): string {
|
||||
if (modelId.includes('claude')) return 'anthropic';
|
||||
if (modelId.includes('gpt') || /^o\d/.test(modelId)) return 'openai';
|
||||
if (modelId.includes('gpt') || /^o[1-9]\d*(-|$)/.test(modelId)) return 'openai';
|
||||
if (modelId.includes('gemini')) return 'google';
|
||||
if (modelId.includes('cursor')) return 'cursor';
|
||||
return 'unknown';
|
||||
|
||||
@@ -137,6 +137,29 @@ describe('startDaemon', () => {
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Invalid port');
|
||||
});
|
||||
|
||||
it(
|
||||
'starts and stops daemon successfully',
|
||||
async () => {
|
||||
const port = 18765;
|
||||
const result = await startDaemon({ port, model: 'test' });
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.pid).toBeDefined();
|
||||
|
||||
// Verify health
|
||||
const running = await isDaemonRunning(port);
|
||||
expect(running).toBe(true);
|
||||
|
||||
// Stop
|
||||
const stopResult = await stopDaemon();
|
||||
expect(stopResult.success).toBe(true);
|
||||
|
||||
// Verify stopped
|
||||
const stillRunning = await isDaemonRunning(port);
|
||||
expect(stillRunning).toBe(false);
|
||||
},
|
||||
35000
|
||||
);
|
||||
});
|
||||
|
||||
describe('isDaemonRunning', () => {
|
||||
|
||||
Reference in New Issue
Block a user