Fix Cursor runtime probes to execute Windows cmd and bat shims reliably while preserving diagnostics. - Run shared Cursor CLI probe and discovery spawns through the shell only on Windows. - Surface bounded spawn error details in unavailable Cursor probe reasons. - Cover Windows shell spawning, failure diagnostics, probe, and process-manager behavior with tests. - Document the Windows PATH shim invocation contract and add a patch changeset. Files changed: .changeset/fn-7418-cursor-cli-windows-cmd.md | 7 ++ docs/cursor-cli-contract.md | 17 ++++ .../src/__tests__/cli-spawn.test.ts | 103 +++++++++++++++++++++ .../src/__tests__/probe.test.ts | 44 ++++++++- .../src/__tests__/process-manager.test.ts | 24 ++++- .../fusion-plugin-cursor-runtime/src/cli-spawn.ts | 41 ++++++-- plugins/fusion-plugin-cursor-runtime/src/probe.ts | 14 ++- 7 files changed, 236 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-7418 Fusion-Task-Lineage: d689bea7-4676-4190-a60a-f85efab90aba Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
|
|
function formatSpawnError(error: Error & { code?: unknown }): string {
|
|
const code = typeof error.code === "string" ? `${error.code}: ` : "";
|
|
return `spawn error: ${code}${error.message}`.trim();
|
|
}
|
|
|
|
export async function runCursorCommand(binary: string, args: string[], timeoutMs: number): Promise<{ code: number | null; stdout: string; stderr: string }> {
|
|
return new Promise((resolve) => {
|
|
let stdout = "";
|
|
let stderr = "";
|
|
let settled = false;
|
|
let timer: NodeJS.Timeout | undefined;
|
|
|
|
const finish = (result: { code: number | null; stdout: string; stderr: string }) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
if (timer) clearTimeout(timer);
|
|
resolve(result);
|
|
};
|
|
|
|
/*
|
|
FNXC:CursorCli 2026-07-02-00:00:
|
|
Windows Cursor installers and npm-style shims can expose `cursor-agent.cmd` or `cursor.cmd` on PATH, and Node cannot direct-spawn those batch wrappers without the command shell.
|
|
Keep Unix/macOS on direct spawn so only the known Cursor CLI probe/discovery seam uses shell resolution where Windows requires it.
|
|
*/
|
|
const child = spawn(binary, args, {
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
shell: process.platform === "win32",
|
|
});
|
|
|
|
timer = setTimeout(() => {
|
|
try { child.kill("SIGKILL"); } catch {
|
|
// best effort
|
|
}
|
|
finish({ code: 124, stdout, stderr });
|
|
}, timeoutMs);
|
|
|
|
child.stdout?.on("data", (c: Buffer) => { stdout += c.toString("utf-8"); });
|
|
child.stderr?.on("data", (c: Buffer) => { stderr += c.toString("utf-8"); });
|
|
child.once("error", (error: Error & { code?: unknown }) => {
|
|
const diagnostic = formatSpawnError(error);
|
|
stderr = stderr ? `${stderr}\n${diagnostic}` : diagnostic;
|
|
finish({ code: 127, stdout, stderr });
|
|
});
|
|
child.once("close", (code) => {
|
|
finish({ code, stdout, stderr });
|
|
});
|
|
});
|
|
}
|