feat(FN-3396): bundle Cursor CLI as a plugin provider with dashboard auth w

Merges FN-3396's full Cursor CLI provider integration (Steps 1–4): defines a CLI-backed provider contract, adds the `fusion-plugin-cursor-runtime` plugin package with process management and runtime probes, wires dashboard auth flows and UI (ProviderCard, onboarding modal, settings), and bundles the

Fusion-Task-Id: FN-3396
This commit is contained in:
Fusion
2026-05-07 04:10:15 -07:00
committed by gsxdsm
parent 2eba5a007c
commit dca07892ff
50 changed files with 1292 additions and 3 deletions

View File

@@ -0,0 +1,46 @@
import { runCursorCommand } from "./cli-spawn.js";
function parseModelLines(raw: string): string[] {
return raw
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
.filter((line) => !line.toLowerCase().startsWith("usage"));
}
export async function discoverCursorModels(binary: string, timeoutMs = 5000): Promise<{ models: string[]; source: string; fallbackUsed: boolean; reason?: string }> {
const attempts: Array<{ args: string[]; source: string; structured: boolean }> = [
{ args: ["models", "--json"], source: "models-json", structured: true },
{ args: ["model", "list", "--json"], source: "model-list-json", structured: true },
{ args: ["models"], source: "models-text", structured: false },
];
for (const attempt of attempts) {
const res = await runCursorCommand(binary, attempt.args, timeoutMs);
if (res.code !== 0) continue;
const output = (res.stdout || "").trim();
if (!output) continue;
try {
const parsed = JSON.parse(output);
if (Array.isArray(parsed)) {
const ids = parsed
.map((entry) => (typeof entry === "string" ? entry : typeof entry?.id === "string" ? entry.id : undefined))
.filter((id): id is string => Boolean(id));
if (ids.length > 0) {
return { models: Array.from(new Set(ids)), source: attempt.source, fallbackUsed: !attempt.structured };
}
}
} catch {
// output is not JSON; continue with line-based fallback
}
const ids = Array.from(new Set(parseModelLines(output)));
if (ids.length > 0) {
return { models: ids, source: attempt.source, fallbackUsed: !attempt.structured };
}
}
return { models: [], source: "none", fallbackUsed: true, reason: "model discovery command unavailable" };
}