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:
Tam Nhu Tran
2026-02-12 08:28:32 +07:00
parent 934238740e
commit 760a5c3ca4
4 changed files with 38 additions and 3 deletions
+1 -1
View File
@@ -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;
}
}
+12
View File
@@ -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 -2
View File
@@ -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';
+23
View File
@@ -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', () => {