feat(agent): add pi-native backend and drop cursor-cli
Keep the container pi-agent backend as the default while the in-process runtime is available beside it, and remove the unused cursor bridge.
This commit is contained in:
@@ -1,192 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Cursor SDK bridge (stdio JSON).
|
||||
*
|
||||
* Input (stdin):
|
||||
* {
|
||||
* "workspace": "/abs/path",
|
||||
* "api_key": "...",
|
||||
* "model": "auto",
|
||||
* "prompt": "...",
|
||||
* "instructions": "...",
|
||||
* "targets": ["docs/RD-UN.md"]
|
||||
* }
|
||||
*
|
||||
* Output (stdout, last line JSON):
|
||||
* {
|
||||
* "ok": true,
|
||||
* "agent_id": "...",
|
||||
* "run_id": "...",
|
||||
* "patches": [{ "rel_path": "docs/RD-UN.md", "content": "..." }],
|
||||
* "fallback_reason": ""
|
||||
* }
|
||||
*/
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { resolve, relative, isAbsolute } from "node:path";
|
||||
import { Agent, CursorAgentError } from "@cursor/sdk";
|
||||
|
||||
async function readStdin() {
|
||||
const chunks = [];
|
||||
for await (const chunk of process.stdin) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return Buffer.concat(chunks).toString("utf8");
|
||||
}
|
||||
|
||||
function fail(reason, extra = {}) {
|
||||
process.stdout.write(
|
||||
JSON.stringify({
|
||||
ok: false,
|
||||
patches: [],
|
||||
fallback_reason: reason,
|
||||
...extra,
|
||||
}) + "\n",
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function sanitizeApiKey(raw) {
|
||||
if (!raw) return "";
|
||||
let key = String(raw).trim();
|
||||
const commentIdx = key.indexOf(" #");
|
||||
if (commentIdx !== -1) {
|
||||
key = key.slice(0, commentIdx).trim();
|
||||
}
|
||||
const tabCommentIdx = key.indexOf("\t#");
|
||||
if (tabCommentIdx !== -1) {
|
||||
key = key.slice(0, tabCommentIdx).trim();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function safeRel(workspace, target) {
|
||||
const abs = isAbsolute(target) ? target : resolve(workspace, target);
|
||||
const rel = relative(workspace, abs);
|
||||
if (!rel || rel.startsWith("..") || isAbsolute(rel)) {
|
||||
return null;
|
||||
}
|
||||
return rel.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let req;
|
||||
try {
|
||||
const raw = await readStdin();
|
||||
req = JSON.parse(raw);
|
||||
} catch (e) {
|
||||
fail("invalid_json: " + (e && e.message ? e.message : String(e)));
|
||||
return;
|
||||
}
|
||||
|
||||
const workspace = req.workspace;
|
||||
const apiKey = sanitizeApiKey(req.api_key || process.env.CURSOR_API_KEY || "");
|
||||
const model = (req.model || process.env.CURSOR_MODEL || "auto").trim();
|
||||
const prompt = req.prompt || "";
|
||||
const instructions = req.instructions || "";
|
||||
const targets = Array.isArray(req.targets) ? req.targets : [];
|
||||
|
||||
if (!workspace || !existsSync(workspace)) {
|
||||
fail("workspace_missing");
|
||||
return;
|
||||
}
|
||||
if (!apiKey) {
|
||||
fail("api_key_missing");
|
||||
return;
|
||||
}
|
||||
if (!prompt.trim()) {
|
||||
fail("prompt_empty");
|
||||
return;
|
||||
}
|
||||
|
||||
const allowed = new Set();
|
||||
for (const t of targets) {
|
||||
const rel = safeRel(workspace, t);
|
||||
if (rel) allowed.add(rel);
|
||||
}
|
||||
if (allowed.size === 0) {
|
||||
fail("no_targets");
|
||||
return;
|
||||
}
|
||||
|
||||
const fullPrompt = instructions ? `${instructions}\n\n---\n\n${prompt}` : prompt;
|
||||
|
||||
let agentId = "";
|
||||
let runId = "";
|
||||
try {
|
||||
const result = await Agent.prompt(fullPrompt, {
|
||||
apiKey,
|
||||
model: { id: model },
|
||||
local: {
|
||||
cwd: workspace,
|
||||
settingSources: [],
|
||||
},
|
||||
});
|
||||
agentId = result?.agentId || result?.agent_id || "";
|
||||
runId = result?.id || result?.runId || result?.run_id || "";
|
||||
if (result?.status === "error") {
|
||||
fail("run_error", { agent_id: agentId, run_id: runId });
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
const rawMsg =
|
||||
e instanceof CursorAgentError
|
||||
? e.message
|
||||
: e && e.message
|
||||
? e.message
|
||||
: String(e);
|
||||
let category = "startup_or_run";
|
||||
const lower = rawMsg.toLowerCase();
|
||||
if (
|
||||
lower.includes("invalid user api key") ||
|
||||
lower.includes("unauthorized") ||
|
||||
lower.includes("401") ||
|
||||
lower.includes("authentication")
|
||||
) {
|
||||
category = "cursor_auth_failed";
|
||||
} else if (
|
||||
lower.includes("timeout") ||
|
||||
lower.includes("timed out") ||
|
||||
lower.includes("deadline")
|
||||
) {
|
||||
category = "cursor_timeout";
|
||||
} else if (
|
||||
lower.includes("enotfound") ||
|
||||
lower.includes("econnrefused") ||
|
||||
lower.includes("fetch failed") ||
|
||||
lower.includes("network")
|
||||
) {
|
||||
category = "cursor_network_error";
|
||||
} else if (lower.includes("cannot use this model") || lower.includes("available models")) {
|
||||
category = "cursor_model_unsupported";
|
||||
}
|
||||
fail(category + ": " + rawMsg, { agent_id: agentId, run_id: runId });
|
||||
return;
|
||||
}
|
||||
|
||||
const patches = [];
|
||||
for (const rel of allowed) {
|
||||
const abs = resolve(workspace, rel);
|
||||
if (!existsSync(abs)) continue;
|
||||
const content = readFileSync(abs, "utf8");
|
||||
patches.push({ rel_path: rel, content });
|
||||
}
|
||||
|
||||
if (patches.length === 0) {
|
||||
fail("empty_patches", { agent_id: agentId, run_id: runId });
|
||||
return;
|
||||
}
|
||||
|
||||
process.stdout.write(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
agent_id: agentId,
|
||||
run_id: runId,
|
||||
patches,
|
||||
fallback_reason: "",
|
||||
}) + "\n",
|
||||
);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
fail("unhandled: " + (e && e.message ? e.message : String(e)));
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "cursor-bridge",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "stdio JSON bridge: Cursor local Agent drafts document patches for agent-system",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"cursor-bridge": "./index.mjs"
|
||||
},
|
||||
"main": "index.mjs",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cursor/sdk": "1.0.31"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user