pi-cursor-bridge 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +1033 -242
- package/dist/cursor-lifecycle-supervisor.mjs +144 -18
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
- package/skills/cce-routing/SKILL.md +2 -0
- package/skills/cursor-delegate/SKILL.md +7 -5
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -2990,7 +2990,7 @@ var require_compile = __commonJS({
|
|
|
2990
2990
|
const schOrFunc = root.refs[ref];
|
|
2991
2991
|
if (schOrFunc)
|
|
2992
2992
|
return schOrFunc;
|
|
2993
|
-
let _sch =
|
|
2993
|
+
let _sch = resolve7.call(this, root, ref);
|
|
2994
2994
|
if (_sch === void 0) {
|
|
2995
2995
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2996
2996
|
const { schemaId } = this.opts;
|
|
@@ -3017,7 +3017,7 @@ var require_compile = __commonJS({
|
|
|
3017
3017
|
function sameSchemaEnv(s1, s2) {
|
|
3018
3018
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3019
3019
|
}
|
|
3020
|
-
function
|
|
3020
|
+
function resolve7(root, ref) {
|
|
3021
3021
|
let sch;
|
|
3022
3022
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3023
3023
|
ref = sch;
|
|
@@ -3648,7 +3648,7 @@ var require_fast_uri = __commonJS({
|
|
|
3648
3648
|
}
|
|
3649
3649
|
return uri;
|
|
3650
3650
|
}
|
|
3651
|
-
function
|
|
3651
|
+
function resolve7(baseURI, relativeURI, options) {
|
|
3652
3652
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3653
3653
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3654
3654
|
schemelessOptions.skipEscape = true;
|
|
@@ -3906,7 +3906,7 @@ var require_fast_uri = __commonJS({
|
|
|
3906
3906
|
var fastUri = {
|
|
3907
3907
|
SCHEMES,
|
|
3908
3908
|
normalize,
|
|
3909
|
-
resolve:
|
|
3909
|
+
resolve: resolve7,
|
|
3910
3910
|
resolveComponent,
|
|
3911
3911
|
equal,
|
|
3912
3912
|
serialize,
|
|
@@ -10786,6 +10786,8 @@ function startMinimalWindowGuard(pid, options = {}) {
|
|
|
10786
10786
|
"-EncodedCommand",
|
|
10787
10787
|
encodePowerShell(script)
|
|
10788
10788
|
], { detached: !lifetime, stdio: "ignore", windowsHide: true });
|
|
10789
|
+
if (child && typeof child.once === "function") child.once("error", () => {
|
|
10790
|
+
});
|
|
10789
10791
|
if (!lifetime && child && typeof child.unref === "function") child.unref();
|
|
10790
10792
|
return {
|
|
10791
10793
|
started: true,
|
|
@@ -10897,20 +10899,20 @@ public static class CursorBridgeWindowControl {
|
|
|
10897
10899
|
|
|
10898
10900
|
// lifecycle-paths.mjs
|
|
10899
10901
|
import { createHash } from "node:crypto";
|
|
10900
|
-
import { homedir as
|
|
10901
|
-
import { join as
|
|
10902
|
-
import { mkdirSync as
|
|
10902
|
+
import { homedir as homedir3 } from "node:os";
|
|
10903
|
+
import { join as join3 } from "node:path";
|
|
10904
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
10903
10905
|
function defaultLifecycleDir() {
|
|
10904
10906
|
if (process.env.CURSOR_BRIDGE_LIFECYCLE_DIR) return process.env.CURSOR_BRIDGE_LIFECYCLE_DIR;
|
|
10905
10907
|
if (process.platform === "win32") {
|
|
10906
|
-
const root2 = process.env.LOCALAPPDATA ||
|
|
10907
|
-
return
|
|
10908
|
+
const root2 = process.env.LOCALAPPDATA || join3(homedir3(), "AppData", "Local");
|
|
10909
|
+
return join3(root2, "cursor-bridge", "lifecycle");
|
|
10908
10910
|
}
|
|
10909
|
-
const root = process.env.XDG_RUNTIME_DIR || process.env.XDG_STATE_HOME ||
|
|
10910
|
-
return
|
|
10911
|
+
const root = process.env.XDG_RUNTIME_DIR || process.env.XDG_STATE_HOME || join3(homedir3(), ".local", "state");
|
|
10912
|
+
return join3(root, "cursor-bridge", "lifecycle");
|
|
10911
10913
|
}
|
|
10912
10914
|
function ensureLifecycleDir(dir = defaultLifecycleDir()) {
|
|
10913
|
-
|
|
10915
|
+
mkdirSync3(dir, { recursive: true });
|
|
10914
10916
|
return dir;
|
|
10915
10917
|
}
|
|
10916
10918
|
function lifecycleEndpointTag(dir) {
|
|
@@ -10921,13 +10923,13 @@ function supervisorSockPath(dir = defaultLifecycleDir()) {
|
|
|
10921
10923
|
if (process.platform === "win32") {
|
|
10922
10924
|
return `\\\\.\\pipe\\cursor-bridge-lifecycle-${lifecycleEndpointTag(dir)}`;
|
|
10923
10925
|
}
|
|
10924
|
-
return
|
|
10926
|
+
return join3(dir, "supervisor.sock");
|
|
10925
10927
|
}
|
|
10926
10928
|
function supervisorPidPath(dir = defaultLifecycleDir()) {
|
|
10927
|
-
return
|
|
10929
|
+
return join3(dir, "supervisor.pid");
|
|
10928
10930
|
}
|
|
10929
10931
|
function supervisorLockPath(dir = defaultLifecycleDir()) {
|
|
10930
|
-
return
|
|
10932
|
+
return join3(dir, "supervisor.lock");
|
|
10931
10933
|
}
|
|
10932
10934
|
var init_lifecycle_paths = __esm({
|
|
10933
10935
|
"lifecycle-paths.mjs"() {
|
|
@@ -10937,15 +10939,15 @@ var init_lifecycle_paths = __esm({
|
|
|
10937
10939
|
// workspace-binding.mjs
|
|
10938
10940
|
import {
|
|
10939
10941
|
existsSync,
|
|
10940
|
-
mkdirSync as
|
|
10941
|
-
readFileSync as
|
|
10942
|
-
renameSync as
|
|
10943
|
-
rmSync as
|
|
10942
|
+
mkdirSync as mkdirSync4,
|
|
10943
|
+
readFileSync as readFileSync3,
|
|
10944
|
+
renameSync as renameSync3,
|
|
10945
|
+
rmSync as rmSync3,
|
|
10944
10946
|
statSync,
|
|
10945
|
-
writeFileSync as
|
|
10947
|
+
writeFileSync as writeFileSync3
|
|
10946
10948
|
} from "node:fs";
|
|
10947
|
-
import { dirname as
|
|
10948
|
-
import { homedir as
|
|
10949
|
+
import { dirname as dirname3, extname, isAbsolute, join as join4, resolve as resolve3 } from "node:path";
|
|
10950
|
+
import { homedir as homedir4 } from "node:os";
|
|
10949
10951
|
function pluginRuntimePath(candidate) {
|
|
10950
10952
|
const value = String(candidate || "").replace(/\//g, "\\").toLowerCase();
|
|
10951
10953
|
return value.includes("\\.codex\\.tmp\\marketplaces\\") || value.includes("\\.codex\\plugins\\cache\\") || value.includes("\\.claude\\plugins\\cache\\") || value.includes("\\appdata\\local\\npm-cache\\_npx\\");
|
|
@@ -10953,11 +10955,11 @@ function pluginRuntimePath(candidate) {
|
|
|
10953
10955
|
function normalizeWorkspacePath(value) {
|
|
10954
10956
|
let raw = String(value || "").trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
10955
10957
|
if (!raw) return "";
|
|
10956
|
-
if (raw === "~") raw =
|
|
10957
|
-
else if (raw.startsWith("~/") || raw.startsWith("~\\")) raw =
|
|
10958
|
-
if (/^\\\\\?\\UNC\\/i.test(raw)) return
|
|
10959
|
-
if (/^\\\\\?\\[a-zA-Z]:\\/.test(raw)) return
|
|
10960
|
-
return
|
|
10958
|
+
if (raw === "~") raw = homedir4();
|
|
10959
|
+
else if (raw.startsWith("~/") || raw.startsWith("~\\")) raw = join4(homedir4(), raw.slice(2));
|
|
10960
|
+
if (/^\\\\\?\\UNC\\/i.test(raw)) return resolve3(`\\\\${raw.slice(8)}`);
|
|
10961
|
+
if (/^\\\\\?\\[a-zA-Z]:\\/.test(raw)) return resolve3(raw.slice(4));
|
|
10962
|
+
return resolve3(raw);
|
|
10961
10963
|
}
|
|
10962
10964
|
function isAbsoluteWorkspacePath(value) {
|
|
10963
10965
|
const raw = String(value || "").trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
@@ -10977,7 +10979,7 @@ function isWorkspaceTarget(projectPath, options = {}) {
|
|
|
10977
10979
|
}
|
|
10978
10980
|
}
|
|
10979
10981
|
function resolveWorkspaceBindingFile(env = process.env) {
|
|
10980
|
-
return
|
|
10982
|
+
return resolve3(env.CURSOR_BRIDGE_WORKSPACE_FILE || join4(defaultLifecycleDir(), "workspaces.json"));
|
|
10981
10983
|
}
|
|
10982
10984
|
function resolveWorkspaceBindingKey(env = process.env, options = {}) {
|
|
10983
10985
|
const codexThreadId = String(env.CODEX_THREAD_ID || "").trim();
|
|
@@ -11001,7 +11003,7 @@ function resolveWorkspaceBindingKey(env = process.env, options = {}) {
|
|
|
11001
11003
|
function readWorkspaceBindings(filePath) {
|
|
11002
11004
|
if (!filePath) return { version: WORKSPACE_BINDING_VERSION, bindings: {} };
|
|
11003
11005
|
try {
|
|
11004
|
-
const parsed = JSON.parse(
|
|
11006
|
+
const parsed = JSON.parse(readFileSync3(filePath, "utf8"));
|
|
11005
11007
|
if (!parsed || parsed.version !== WORKSPACE_BINDING_VERSION || !parsed.bindings || typeof parsed.bindings !== "object") {
|
|
11006
11008
|
return { version: WORKSPACE_BINDING_VERSION, bindings: {} };
|
|
11007
11009
|
}
|
|
@@ -11038,13 +11040,13 @@ function writeWorkspaceBinding(filePath, bindingKey, projectPath, options = {})
|
|
|
11038
11040
|
const state = readWorkspaceBindings(filePath);
|
|
11039
11041
|
const updatedAt = options.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
11040
11042
|
state.bindings[key] = { projectPath: normalized, updatedAt };
|
|
11041
|
-
|
|
11043
|
+
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
11042
11044
|
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
11043
11045
|
try {
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
+
writeFileSync3(temporary, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
11047
|
+
renameSync3(temporary, filePath);
|
|
11046
11048
|
} catch (error2) {
|
|
11047
|
-
|
|
11049
|
+
rmSync3(temporary, { force: true });
|
|
11048
11050
|
throw new Error(`failed to persist cursor_init at ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11049
11051
|
}
|
|
11050
11052
|
return { projectPath: normalized, updatedAt };
|
|
@@ -11068,8 +11070,8 @@ var init_workspace_binding = __esm({
|
|
|
11068
11070
|
import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
|
|
11069
11071
|
import { existsSync as existsSync2 } from "fs";
|
|
11070
11072
|
import { createRequire as createNodeRequire } from "node:module";
|
|
11071
|
-
import { homedir as
|
|
11072
|
-
import { basename as
|
|
11073
|
+
import { homedir as homedir5 } from "node:os";
|
|
11074
|
+
import { basename as basename3, extname as extname2, join as join5, resolve as resolve4, win32 as winPath, posix as posixPath } from "node:path";
|
|
11073
11075
|
import http from "http";
|
|
11074
11076
|
function resolveCursorLaunchCdpPort(port = process.env.CURSOR_BRIDGE_CDP_PORT) {
|
|
11075
11077
|
const parsed = Number(port == null || String(port).trim() === "" ? 9223 : port);
|
|
@@ -11096,13 +11098,13 @@ function resolveCodexThreadProjectPath(options = {}) {
|
|
|
11096
11098
|
try {
|
|
11097
11099
|
const lookupThreadCwd = options.lookupThreadCwd || ((id) => {
|
|
11098
11100
|
const { DatabaseSync } = (options.requireImpl || loadModule)("node:sqlite");
|
|
11099
|
-
const databasePath = options.databasePath ||
|
|
11101
|
+
const databasePath = options.databasePath || join5(homedir5(), ".codex", "state_5.sqlite");
|
|
11100
11102
|
database = new DatabaseSync(databasePath, { readOnly: true });
|
|
11101
11103
|
return database.prepare("SELECT cwd FROM threads WHERE id = ?").get(id)?.cwd || null;
|
|
11102
11104
|
});
|
|
11103
11105
|
const candidate = normalizeCodexThreadCwd(lookupThreadCwd(threadId));
|
|
11104
11106
|
const existsImpl = options.existsImpl || existsSync2;
|
|
11105
|
-
const resolved = candidate && !looksLikePluginRuntimePath(candidate) && existsImpl(candidate) ?
|
|
11107
|
+
const resolved = candidate && !looksLikePluginRuntimePath(candidate) && existsImpl(candidate) ? resolve4(candidate) : null;
|
|
11106
11108
|
if (options.useCache !== false) CODEX_THREAD_PROJECTS.set(threadId, resolved);
|
|
11107
11109
|
return resolved;
|
|
11108
11110
|
} catch {
|
|
@@ -11117,14 +11119,14 @@ function resolveCodexThreadProjectPath(options = {}) {
|
|
|
11117
11119
|
}
|
|
11118
11120
|
function resolveProjectPath(value = process.env.CURSOR_PROJECT_PATH, options = {}) {
|
|
11119
11121
|
const explicit = String(value || "").trim();
|
|
11120
|
-
if (explicit) return
|
|
11122
|
+
if (explicit) return resolve4(explicit);
|
|
11121
11123
|
const persisted = String(options.persistedProjectPath || "").trim();
|
|
11122
|
-
if (persisted) return
|
|
11124
|
+
if (persisted) return resolve4(normalizeCodexThreadCwd(persisted));
|
|
11123
11125
|
const threadProjectPath = options.threadProjectPath === void 0 ? resolveCodexThreadProjectPath(options) : options.threadProjectPath;
|
|
11124
|
-
if (threadProjectPath) return
|
|
11126
|
+
if (threadProjectPath) return resolve4(normalizeCodexThreadCwd(threadProjectPath));
|
|
11125
11127
|
const cwd = options.cwd ?? process.cwd();
|
|
11126
11128
|
if (!cwd || looksLikePluginRuntimePath(cwd)) return null;
|
|
11127
|
-
return
|
|
11129
|
+
return resolve4(cwd);
|
|
11128
11130
|
}
|
|
11129
11131
|
function cursorFromRegistry(options = {}) {
|
|
11130
11132
|
const execFileSyncImpl = options.execFileSyncImpl || execFileSync2;
|
|
@@ -11189,7 +11191,7 @@ function findCursorExeDetails(options = {}) {
|
|
|
11189
11191
|
existsImpl
|
|
11190
11192
|
});
|
|
11191
11193
|
if (fromReg) return { path: fromReg, source: "windows_registry", platform };
|
|
11192
|
-
const localAppData = env.LOCALAPPDATA ||
|
|
11194
|
+
const localAppData = env.LOCALAPPDATA || join5(homedir5(), "AppData", "Local");
|
|
11193
11195
|
const programFiles = env.ProgramFiles || env.PROGRAMFILES || "C:\\Program Files";
|
|
11194
11196
|
const programFilesX86 = env["ProgramFiles(x86)"] || env.PROGRAMFILES_X86 || "";
|
|
11195
11197
|
const candidates = [
|
|
@@ -11206,7 +11208,7 @@ function findCursorExeDetails(options = {}) {
|
|
|
11206
11208
|
return null;
|
|
11207
11209
|
}
|
|
11208
11210
|
if (platform === "darwin") {
|
|
11209
|
-
const userHome = env.HOME ||
|
|
11211
|
+
const userHome = env.HOME || homedir5();
|
|
11210
11212
|
const candidates = [
|
|
11211
11213
|
"/Applications/Cursor.app/Contents/MacOS/Cursor",
|
|
11212
11214
|
userHome && posixPath.join(userHome, "Applications", "Cursor.app", "Contents", "MacOS", "Cursor")
|
|
@@ -11225,42 +11227,42 @@ function findCursorExe(options = {}) {
|
|
|
11225
11227
|
return findCursorExeDetails(options)?.path || null;
|
|
11226
11228
|
}
|
|
11227
11229
|
function cdpUp(timeoutMs = 1500) {
|
|
11228
|
-
return new Promise((
|
|
11230
|
+
return new Promise((resolve7) => {
|
|
11229
11231
|
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/version" }, (res) => {
|
|
11230
11232
|
res.resume();
|
|
11231
|
-
|
|
11233
|
+
resolve7(res.statusCode === 200);
|
|
11232
11234
|
});
|
|
11233
|
-
req.on("error", () =>
|
|
11235
|
+
req.on("error", () => resolve7(false));
|
|
11234
11236
|
req.setTimeout(timeoutMs, () => {
|
|
11235
11237
|
try {
|
|
11236
11238
|
req.destroy();
|
|
11237
11239
|
} catch {
|
|
11238
11240
|
}
|
|
11239
|
-
|
|
11241
|
+
resolve7(false);
|
|
11240
11242
|
});
|
|
11241
11243
|
});
|
|
11242
11244
|
}
|
|
11243
11245
|
function cdpIsCursor(timeoutMs = 1500) {
|
|
11244
|
-
return new Promise((
|
|
11246
|
+
return new Promise((resolve7) => {
|
|
11245
11247
|
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
|
|
11246
11248
|
let d = "";
|
|
11247
11249
|
res.on("data", (c) => d += c);
|
|
11248
11250
|
res.on("end", () => {
|
|
11249
11251
|
try {
|
|
11250
|
-
if (/[\/\\](windsurf)[\/\\]/i.test(d)) return
|
|
11251
|
-
|
|
11252
|
+
if (/[\/\\](windsurf)[\/\\]/i.test(d)) return resolve7(false);
|
|
11253
|
+
resolve7(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
|
|
11252
11254
|
} catch {
|
|
11253
|
-
|
|
11255
|
+
resolve7(false);
|
|
11254
11256
|
}
|
|
11255
11257
|
});
|
|
11256
11258
|
});
|
|
11257
|
-
req.on("error", () =>
|
|
11259
|
+
req.on("error", () => resolve7(false));
|
|
11258
11260
|
req.setTimeout(timeoutMs, () => {
|
|
11259
11261
|
try {
|
|
11260
11262
|
req.destroy();
|
|
11261
11263
|
} catch {
|
|
11262
11264
|
}
|
|
11263
|
-
|
|
11265
|
+
resolve7(false);
|
|
11264
11266
|
});
|
|
11265
11267
|
});
|
|
11266
11268
|
}
|
|
@@ -11292,6 +11294,58 @@ async function waitForCdp(maxMs = 3e4, stepMs = 1e3) {
|
|
|
11292
11294
|
}
|
|
11293
11295
|
return false;
|
|
11294
11296
|
}
|
|
11297
|
+
async function spawnDetachedSafely(spawnImpl, file, args, spawnOptions) {
|
|
11298
|
+
let child;
|
|
11299
|
+
try {
|
|
11300
|
+
child = spawnImpl(file, args, spawnOptions);
|
|
11301
|
+
} catch (error2) {
|
|
11302
|
+
return {
|
|
11303
|
+
ok: false,
|
|
11304
|
+
child: null,
|
|
11305
|
+
error: error2,
|
|
11306
|
+
errorCode: error2 && typeof error2 === "object" && error2.code != null ? String(error2.code) : null
|
|
11307
|
+
};
|
|
11308
|
+
}
|
|
11309
|
+
if (child && typeof child.once === "function") {
|
|
11310
|
+
const startup = await new Promise((resolvePromise) => {
|
|
11311
|
+
let settled = false;
|
|
11312
|
+
const finish = (result) => {
|
|
11313
|
+
if (settled) return;
|
|
11314
|
+
settled = true;
|
|
11315
|
+
child.off?.("spawn", onSpawn);
|
|
11316
|
+
child.off?.("error", onError);
|
|
11317
|
+
resolvePromise(result);
|
|
11318
|
+
};
|
|
11319
|
+
const onSpawn = () => finish({ ok: true });
|
|
11320
|
+
const onError = (error2) => finish({ ok: false, error: error2 });
|
|
11321
|
+
child.once("spawn", onSpawn);
|
|
11322
|
+
child.once("error", onError);
|
|
11323
|
+
if (Number.isInteger(child.pid) && child.pid > 0) queueMicrotask(onSpawn);
|
|
11324
|
+
});
|
|
11325
|
+
if (!startup.ok) {
|
|
11326
|
+
return {
|
|
11327
|
+
ok: false,
|
|
11328
|
+
child,
|
|
11329
|
+
error: startup.error,
|
|
11330
|
+
errorCode: startup.error && typeof startup.error === "object" && startup.error.code != null ? String(startup.error.code) : null
|
|
11331
|
+
};
|
|
11332
|
+
}
|
|
11333
|
+
child.once("error", () => {
|
|
11334
|
+
});
|
|
11335
|
+
}
|
|
11336
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
11337
|
+
return { ok: true, child };
|
|
11338
|
+
}
|
|
11339
|
+
function attachedPresentation(runtimeMode, port) {
|
|
11340
|
+
if (runtimeMode !== "minimal") return null;
|
|
11341
|
+
return {
|
|
11342
|
+
supported: true,
|
|
11343
|
+
applied: false,
|
|
11344
|
+
action: "hide",
|
|
11345
|
+
port,
|
|
11346
|
+
reason: "attached lifecycle cannot start the PowerShell window guard under the current process policy"
|
|
11347
|
+
};
|
|
11348
|
+
}
|
|
11295
11349
|
async function ensureCursorRunningLocal(options = {}) {
|
|
11296
11350
|
const waitMs = Number(options.waitMs || 3e4);
|
|
11297
11351
|
const runtimeMode = options.runtimeMode || "normal";
|
|
@@ -11300,15 +11354,17 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11300
11354
|
const cdpIsCursorImpl = options.cdpIsCursorImpl || cdpIsCursor;
|
|
11301
11355
|
const cursorRunningImpl = options.cursorRunningImpl || cursorRunning;
|
|
11302
11356
|
const findCursorExeDetailsImpl = options.findCursorExeDetailsImpl || findCursorExeDetails;
|
|
11303
|
-
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ?
|
|
11357
|
+
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ? resolve4(String(options.projectPath)) : null : resolveProjectPath();
|
|
11304
11358
|
const listCdpPageTargetsImpl = options.listCdpPageTargetsImpl || listCdpPageTargets;
|
|
11305
11359
|
const spawnImpl = options.spawnImpl || spawn2;
|
|
11360
|
+
const allowSpawn = options.allowSpawn !== false;
|
|
11361
|
+
const allowProcessControl = options.allowProcessControl !== false;
|
|
11306
11362
|
if (await cdpUpImpl()) {
|
|
11307
11363
|
const isCursor = await cdpIsCursorImpl();
|
|
11308
11364
|
if (isCursor) {
|
|
11309
|
-
const cursorPid2 = findCursorPidByPort(CDP_PORT);
|
|
11310
|
-
const windowGuard2 = effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
11311
|
-
const presentation2 = effectiveRuntimeMode === "minimal" ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : null;
|
|
11365
|
+
const cursorPid2 = allowProcessControl ? findCursorPidByPort(CDP_PORT) : null;
|
|
11366
|
+
const windowGuard2 = allowProcessControl && effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
11367
|
+
const presentation2 = effectiveRuntimeMode === "minimal" ? allowProcessControl ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : attachedPresentation(effectiveRuntimeMode, CDP_PORT) : null;
|
|
11312
11368
|
const currentTargets = await listCdpPageTargetsImpl();
|
|
11313
11369
|
const projectKey = normalizeProjectKey(projectPath);
|
|
11314
11370
|
let targetId2 = projectKey ? PROJECT_TARGETS.get(projectKey) || null : currentTargets[0] && currentTargets[0].id || null;
|
|
@@ -11337,6 +11393,22 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11337
11393
|
}
|
|
11338
11394
|
}
|
|
11339
11395
|
if (projectPath && existsSync2(projectPath) && !targetId2) {
|
|
11396
|
+
if (!allowSpawn) {
|
|
11397
|
+
return {
|
|
11398
|
+
ok: false,
|
|
11399
|
+
status: "workspace-not-ready",
|
|
11400
|
+
port: CDP_PORT,
|
|
11401
|
+
cursorPid: cursorPid2,
|
|
11402
|
+
runtimeMode: effectiveRuntimeMode,
|
|
11403
|
+
projectPath,
|
|
11404
|
+
presentation: presentation2,
|
|
11405
|
+
windowGuard: windowGuard2,
|
|
11406
|
+
needsAction: "open_workspace_in_cursor",
|
|
11407
|
+
retryable: true,
|
|
11408
|
+
nextStep: `Open workspace ${projectPath} in the existing Cursor Agents Window, then retry the same operation.`,
|
|
11409
|
+
message: `CCE connected to Cursor, but the current workspace target for ${projectPath} is not ready and the current lifecycle cannot open a new window.`
|
|
11410
|
+
};
|
|
11411
|
+
}
|
|
11340
11412
|
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
11341
11413
|
const exe2 = cursorExecutable2 && cursorExecutable2.path;
|
|
11342
11414
|
if (!exe2) {
|
|
@@ -11356,12 +11428,28 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11356
11428
|
};
|
|
11357
11429
|
}
|
|
11358
11430
|
const beforeTargetIds = new Set(currentTargets.map((target2) => target2.id));
|
|
11359
|
-
const
|
|
11431
|
+
const opened = await spawnDetachedSafely(spawnImpl, exe2, ["--new-window", projectPath], {
|
|
11360
11432
|
detached: true,
|
|
11361
11433
|
stdio: "ignore",
|
|
11362
11434
|
windowsHide: effectiveRuntimeMode === "minimal"
|
|
11363
11435
|
});
|
|
11364
|
-
|
|
11436
|
+
if (!opened.ok) {
|
|
11437
|
+
return {
|
|
11438
|
+
ok: false,
|
|
11439
|
+
status: "spawn-blocked",
|
|
11440
|
+
port: CDP_PORT,
|
|
11441
|
+
cursorPid: cursorPid2,
|
|
11442
|
+
runtimeMode: effectiveRuntimeMode,
|
|
11443
|
+
projectPath,
|
|
11444
|
+
presentation: presentation2,
|
|
11445
|
+
windowGuard: windowGuard2,
|
|
11446
|
+
errorCode: opened.errorCode,
|
|
11447
|
+
needsAction: "open_workspace_in_cursor",
|
|
11448
|
+
retryable: true,
|
|
11449
|
+
nextStep: `Open workspace ${projectPath} in Cursor, then retry the same operation.`,
|
|
11450
|
+
message: `Cursor Bridge could not open a new workspace window: ${opened.error instanceof Error ? opened.error.message : String(opened.error)}`
|
|
11451
|
+
};
|
|
11452
|
+
}
|
|
11365
11453
|
workspaceAction = "opened-new-window";
|
|
11366
11454
|
const openedTarget2 = await waitForNewCdpTarget(beforeTargetIds, 12e3, projectPath, listCdpPageTargetsImpl);
|
|
11367
11455
|
if (!openedTarget2) {
|
|
@@ -11410,6 +11498,21 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11410
11498
|
message: `CCE cannot connect to Cursor because required local port ${CDP_PORT} is occupied by another program.`
|
|
11411
11499
|
};
|
|
11412
11500
|
}
|
|
11501
|
+
if (!allowSpawn) {
|
|
11502
|
+
return {
|
|
11503
|
+
ok: false,
|
|
11504
|
+
status: "external-launch-required",
|
|
11505
|
+
port: CDP_PORT,
|
|
11506
|
+
cursorPid: null,
|
|
11507
|
+
runtimeMode: effectiveRuntimeMode,
|
|
11508
|
+
projectPath,
|
|
11509
|
+
presentation: attachedPresentation(effectiveRuntimeMode, CDP_PORT),
|
|
11510
|
+
needsAction: "launch_cursor_with_cdp",
|
|
11511
|
+
retryable: true,
|
|
11512
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, open ${projectPath || "the target workspace"}, then retry the same operation.`,
|
|
11513
|
+
message: `Cursor is not reachable on the configured CDP port ${CDP_PORT}, and the current lifecycle policy cannot launch it.`
|
|
11514
|
+
};
|
|
11515
|
+
}
|
|
11413
11516
|
if (cursorRunningImpl()) {
|
|
11414
11517
|
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
11415
11518
|
return {
|
|
@@ -11447,13 +11550,29 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11447
11550
|
);
|
|
11448
11551
|
}
|
|
11449
11552
|
if (projectPath && existsSync2(projectPath)) args.push(projectPath);
|
|
11450
|
-
const
|
|
11553
|
+
const launched = await spawnDetachedSafely(spawnImpl, exe, args, {
|
|
11451
11554
|
detached: true,
|
|
11452
11555
|
stdio: "ignore",
|
|
11453
11556
|
windowsHide: effectiveRuntimeMode === "minimal"
|
|
11454
11557
|
});
|
|
11455
|
-
|
|
11456
|
-
|
|
11558
|
+
if (!launched.ok) {
|
|
11559
|
+
return {
|
|
11560
|
+
ok: false,
|
|
11561
|
+
status: "spawn-blocked",
|
|
11562
|
+
exe,
|
|
11563
|
+
port: CDP_PORT,
|
|
11564
|
+
cursorPid: null,
|
|
11565
|
+
runtimeMode: effectiveRuntimeMode,
|
|
11566
|
+
projectPath,
|
|
11567
|
+
errorCode: launched.errorCode,
|
|
11568
|
+
needsAction: "launch_cursor_manually",
|
|
11569
|
+
retryable: true,
|
|
11570
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, then retry the same operation.`,
|
|
11571
|
+
message: `Cursor Bridge could not launch Cursor: ${launched.error instanceof Error ? launched.error.message : String(launched.error)}`
|
|
11572
|
+
};
|
|
11573
|
+
}
|
|
11574
|
+
const child = launched.child;
|
|
11575
|
+
const startupWindowGuard = effectiveRuntimeMode === "minimal" ? startMinimalWindowGuard(child && child.pid) : null;
|
|
11457
11576
|
const up = await waitForCdp(waitMs);
|
|
11458
11577
|
if (!up) {
|
|
11459
11578
|
return {
|
|
@@ -11517,10 +11636,10 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11517
11636
|
};
|
|
11518
11637
|
}
|
|
11519
11638
|
function normalizeProjectKey(projectPath) {
|
|
11520
|
-
return projectPath ?
|
|
11639
|
+
return projectPath ? resolve4(String(projectPath)).replace(/\\/g, "/").toLowerCase() : "";
|
|
11521
11640
|
}
|
|
11522
11641
|
function targetTitleMatchesProject(title, projectPath) {
|
|
11523
|
-
const name =
|
|
11642
|
+
const name = basename3(String(projectPath || "")).trim().toLowerCase();
|
|
11524
11643
|
if (!name) return false;
|
|
11525
11644
|
const extension2 = extname2(name);
|
|
11526
11645
|
const candidates = [...new Set([name, extension2 ? name.slice(0, -extension2.length) : name].filter(Boolean))];
|
|
@@ -11643,6 +11762,67 @@ function whichNode() {
|
|
|
11643
11762
|
}
|
|
11644
11763
|
return process.execPath;
|
|
11645
11764
|
}
|
|
11765
|
+
function wmiReturnValueFromError(error2) {
|
|
11766
|
+
const message = error2 instanceof Error ? error2.message : String(error2 || "");
|
|
11767
|
+
const match = message.match(/Win32_Process\.Create failed:\s*(\d+)/i);
|
|
11768
|
+
return match ? Number(match[1]) : null;
|
|
11769
|
+
}
|
|
11770
|
+
function classifyOutsideJobSpawnError(error2) {
|
|
11771
|
+
const code = error2 && typeof error2 === "object" && error2.code != null ? String(error2.code) : null;
|
|
11772
|
+
const returnValue = wmiReturnValueFromError(error2);
|
|
11773
|
+
if (code === "EPERM" || code === "EACCES") {
|
|
11774
|
+
return {
|
|
11775
|
+
errorKind: "policy-blocked",
|
|
11776
|
+
degradedReason: "spawn-policy-blocked",
|
|
11777
|
+
errorCode: code,
|
|
11778
|
+
returnValue,
|
|
11779
|
+
canAttachFallback: true
|
|
11780
|
+
};
|
|
11781
|
+
}
|
|
11782
|
+
if (returnValue === 2 || returnValue === 3) {
|
|
11783
|
+
return {
|
|
11784
|
+
errorKind: "policy-blocked",
|
|
11785
|
+
degradedReason: "wmi-access-denied",
|
|
11786
|
+
errorCode: returnValue,
|
|
11787
|
+
returnValue,
|
|
11788
|
+
canAttachFallback: true
|
|
11789
|
+
};
|
|
11790
|
+
}
|
|
11791
|
+
if (returnValue === 8) {
|
|
11792
|
+
return {
|
|
11793
|
+
errorKind: "wmi-unknown",
|
|
11794
|
+
degradedReason: "wmi-unknown-8",
|
|
11795
|
+
errorCode: returnValue,
|
|
11796
|
+
returnValue,
|
|
11797
|
+
canAttachFallback: true
|
|
11798
|
+
};
|
|
11799
|
+
}
|
|
11800
|
+
if (returnValue === 9 || returnValue === 21) {
|
|
11801
|
+
return {
|
|
11802
|
+
errorKind: "configuration",
|
|
11803
|
+
degradedReason: null,
|
|
11804
|
+
errorCode: returnValue,
|
|
11805
|
+
returnValue,
|
|
11806
|
+
canAttachFallback: false
|
|
11807
|
+
};
|
|
11808
|
+
}
|
|
11809
|
+
if (code === "ETIMEDOUT" || error2 && typeof error2 === "object" && error2.killed === true) {
|
|
11810
|
+
return {
|
|
11811
|
+
errorKind: "timeout",
|
|
11812
|
+
degradedReason: "spawn-timeout",
|
|
11813
|
+
errorCode: code || "ETIMEDOUT",
|
|
11814
|
+
returnValue,
|
|
11815
|
+
canAttachFallback: true
|
|
11816
|
+
};
|
|
11817
|
+
}
|
|
11818
|
+
return {
|
|
11819
|
+
errorKind: "unknown",
|
|
11820
|
+
degradedReason: null,
|
|
11821
|
+
errorCode: code,
|
|
11822
|
+
returnValue,
|
|
11823
|
+
canAttachFallback: false
|
|
11824
|
+
};
|
|
11825
|
+
}
|
|
11646
11826
|
function spawnOutsideJob(file, args = [], options = {}) {
|
|
11647
11827
|
const cwd = options.cwd || process.cwd();
|
|
11648
11828
|
const env = options.env || process.env;
|
|
@@ -11663,7 +11843,8 @@ function spawnOutsideJob(file, args = [], options = {}) {
|
|
|
11663
11843
|
throw new Error("forced WMI failure for tests");
|
|
11664
11844
|
}
|
|
11665
11845
|
const ps = buildHiddenWmiCreateScript(commandLine, cwd);
|
|
11666
|
-
const
|
|
11846
|
+
const run = options.execFileSyncImpl || execFileSync3;
|
|
11847
|
+
const out = run("powershell.exe", [
|
|
11667
11848
|
"-NoProfile",
|
|
11668
11849
|
"-NonInteractive",
|
|
11669
11850
|
"-ExecutionPolicy",
|
|
@@ -11683,10 +11864,14 @@ function spawnOutsideJob(file, args = [], options = {}) {
|
|
|
11683
11864
|
return { ok: true, method: "wmi-win32-process-create", pid, commandLine };
|
|
11684
11865
|
} catch (wmiError) {
|
|
11685
11866
|
const wmiMsg = wmiError instanceof Error ? wmiError.message : String(wmiError);
|
|
11867
|
+
const classification = classifyOutsideJobSpawnError(wmiError);
|
|
11868
|
+
const stderr = wmiError && typeof wmiError === "object" && wmiError.stderr != null ? String(wmiError.stderr).trim() || null : null;
|
|
11686
11869
|
return {
|
|
11687
11870
|
ok: false,
|
|
11688
11871
|
method: "failed",
|
|
11689
11872
|
commandLine,
|
|
11873
|
+
...classification,
|
|
11874
|
+
stderr,
|
|
11690
11875
|
error: `WMI Win32_Process.Create failed: ${wmiMsg}. Launch stopped without a shell fallback so Cursor Bridge cannot flash a console or create an unreliable orphan.`
|
|
11691
11876
|
};
|
|
11692
11877
|
}
|
|
@@ -11707,69 +11892,76 @@ import net from "node:net";
|
|
|
11707
11892
|
import { createHash as createHash2 } from "node:crypto";
|
|
11708
11893
|
import {
|
|
11709
11894
|
existsSync as existsSync4,
|
|
11710
|
-
mkdirSync as
|
|
11711
|
-
readFileSync as
|
|
11895
|
+
mkdirSync as mkdirSync5,
|
|
11896
|
+
readFileSync as readFileSync4,
|
|
11712
11897
|
readdirSync,
|
|
11713
|
-
renameSync as
|
|
11714
|
-
rmSync as
|
|
11898
|
+
renameSync as renameSync4,
|
|
11899
|
+
rmSync as rmSync4,
|
|
11715
11900
|
unlinkSync,
|
|
11716
|
-
writeFileSync as
|
|
11901
|
+
writeFileSync as writeFileSync4
|
|
11717
11902
|
} from "node:fs";
|
|
11718
11903
|
import { fileURLToPath } from "node:url";
|
|
11719
|
-
import { dirname as
|
|
11904
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "node:path";
|
|
11720
11905
|
function sleep(ms) {
|
|
11721
11906
|
return new Promise((r) => setTimeout(r, ms));
|
|
11722
11907
|
}
|
|
11723
11908
|
function resolveSupervisorScript() {
|
|
11724
11909
|
if (process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT && existsSync4(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT)) {
|
|
11725
|
-
return
|
|
11910
|
+
return resolve5(process.env.CURSOR_BRIDGE_SUPERVISOR_SCRIPT);
|
|
11726
11911
|
}
|
|
11727
|
-
const here =
|
|
11912
|
+
const here = dirname4(fileURLToPath(import.meta.url));
|
|
11728
11913
|
const candidates = [];
|
|
11729
11914
|
if (typeof process.argv[1] === "string") {
|
|
11730
|
-
const entryDir =
|
|
11731
|
-
candidates.push(
|
|
11732
|
-
candidates.push(
|
|
11915
|
+
const entryDir = dirname4(resolve5(process.argv[1]));
|
|
11916
|
+
candidates.push(join6(entryDir, "dist", "cursor-lifecycle-supervisor.mjs"));
|
|
11917
|
+
candidates.push(join6(entryDir, "cursor-lifecycle-supervisor.mjs"));
|
|
11733
11918
|
}
|
|
11734
11919
|
candidates.push(
|
|
11735
|
-
|
|
11736
|
-
|
|
11920
|
+
join6(here, "dist", "cursor-lifecycle-supervisor.mjs"),
|
|
11921
|
+
join6(here, "cursor-lifecycle-supervisor.mjs")
|
|
11737
11922
|
);
|
|
11738
11923
|
for (const c of candidates) {
|
|
11739
11924
|
if (existsSync4(c)) return c;
|
|
11740
11925
|
}
|
|
11741
|
-
return
|
|
11926
|
+
return join6(here, "cursor-lifecycle-supervisor.mjs");
|
|
11742
11927
|
}
|
|
11743
11928
|
function writeRuntimeFile(target, content) {
|
|
11744
|
-
if (existsSync4(target) &&
|
|
11929
|
+
if (existsSync4(target) && readFileSync4(target).equals(content)) return;
|
|
11745
11930
|
const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
11746
|
-
|
|
11931
|
+
writeFileSync4(temporary, content);
|
|
11747
11932
|
try {
|
|
11748
|
-
|
|
11933
|
+
renameSync4(temporary, target);
|
|
11749
11934
|
} catch (error2) {
|
|
11750
11935
|
if (!existsSync4(target)) {
|
|
11751
|
-
|
|
11936
|
+
rmSync4(temporary, { force: true });
|
|
11752
11937
|
throw error2;
|
|
11753
11938
|
}
|
|
11754
|
-
if (
|
|
11755
|
-
|
|
11939
|
+
if (readFileSync4(target).equals(content)) {
|
|
11940
|
+
rmSync4(temporary, { force: true });
|
|
11756
11941
|
return;
|
|
11757
11942
|
}
|
|
11758
|
-
|
|
11759
|
-
|
|
11943
|
+
rmSync4(target, { force: true });
|
|
11944
|
+
renameSync4(temporary, target);
|
|
11760
11945
|
}
|
|
11761
11946
|
}
|
|
11762
11947
|
function materializeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
11763
|
-
const
|
|
11764
|
-
|
|
11765
|
-
const
|
|
11766
|
-
|
|
11767
|
-
const
|
|
11768
|
-
mkdirSync4(runtimeRoot, { recursive: true });
|
|
11769
|
-
const script = join5(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
11948
|
+
const described = describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
11949
|
+
const { sourceScript: source, content, fingerprint } = described;
|
|
11950
|
+
const runtimeRoot = join6(ensureLifecycleDir(dir), "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
11951
|
+
mkdirSync5(runtimeRoot, { recursive: true });
|
|
11952
|
+
const script = join6(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
11770
11953
|
writeRuntimeFile(script, content);
|
|
11771
11954
|
return { sourceScript: source, script, runtimeRoot, fingerprint };
|
|
11772
11955
|
}
|
|
11956
|
+
function describeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
11957
|
+
const source = resolve5(sourceScript || resolveSupervisorScript());
|
|
11958
|
+
if (!existsSync4(source)) throw new Error(`lifecycle supervisor script missing: ${source}`);
|
|
11959
|
+
const content = readFileSync4(source);
|
|
11960
|
+
const fingerprint = createHash2("sha256").update(content).digest("hex");
|
|
11961
|
+
const runtimeRoot = join6(dir, "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
11962
|
+
const script = join6(runtimeRoot, "cursor-lifecycle-supervisor.mjs");
|
|
11963
|
+
return { sourceScript: source, script, runtimeRoot, fingerprint, content };
|
|
11964
|
+
}
|
|
11773
11965
|
function isProcessAlive(pid) {
|
|
11774
11966
|
if (!pid || !Number.isFinite(pid)) return false;
|
|
11775
11967
|
try {
|
|
@@ -11781,7 +11973,7 @@ function isProcessAlive(pid) {
|
|
|
11781
11973
|
}
|
|
11782
11974
|
function readPidFile(pidPath) {
|
|
11783
11975
|
try {
|
|
11784
|
-
const n = Number(String(
|
|
11976
|
+
const n = Number(String(readFileSync4(pidPath, "utf8")).trim());
|
|
11785
11977
|
return Number.isFinite(n) ? n : null;
|
|
11786
11978
|
} catch {
|
|
11787
11979
|
return null;
|
|
@@ -11867,6 +12059,28 @@ async function tryConnect(sock) {
|
|
|
11867
12059
|
return null;
|
|
11868
12060
|
}
|
|
11869
12061
|
}
|
|
12062
|
+
async function tryConnectDetailed(sock, connectImpl = connectSupervisor) {
|
|
12063
|
+
try {
|
|
12064
|
+
return { socket: await connectImpl(sock, 1500), error: null };
|
|
12065
|
+
} catch (error2) {
|
|
12066
|
+
return { socket: null, error: error2 };
|
|
12067
|
+
}
|
|
12068
|
+
}
|
|
12069
|
+
function lifecycleClientError(message, details = {}, cause = null) {
|
|
12070
|
+
const error2 = new Error(message, cause ? { cause } : void 0);
|
|
12071
|
+
Object.assign(error2, details);
|
|
12072
|
+
return error2;
|
|
12073
|
+
}
|
|
12074
|
+
function filesystemFallbackDetails(error2) {
|
|
12075
|
+
const code = error2 && typeof error2 === "object" && error2.code != null ? String(error2.code) : null;
|
|
12076
|
+
const blocked = code === "EPERM" || code === "EACCES" || code === "EROFS";
|
|
12077
|
+
return {
|
|
12078
|
+
errorKind: blocked ? "policy-blocked" : "configuration",
|
|
12079
|
+
degradedReason: blocked ? "fs-policy-blocked" : null,
|
|
12080
|
+
errorCode: code,
|
|
12081
|
+
canAttachFallback: blocked
|
|
12082
|
+
};
|
|
12083
|
+
}
|
|
11870
12084
|
function tryUnlink(path) {
|
|
11871
12085
|
try {
|
|
11872
12086
|
if (path && existsSync4(path)) unlinkSync(path);
|
|
@@ -11874,7 +12088,7 @@ function tryUnlink(path) {
|
|
|
11874
12088
|
}
|
|
11875
12089
|
}
|
|
11876
12090
|
function writeBootEnv(dir, extra = {}) {
|
|
11877
|
-
const bootPath =
|
|
12091
|
+
const bootPath = join6(dir, `boot-env-${process.pid}-${Date.now()}.json`);
|
|
11878
12092
|
const payload = { ...extra };
|
|
11879
12093
|
for (const [key, value] of Object.entries(process.env)) {
|
|
11880
12094
|
if (key.startsWith("CURSOR_BRIDGE_") || key === "CURSOR_PROJECT_PATH" || key === "CURSOR_EXE") {
|
|
@@ -11885,70 +12099,92 @@ function writeBootEnv(dir, extra = {}) {
|
|
|
11885
12099
|
for (const [k, v] of Object.entries(payload)) {
|
|
11886
12100
|
if (v != null && v !== "") cleaned[k] = String(v);
|
|
11887
12101
|
}
|
|
11888
|
-
|
|
12102
|
+
writeFileSync4(bootPath, `${JSON.stringify(cleaned, null, 2)}
|
|
11889
12103
|
`, { encoding: "utf8" });
|
|
11890
12104
|
return bootPath;
|
|
11891
12105
|
}
|
|
11892
12106
|
async function ensureSupervisorConnected(options = {}) {
|
|
11893
|
-
const dir =
|
|
12107
|
+
const dir = options.dir || defaultLifecycleDir();
|
|
11894
12108
|
const sock = options.sock || supervisorSockPath(dir);
|
|
11895
12109
|
const pidPath = options.pidPath || supervisorPidPath(dir);
|
|
11896
12110
|
const lockPath = options.lockPath || supervisorLockPath(dir);
|
|
11897
12111
|
const createWaitMs = Number(options.createWaitMs || DEFAULT_CREATE_WAIT_MS);
|
|
11898
12112
|
const sourceScript = options.supervisorScript || resolveSupervisorScript();
|
|
11899
|
-
const
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
runtimeRoot: dirname3(resolve4(sourceScript)),
|
|
11903
|
-
fingerprint: null
|
|
11904
|
-
} : materializeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
11905
|
-
let socket = await tryConnect(sock);
|
|
12113
|
+
const connectImpl = options.connectSupervisorImpl || connectSupervisor;
|
|
12114
|
+
const initialConnection = await tryConnectDetailed(sock, connectImpl);
|
|
12115
|
+
let socket = initialConnection.socket;
|
|
11906
12116
|
if (socket) {
|
|
11907
12117
|
const pid = readPidFile(pidPath);
|
|
11908
12118
|
let current = null;
|
|
11909
12119
|
try {
|
|
11910
12120
|
current = await request(socket, { type: "ping" }, 5e3);
|
|
11911
|
-
} catch {
|
|
11912
|
-
}
|
|
11913
|
-
const mismatch = Boolean(runtime.fingerprint && current?.runtimeFingerprint && current.runtimeFingerprint !== runtime.fingerprint);
|
|
11914
|
-
if (mismatch) {
|
|
11915
|
-
let upgrade = null;
|
|
12121
|
+
} catch (error2) {
|
|
11916
12122
|
try {
|
|
11917
|
-
|
|
11918
|
-
type: "shutdown_if_idle",
|
|
11919
|
-
confirmation: "ROLL_CURSOR_LIFECYCLE_SUPERVISOR",
|
|
11920
|
-
targetRuntimeFingerprint: runtime.fingerprint
|
|
11921
|
-
}, 5e3);
|
|
12123
|
+
socket.destroy();
|
|
11922
12124
|
} catch {
|
|
11923
12125
|
}
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
socket.destroy();
|
|
11931
|
-
} catch {
|
|
11932
|
-
}
|
|
11933
|
-
socket = null;
|
|
11934
|
-
const deadline = Date.now() + 5e3;
|
|
11935
|
-
while (Date.now() < deadline && isProcessAlive(pid)) await sleep(50);
|
|
11936
|
-
}
|
|
12126
|
+
throw lifecycleClientError(`lifecycle supervisor is reachable but unresponsive: ${error2 instanceof Error ? error2.message : String(error2)}`, {
|
|
12127
|
+
errorKind: "supervisor-unresponsive",
|
|
12128
|
+
degradedReason: "supervisor-unresponsive",
|
|
12129
|
+
errorCode: error2 && typeof error2 === "object" && error2.code != null ? String(error2.code) : null,
|
|
12130
|
+
canAttachFallback: true
|
|
12131
|
+
}, error2);
|
|
11937
12132
|
}
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
runtimeFingerprint: current?.runtimeFingerprint || null,
|
|
11948
|
-
runtimeScript: current?.runtimeScript || null,
|
|
11949
|
-
targetRuntimeFingerprint: runtime.fingerprint
|
|
11950
|
-
};
|
|
12133
|
+
let targetRuntime = null;
|
|
12134
|
+
try {
|
|
12135
|
+
targetRuntime = options.persistSupervisorRuntime === false ? {
|
|
12136
|
+
sourceScript: resolve5(sourceScript),
|
|
12137
|
+
script: resolve5(sourceScript),
|
|
12138
|
+
runtimeRoot: dirname4(resolve5(sourceScript)),
|
|
12139
|
+
fingerprint: null
|
|
12140
|
+
} : describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
12141
|
+
} catch {
|
|
11951
12142
|
}
|
|
12143
|
+
const mismatch = Boolean(targetRuntime?.fingerprint && current?.runtimeFingerprint && current.runtimeFingerprint !== targetRuntime.fingerprint);
|
|
12144
|
+
return {
|
|
12145
|
+
socket,
|
|
12146
|
+
sock,
|
|
12147
|
+
dir,
|
|
12148
|
+
supervisorPid: pid,
|
|
12149
|
+
reusedSupervisor: true,
|
|
12150
|
+
createdSupervisor: false,
|
|
12151
|
+
spawnMethod: null,
|
|
12152
|
+
runtimeFingerprint: current?.runtimeFingerprint || null,
|
|
12153
|
+
runtimeScript: current?.runtimeScript || null,
|
|
12154
|
+
targetRuntimeFingerprint: targetRuntime?.fingerprint || null,
|
|
12155
|
+
runtimeUpgradeDeferred: mismatch
|
|
12156
|
+
};
|
|
12157
|
+
}
|
|
12158
|
+
const connectCode = initialConnection.error && typeof initialConnection.error === "object" ? String(initialConnection.error.code || "") : "";
|
|
12159
|
+
const connectMessage = initialConnection.error instanceof Error ? initialConnection.error.message : String(initialConnection.error || "");
|
|
12160
|
+
if (connectCode === "EPERM" || connectCode === "EACCES") {
|
|
12161
|
+
throw lifecycleClientError(`lifecycle supervisor pipe access was blocked: ${connectMessage || connectCode}`, {
|
|
12162
|
+
errorKind: "policy-blocked",
|
|
12163
|
+
degradedReason: "pipe-policy-blocked",
|
|
12164
|
+
errorCode: connectCode,
|
|
12165
|
+
canAttachFallback: true
|
|
12166
|
+
}, initialConnection.error);
|
|
12167
|
+
}
|
|
12168
|
+
const normalAbsenceCodes = /* @__PURE__ */ new Set(["ENOENT", "ECONNREFUSED", "ECONNRESET", "EPIPE"]);
|
|
12169
|
+
if (initialConnection.error && (!normalAbsenceCodes.has(connectCode) || /connect timeout/i.test(connectMessage))) {
|
|
12170
|
+
throw lifecycleClientError(`lifecycle supervisor pipe is unavailable without a clean absence signal: ${connectMessage || connectCode || "unknown connect error"}`, {
|
|
12171
|
+
errorKind: "supervisor-unresponsive",
|
|
12172
|
+
degradedReason: "supervisor-unresponsive",
|
|
12173
|
+
errorCode: connectCode || null,
|
|
12174
|
+
canAttachFallback: true
|
|
12175
|
+
}, initialConnection.error);
|
|
12176
|
+
}
|
|
12177
|
+
let runtime;
|
|
12178
|
+
try {
|
|
12179
|
+
ensureLifecycleDir(dir);
|
|
12180
|
+
runtime = options.persistSupervisorRuntime === false ? {
|
|
12181
|
+
sourceScript: resolve5(sourceScript),
|
|
12182
|
+
script: resolve5(sourceScript),
|
|
12183
|
+
runtimeRoot: dirname4(resolve5(sourceScript)),
|
|
12184
|
+
fingerprint: null
|
|
12185
|
+
} : (options.materializeRuntimeImpl || materializeLifecycleSupervisorRuntime)({ sourceScript, dir });
|
|
12186
|
+
} catch (error2) {
|
|
12187
|
+
throw lifecycleClientError(`failed to prepare lifecycle supervisor runtime: ${error2 instanceof Error ? error2.message : String(error2)}`, filesystemFallbackDetails(error2), error2);
|
|
11952
12188
|
}
|
|
11953
12189
|
const stalePid = readPidFile(pidPath);
|
|
11954
12190
|
if (stalePid && !isProcessAlive(stalePid)) {
|
|
@@ -11984,12 +12220,20 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
11984
12220
|
CURSOR_BRIDGE_LIFECYCLE_DIR: dir,
|
|
11985
12221
|
CURSOR_BRIDGE_SUPERVISOR_SOCK: sock
|
|
11986
12222
|
};
|
|
11987
|
-
const spawned = spawnNodeOutsideJob(script, scriptArgs, {
|
|
12223
|
+
const spawned = (options.spawnNodeOutsideJobImpl || spawnNodeOutsideJob)(script, scriptArgs, {
|
|
11988
12224
|
cwd: options.cwd || runtime.runtimeRoot,
|
|
11989
12225
|
env: childEnv
|
|
11990
12226
|
});
|
|
11991
12227
|
if (!spawned.ok) {
|
|
11992
|
-
throw
|
|
12228
|
+
throw lifecycleClientError(`failed to spawn lifecycle supervisor: ${spawned.error || spawned.method}`, {
|
|
12229
|
+
errorKind: spawned.errorKind || "unknown",
|
|
12230
|
+
degradedReason: spawned.degradedReason || null,
|
|
12231
|
+
errorCode: spawned.errorCode ?? null,
|
|
12232
|
+
returnValue: spawned.returnValue ?? null,
|
|
12233
|
+
canAttachFallback: spawned.canAttachFallback === true,
|
|
12234
|
+
commandLine: spawned.commandLine || null,
|
|
12235
|
+
stderr: spawned.stderr || null
|
|
12236
|
+
});
|
|
11993
12237
|
}
|
|
11994
12238
|
const deadline = Date.now() + createWaitMs;
|
|
11995
12239
|
while (Date.now() < deadline) {
|
|
@@ -12009,7 +12253,8 @@ async function ensureSupervisorConnected(options = {}) {
|
|
|
12009
12253
|
unsafe: !!spawned.unsafe,
|
|
12010
12254
|
runtimeFingerprint: runtime.fingerprint,
|
|
12011
12255
|
runtimeScript: script,
|
|
12012
|
-
targetRuntimeFingerprint: runtime.fingerprint
|
|
12256
|
+
targetRuntimeFingerprint: runtime.fingerprint,
|
|
12257
|
+
runtimeUpgradeDeferred: false
|
|
12013
12258
|
};
|
|
12014
12259
|
}
|
|
12015
12260
|
await sleep(100);
|
|
@@ -12045,7 +12290,8 @@ async function ensureCursorViaSupervisor(options = {}) {
|
|
|
12045
12290
|
launchReason: "supervisor-error",
|
|
12046
12291
|
spawnMethod: conn.spawnMethod,
|
|
12047
12292
|
runtimeFingerprint: conn.runtimeFingerprint || null,
|
|
12048
|
-
runtimeScript: conn.runtimeScript || null
|
|
12293
|
+
runtimeScript: conn.runtimeScript || null,
|
|
12294
|
+
runtimeUpgradeDeferred: conn.runtimeUpgradeDeferred === true
|
|
12049
12295
|
};
|
|
12050
12296
|
}
|
|
12051
12297
|
return {
|
|
@@ -12070,7 +12316,8 @@ async function ensureCursorViaSupervisor(options = {}) {
|
|
|
12070
12316
|
spawnMethod: conn.spawnMethod,
|
|
12071
12317
|
ensureCount: response.ensureCount,
|
|
12072
12318
|
runtimeFingerprint: response.runtimeFingerprint || conn.runtimeFingerprint || null,
|
|
12073
|
-
runtimeScript: response.runtimeScript || conn.runtimeScript || null
|
|
12319
|
+
runtimeScript: response.runtimeScript || conn.runtimeScript || null,
|
|
12320
|
+
runtimeUpgradeDeferred: conn.runtimeUpgradeDeferred === true
|
|
12074
12321
|
};
|
|
12075
12322
|
} finally {
|
|
12076
12323
|
try {
|
|
@@ -12109,6 +12356,26 @@ __export(launch_cursor_exports, {
|
|
|
12109
12356
|
waitForCdp: () => waitForCdp
|
|
12110
12357
|
});
|
|
12111
12358
|
import { pathToFileURL } from "url";
|
|
12359
|
+
function lifecycleCapabilities(mode) {
|
|
12360
|
+
if (mode === "supervised") {
|
|
12361
|
+
return { canLaunchCursor: true, canOpenWorkspaceWindow: true, survivesHostExit: "cursor-yes-tasks-no" };
|
|
12362
|
+
}
|
|
12363
|
+
if (mode === "attached") {
|
|
12364
|
+
return { canLaunchCursor: false, canOpenWorkspaceWindow: false, survivesHostExit: "cursor-yes-tasks-no" };
|
|
12365
|
+
}
|
|
12366
|
+
return { canLaunchCursor: true, canOpenWorkspaceWindow: true, survivesHostExit: "not-guaranteed" };
|
|
12367
|
+
}
|
|
12368
|
+
function withLifecycle(result, mode, extra = {}) {
|
|
12369
|
+
return {
|
|
12370
|
+
...result,
|
|
12371
|
+
lifecycleMode: mode,
|
|
12372
|
+
persistent: mode === "supervised",
|
|
12373
|
+
degradedReason: extra.degradedReason || null,
|
|
12374
|
+
spawnErrorCode: extra.spawnErrorCode ?? null,
|
|
12375
|
+
capabilities: lifecycleCapabilities(mode),
|
|
12376
|
+
...extra
|
|
12377
|
+
};
|
|
12378
|
+
}
|
|
12112
12379
|
async function ensureCursorRunning(options = {}) {
|
|
12113
12380
|
const bindingFile = options.workspaceFile || resolveWorkspaceBindingFile();
|
|
12114
12381
|
const bindingKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
@@ -12120,19 +12387,47 @@ async function ensureCursorRunning(options = {}) {
|
|
|
12120
12387
|
const ensureOptions = { ...options, projectPath };
|
|
12121
12388
|
if (process.env.CURSOR_BRIDGE_INLINE_ENSURE === "1" || process.env.CURSOR_BRIDGE_NO_SUPERVISOR === "1") {
|
|
12122
12389
|
const local = await ensureCursorRunningLocal(ensureOptions);
|
|
12123
|
-
return {
|
|
12390
|
+
return withLifecycle({
|
|
12124
12391
|
...local,
|
|
12125
12392
|
adapterPid: process.pid,
|
|
12126
12393
|
supervisorPid: null,
|
|
12127
12394
|
reusedSupervisor: false,
|
|
12128
12395
|
createdSupervisor: false,
|
|
12129
12396
|
launchReason: local.status === "launched" ? "inline-spawned-cursor" : `inline-${local.status}`
|
|
12130
|
-
};
|
|
12397
|
+
}, "inline");
|
|
12398
|
+
}
|
|
12399
|
+
try {
|
|
12400
|
+
const supervised = await ensureCursorViaSupervisor({
|
|
12401
|
+
...ensureOptions,
|
|
12402
|
+
reason: options.reason || "ensureCursorRunning"
|
|
12403
|
+
});
|
|
12404
|
+
return withLifecycle(supervised, "supervised", {
|
|
12405
|
+
runtimeUpgradeDeferred: supervised.runtimeUpgradeDeferred === true
|
|
12406
|
+
});
|
|
12407
|
+
} catch (error2) {
|
|
12408
|
+
if (error2?.canAttachFallback !== true) throw error2;
|
|
12409
|
+
const local = await ensureCursorRunningLocal({
|
|
12410
|
+
...ensureOptions,
|
|
12411
|
+
allowSpawn: false,
|
|
12412
|
+
allowProcessControl: false
|
|
12413
|
+
});
|
|
12414
|
+
return withLifecycle({
|
|
12415
|
+
...local,
|
|
12416
|
+
adapterPid: process.pid,
|
|
12417
|
+
supervisorPid: null,
|
|
12418
|
+
reusedSupervisor: false,
|
|
12419
|
+
createdSupervisor: false,
|
|
12420
|
+
spawnMethod: null,
|
|
12421
|
+
launchReason: local.ok ? "attached-after-supervisor-blocked" : `attached-${local.status}`,
|
|
12422
|
+
supervisorError: error2 instanceof Error ? error2.message : String(error2)
|
|
12423
|
+
}, "attached", {
|
|
12424
|
+
degradedReason: error2.degradedReason || "supervisor-unavailable",
|
|
12425
|
+
spawnErrorCode: error2.errorCode ?? error2.returnValue ?? null,
|
|
12426
|
+
supervisorErrorKind: error2.errorKind || null,
|
|
12427
|
+
supervisorCommandLine: error2.commandLine || null,
|
|
12428
|
+
runtimeUpgradeDeferred: false
|
|
12429
|
+
});
|
|
12131
12430
|
}
|
|
12132
|
-
return ensureCursorViaSupervisor({
|
|
12133
|
-
...ensureOptions,
|
|
12134
|
-
reason: options.reason || "ensureCursorRunning"
|
|
12135
|
-
});
|
|
12136
12431
|
}
|
|
12137
12432
|
var isMain;
|
|
12138
12433
|
var init_launch_cursor = __esm({
|
|
@@ -19478,7 +19773,7 @@ var Protocol = class {
|
|
|
19478
19773
|
return;
|
|
19479
19774
|
}
|
|
19480
19775
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
19481
|
-
await new Promise((
|
|
19776
|
+
await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
|
|
19482
19777
|
options?.signal?.throwIfAborted();
|
|
19483
19778
|
}
|
|
19484
19779
|
} catch (error2) {
|
|
@@ -19495,7 +19790,7 @@ var Protocol = class {
|
|
|
19495
19790
|
*/
|
|
19496
19791
|
request(request2, resultSchema, options) {
|
|
19497
19792
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
19498
|
-
return new Promise((
|
|
19793
|
+
return new Promise((resolve7, reject) => {
|
|
19499
19794
|
const earlyReject = (error2) => {
|
|
19500
19795
|
reject(error2);
|
|
19501
19796
|
};
|
|
@@ -19573,7 +19868,7 @@ var Protocol = class {
|
|
|
19573
19868
|
if (!parseResult.success) {
|
|
19574
19869
|
reject(parseResult.error);
|
|
19575
19870
|
} else {
|
|
19576
|
-
|
|
19871
|
+
resolve7(parseResult.data);
|
|
19577
19872
|
}
|
|
19578
19873
|
} catch (error2) {
|
|
19579
19874
|
reject(error2);
|
|
@@ -19834,12 +20129,12 @@ var Protocol = class {
|
|
|
19834
20129
|
}
|
|
19835
20130
|
} catch {
|
|
19836
20131
|
}
|
|
19837
|
-
return new Promise((
|
|
20132
|
+
return new Promise((resolve7, reject) => {
|
|
19838
20133
|
if (signal.aborted) {
|
|
19839
20134
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
19840
20135
|
return;
|
|
19841
20136
|
}
|
|
19842
|
-
const timeoutId = setTimeout(
|
|
20137
|
+
const timeoutId = setTimeout(resolve7, interval);
|
|
19843
20138
|
signal.addEventListener("abort", () => {
|
|
19844
20139
|
clearTimeout(timeoutId);
|
|
19845
20140
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -20709,19 +21004,19 @@ var StdioServerTransport = class {
|
|
|
20709
21004
|
this.onclose?.();
|
|
20710
21005
|
}
|
|
20711
21006
|
send(message) {
|
|
20712
|
-
return new Promise((
|
|
21007
|
+
return new Promise((resolve7) => {
|
|
20713
21008
|
const json = serializeMessage(message);
|
|
20714
21009
|
if (this._stdout.write(json)) {
|
|
20715
|
-
|
|
21010
|
+
resolve7();
|
|
20716
21011
|
} else {
|
|
20717
|
-
this._stdout.once("drain",
|
|
21012
|
+
this._stdout.once("drain", resolve7);
|
|
20718
21013
|
}
|
|
20719
21014
|
});
|
|
20720
21015
|
}
|
|
20721
21016
|
};
|
|
20722
21017
|
|
|
20723
21018
|
// server.mjs
|
|
20724
|
-
import { basename as
|
|
21019
|
+
import { basename as basename4, dirname as dirname5, join as join7, resolve as resolve6 } from "node:path";
|
|
20725
21020
|
|
|
20726
21021
|
// node_modules/ws/wrapper.mjs
|
|
20727
21022
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -20735,12 +21030,119 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
20735
21030
|
|
|
20736
21031
|
// server.mjs
|
|
20737
21032
|
init_cursor_runtime();
|
|
21033
|
+
import http2 from "http";
|
|
21034
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
21035
|
+
|
|
21036
|
+
// cursor-model-preferences.mjs
|
|
21037
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
21038
|
+
import { homedir as homedir2 } from "node:os";
|
|
21039
|
+
import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
|
|
21040
|
+
var CURSOR_MODEL_TARGETS = Object.freeze(["cce", "cursor_do"]);
|
|
21041
|
+
var CURSOR_MODEL_EFFORTS = Object.freeze(["low", "medium", "high", "xhigh", "max"]);
|
|
21042
|
+
function normalizeCursorModelTarget(value, fallback = "") {
|
|
21043
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/-/g, "_");
|
|
21044
|
+
if (normalized === "context_engine" || normalized === "cursor_context_engine") return "cce";
|
|
21045
|
+
if (normalized === "do" || normalized === "delegate") return "cursor_do";
|
|
21046
|
+
return CURSOR_MODEL_TARGETS.includes(normalized) ? normalized : fallback;
|
|
21047
|
+
}
|
|
21048
|
+
function normalizeCursorModelEffort(value, fallback = "") {
|
|
21049
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/[_\s]+/g, "-");
|
|
21050
|
+
if (normalized === "extra-high" || normalized === "extra-high-thinking") return "xhigh";
|
|
21051
|
+
return CURSOR_MODEL_EFFORTS.includes(normalized) ? normalized : fallback;
|
|
21052
|
+
}
|
|
21053
|
+
function cursorEffortUiValue(value) {
|
|
21054
|
+
const normalized = normalizeCursorModelEffort(value, "");
|
|
21055
|
+
return normalized === "xhigh" ? "extra-high" : normalized;
|
|
21056
|
+
}
|
|
21057
|
+
function normalizeCursorModelPreference(value, options = {}) {
|
|
21058
|
+
if (value == null) return null;
|
|
21059
|
+
const model = String(value.model || "").trim();
|
|
21060
|
+
if (!model) {
|
|
21061
|
+
if (options.allowEmpty) return null;
|
|
21062
|
+
throw new Error("model must not be empty");
|
|
21063
|
+
}
|
|
21064
|
+
if (model.length > 200) throw new Error("model exceeds the 200-character limit");
|
|
21065
|
+
const rawEffort = value.effort == null ? "" : String(value.effort).trim();
|
|
21066
|
+
const effort = rawEffort ? normalizeCursorModelEffort(rawEffort, "") : null;
|
|
21067
|
+
if (rawEffort && !effort) {
|
|
21068
|
+
throw new Error(`unsupported Cursor model effort: ${value.effort}; expected low, medium, high, xhigh, or max`);
|
|
21069
|
+
}
|
|
21070
|
+
return { model, effort };
|
|
21071
|
+
}
|
|
21072
|
+
function resolveCursorModelPreferencesFile(value = process.env.CURSOR_BRIDGE_MODEL_PREFERENCES_FILE) {
|
|
21073
|
+
const configured = String(value || "").trim();
|
|
21074
|
+
if (configured) return resolve2(configured);
|
|
21075
|
+
const configRoot = process.platform === "win32" && process.env.APPDATA ? process.env.APPDATA : process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
21076
|
+
return join2(configRoot, "cursor-bridge", "model-preferences.json");
|
|
21077
|
+
}
|
|
21078
|
+
function emptyPreferences() {
|
|
21079
|
+
return { version: 1, targets: { cce: null, cursor_do: null }, updatedAt: null };
|
|
21080
|
+
}
|
|
21081
|
+
function readCursorModelPreferences(filePath) {
|
|
21082
|
+
const empty = emptyPreferences();
|
|
21083
|
+
if (!filePath) return empty;
|
|
21084
|
+
try {
|
|
21085
|
+
const parsed = JSON.parse(readFileSync2(filePath, "utf8"));
|
|
21086
|
+
const targets = parsed && typeof parsed.targets === "object" ? parsed.targets : {};
|
|
21087
|
+
return {
|
|
21088
|
+
version: 1,
|
|
21089
|
+
targets: {
|
|
21090
|
+
cce: normalizeCursorModelPreference(targets.cce, { allowEmpty: true }),
|
|
21091
|
+
cursor_do: normalizeCursorModelPreference(targets.cursor_do, { allowEmpty: true })
|
|
21092
|
+
},
|
|
21093
|
+
updatedAt: parsed && parsed.updatedAt ? String(parsed.updatedAt) : null
|
|
21094
|
+
};
|
|
21095
|
+
} catch (error2) {
|
|
21096
|
+
if (error2 && error2.code === "ENOENT") return empty;
|
|
21097
|
+
console.error(`[cursor-bridge] ignoring unreadable model preferences file ${filePath}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
21098
|
+
return empty;
|
|
21099
|
+
}
|
|
21100
|
+
}
|
|
21101
|
+
function writeCursorModelPreferences(filePath, preferences) {
|
|
21102
|
+
if (!filePath) throw new Error("persistent cursor_model storage is disabled for this server");
|
|
21103
|
+
const target = resolve2(filePath);
|
|
21104
|
+
const normalized = {
|
|
21105
|
+
version: 1,
|
|
21106
|
+
targets: {
|
|
21107
|
+
cce: normalizeCursorModelPreference(preferences && preferences.targets && preferences.targets.cce, { allowEmpty: true }),
|
|
21108
|
+
cursor_do: normalizeCursorModelPreference(preferences && preferences.targets && preferences.targets.cursor_do, { allowEmpty: true })
|
|
21109
|
+
},
|
|
21110
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21111
|
+
};
|
|
21112
|
+
mkdirSync2(dirname2(target), { recursive: true });
|
|
21113
|
+
const temporary = join2(dirname2(target), `.${basename2(target)}.${process.pid}.${Date.now()}.tmp`);
|
|
21114
|
+
try {
|
|
21115
|
+
writeFileSync2(temporary, `${JSON.stringify(normalized, null, 2)}
|
|
21116
|
+
`, { encoding: "utf8", mode: 384 });
|
|
21117
|
+
renameSync2(temporary, target);
|
|
21118
|
+
} catch (error2) {
|
|
21119
|
+
rmSync2(temporary, { force: true });
|
|
21120
|
+
throw new Error(`failed to persist cursor_model at ${target}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
21121
|
+
}
|
|
21122
|
+
return normalized;
|
|
21123
|
+
}
|
|
21124
|
+
function updateCursorModelPreferences(filePath, { action, target, model, effort } = {}) {
|
|
21125
|
+
const normalizedAction = String(action || "show").trim().toLowerCase();
|
|
21126
|
+
if (!["show", "set", "reset"].includes(normalizedAction)) {
|
|
21127
|
+
throw new Error(`unsupported cursor_model action: ${action}; expected show, set, or reset`);
|
|
21128
|
+
}
|
|
21129
|
+
const selectedTargets = String(target || "").trim().toLowerCase() === "both" ? [...CURSOR_MODEL_TARGETS] : [normalizeCursorModelTarget(target, "")].filter(Boolean);
|
|
21130
|
+
if (normalizedAction !== "show" && selectedTargets.length === 0) {
|
|
21131
|
+
throw new Error("cursor_model set/reset requires target=cce, cursor_do, or both");
|
|
21132
|
+
}
|
|
21133
|
+
const current = readCursorModelPreferences(filePath);
|
|
21134
|
+
if (normalizedAction === "show") return current;
|
|
21135
|
+
const next = { ...current, targets: { ...current.targets } };
|
|
21136
|
+
const preference = normalizedAction === "set" ? normalizeCursorModelPreference({ model, effort }) : null;
|
|
21137
|
+
for (const selectedTarget of selectedTargets) next.targets[selectedTarget] = preference;
|
|
21138
|
+
return writeCursorModelPreferences(filePath, next);
|
|
21139
|
+
}
|
|
21140
|
+
|
|
21141
|
+
// server.mjs
|
|
20738
21142
|
init_workspace_binding();
|
|
20739
21143
|
init_cursor_ensure_core();
|
|
20740
21144
|
init_lifecycle_paths();
|
|
20741
|
-
|
|
20742
|
-
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
20743
|
-
var PLUGIN_VERSION = "5.5.0";
|
|
21145
|
+
var PLUGIN_VERSION = "5.6.1";
|
|
20744
21146
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
20745
21147
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
20746
21148
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -20797,13 +21199,13 @@ var DO_DEFAULT_CONTRACT = "\n\nCompletion requirements: Work directly in the wor
|
|
|
20797
21199
|
var DO_LANGUAGE_CONTRACT = "\n\nResponse language: Reply in the language of the user task unless it explicitly requests another language. Never translate paths, commands, identifiers, keys, enum values, exact options, or error/status codes.";
|
|
20798
21200
|
var CDP_HOST2 = "127.0.0.1";
|
|
20799
21201
|
function httpJson(path) {
|
|
20800
|
-
return new Promise((
|
|
21202
|
+
return new Promise((resolve7, reject) => {
|
|
20801
21203
|
const req = http2.get({ host: CDP_HOST2, port: CDP_PORT2, path }, (res) => {
|
|
20802
21204
|
let d = "";
|
|
20803
21205
|
res.on("data", (c) => d += c);
|
|
20804
21206
|
res.on("end", () => {
|
|
20805
21207
|
try {
|
|
20806
|
-
|
|
21208
|
+
resolve7(JSON.parse(d));
|
|
20807
21209
|
} catch {
|
|
20808
21210
|
reject(new Error("CDP returned a non-JSON response"));
|
|
20809
21211
|
}
|
|
@@ -21185,6 +21587,73 @@ function exprClickBoundComposerStop(agentId) {
|
|
|
21185
21587
|
})()`;
|
|
21186
21588
|
}
|
|
21187
21589
|
var EXPR_FIND_NEWAGENT = `(function(){const b=[...document.querySelectorAll('button,[role=button],a.action-label,.codicon')].find(e=>{if(e.offsetParent===null||e.closest('.glass-sidebar-agent-menu-btn'))return false;const s=(e.getAttribute('aria-label')||'')+' '+(e.getAttribute('title')||'')+' '+(e.innerText||'');return /(?:^|\\s)New (?:Agent|Chat)(?:\\s|$)/i.test(s);});if(!b)return '';const r=b.getBoundingClientRect();return JSON.stringify({x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)});})()`;
|
|
21590
|
+
var MODEL_PICKER_VISIBLE_BODY = `
|
|
21591
|
+
const visible=(node)=>!!(node&&(node.offsetParent!==null||(node.getClientRects&&node.getClientRects().length>0)));
|
|
21592
|
+
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id],.composer-bar,.ui-prompt-input-root')].filter(visible);
|
|
21593
|
+
const composer=composers[composers.length-1]||document;
|
|
21594
|
+
`;
|
|
21595
|
+
var EXPR_MODEL_PICKER_TRIGGER = `(function(){
|
|
21596
|
+
${MODEL_PICKER_VISIBLE_BODY}
|
|
21597
|
+
const triggerSelector='.ui-model-picker__trigger,.vscode-model-picker__trigger';
|
|
21598
|
+
const candidates=[...composer.querySelectorAll(triggerSelector)].filter(visible);
|
|
21599
|
+
const trigger=candidates[candidates.length-1]||[...document.querySelectorAll(triggerSelector)].filter(visible).pop();
|
|
21600
|
+
if(!trigger)return JSON.stringify({found:false,state:'trigger_missing'});
|
|
21601
|
+
const rect=trigger.getBoundingClientRect();
|
|
21602
|
+
const text=String(trigger.querySelector('.ui-model-picker__trigger-text,.vscode-model-picker__trigger-text')?.innerText||trigger.innerText||'').replace(/\\s+/g,' ').trim();
|
|
21603
|
+
const detail=String(trigger.querySelector('.ui-model-picker__trigger-variant-suffix,.vscode-model-picker__trigger-variant-suffix')?.innerText||'').replace(/\\s+/g,' ').trim();
|
|
21604
|
+
return JSON.stringify({found:true,state:'ready',text,detail,x:Math.round(rect.x+rect.width/2),y:Math.round(rect.y+rect.height/2)});
|
|
21605
|
+
})()`;
|
|
21606
|
+
var EXPR_MODEL_PICKER_ROWS = `(function(){
|
|
21607
|
+
${MODEL_PICKER_VISIBLE_BODY}
|
|
21608
|
+
const menus=[...document.querySelectorAll('[data-testid="model-picker-menu"],[data-testid*="model-parameters"],[data-testid*="parameter-submenu"],[data-component="menu-popup"][data-submenu]')].filter(visible);
|
|
21609
|
+
const rows=[];
|
|
21610
|
+
const seen=new Set();
|
|
21611
|
+
for(const menu of menus){
|
|
21612
|
+
for(const row of menu.querySelectorAll('[data-component="menu-row"],[data-component="menu-submenu-trigger"],[role="menuitem"],[role="menuitemradio"],[role="menuitemcheckbox"]')){
|
|
21613
|
+
if(!visible(row)||seen.has(row))continue;
|
|
21614
|
+
seen.add(row);
|
|
21615
|
+
const rect=row.getBoundingClientRect();
|
|
21616
|
+
const text=String(row.innerText||row.textContent||'').replace(/\\s+/g,' ').trim();
|
|
21617
|
+
if(!text)continue;
|
|
21618
|
+
const menuTestId=String(menu.getAttribute('data-testid')||'').toLowerCase();
|
|
21619
|
+
let kind='control';
|
|
21620
|
+
if(row.querySelector('.ui-model-picker__item-content-name,.vscode-model-picker__item-content-name'))kind='model';
|
|
21621
|
+
else if(/^model(?:\\s|$)/i.test(text)&&row.getAttribute('aria-haspopup')==='menu')kind='model_control';
|
|
21622
|
+
else if(/^effort(?:\\s|$)/i.test(text)&&row.getAttribute('aria-haspopup')==='menu')kind='effort_control';
|
|
21623
|
+
else if(menuTestId.includes('parameter-submenu')||row.closest('[data-submenu]'))kind='parameter';
|
|
21624
|
+
else if(menuTestId.includes('model-picker-menu')||menuTestId.includes('model-selection'))kind='model';
|
|
21625
|
+
rows.push({
|
|
21626
|
+
text,
|
|
21627
|
+
kind,
|
|
21628
|
+
selected:row.getAttribute('data-selected')==='true'||row.getAttribute('aria-checked')==='true'||!!row.querySelector('.ui-model-picker__item-check,.ui-model-picker__param-check'),
|
|
21629
|
+
disabled:row.getAttribute('data-disabled')==='true'||row.getAttribute('aria-disabled')==='true',
|
|
21630
|
+
hasSubmenu:row.getAttribute('aria-haspopup')==='menu',
|
|
21631
|
+
submenu:!!row.closest('[data-submenu]'),
|
|
21632
|
+
x:Math.round(rect.x+rect.width/2),
|
|
21633
|
+
y:Math.round(rect.y+rect.height/2),
|
|
21634
|
+
});
|
|
21635
|
+
}
|
|
21636
|
+
}
|
|
21637
|
+
return JSON.stringify({open:menus.length>0,rows});
|
|
21638
|
+
})()`;
|
|
21639
|
+
function normalizeModelPickerText(value) {
|
|
21640
|
+
return String(value || "").trim().toLowerCase().replace(/extra[\s_-]*high/g, "xhigh").replace(/[^a-z0-9]+/g, "");
|
|
21641
|
+
}
|
|
21642
|
+
function selectModelPickerRow(rows, requested, kind = "model") {
|
|
21643
|
+
const wanted = normalizeModelPickerText(requested);
|
|
21644
|
+
if (!wanted) return null;
|
|
21645
|
+
const candidates = (Array.isArray(rows) ? rows : []).filter((row) => row && row.disabled !== true && (kind === "any" || row.kind === kind)).map((row) => {
|
|
21646
|
+
const normalized = normalizeModelPickerText(row.text);
|
|
21647
|
+
let score = normalized === wanted ? 1e3 : 0;
|
|
21648
|
+
if (!score && normalized.startsWith(wanted)) score = 800;
|
|
21649
|
+
if (!score && wanted.startsWith(normalized)) score = 700;
|
|
21650
|
+
if (!score && normalized.includes(wanted)) score = 600;
|
|
21651
|
+
return { row, score, distance: Math.abs(normalized.length - wanted.length) };
|
|
21652
|
+
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.distance - b.distance);
|
|
21653
|
+
if (candidates.length === 0) return null;
|
|
21654
|
+
if (candidates.length > 1 && candidates[0].score === candidates[1].score && candidates[0].distance === candidates[1].distance) return null;
|
|
21655
|
+
return candidates[0].row;
|
|
21656
|
+
}
|
|
21188
21657
|
var WORKSPACE_SECTION_BODY = `
|
|
21189
21658
|
const headText=(el)=>String(el&&(el.innerText||el.textContent)||'').trim();
|
|
21190
21659
|
const isNewAgentButton=(node)=>{
|
|
@@ -21228,7 +21697,7 @@ var WORKSPACE_SECTION_BODY = `
|
|
|
21228
21697
|
};
|
|
21229
21698
|
`;
|
|
21230
21699
|
function exprCreateAgentForWorkspace(projectPath) {
|
|
21231
|
-
const workspaceLabel = JSON.stringify(
|
|
21700
|
+
const workspaceLabel = JSON.stringify(basename4(String(projectPath || "")).trim().toLowerCase());
|
|
21232
21701
|
return `(function(){
|
|
21233
21702
|
${WORKSPACE_SECTION_BODY}
|
|
21234
21703
|
const wanted=${workspaceLabel};
|
|
@@ -21244,7 +21713,7 @@ function exprCreateAgentForWorkspace(projectPath) {
|
|
|
21244
21713
|
})()`;
|
|
21245
21714
|
}
|
|
21246
21715
|
function exprInspectWorkspaceRepository(projectPath) {
|
|
21247
|
-
const workspaceLabel = JSON.stringify(
|
|
21716
|
+
const workspaceLabel = JSON.stringify(basename4(String(projectPath || "")).trim().toLowerCase());
|
|
21248
21717
|
return `(function(){
|
|
21249
21718
|
${WORKSPACE_SECTION_BODY}
|
|
21250
21719
|
const wanted=${workspaceLabel};
|
|
@@ -21314,6 +21783,8 @@ var REACT_ADAPTER_BODY = `
|
|
|
21314
21783
|
};
|
|
21315
21784
|
const findV2Props=()=>{
|
|
21316
21785
|
const found=[]; const seen=new Set();
|
|
21786
|
+
let globalSelectAgent=null;
|
|
21787
|
+
let sectionIdByAgentId=null;
|
|
21317
21788
|
for(const root of document.querySelectorAll('.glass-sidebar-agent-list-container')){
|
|
21318
21789
|
const nodes=[]; let n=root;
|
|
21319
21790
|
for(let i=0;n&&i<24;i++,n=n.parentElement)nodes.push(n);
|
|
@@ -21324,6 +21795,8 @@ var REACT_ADAPTER_BODY = `
|
|
|
21324
21795
|
let f=key.startsWith('__reactFiber$')?seed:{memoizedProps:seed,return:null};
|
|
21325
21796
|
for(let j=0;f&&j<80;j++,f=f.return){
|
|
21326
21797
|
for(const p of [f.memoizedProps,f.pendingProps,f.stateNode&&f.stateNode.props]){
|
|
21798
|
+
if(p&&typeof p.onSelectAgent==='function'&&!globalSelectAgent)globalSelectAgent=p.onSelectAgent;
|
|
21799
|
+
if(p&&p.sectionIdByAgentId&&!sectionIdByAgentId)sectionIdByAgentId=p.sectionIdByAgentId;
|
|
21327
21800
|
const selectAgent=p&&(typeof p.onSelectAgent==='function'
|
|
21328
21801
|
?p.onSelectAgent
|
|
21329
21802
|
:(p.rowHandlers&&typeof p.rowHandlers.onSelect==='function'?p.rowHandlers.onSelect:null));
|
|
@@ -21339,6 +21812,8 @@ var REACT_ADAPTER_BODY = `
|
|
|
21339
21812
|
}
|
|
21340
21813
|
}
|
|
21341
21814
|
}
|
|
21815
|
+
found.globalSelectAgent=globalSelectAgent;
|
|
21816
|
+
found.sectionIdByAgentId=sectionIdByAgentId;
|
|
21342
21817
|
return found;
|
|
21343
21818
|
};
|
|
21344
21819
|
const findAdapter=()=>{
|
|
@@ -21368,10 +21843,28 @@ var REACT_ADAPTER_BODY = `
|
|
|
21368
21843
|
else if(/needs_attention/.test(status))icon='needs-attention';
|
|
21369
21844
|
else if(/failed|error/.test(status))icon='warning';
|
|
21370
21845
|
else if(/cancel/.test(status))icon='circle-slash';
|
|
21846
|
+
seen.add(selectedId);
|
|
21371
21847
|
entries.push({
|
|
21372
21848
|
id:selectedId,label:'',searchText:'',timestamp:0,isSelected:true,
|
|
21373
21849
|
showSpinner:/in_progress|running|generating/.test(status),icon,
|
|
21374
|
-
workspaceId:''
|
|
21850
|
+
workspaceId:String(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.get(selectedRaw)||'':v2.sectionIdByAgentId&&readScalar(v2.sectionIdByAgentId[selectedRaw])||''),
|
|
21851
|
+
workspaceLabel:'',durable:!!(v2.sectionIdByAgentId&&(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.has(selectedRaw):v2.sectionIdByAgentId[selectedRaw]!==undefined)),
|
|
21852
|
+
registeredBySectionMap:true
|
|
21853
|
+
});
|
|
21854
|
+
}
|
|
21855
|
+
}
|
|
21856
|
+
if(v2.sectionIdByAgentId){
|
|
21857
|
+
const registered=v2.sectionIdByAgentId instanceof Map?[...v2.sectionIdByAgentId.entries()]:Object.entries(v2.sectionIdByAgentId);
|
|
21858
|
+
for(const pair of registered){
|
|
21859
|
+
const raw=String(pair&&pair[0]||'').replace(/^local:/,'');
|
|
21860
|
+
if(!raw)continue;
|
|
21861
|
+
const id='local:'+raw;
|
|
21862
|
+
if(seen.has(id))continue;
|
|
21863
|
+
seen.add(id);
|
|
21864
|
+
entries.push({
|
|
21865
|
+
id,label:'',searchText:'',timestamp:0,isSelected:id===selectedId,
|
|
21866
|
+
showSpinner:false,icon:'registered',workspaceId:String(readScalar(pair[1])||''),workspaceLabel:'',
|
|
21867
|
+
durable:true,registeredBySectionMap:true
|
|
21375
21868
|
});
|
|
21376
21869
|
}
|
|
21377
21870
|
}
|
|
@@ -21384,6 +21877,8 @@ var REACT_ADAPTER_BODY = `
|
|
|
21384
21877
|
const header=p.section.headers.find(h=>String(readScalar(h&&h.id)||'')===raw);
|
|
21385
21878
|
if(header){p.onSelectAgent(header);return true;}
|
|
21386
21879
|
}
|
|
21880
|
+
const registered=v2.sectionIdByAgentId&&(v2.sectionIdByAgentId instanceof Map?v2.sectionIdByAgentId.has(raw):v2.sectionIdByAgentId[raw]!==undefined);
|
|
21881
|
+
if(registered&&typeof v2.globalSelectAgent==='function'){v2.globalSelectAgent(raw);return true;}
|
|
21387
21882
|
return false;
|
|
21388
21883
|
}
|
|
21389
21884
|
};
|
|
@@ -21481,6 +21976,13 @@ function classifyParallelTerminalIcon(icon) {
|
|
|
21481
21976
|
function isDurablyRegisteredParallelEntry(entry) {
|
|
21482
21977
|
return !!entry && entry.durable !== false && (entry.showSpinner || classifyParallelTerminalIcon(entry.icon) !== "unknown");
|
|
21483
21978
|
}
|
|
21979
|
+
function selectPromotedFifoEntry(beforeEntries, currentAgentId, afterEntries) {
|
|
21980
|
+
if (!Array.isArray(beforeEntries) || !Array.isArray(afterEntries)) return null;
|
|
21981
|
+
if (currentAgentId && afterEntries.some((entry) => entry && entry.id === currentAgentId)) return null;
|
|
21982
|
+
const candidate = selectNewAgentEntry(beforeEntries, afterEntries);
|
|
21983
|
+
if (!candidate || candidate.id === currentAgentId || candidate.isSelected !== true) return null;
|
|
21984
|
+
return isDurablyRegisteredParallelEntry(candidate) ? candidate : null;
|
|
21985
|
+
}
|
|
21484
21986
|
function uncertainSubmissionReservationScope(job, error2) {
|
|
21485
21987
|
if (error2 && error2.requiresGlobalReservation) return "global";
|
|
21486
21988
|
return job && job.readOnly ? "agent" : "paths";
|
|
@@ -21520,6 +22022,40 @@ function promoteAgentsWorkspaceLifecycle(lifecycle, agentsWorkspace) {
|
|
|
21520
22022
|
retryable: false
|
|
21521
22023
|
};
|
|
21522
22024
|
}
|
|
22025
|
+
function lifecycleFromEnsureResult(result, fallbackRuntimeMode) {
|
|
22026
|
+
return {
|
|
22027
|
+
adapterPid: result.adapterPid ?? process.pid,
|
|
22028
|
+
supervisorPid: result.supervisorPid ?? null,
|
|
22029
|
+
reusedSupervisor: !!result.reusedSupervisor,
|
|
22030
|
+
createdSupervisor: !!result.createdSupervisor,
|
|
22031
|
+
launchReason: result.launchReason || result.status,
|
|
22032
|
+
status: result.status,
|
|
22033
|
+
spawnMethod: result.spawnMethod || null,
|
|
22034
|
+
lifecycleMode: result.lifecycleMode || null,
|
|
22035
|
+
persistent: result.persistent === true,
|
|
22036
|
+
degradedReason: result.degradedReason || null,
|
|
22037
|
+
spawnErrorCode: result.spawnErrorCode ?? null,
|
|
22038
|
+
supervisorErrorKind: result.supervisorErrorKind || null,
|
|
22039
|
+
capabilities: result.capabilities || null,
|
|
22040
|
+
cursorPid: result.cursorPid || null,
|
|
22041
|
+
runtimeMode: result.runtimeMode || fallbackRuntimeMode,
|
|
22042
|
+
projectPath: result.projectPath || null,
|
|
22043
|
+
targetId: result.targetId || null,
|
|
22044
|
+
workspaceAction: result.workspaceAction || null,
|
|
22045
|
+
presentation: result.presentation || null,
|
|
22046
|
+
windowGuard: result.windowGuard || null,
|
|
22047
|
+
startupWindowGuard: result.startupWindowGuard || null,
|
|
22048
|
+
message: result.message || null,
|
|
22049
|
+
needsAction: result.needsAction || null,
|
|
22050
|
+
nextStep: result.nextStep || null,
|
|
22051
|
+
retryable: result.retryable === true,
|
|
22052
|
+
cursorExecutable: result.cursorExecutable || null,
|
|
22053
|
+
cursorExecutableSource: result.cursorExecutableSource || null,
|
|
22054
|
+
runtimeFingerprint: result.runtimeFingerprint || null,
|
|
22055
|
+
runtimeScript: result.runtimeScript || null,
|
|
22056
|
+
runtimeUpgradeDeferred: result.runtimeUpgradeDeferred === true
|
|
22057
|
+
};
|
|
22058
|
+
}
|
|
21523
22059
|
function releaseAdapterWorkingDirectory({ targetDir = defaultLifecycleDir(), chdir = process.chdir } = {}) {
|
|
21524
22060
|
const target = ensureLifecycleDir(targetDir);
|
|
21525
22061
|
chdir(target);
|
|
@@ -21527,10 +22063,10 @@ function releaseAdapterWorkingDirectory({ targetDir = defaultLifecycleDir(), chd
|
|
|
21527
22063
|
}
|
|
21528
22064
|
var CursorBridge = class {
|
|
21529
22065
|
constructor(options = {}) {
|
|
21530
|
-
this.adapterStartCwd =
|
|
22066
|
+
this.adapterStartCwd = resolve6(options.adapterStartCwd || process.cwd());
|
|
21531
22067
|
this.environmentDelegationMode = normalizeDelegationMode(options.delegationMode || DELEGATION_MODE);
|
|
21532
22068
|
this._syncDelegationState();
|
|
21533
|
-
this.runtimeFile = options.runtimeFile === null ? null :
|
|
22069
|
+
this.runtimeFile = options.runtimeFile === null ? null : resolve6(options.runtimeFile || resolveCursorRuntimeFile());
|
|
21534
22070
|
this.runtimeModeDefault = normalizeCursorRuntimeMode(
|
|
21535
22071
|
options.runtimeModeDefault || process.env.CURSOR_BRIDGE_RUNTIME_MODE,
|
|
21536
22072
|
"normal"
|
|
@@ -21541,12 +22077,14 @@ var CursorBridge = class {
|
|
|
21541
22077
|
this.runtimeMode = normalizeCursorRuntimeMode(requestedRuntimeMode);
|
|
21542
22078
|
this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
21543
22079
|
this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
21544
|
-
this.workspaceFile = options.workspaceFile === null ? null :
|
|
22080
|
+
this.workspaceFile = options.workspaceFile === null ? null : resolve6(options.workspaceFile || resolveWorkspaceBindingFile());
|
|
21545
22081
|
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
21546
22082
|
const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
|
|
21547
|
-
this.projectPath = options.projectPath !== void 0 ?
|
|
22083
|
+
this.projectPath = options.projectPath !== void 0 ? resolve6(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
21548
22084
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
21549
22085
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
22086
|
+
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve6(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
22087
|
+
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
21550
22088
|
this._lastPresentation = null;
|
|
21551
22089
|
this.busy = false;
|
|
21552
22090
|
this.queue = [];
|
|
@@ -21592,7 +22130,9 @@ var CursorBridge = class {
|
|
|
21592
22130
|
"port-not-cursor",
|
|
21593
22131
|
"no-exe",
|
|
21594
22132
|
"timeout",
|
|
21595
|
-
"workspace-not-ready"
|
|
22133
|
+
"workspace-not-ready",
|
|
22134
|
+
"external-launch-required",
|
|
22135
|
+
"spawn-blocked"
|
|
21596
22136
|
]);
|
|
21597
22137
|
if (!lifecycle || !recoverableStatuses.has(lifecycle.status)) throw error2;
|
|
21598
22138
|
return {
|
|
@@ -21654,6 +22194,36 @@ var CursorBridge = class {
|
|
|
21654
22194
|
environmentLockedOff: this.environmentDelegationMode === "off"
|
|
21655
22195
|
};
|
|
21656
22196
|
}
|
|
22197
|
+
_refreshModelPreferences() {
|
|
22198
|
+
if (!this.modelPreferencesFile) return false;
|
|
22199
|
+
const next = readCursorModelPreferences(this.modelPreferencesFile);
|
|
22200
|
+
const changed = JSON.stringify(next) !== JSON.stringify(this.modelPreferences);
|
|
22201
|
+
this.modelPreferences = next;
|
|
22202
|
+
return changed;
|
|
22203
|
+
}
|
|
22204
|
+
modelPreferencesView() {
|
|
22205
|
+
this._refreshModelPreferences();
|
|
22206
|
+
return {
|
|
22207
|
+
modelPreferencesFile: this.modelPreferencesFile,
|
|
22208
|
+
modelPreferencesPersistAcrossRestart: !!this.modelPreferencesFile,
|
|
22209
|
+
modelPreferenceTargets: [...CURSOR_MODEL_TARGETS],
|
|
22210
|
+
availableEfforts: [...CURSOR_MODEL_EFFORTS],
|
|
22211
|
+
modelPreferences: {
|
|
22212
|
+
cce: this.modelPreferences.targets.cce,
|
|
22213
|
+
cursor_do: this.modelPreferences.targets.cursor_do
|
|
22214
|
+
},
|
|
22215
|
+
modelPreferencesUpdatedAt: this.modelPreferences.updatedAt
|
|
22216
|
+
};
|
|
22217
|
+
}
|
|
22218
|
+
configureModelPreferences(options = {}) {
|
|
22219
|
+
this.modelPreferences = updateCursorModelPreferences(this.modelPreferencesFile, options);
|
|
22220
|
+
return this.modelPreferencesView();
|
|
22221
|
+
}
|
|
22222
|
+
_modelPreferenceFor(target) {
|
|
22223
|
+
this._refreshModelPreferences();
|
|
22224
|
+
const preference = this.modelPreferences.targets[target];
|
|
22225
|
+
return preference ? { ...preference } : null;
|
|
22226
|
+
}
|
|
21657
22227
|
_refreshPersistedRuntimeMode() {
|
|
21658
22228
|
if (!this.runtimeFile || this.runtimeModeScope === "session" || this.runtimeModeScope === "constructor") {
|
|
21659
22229
|
return false;
|
|
@@ -21750,7 +22320,8 @@ var CursorBridge = class {
|
|
|
21750
22320
|
newChat: true,
|
|
21751
22321
|
execution: "fifo",
|
|
21752
22322
|
readOnly: true,
|
|
21753
|
-
allowedPaths: []
|
|
22323
|
+
allowedPaths: [],
|
|
22324
|
+
modelPreference: this._modelPreferenceFor("cce")
|
|
21754
22325
|
});
|
|
21755
22326
|
return normalizeCceSearchResult(await job.promise);
|
|
21756
22327
|
}
|
|
@@ -21802,7 +22373,8 @@ var CursorBridge = class {
|
|
|
21802
22373
|
execution,
|
|
21803
22374
|
readOnly,
|
|
21804
22375
|
allowedPaths,
|
|
21805
|
-
preferLegacyUi: options.preferLegacyUi === true
|
|
22376
|
+
preferLegacyUi: options.preferLegacyUi === true,
|
|
22377
|
+
modelPreference: this._modelPreferenceFor("cursor_do")
|
|
21806
22378
|
});
|
|
21807
22379
|
if (options.background !== false) return this._taskView(job);
|
|
21808
22380
|
await job.promise;
|
|
@@ -21835,8 +22407,8 @@ var CursorBridge = class {
|
|
|
21835
22407
|
const id = `cursor-${Date.now().toString(36)}-${this.nextTaskId++}`;
|
|
21836
22408
|
let resolvePromise;
|
|
21837
22409
|
let rejectPromise;
|
|
21838
|
-
const promise = new Promise((
|
|
21839
|
-
resolvePromise =
|
|
22410
|
+
const promise = new Promise((resolve7, reject) => {
|
|
22411
|
+
resolvePromise = resolve7;
|
|
21840
22412
|
rejectPromise = reject;
|
|
21841
22413
|
});
|
|
21842
22414
|
promise.catch(() => {
|
|
@@ -21852,6 +22424,8 @@ var CursorBridge = class {
|
|
|
21852
22424
|
effectiveExecution: options.execution || "fifo",
|
|
21853
22425
|
readOnly: options.readOnly === true,
|
|
21854
22426
|
allowedPaths: options.allowedPaths || [],
|
|
22427
|
+
modelPreference: options.modelPreference ? { ...options.modelPreference } : null,
|
|
22428
|
+
modelSelection: options.modelPreference ? { configured: true, applied: false, ...options.modelPreference } : null,
|
|
21855
22429
|
projectPath: options.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null,
|
|
21856
22430
|
status: "queued",
|
|
21857
22431
|
phase: "queued",
|
|
@@ -22001,42 +22575,35 @@ var CursorBridge = class {
|
|
|
22001
22575
|
adapterStartCwd: this.adapterStartCwd,
|
|
22002
22576
|
...this.projectPath ? { projectPath: this.projectPath } : {}
|
|
22003
22577
|
});
|
|
22004
|
-
this._lastLifecycle =
|
|
22005
|
-
adapterPid: rr.adapterPid ?? process.pid,
|
|
22006
|
-
supervisorPid: rr.supervisorPid ?? null,
|
|
22007
|
-
reusedSupervisor: !!rr.reusedSupervisor,
|
|
22008
|
-
createdSupervisor: !!rr.createdSupervisor,
|
|
22009
|
-
launchReason: rr.launchReason || rr.status,
|
|
22010
|
-
status: rr.status,
|
|
22011
|
-
spawnMethod: rr.spawnMethod || null,
|
|
22012
|
-
cursorPid: rr.cursorPid || null,
|
|
22013
|
-
runtimeMode: rr.runtimeMode || this.runtimeMode,
|
|
22014
|
-
projectPath: rr.projectPath || null,
|
|
22015
|
-
targetId: rr.targetId || null,
|
|
22016
|
-
workspaceAction: rr.workspaceAction || null,
|
|
22017
|
-
presentation: rr.presentation || null,
|
|
22018
|
-
message: rr.message || null,
|
|
22019
|
-
needsAction: rr.needsAction || null,
|
|
22020
|
-
nextStep: rr.nextStep || null,
|
|
22021
|
-
retryable: rr.retryable === true,
|
|
22022
|
-
cursorExecutable: rr.cursorExecutable || null,
|
|
22023
|
-
cursorExecutableSource: rr.cursorExecutableSource || null,
|
|
22024
|
-
runtimeFingerprint: rr.runtimeFingerprint || null,
|
|
22025
|
-
runtimeScript: rr.runtimeScript || null
|
|
22026
|
-
};
|
|
22578
|
+
this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
|
|
22027
22579
|
if (!rr.ok && rr.status === "workspace-not-ready" && rr.projectPath) {
|
|
22028
22580
|
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
|
|
22029
22581
|
if (agentsWorkspace) {
|
|
22030
22582
|
this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
|
|
22031
22583
|
}
|
|
22032
22584
|
}
|
|
22585
|
+
if (rr.ok && rr.lifecycleMode === "attached" && rr.workspaceAction === "reused-agents-window" && rr.projectPath) {
|
|
22586
|
+
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
|
|
22587
|
+
if (agentsWorkspace) {
|
|
22588
|
+
this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
|
|
22589
|
+
} else {
|
|
22590
|
+
this._lastLifecycle = {
|
|
22591
|
+
...this._lastLifecycle,
|
|
22592
|
+
status: "workspace-not-ready",
|
|
22593
|
+
message: `Cursor is reachable, but Cursor Bridge could not verify workspace ${rr.projectPath} in the attached Agents Window.`,
|
|
22594
|
+
needsAction: "open_workspace_in_cursor",
|
|
22595
|
+
nextStep: `Open workspace ${rr.projectPath} in Cursor, then retry the same operation.`,
|
|
22596
|
+
retryable: true
|
|
22597
|
+
};
|
|
22598
|
+
}
|
|
22599
|
+
}
|
|
22033
22600
|
if (this.runtimeMode === "minimal") {
|
|
22034
22601
|
this._lastPresentation = rr.presentation ? { ...rr.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await this.applyRuntimePresentation("hide");
|
|
22035
22602
|
} else {
|
|
22036
22603
|
await this.recoverNormalAgentsPresentation(this._lastLifecycle);
|
|
22037
22604
|
}
|
|
22038
22605
|
const life = "adapterPid=" + this._lastLifecycle.adapterPid + " supervisorPid=" + this._lastLifecycle.supervisorPid + " reused=" + this._lastLifecycle.reusedSupervisor + " reason=" + this._lastLifecycle.launchReason;
|
|
22039
|
-
if (!rr.ok && this._lastLifecycle.status !== "agents-workspace-ready") {
|
|
22606
|
+
if ((!rr.ok || this._lastLifecycle.status === "workspace-not-ready") && this._lastLifecycle.status !== "agents-workspace-ready") {
|
|
22040
22607
|
throw new Error([rr.message || `Cursor lifecycle failed: ${rr.status}`, rr.nextStep].filter(Boolean).join(" "));
|
|
22041
22608
|
}
|
|
22042
22609
|
if (this._lastLifecycle.status === "agents-workspace-ready") {
|
|
@@ -22202,6 +22769,8 @@ var CursorBridge = class {
|
|
|
22202
22769
|
await this._bindFifoAgentAfterComposerReady(c, options, historyBefore);
|
|
22203
22770
|
await this._bindFifoComposerIdentity(c, options);
|
|
22204
22771
|
this._throwIfCancelledBeforeSend(options);
|
|
22772
|
+
await this._applyModelPreference(c, options.modelPreference, options);
|
|
22773
|
+
this._throwIfCancelledBeforeSend(options);
|
|
22205
22774
|
const filled = await evalJS(c, exprFill(prompt));
|
|
22206
22775
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the query because the input state was invalid");
|
|
22207
22776
|
await sleep2(450);
|
|
@@ -22218,10 +22787,8 @@ var CursorBridge = class {
|
|
|
22218
22787
|
await this._confirmSubmission(c, baseline.messageCount || 0, providerErrorBaseline);
|
|
22219
22788
|
options.sendState = "sent";
|
|
22220
22789
|
options.sentAt = options.sentAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
await this._bindFifoComposerIdentity(c, options);
|
|
22224
|
-
}
|
|
22790
|
+
await this._bindFifoAgentAfterSend(c, options, historyBefore, providerErrorBaseline);
|
|
22791
|
+
await this._bindFifoComposerIdentity(c, options);
|
|
22225
22792
|
return await this._waitComplete(
|
|
22226
22793
|
c,
|
|
22227
22794
|
options.timeoutMs || QUERY_TIMEOUT,
|
|
@@ -22250,6 +22817,176 @@ var CursorBridge = class {
|
|
|
22250
22817
|
error2.preSend = true;
|
|
22251
22818
|
throw error2;
|
|
22252
22819
|
}
|
|
22820
|
+
async _readModelPickerTrigger(c) {
|
|
22821
|
+
try {
|
|
22822
|
+
return JSON.parse(await evalJS(c, EXPR_MODEL_PICKER_TRIGGER) || "{}");
|
|
22823
|
+
} catch {
|
|
22824
|
+
return { found: false, state: "trigger_unreadable" };
|
|
22825
|
+
}
|
|
22826
|
+
}
|
|
22827
|
+
async _readModelPickerRows(c) {
|
|
22828
|
+
try {
|
|
22829
|
+
const snapshot = JSON.parse(await evalJS(c, EXPR_MODEL_PICKER_ROWS) || "{}");
|
|
22830
|
+
return { open: snapshot.open === true, rows: Array.isArray(snapshot.rows) ? snapshot.rows : [] };
|
|
22831
|
+
} catch {
|
|
22832
|
+
return { open: false, rows: [] };
|
|
22833
|
+
}
|
|
22834
|
+
}
|
|
22835
|
+
async _clickModelPickerPoint(c, point) {
|
|
22836
|
+
if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y))) {
|
|
22837
|
+
throw new Error("Cursor model picker returned an invalid target");
|
|
22838
|
+
}
|
|
22839
|
+
const x = Number(point.x);
|
|
22840
|
+
const y = Number(point.y);
|
|
22841
|
+
await c.send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
|
|
22842
|
+
await c.send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
|
|
22843
|
+
}
|
|
22844
|
+
async _hoverModelPickerPoint(c, point) {
|
|
22845
|
+
if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y))) return;
|
|
22846
|
+
await c.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: Number(point.x), y: Number(point.y) });
|
|
22847
|
+
}
|
|
22848
|
+
async _openModelPicker(c) {
|
|
22849
|
+
const trigger = await this._readModelPickerTrigger(c);
|
|
22850
|
+
if (!trigger.found) {
|
|
22851
|
+
throw new Error("Cursor model picker is unavailable in the active Agent composer");
|
|
22852
|
+
}
|
|
22853
|
+
let snapshot = await this._readModelPickerRows(c);
|
|
22854
|
+
if (!snapshot.open) {
|
|
22855
|
+
await this._clickModelPickerPoint(c, trigger);
|
|
22856
|
+
await sleep2(450);
|
|
22857
|
+
snapshot = await this._readModelPickerRows(c);
|
|
22858
|
+
}
|
|
22859
|
+
if (!snapshot.open) throw new Error("Cursor model picker did not open");
|
|
22860
|
+
return { trigger, ...snapshot };
|
|
22861
|
+
}
|
|
22862
|
+
async _closeModelPicker(c) {
|
|
22863
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
22864
|
+
const snapshot = await this._readModelPickerRows(c);
|
|
22865
|
+
if (!snapshot.open) return;
|
|
22866
|
+
await c.send("Input.dispatchKeyEvent", { type: "keyDown", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });
|
|
22867
|
+
await c.send("Input.dispatchKeyEvent", { type: "keyUp", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });
|
|
22868
|
+
await sleep2(150);
|
|
22869
|
+
}
|
|
22870
|
+
if ((await this._readModelPickerRows(c)).open) throw new Error("Cursor model picker did not close after selection");
|
|
22871
|
+
}
|
|
22872
|
+
async _openModelPickerControl(c, snapshot, controlKind) {
|
|
22873
|
+
const control = (snapshot && snapshot.rows || []).find((row) => row.kind === controlKind && row.disabled !== true);
|
|
22874
|
+
if (!control) return snapshot;
|
|
22875
|
+
await this._clickModelPickerPoint(c, control);
|
|
22876
|
+
await sleep2(400);
|
|
22877
|
+
let next = await this._readModelPickerRows(c);
|
|
22878
|
+
const expectedKind = controlKind === "model_control" ? "model" : "parameter";
|
|
22879
|
+
if (!next.rows.some((row) => row.kind === expectedKind)) {
|
|
22880
|
+
await this._hoverModelPickerPoint(c, control);
|
|
22881
|
+
await sleep2(350);
|
|
22882
|
+
next = await this._readModelPickerRows(c);
|
|
22883
|
+
}
|
|
22884
|
+
return next;
|
|
22885
|
+
}
|
|
22886
|
+
async _findModelPickerModel(c, snapshot, requestedModel) {
|
|
22887
|
+
let modelRow = selectModelPickerRow(snapshot && snapshot.rows, requestedModel, "model");
|
|
22888
|
+
if (modelRow) return { snapshot, modelRow };
|
|
22889
|
+
const expanded = await this._openModelPickerControl(c, snapshot, "model_control");
|
|
22890
|
+
modelRow = selectModelPickerRow(expanded && expanded.rows, requestedModel, "model");
|
|
22891
|
+
return { snapshot: expanded, modelRow };
|
|
22892
|
+
}
|
|
22893
|
+
async _selectedEffortRow(c, modelRow, effort) {
|
|
22894
|
+
let snapshot = await this._readModelPickerRows(c);
|
|
22895
|
+
let effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
22896
|
+
if (!effortRow) {
|
|
22897
|
+
snapshot = await this._openModelPickerControl(c, snapshot, "effort_control");
|
|
22898
|
+
effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
22899
|
+
}
|
|
22900
|
+
if (effortRow) return effortRow;
|
|
22901
|
+
await this._hoverModelPickerPoint(c, modelRow);
|
|
22902
|
+
await sleep2(400);
|
|
22903
|
+
snapshot = await this._readModelPickerRows(c);
|
|
22904
|
+
effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
22905
|
+
if (!effortRow && modelRow && modelRow.hasSubmenu) {
|
|
22906
|
+
await this._clickModelPickerPoint(c, modelRow);
|
|
22907
|
+
await sleep2(350);
|
|
22908
|
+
snapshot = await this._readModelPickerRows(c);
|
|
22909
|
+
effortRow = selectModelPickerRow(snapshot.rows, cursorEffortUiValue(effort), "parameter");
|
|
22910
|
+
}
|
|
22911
|
+
return effortRow;
|
|
22912
|
+
}
|
|
22913
|
+
async _applyModelPreference(c, preference, job) {
|
|
22914
|
+
if (!preference) {
|
|
22915
|
+
if (job) job.modelSelection = null;
|
|
22916
|
+
return null;
|
|
22917
|
+
}
|
|
22918
|
+
const requestedModel = String(preference.model || "").trim();
|
|
22919
|
+
const requestedEffort = preference.effort ? normalizeCursorModelEffort(preference.effort, "") : null;
|
|
22920
|
+
const opened = await this._openModelPicker(c);
|
|
22921
|
+
let located = await this._findModelPickerModel(c, opened, requestedModel);
|
|
22922
|
+
let modelRow = located.modelRow;
|
|
22923
|
+
if (!modelRow) {
|
|
22924
|
+
throw new Error(`Configured Cursor model is unavailable or ambiguous: ${requestedModel}`);
|
|
22925
|
+
}
|
|
22926
|
+
let selectedEffortRow = null;
|
|
22927
|
+
if (requestedEffort && modelRow.hasSubmenu) {
|
|
22928
|
+
selectedEffortRow = await this._selectedEffortRow(c, modelRow, requestedEffort);
|
|
22929
|
+
if (!selectedEffortRow) {
|
|
22930
|
+
throw new Error(`Cursor model ${requestedModel} does not expose effort ${requestedEffort}`);
|
|
22931
|
+
}
|
|
22932
|
+
if (!selectedEffortRow.selected || !modelRow.selected) {
|
|
22933
|
+
await this._clickModelPickerPoint(c, selectedEffortRow);
|
|
22934
|
+
await sleep2(550);
|
|
22935
|
+
}
|
|
22936
|
+
} else if (!modelRow.selected) {
|
|
22937
|
+
await this._clickModelPickerPoint(c, modelRow);
|
|
22938
|
+
await sleep2(550);
|
|
22939
|
+
}
|
|
22940
|
+
let trigger = await this._readModelPickerTrigger(c);
|
|
22941
|
+
if (!trigger.found || !normalizeModelPickerText(trigger.text).includes(normalizeModelPickerText(requestedModel))) {
|
|
22942
|
+
const reopened = await this._openModelPicker(c);
|
|
22943
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
22944
|
+
const selected = selectModelPickerRow(located.snapshot.rows.filter((row) => row.selected), requestedModel, "model");
|
|
22945
|
+
if (!selected) throw new Error(`Cursor did not confirm configured model ${requestedModel}`);
|
|
22946
|
+
modelRow = selected;
|
|
22947
|
+
}
|
|
22948
|
+
let effectiveEffort = null;
|
|
22949
|
+
if (requestedEffort) {
|
|
22950
|
+
const reopened = await this._openModelPicker(c);
|
|
22951
|
+
located = await this._findModelPickerModel(c, reopened, requestedModel);
|
|
22952
|
+
modelRow = located.modelRow;
|
|
22953
|
+
if (!modelRow) throw new Error(`Cursor model row disappeared while applying effort: ${requestedModel}`);
|
|
22954
|
+
let effortRow = await this._selectedEffortRow(c, modelRow, requestedEffort);
|
|
22955
|
+
if (!effortRow) {
|
|
22956
|
+
throw new Error(`Cursor model ${requestedModel} does not expose effort ${requestedEffort}`);
|
|
22957
|
+
}
|
|
22958
|
+
if (!effortRow.selected) {
|
|
22959
|
+
await this._clickModelPickerPoint(c, effortRow);
|
|
22960
|
+
await sleep2(450);
|
|
22961
|
+
}
|
|
22962
|
+
trigger = await this._readModelPickerTrigger(c);
|
|
22963
|
+
const detailMatches = normalizeModelPickerText(`${trigger.detail || ""} ${trigger.text || ""}`).includes(normalizeModelPickerText(requestedEffort));
|
|
22964
|
+
if (!detailMatches) {
|
|
22965
|
+
const verify = await this._openModelPicker(c);
|
|
22966
|
+
const verifiedModel = await this._findModelPickerModel(c, verify, requestedModel);
|
|
22967
|
+
const selectedModel = verifiedModel.modelRow;
|
|
22968
|
+
effortRow = await this._selectedEffortRow(c, selectedModel, requestedEffort);
|
|
22969
|
+
if (!effortRow || !effortRow.selected) {
|
|
22970
|
+
throw new Error(`Cursor did not confirm effort ${requestedEffort} for model ${requestedModel}`);
|
|
22971
|
+
}
|
|
22972
|
+
}
|
|
22973
|
+
effectiveEffort = requestedEffort;
|
|
22974
|
+
}
|
|
22975
|
+
await this._closeModelPicker(c);
|
|
22976
|
+
trigger = await this._readModelPickerTrigger(c);
|
|
22977
|
+
const result = {
|
|
22978
|
+
configured: true,
|
|
22979
|
+
applied: true,
|
|
22980
|
+
requestedModel,
|
|
22981
|
+
requestedEffort,
|
|
22982
|
+
effectiveModel: trigger.text || modelRow.text,
|
|
22983
|
+
effectiveEffort,
|
|
22984
|
+
pickerDetail: trigger.detail || null,
|
|
22985
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22986
|
+
};
|
|
22987
|
+
if (job) job.modelSelection = result;
|
|
22988
|
+
return result;
|
|
22989
|
+
}
|
|
22253
22990
|
async _ensureChatPanel(c) {
|
|
22254
22991
|
let vis = await evalJS(c, EXPR_VISIBLE);
|
|
22255
22992
|
if (!vis) {
|
|
@@ -22272,7 +23009,7 @@ var CursorBridge = class {
|
|
|
22272
23009
|
const created = JSON.parse(await evalJS(c, exprCreateAgentForWorkspace(options.projectPath)) || "{}");
|
|
22273
23010
|
if (!created.ok) {
|
|
22274
23011
|
const available = Array.isArray(created.available) ? `; available=${created.available.join(", ")}` : "";
|
|
22275
|
-
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted ||
|
|
23012
|
+
throw new Error(`Cursor Agents workspace binding failed: ${created.state || "unknown"}; wanted=${created.wanted || basename4(options.projectPath)}${available}`);
|
|
22276
23013
|
}
|
|
22277
23014
|
await sleep2(1100);
|
|
22278
23015
|
return true;
|
|
@@ -22383,12 +23120,31 @@ var CursorBridge = class {
|
|
|
22383
23120
|
}
|
|
22384
23121
|
}
|
|
22385
23122
|
async _bindFifoAgentAfterSend(c, job, beforeEntries, providerErrorBaseline) {
|
|
22386
|
-
if (!this._canBindFifoHistory(job) || !Array.isArray(beforeEntries)
|
|
22387
|
-
for (let i = 0; i < 24
|
|
23123
|
+
if (!this._canBindFifoHistory(job) || !Array.isArray(beforeEntries)) return;
|
|
23124
|
+
for (let i = 0; i < 24; i++) {
|
|
22388
23125
|
await sleep2(350);
|
|
22389
23126
|
await this._throwIfNewProviderError(c, providerErrorBaseline);
|
|
22390
|
-
|
|
22391
|
-
|
|
23127
|
+
let entries = null;
|
|
23128
|
+
try {
|
|
23129
|
+
entries = await this._readAgentEntries(c);
|
|
23130
|
+
} catch {
|
|
23131
|
+
}
|
|
23132
|
+
if (!Array.isArray(entries)) continue;
|
|
23133
|
+
const promoted = selectPromotedFifoEntry(beforeEntries, job.agentId, entries);
|
|
23134
|
+
if (promoted) {
|
|
23135
|
+
job.provisionalAgentId = job.agentId || null;
|
|
23136
|
+
this._applyAgentIdentity(job, promoted);
|
|
23137
|
+
return;
|
|
23138
|
+
}
|
|
23139
|
+
const current = job.agentId ? entries.find((entry) => entry && entry.id === job.agentId) : null;
|
|
23140
|
+
if (current && isDurablyRegisteredParallelEntry(current)) return;
|
|
23141
|
+
if (!job.agentId) {
|
|
23142
|
+
const candidate = selectNewAgentEntry(beforeEntries, entries);
|
|
23143
|
+
if (candidate && isDurablyRegisteredParallelEntry(candidate)) {
|
|
23144
|
+
this._applyAgentIdentity(job, candidate);
|
|
23145
|
+
return;
|
|
23146
|
+
}
|
|
23147
|
+
}
|
|
22392
23148
|
}
|
|
22393
23149
|
}
|
|
22394
23150
|
async _confirmSubmission(c, baselineCount = 0, providerErrorBaseline = "") {
|
|
@@ -22481,6 +23237,8 @@ var CursorBridge = class {
|
|
|
22481
23237
|
await this._closeHistory(c);
|
|
22482
23238
|
await this._ensureChatPanel(c);
|
|
22483
23239
|
this._throwIfCancelledBeforeSend(job);
|
|
23240
|
+
await this._applyModelPreference(c, job.modelPreference, job);
|
|
23241
|
+
this._throwIfCancelledBeforeSend(job);
|
|
22484
23242
|
const filled = await evalJS(c, exprFill(job.prompt));
|
|
22485
23243
|
if (filled === "NO_INPUT" || filled === "EXEC_FAIL") throw new Error("Failed to enter the parallel_agent task");
|
|
22486
23244
|
await sleep2(350);
|
|
@@ -22537,8 +23295,25 @@ var CursorBridge = class {
|
|
|
22537
23295
|
const c = makeClient(page.webSocketDebuggerUrl);
|
|
22538
23296
|
await c.ready;
|
|
22539
23297
|
try {
|
|
22540
|
-
|
|
22541
|
-
|
|
23298
|
+
let entries = await this._readAgentEntries(c);
|
|
23299
|
+
let entry = entries.find((e) => e.id === job.agentId) || null;
|
|
23300
|
+
if (!entry && (job.execution === "fifo" || job.effectiveExecution === "fifo")) {
|
|
23301
|
+
const promoted = selectPromotedFifoEntry(job.historyBeforeEntries, job.agentId, entries);
|
|
23302
|
+
if (promoted) {
|
|
23303
|
+
job.provisionalAgentId = job.agentId || null;
|
|
23304
|
+
this._applyAgentIdentity(job, promoted);
|
|
23305
|
+
entry = promoted;
|
|
23306
|
+
}
|
|
23307
|
+
}
|
|
23308
|
+
if (entry && entry.registeredBySectionMap && classifyParallelTerminalIcon(entry.icon) === "unknown") {
|
|
23309
|
+
const opened = await evalJS(c, exprOpenAgent(job.agentId));
|
|
23310
|
+
if (opened === "OPENED") {
|
|
23311
|
+
await sleep2(350);
|
|
23312
|
+
entries = await this._readAgentEntries(c);
|
|
23313
|
+
entry = entries.find((e) => e.id === job.agentId) || entry;
|
|
23314
|
+
}
|
|
23315
|
+
}
|
|
23316
|
+
return entry;
|
|
22542
23317
|
} finally {
|
|
22543
23318
|
c.close();
|
|
22544
23319
|
}
|
|
@@ -23287,8 +24062,11 @@ var CursorBridge = class {
|
|
|
23287
24062
|
effectiveExecution: job.effectiveExecution,
|
|
23288
24063
|
readOnly: job.readOnly,
|
|
23289
24064
|
allowedPaths: job.allowedPaths,
|
|
24065
|
+
modelPreference: job.modelPreference,
|
|
24066
|
+
modelSelection: job.modelSelection,
|
|
23290
24067
|
projectPath: job.projectPath,
|
|
23291
24068
|
agentId: job.agentId,
|
|
24069
|
+
provisionalAgentId: job.provisionalAgentId || null,
|
|
23292
24070
|
agentLabel: job.agentLabel,
|
|
23293
24071
|
targetId: job.targetId,
|
|
23294
24072
|
targetUiFlavor: job.targetUiFlavor,
|
|
@@ -23335,8 +24113,8 @@ var CursorBridge = class {
|
|
|
23335
24113
|
async status(taskId = "") {
|
|
23336
24114
|
if (taskId) {
|
|
23337
24115
|
const job = this.tasks.get(String(taskId));
|
|
23338
|
-
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView() };
|
|
23339
|
-
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this._taskView(job, true) };
|
|
24116
|
+
if (!job) return { found: false, taskId: String(taskId), ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView() };
|
|
24117
|
+
return { found: true, ...this.workspaceView(), ...this.delegationView(), ...this.runtimeModeView(), ...this.modelPreferencesView(), ...this._taskView(job, true) };
|
|
23340
24118
|
}
|
|
23341
24119
|
const parallelRunning = this.activeParallel.size;
|
|
23342
24120
|
const uiBusy = this.busy;
|
|
@@ -23347,6 +24125,7 @@ var CursorBridge = class {
|
|
|
23347
24125
|
...this.workspaceView(),
|
|
23348
24126
|
...this.delegationView(),
|
|
23349
24127
|
...this.runtimeModeView(),
|
|
24128
|
+
...this.modelPreferencesView(),
|
|
23350
24129
|
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
23351
24130
|
uiBusy,
|
|
23352
24131
|
parallelRunning,
|
|
@@ -23366,6 +24145,11 @@ var CursorBridge = class {
|
|
|
23366
24145
|
launchReason: null,
|
|
23367
24146
|
status: null,
|
|
23368
24147
|
spawnMethod: null,
|
|
24148
|
+
lifecycleMode: null,
|
|
24149
|
+
persistent: null,
|
|
24150
|
+
degradedReason: null,
|
|
24151
|
+
spawnErrorCode: null,
|
|
24152
|
+
capabilities: null,
|
|
23369
24153
|
cursorPid: null,
|
|
23370
24154
|
runtimeMode: this.runtimeMode,
|
|
23371
24155
|
presentation: null
|
|
@@ -23451,9 +24235,23 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
23451
24235
|
required: ["mode"]
|
|
23452
24236
|
}
|
|
23453
24237
|
},
|
|
24238
|
+
{
|
|
24239
|
+
name: "cursor_model",
|
|
24240
|
+
description: "Show, set, or reset persistent Cursor model defaults for CCE and cursor_do. Settings are independent per target and survive host tasks, MCP restarts, and Cursor Bridge restarts until the user explicitly changes or resets them. When configured, Bridge applies the model and optional effort in every newly created Cursor Agent before sending the prompt, then verifies the visible selection. An unavailable, ambiguous, unsupported, or unconfirmed selection fails before prompt submission instead of silently falling back to Auto. Use set/reset only when the user explicitly asks to change these defaults; use show for read-only inspection.",
|
|
24241
|
+
inputSchema: {
|
|
24242
|
+
type: "object",
|
|
24243
|
+
properties: {
|
|
24244
|
+
action: { type: "string", enum: ["show", "set", "reset"], description: "show reads current defaults; set persists a model and optional effort; reset restores Cursor default selection for the target." },
|
|
24245
|
+
target: { type: "string", enum: ["cce", "cursor_do", "both"], description: "Required for set/reset. CCE and cursor_do keep independent defaults; both changes both targets together." },
|
|
24246
|
+
model: { type: "string", description: "Required for set. Use a model ID or display name currently available in the signed-in Cursor account." },
|
|
24247
|
+
effort: { type: "string", enum: [...CURSOR_MODEL_EFFORTS], description: "Optional reasoning effort for set. Omit it to use that model's Cursor default." }
|
|
24248
|
+
},
|
|
24249
|
+
required: ["action"]
|
|
24250
|
+
}
|
|
24251
|
+
},
|
|
23454
24252
|
{
|
|
23455
24253
|
name: "cursor_status",
|
|
23456
|
-
description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, and normal/minimal runtime presentation. Pass a task ID to read its
|
|
24254
|
+
description: "Read-only snapshot of Cursor connectivity, queued/running work, reservations, execution availability, persistent model/effort defaults, and normal/minimal runtime presentation. Pass a task ID to read its configured and effective model selection plus any result already collected; this tool never switches Agents, reconciles, or stops work.",
|
|
23457
24255
|
inputSchema: { type: "object", properties: { task_id: { type: "string", description: "A task ID returned by cursor_do. Omit it for an overall status view." } } }
|
|
23458
24256
|
}
|
|
23459
24257
|
].filter(Boolean);
|
|
@@ -23461,7 +24259,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
23461
24259
|
var ADAPTER_START_CWD = process.cwd();
|
|
23462
24260
|
var bridge = new CursorBridge({ adapterStartCwd: ADAPTER_START_CWD });
|
|
23463
24261
|
var server = new Server(
|
|
23464
|
-
{ name: "cursor-bridge", version: "5.
|
|
24262
|
+
{ name: "cursor-bridge", version: "5.6.1" },
|
|
23465
24263
|
{ capabilities: { tools: { listChanged: true } } }
|
|
23466
24264
|
);
|
|
23467
24265
|
async function ensureBridgeCursor(targetBridge, reason) {
|
|
@@ -23473,31 +24271,7 @@ async function ensureBridgeCursor(targetBridge, reason) {
|
|
|
23473
24271
|
adapterStartCwd: targetBridge.adapterStartCwd,
|
|
23474
24272
|
...targetBridge.projectPath ? { projectPath: targetBridge.projectPath } : {}
|
|
23475
24273
|
});
|
|
23476
|
-
targetBridge._lastLifecycle =
|
|
23477
|
-
adapterPid: r.adapterPid ?? process.pid,
|
|
23478
|
-
supervisorPid: r.supervisorPid ?? null,
|
|
23479
|
-
reusedSupervisor: !!r.reusedSupervisor,
|
|
23480
|
-
createdSupervisor: !!r.createdSupervisor,
|
|
23481
|
-
launchReason: r.launchReason || r.status,
|
|
23482
|
-
status: r.status,
|
|
23483
|
-
spawnMethod: r.spawnMethod || null,
|
|
23484
|
-
cursorPid: r.cursorPid || null,
|
|
23485
|
-
runtimeMode: r.runtimeMode || targetBridge.runtimeMode,
|
|
23486
|
-
projectPath: r.projectPath || null,
|
|
23487
|
-
targetId: r.targetId || null,
|
|
23488
|
-
workspaceAction: r.workspaceAction || null,
|
|
23489
|
-
presentation: r.presentation || null,
|
|
23490
|
-
windowGuard: r.windowGuard || null,
|
|
23491
|
-
startupWindowGuard: r.startupWindowGuard || null,
|
|
23492
|
-
message: r.message || null,
|
|
23493
|
-
needsAction: r.needsAction || null,
|
|
23494
|
-
nextStep: r.nextStep || null,
|
|
23495
|
-
retryable: r.retryable === true,
|
|
23496
|
-
cursorExecutable: r.cursorExecutable || null,
|
|
23497
|
-
cursorExecutableSource: r.cursorExecutableSource || null,
|
|
23498
|
-
runtimeFingerprint: r.runtimeFingerprint || null,
|
|
23499
|
-
runtimeScript: r.runtimeScript || null
|
|
23500
|
-
};
|
|
24274
|
+
targetBridge._lastLifecycle = lifecycleFromEnsureResult(r, targetBridge.runtimeMode);
|
|
23501
24275
|
if (targetBridge.runtimeMode === "minimal") {
|
|
23502
24276
|
targetBridge._lastPresentation = r.presentation ? { ...r.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await targetBridge.applyRuntimePresentation("hide");
|
|
23503
24277
|
}
|
|
@@ -23550,6 +24324,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
23550
24324
|
}
|
|
23551
24325
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
23552
24326
|
}
|
|
24327
|
+
if (name === "cursor_model") {
|
|
24328
|
+
const result = bridge.configureModelPreferences({
|
|
24329
|
+
action: args && args.action,
|
|
24330
|
+
target: args && args.target,
|
|
24331
|
+
model: args && args.model,
|
|
24332
|
+
effort: args && args.effort
|
|
24333
|
+
});
|
|
24334
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
24335
|
+
}
|
|
23553
24336
|
if (name === "cursor_status") {
|
|
23554
24337
|
const statusMs = Math.max(1e3, Number(process.env.CURSOR_BRIDGE_STATUS_TIMEOUT || 8e3));
|
|
23555
24338
|
let result;
|
|
@@ -23607,11 +24390,15 @@ if (isMain2) {
|
|
|
23607
24390
|
});
|
|
23608
24391
|
}
|
|
23609
24392
|
export {
|
|
24393
|
+
CURSOR_MODEL_EFFORTS,
|
|
24394
|
+
CURSOR_MODEL_TARGETS,
|
|
23610
24395
|
CURSOR_RUNTIME_MODES,
|
|
23611
24396
|
CursorBridge,
|
|
23612
24397
|
EXPR_CLICK_SEND,
|
|
23613
24398
|
EXPR_FIND_NEWAGENT,
|
|
23614
24399
|
EXPR_HISTORY_ENTRIES,
|
|
24400
|
+
EXPR_MODEL_PICKER_ROWS,
|
|
24401
|
+
EXPR_MODEL_PICKER_TRIGGER,
|
|
23615
24402
|
EXPR_PAGE_CAPABILITIES,
|
|
23616
24403
|
EXPR_PROVIDER_ERROR,
|
|
23617
24404
|
EXPR_VISIBLE,
|
|
@@ -23635,16 +24422,20 @@ export {
|
|
|
23635
24422
|
isTargetedStopConfirmed,
|
|
23636
24423
|
normalizeAllowedPath,
|
|
23637
24424
|
normalizeCceSearchResult,
|
|
24425
|
+
normalizeCursorModelEffort,
|
|
23638
24426
|
normalizeCursorRuntimeMode,
|
|
23639
24427
|
normalizeDelegationMode,
|
|
24428
|
+
normalizeModelPickerText,
|
|
23640
24429
|
pathsOverlap,
|
|
23641
24430
|
promoteAgentsWorkspaceLifecycle,
|
|
23642
24431
|
providerErrorSignature,
|
|
23643
24432
|
releaseAdapterWorkingDirectory,
|
|
23644
24433
|
scoreCursorPageCandidate,
|
|
23645
24434
|
selectCursorPageCandidate,
|
|
24435
|
+
selectModelPickerRow,
|
|
23646
24436
|
selectNewAgentEntry,
|
|
23647
24437
|
selectPageForUiPreference,
|
|
24438
|
+
selectPromotedFifoEntry,
|
|
23648
24439
|
shouldAutoLaunchCursor,
|
|
23649
24440
|
shouldRecoverNormalAgentsPresentation,
|
|
23650
24441
|
summarizeCdpPages,
|