[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,559 @@
"use strict";
/**
* Cursor backend driver — wraps @cursor/sdk.
*
* Cursor SDK local agents use Agent.create({ apiKey, model, local:{cwd},
* mcpServers }) and stream SDKMessage events from run.stream().
*/
const { mcpEnvPairsToObject } = require("./injectMcp.cjs");
const DEFAULT_CURSOR_MODEL = "composer-2.5";
function toCursorMcpServers(injectedMcpServers) {
const servers = {};
for (const cfg of injectedMcpServers || []) {
if (!cfg || !cfg.name || !cfg.command) continue;
servers[cfg.name] = {
type: "stdio",
command: cfg.command,
args: cfg.args || [],
env: mcpEnvPairsToObject(cfg.env),
};
}
return servers;
}
const CURSOR_REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh"]);
const CURSOR_FALLBACK_THINKING = {
"gpt-5.5": ["low", "medium", "high"],
"gpt-5.2": ["low", "medium", "high"],
"gpt-5.1": ["low", "medium", "high"],
"gpt-5": ["low", "medium", "high"],
"claude-opus-4.6": ["low", "medium", "high"],
"claude-sonnet-4.6": ["low", "medium", "high"],
};
function parseCursorModelSelection(model) {
const raw = String(model || DEFAULT_CURSOR_MODEL).trim() || DEFAULT_CURSOR_MODEL;
const queryIndex = raw.indexOf("?");
if (queryIndex >= 0) {
const id = raw.slice(0, queryIndex);
const search = new URLSearchParams(raw.slice(queryIndex + 1));
const params = [];
for (const [paramId, value] of search.entries()) {
if (paramId && value) params.push({ id: paramId, value });
}
return params.length > 0 ? { id, params } : { id };
}
const slash = raw.lastIndexOf("/");
if (slash > 0) {
const effort = raw.slice(slash + 1).toLowerCase();
if (CURSOR_REASONING_EFFORTS.has(effort)) {
return { id: raw.slice(0, slash), params: [{ id: "effort", value: effort }] };
}
}
return { id: raw };
}
function encodeCursorCliModel(model) {
const raw = String(model || "").trim();
if (!raw) return "";
const selection = parseCursorModelSelection(raw);
if (!selection.params?.length) return selection.id || "";
const search = new URLSearchParams();
for (const param of selection.params) {
if (param?.id && param?.value) search.set(param.id, param.value);
}
const qs = search.toString();
return qs ? `${selection.id}?${qs}` : (selection.id || "");
}
function buildCursorAgentOptions({ apiKey, env, model, cwd, injectedMcpServers }) {
const effectiveApiKey = apiKey || env?.CURSOR_API_KEY || process.env.CURSOR_API_KEY;
const options = {
apiKey: effectiveApiKey,
model: parseCursorModelSelection(model),
local: {
cwd: cwd || process.cwd(),
autoReview: false,
},
};
const mcpServers = toCursorMcpServers(injectedMcpServers);
if (Object.keys(mcpServers).length > 0) options.mcpServers = mcpServers;
return options;
}
function applyTemporaryProcessEnv(env) {
if (!env || typeof env !== "object") return () => {};
const previous = new Map();
for (const [key, value] of Object.entries(env)) {
if (typeof value !== "string") continue;
previous.set(key, Object.prototype.hasOwnProperty.call(process.env, key) ? process.env[key] : undefined);
process.env[key] = value;
}
return () => {
for (const [key, value] of previous.entries()) {
if (value === undefined) delete process.env[key];
else process.env[key] = value;
}
};
}
async function withTemporaryProcessEnv(env, fn) {
const restore = applyTemporaryProcessEnv(env);
try {
return await fn();
} finally {
restore();
}
}
function buildCursorSendMessage(prompt, attachments) {
const images = [];
for (const attachment of Array.isArray(attachments) ? attachments : []) {
if (!attachment?.base64Data || !attachment?.mediaType) continue;
if (!String(attachment.mediaType).toLowerCase().startsWith("image/")) continue;
images.push({ data: attachment.base64Data, mimeType: attachment.mediaType });
}
if (images.length === 0) return String(prompt || "");
return { text: String(prompt || ""), images };
}
function resultToText(result) {
if (result == null) return "";
if (typeof result === "string") return result;
if (typeof result === "number" || typeof result === "boolean") return String(result);
const content = result.content;
if (Array.isArray(content)) {
return content
.map((block) => {
if (!block) return "";
if (typeof block.text === "string") return block.text;
if (block.type === "image") return "[image]";
return JSON.stringify(block);
})
.join("");
}
return JSON.stringify(result);
}
function redactCursorSecret(value) {
return String(value || "")
.replace(/crsr[_-]?[A-Za-z0-9_-]{8,}/g, "[redacted-cursor-key]")
.replace(/Bearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [redacted-token]");
}
function cursorErrorDiagnostics(error) {
if (!error || typeof error !== "object") {
return { message: redactCursorSecret(error) };
}
return {
name: error.name || null,
message: redactCursorSecret(error.message || String(error)),
code: error.code || null,
status: error.status || null,
operation: error.operation || null,
endpoint: error.endpoint || null,
requestId: error.requestId || null,
isRetryable: typeof error.isRetryable === "boolean" ? error.isRetryable : null,
cause: error.cause && typeof error.cause === "object"
? {
name: error.cause.name || null,
message: redactCursorSecret(error.cause.message || String(error.cause)),
}
: null,
};
}
function isCursorAuthMessage(message) {
return /api.?key|auth|unauthorized|unauthenticated/i.test(String(message || ""));
}
async function logCursorApiKeyValidation(resolvedModule, apiKey) {
if (!apiKey || typeof resolvedModule?.Cursor?.me !== "function") return;
try {
const user = await resolvedModule.Cursor.me({ apiKey });
console.info("[Cursor SDK] API key validation ok", {
hasUserId: user?.userId != null,
hasEmail: Boolean(user?.email),
createdAt: user?.createdAt || null,
});
} catch (error) {
console.warn("[Cursor SDK] API key validation failed", cursorErrorDiagnostics(error));
}
}
function closeReasoning(state, emitter) {
if (state?.reasoningOpen) {
emitter.reasoningEnd();
state.reasoningOpen = false;
}
}
function emitCursorToolCallOnce(event, emitter, state, toolName, args, id) {
if (!id) return false;
if (!state.emittedToolCalls) state.emittedToolCalls = new Set();
if (state.emittedToolCalls.has(id)) return false;
state.emittedToolCalls.add(id);
emitter.toolCall(toolName || "tool", args && typeof args === "object" ? args : {}, id);
return true;
}
function emitCursorToolResultOnce(event, emitter, state, id, result, toolName) {
if (!id) return false;
if (!state.emittedToolResults) state.emittedToolResults = new Set();
if (state.emittedToolResults.has(id)) return false;
state.emittedToolResults.add(id);
emitter.toolResult(id, resultToText(result), toolName);
return true;
}
function getCursorDisplayToolName(rawName, args) {
const name = String(rawName || "").trim();
const input = args && typeof args === "object" ? args : {};
const nestedToolName = typeof input.toolName === "string" ? input.toolName.trim() : "";
if ((name === "mcp" || name === "tool" || !name) && nestedToolName) {
return nestedToolName;
}
return name || nestedToolName || "tool";
}
function formatCursorErrorForUser(message) {
const text = String(message || "").trim();
if (/api.?key|auth|unauthorized/i.test(text)) {
return "Cursor authentication failed. Update the Cursor API Key in Settings -> AI.";
}
return text || "Cursor turn failed";
}
function isCursorAgentNotFoundError(error) {
const message = String(error?.message || error || "");
return /\bAgent\b.+\bnot found\b/i.test(message);
}
function translateCursorEvent(event, emitter, state = {}) {
if (!event || typeof event !== "object") return;
switch (event.type) {
case "thinking":
if (event.text) {
emitter.reasoning(String(event.text));
state.reasoningOpen = true;
}
return;
case "assistant": {
closeReasoning(state, emitter);
const content = event.message?.content;
if (!Array.isArray(content)) return;
for (const block of content) {
if (!block) continue;
if (block.type === "text" && block.text) {
emitter.text(String(block.text));
} else if (block.type === "tool_use") {
emitCursorToolCallOnce(
event,
emitter,
state,
getCursorDisplayToolName(block.name, block.input),
block.input,
block.id,
);
}
}
return;
}
case "tool_call": {
closeReasoning(state, emitter);
const id = event.call_id;
const name = getCursorDisplayToolName(event.name, event.args);
if (event.status === "running") {
emitCursorToolCallOnce(event, emitter, state, name, event.args, id);
} else if (event.status === "completed" || event.status === "error") {
emitCursorToolCallOnce(event, emitter, state, name, event.args, id);
emitCursorToolResultOnce(event, emitter, state, id, event.result || event.error || "", name);
}
return;
}
case "status":
if (event.status === "ERROR") {
closeReasoning(state, emitter);
state.failed = true;
state.errorMessage = String(event.message || "");
console.warn("[Cursor SDK] status error", {
message: redactCursorSecret(event.message || ""),
});
emitter.emitError(formatCursorErrorForUser(event.message));
return true;
}
return false;
default:
return false;
}
}
class CursorTurnAbortError extends Error {
constructor() {
super("Cursor turn aborted");
this.name = "CursorTurnAbortError";
}
}
function isCursorTurnAbortError(error) {
return error instanceof CursorTurnAbortError || error?.name === "CursorTurnAbortError";
}
async function abortable(promise, signal, onLateResolve) {
if (!signal) return promise;
if (signal.aborted) {
promise.then((value) => onLateResolve?.(value)).catch(() => {});
throw new CursorTurnAbortError();
}
let aborted = false;
let removeAbortListener = () => {};
const abortPromise = new Promise((_, reject) => {
const onAbort = () => {
aborted = true;
reject(new CursorTurnAbortError());
};
signal.addEventListener("abort", onAbort, { once: true });
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
});
try {
return await Promise.race([promise, abortPromise]);
} finally {
removeAbortListener();
if (aborted) {
promise.then((value) => onLateResolve?.(value)).catch(() => {});
}
}
}
async function runCursorTurn({
prompt, attachments, agentOptions, runtimeEnv, resumeSessionId, emitter, signal, sdkModule,
}) {
let resolvedModule = sdkModule;
if (!resolvedModule) {
try {
resolvedModule = await import("@cursor/sdk");
} catch {
emitter.emitError("Cursor SDK not installed. Run: npm install @cursor/sdk");
return { sessionId: resumeSessionId || null };
}
}
const { Agent } = resolvedModule;
let agent = null;
let run = null;
let sessionId = resumeSessionId || null;
try {
const restoreCreateEnv = applyTemporaryProcessEnv(runtimeEnv);
try {
const createAgent = () => Agent.create(agentOptions);
let agentPromise;
if (resumeSessionId && typeof Agent.resume === "function") {
agentPromise = Agent.resume(resumeSessionId, agentOptions).catch((error) => {
// Stale Cursor agent IDs (expired local store, or a CLI session UUID
// resumed on the SDK path) should start a fresh agent instead of
// failing the whole turn with "Agent … not found".
if (!isCursorAgentNotFoundError(error)) throw error;
console.warn("[Cursor SDK] resume missed; creating a new agent", {
resumeSessionId,
message: error?.message || String(error),
});
sessionId = null;
return createAgent();
});
} else {
agentPromise = createAgent();
}
agent = await abortable(agentPromise, signal, (lateAgent) => {
try { lateAgent?.close?.(); } catch { /* best effort */ }
});
} finally {
restoreCreateEnv();
}
sessionId = agent.agentId || sessionId;
if (sessionId) emitter.sessionId(sessionId);
if (signal?.aborted) return { sessionId };
const sendMessage = buildCursorSendMessage(prompt, attachments);
const restoreSendEnv = applyTemporaryProcessEnv(runtimeEnv);
try {
run = await abortable(agent.send(sendMessage), signal, (lateRun) => {
if (lateRun && typeof lateRun.cancel === "function") {
void lateRun.cancel().catch(() => {});
}
});
} finally {
restoreSendEnv();
}
const state = { reasoningOpen: false };
let hasContent = false;
let failed = false;
const onAbort = () => {
if (run && typeof run.cancel === "function") {
void run.cancel().catch(() => {});
}
};
if (signal) {
if (signal.aborted) onAbort();
else signal.addEventListener("abort", onAbort, { once: true });
}
try {
for await (const event of run.stream()) {
if (signal?.aborted) break;
if (event?.type === "assistant" || event?.type === "tool_call") hasContent = true;
const streamFailed = translateCursorEvent(event, emitter, state);
if (streamFailed || state.failed) {
failed = true;
break;
}
}
} finally {
if (signal) signal.removeEventListener("abort", onAbort);
}
closeReasoning(state, emitter);
if (failed) {
if (isCursorAuthMessage(state.errorMessage)) {
await logCursorApiKeyValidation(resolvedModule, agentOptions?.apiKey);
}
return { sessionId };
}
if (!hasContent && !signal?.aborted) {
emitter.emitError("Cursor returned an empty response. Check the Cursor API Key in Settings -> AI.");
return { sessionId };
}
if (!signal?.aborted) emitter.emitDone();
return { sessionId };
} catch (error) {
if (isCursorTurnAbortError(error) || signal?.aborted) {
return { sessionId };
}
{
const message = error?.message || String(error);
console.warn("[Cursor SDK] run error", cursorErrorDiagnostics(error));
if (isCursorAuthMessage(message)) {
await logCursorApiKeyValidation(resolvedModule, agentOptions?.apiKey);
}
emitter.emitError(formatCursorErrorForUser(message));
}
return { sessionId };
} finally {
try { await agent?.close?.(); } catch { /* best effort */ }
}
}
function modelVariantId(modelId, params) {
const search = new URLSearchParams();
for (const param of params || []) {
if (param?.id && param?.value) search.set(param.id, param.value);
}
const qs = search.toString();
return qs ? `${modelId}?${qs}` : modelId;
}
function collectCursorEffortLevels(model) {
const levels = [];
const add = (raw) => {
const level = String(raw || "").toLowerCase();
if (CURSOR_REASONING_EFFORTS.has(level) && !levels.includes(level)) levels.push(level);
};
const effortParam = (model.parameters || []).find((param) => param?.id === "effort");
if (effortParam && Array.isArray(effortParam.values) && effortParam.values.length > 0) {
for (const item of effortParam.values) add(item?.value);
return levels;
}
for (const level of CURSOR_FALLBACK_THINKING[model.id] || []) add(level);
if (levels.length > 0) return levels;
for (const variant of model.variants || []) {
const params = Array.isArray(variant.params) ? variant.params : [];
const effortOnly = params.length === 1 && params[0]?.id === "effort" && params[0]?.value;
if (effortOnly) add(params[0].value);
}
return levels;
}
function mapCursorModels(models) {
const out = [];
if (!Array.isArray(models)) return out;
for (const model of models) {
if (!model?.id) continue;
const name = model.displayName || model.name || model.id;
const extraVariants = [];
for (const variant of model.variants || []) {
const params = Array.isArray(variant.params) ? variant.params : [];
const effortOnly = params.length === 1 && params[0]?.id === "effort" && params[0]?.value;
if (!effortOnly) extraVariants.push(variant);
}
const thinkingLevels = collectCursorEffortLevels(model);
out.push({
id: model.id,
name,
...(model.description ? { description: model.description } : {}),
...(thinkingLevels.length > 0 ? {
thinkingLevels,
defaultThinkingLevel: thinkingLevels.includes("medium") ? "medium" : thinkingLevels[0],
} : {}),
});
for (const variant of extraVariants) {
const id = modelVariantId(model.id, variant.params || []);
if (id === model.id) continue;
out.push({
id,
name: `${name} - ${variant.displayName || id}`,
...(variant.description ? { description: variant.description } : {}),
});
}
}
return out;
}
async function listCursorModels({ apiKey, env, sdkModule, abortController, signal } = {}) {
const externalSignal = signal || abortController?.signal;
if (externalSignal?.aborted) return [];
let resolvedModule = sdkModule;
if (!resolvedModule) {
try { resolvedModule = await import("@cursor/sdk"); } catch { return []; }
}
const effectiveApiKey = apiKey || env?.CURSOR_API_KEY || process.env.CURSOR_API_KEY;
if (!effectiveApiKey) return [];
let abortHandler;
try {
const result = await Promise.race([
Promise.resolve(resolvedModule.Cursor.models.list({
apiKey: effectiveApiKey,
signal: externalSignal,
})).then((models) => ({ type: "models", models })),
new Promise((resolve) => {
if (externalSignal?.aborted) return resolve({ type: "aborted" });
abortHandler = () => resolve({ type: "aborted" });
externalSignal?.addEventListener("abort", abortHandler, { once: true });
}),
]);
return result.type === "models" ? mapCursorModels(result.models) : [];
} finally {
if (abortHandler) externalSignal?.removeEventListener("abort", abortHandler);
}
}
module.exports = {
DEFAULT_CURSOR_MODEL,
abortable,
applyTemporaryProcessEnv,
buildCursorAgentOptions,
buildCursorSendMessage,
formatCursorErrorForUser,
isCursorAgentNotFoundError,
listCursorModels,
mapCursorModels,
parseCursorModelSelection,
encodeCursorCliModel,
runCursorTurn,
toCursorMcpServers,
translateCursorEvent,
withTemporaryProcessEnv,
};