pi-cursor-bridge 0.1.5 → 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 +425 -120
- package/dist/cursor-lifecycle-supervisor.mjs +144 -18
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.1+codex.20260829070740",
|
|
4
4
|
"description": "Evidence-backed Cursor Context Engine search and bounded Cursor Agent execution, including a UI-suppressed minimal runtime.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vanyangyang"
|
package/README.md
CHANGED
|
@@ -8,6 +8,6 @@ pi install npm:pi-cursor-bridge
|
|
|
8
8
|
|
|
9
9
|
Restart Pi after installation. Initialize the current project by asking Pi to initialize Cursor Bridge for the absolute project path, then use it normally. The package includes the `cce-routing` and `cursor-delegate` Skills and registers the native Cursor Bridge MCP tools directly in Pi.
|
|
10
10
|
|
|
11
|
-
This Pi package embeds Cursor Bridge 5.6.
|
|
11
|
+
This Pi package embeds Cursor Bridge 5.6.1. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
|
|
12
12
|
|
|
13
13
|
Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/master/README.zh-CN.md)
|
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -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,
|
|
@@ -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";
|
|
@@ -11303,12 +11357,14 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
11303
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 {
|
|
@@ -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
|
}
|
|
@@ -11760,16 +11945,23 @@ function writeRuntimeFile(target, content) {
|
|
|
11760
11945
|
}
|
|
11761
11946
|
}
|
|
11762
11947
|
function materializeLifecycleSupervisorRuntime({ sourceScript, dir = defaultLifecycleDir() } = {}) {
|
|
11763
|
-
const
|
|
11764
|
-
|
|
11765
|
-
const content = readFileSync4(source);
|
|
11766
|
-
const fingerprint = createHash2("sha256").update(content).digest("hex");
|
|
11948
|
+
const described = describeLifecycleSupervisorRuntime({ sourceScript, dir });
|
|
11949
|
+
const { sourceScript: source, content, fingerprint } = described;
|
|
11767
11950
|
const runtimeRoot = join6(ensureLifecycleDir(dir), "runtime", `supervisor-${fingerprint.slice(0, 20)}`);
|
|
11768
11951
|
mkdirSync5(runtimeRoot, { recursive: true });
|
|
11769
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 {
|
|
@@ -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);
|
|
@@ -11890,65 +12104,87 @@ function writeBootEnv(dir, extra = {}) {
|
|
|
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: dirname4(resolve5(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({
|
|
@@ -20847,7 +21142,7 @@ function updateCursorModelPreferences(filePath, { action, target, model, effort
|
|
|
20847
21142
|
init_workspace_binding();
|
|
20848
21143
|
init_cursor_ensure_core();
|
|
20849
21144
|
init_lifecycle_paths();
|
|
20850
|
-
var PLUGIN_VERSION = "5.6.
|
|
21145
|
+
var PLUGIN_VERSION = "5.6.1";
|
|
20851
21146
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
20852
21147
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
20853
21148
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -21727,6 +22022,40 @@ function promoteAgentsWorkspaceLifecycle(lifecycle, agentsWorkspace) {
|
|
|
21727
22022
|
retryable: false
|
|
21728
22023
|
};
|
|
21729
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
|
+
}
|
|
21730
22059
|
function releaseAdapterWorkingDirectory({ targetDir = defaultLifecycleDir(), chdir = process.chdir } = {}) {
|
|
21731
22060
|
const target = ensureLifecycleDir(targetDir);
|
|
21732
22061
|
chdir(target);
|
|
@@ -21801,7 +22130,9 @@ var CursorBridge = class {
|
|
|
21801
22130
|
"port-not-cursor",
|
|
21802
22131
|
"no-exe",
|
|
21803
22132
|
"timeout",
|
|
21804
|
-
"workspace-not-ready"
|
|
22133
|
+
"workspace-not-ready",
|
|
22134
|
+
"external-launch-required",
|
|
22135
|
+
"spawn-blocked"
|
|
21805
22136
|
]);
|
|
21806
22137
|
if (!lifecycle || !recoverableStatuses.has(lifecycle.status)) throw error2;
|
|
21807
22138
|
return {
|
|
@@ -22244,42 +22575,35 @@ var CursorBridge = class {
|
|
|
22244
22575
|
adapterStartCwd: this.adapterStartCwd,
|
|
22245
22576
|
...this.projectPath ? { projectPath: this.projectPath } : {}
|
|
22246
22577
|
});
|
|
22247
|
-
this._lastLifecycle =
|
|
22248
|
-
adapterPid: rr.adapterPid ?? process.pid,
|
|
22249
|
-
supervisorPid: rr.supervisorPid ?? null,
|
|
22250
|
-
reusedSupervisor: !!rr.reusedSupervisor,
|
|
22251
|
-
createdSupervisor: !!rr.createdSupervisor,
|
|
22252
|
-
launchReason: rr.launchReason || rr.status,
|
|
22253
|
-
status: rr.status,
|
|
22254
|
-
spawnMethod: rr.spawnMethod || null,
|
|
22255
|
-
cursorPid: rr.cursorPid || null,
|
|
22256
|
-
runtimeMode: rr.runtimeMode || this.runtimeMode,
|
|
22257
|
-
projectPath: rr.projectPath || null,
|
|
22258
|
-
targetId: rr.targetId || null,
|
|
22259
|
-
workspaceAction: rr.workspaceAction || null,
|
|
22260
|
-
presentation: rr.presentation || null,
|
|
22261
|
-
message: rr.message || null,
|
|
22262
|
-
needsAction: rr.needsAction || null,
|
|
22263
|
-
nextStep: rr.nextStep || null,
|
|
22264
|
-
retryable: rr.retryable === true,
|
|
22265
|
-
cursorExecutable: rr.cursorExecutable || null,
|
|
22266
|
-
cursorExecutableSource: rr.cursorExecutableSource || null,
|
|
22267
|
-
runtimeFingerprint: rr.runtimeFingerprint || null,
|
|
22268
|
-
runtimeScript: rr.runtimeScript || null
|
|
22269
|
-
};
|
|
22578
|
+
this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
|
|
22270
22579
|
if (!rr.ok && rr.status === "workspace-not-ready" && rr.projectPath) {
|
|
22271
22580
|
const agentsWorkspace = await this._findAgentsWorkspace(rr.projectPath);
|
|
22272
22581
|
if (agentsWorkspace) {
|
|
22273
22582
|
this._lastLifecycle = promoteAgentsWorkspaceLifecycle(this._lastLifecycle, agentsWorkspace);
|
|
22274
22583
|
}
|
|
22275
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
|
+
}
|
|
22276
22600
|
if (this.runtimeMode === "minimal") {
|
|
22277
22601
|
this._lastPresentation = rr.presentation ? { ...rr.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await this.applyRuntimePresentation("hide");
|
|
22278
22602
|
} else {
|
|
22279
22603
|
await this.recoverNormalAgentsPresentation(this._lastLifecycle);
|
|
22280
22604
|
}
|
|
22281
22605
|
const life = "adapterPid=" + this._lastLifecycle.adapterPid + " supervisorPid=" + this._lastLifecycle.supervisorPid + " reused=" + this._lastLifecycle.reusedSupervisor + " reason=" + this._lastLifecycle.launchReason;
|
|
22282
|
-
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") {
|
|
22283
22607
|
throw new Error([rr.message || `Cursor lifecycle failed: ${rr.status}`, rr.nextStep].filter(Boolean).join(" "));
|
|
22284
22608
|
}
|
|
22285
22609
|
if (this._lastLifecycle.status === "agents-workspace-ready") {
|
|
@@ -23821,6 +24145,11 @@ var CursorBridge = class {
|
|
|
23821
24145
|
launchReason: null,
|
|
23822
24146
|
status: null,
|
|
23823
24147
|
spawnMethod: null,
|
|
24148
|
+
lifecycleMode: null,
|
|
24149
|
+
persistent: null,
|
|
24150
|
+
degradedReason: null,
|
|
24151
|
+
spawnErrorCode: null,
|
|
24152
|
+
capabilities: null,
|
|
23824
24153
|
cursorPid: null,
|
|
23825
24154
|
runtimeMode: this.runtimeMode,
|
|
23826
24155
|
presentation: null
|
|
@@ -23930,7 +24259,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
23930
24259
|
var ADAPTER_START_CWD = process.cwd();
|
|
23931
24260
|
var bridge = new CursorBridge({ adapterStartCwd: ADAPTER_START_CWD });
|
|
23932
24261
|
var server = new Server(
|
|
23933
|
-
{ name: "cursor-bridge", version: "5.6.
|
|
24262
|
+
{ name: "cursor-bridge", version: "5.6.1" },
|
|
23934
24263
|
{ capabilities: { tools: { listChanged: true } } }
|
|
23935
24264
|
);
|
|
23936
24265
|
async function ensureBridgeCursor(targetBridge, reason) {
|
|
@@ -23942,31 +24271,7 @@ async function ensureBridgeCursor(targetBridge, reason) {
|
|
|
23942
24271
|
adapterStartCwd: targetBridge.adapterStartCwd,
|
|
23943
24272
|
...targetBridge.projectPath ? { projectPath: targetBridge.projectPath } : {}
|
|
23944
24273
|
});
|
|
23945
|
-
targetBridge._lastLifecycle =
|
|
23946
|
-
adapterPid: r.adapterPid ?? process.pid,
|
|
23947
|
-
supervisorPid: r.supervisorPid ?? null,
|
|
23948
|
-
reusedSupervisor: !!r.reusedSupervisor,
|
|
23949
|
-
createdSupervisor: !!r.createdSupervisor,
|
|
23950
|
-
launchReason: r.launchReason || r.status,
|
|
23951
|
-
status: r.status,
|
|
23952
|
-
spawnMethod: r.spawnMethod || null,
|
|
23953
|
-
cursorPid: r.cursorPid || null,
|
|
23954
|
-
runtimeMode: r.runtimeMode || targetBridge.runtimeMode,
|
|
23955
|
-
projectPath: r.projectPath || null,
|
|
23956
|
-
targetId: r.targetId || null,
|
|
23957
|
-
workspaceAction: r.workspaceAction || null,
|
|
23958
|
-
presentation: r.presentation || null,
|
|
23959
|
-
windowGuard: r.windowGuard || null,
|
|
23960
|
-
startupWindowGuard: r.startupWindowGuard || null,
|
|
23961
|
-
message: r.message || null,
|
|
23962
|
-
needsAction: r.needsAction || null,
|
|
23963
|
-
nextStep: r.nextStep || null,
|
|
23964
|
-
retryable: r.retryable === true,
|
|
23965
|
-
cursorExecutable: r.cursorExecutable || null,
|
|
23966
|
-
cursorExecutableSource: r.cursorExecutableSource || null,
|
|
23967
|
-
runtimeFingerprint: r.runtimeFingerprint || null,
|
|
23968
|
-
runtimeScript: r.runtimeScript || null
|
|
23969
|
-
};
|
|
24274
|
+
targetBridge._lastLifecycle = lifecycleFromEnsureResult(r, targetBridge.runtimeMode);
|
|
23970
24275
|
if (targetBridge.runtimeMode === "minimal") {
|
|
23971
24276
|
targetBridge._lastPresentation = r.presentation ? { ...r.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await targetBridge.applyRuntimePresentation("hide");
|
|
23972
24277
|
}
|
|
@@ -181,6 +181,8 @@ function startMinimalWindowGuard(pid, options = {}) {
|
|
|
181
181
|
"-EncodedCommand",
|
|
182
182
|
encodePowerShell(script)
|
|
183
183
|
], { detached: !lifetime, stdio: "ignore", windowsHide: true });
|
|
184
|
+
if (child && typeof child.once === "function") child.once("error", () => {
|
|
185
|
+
});
|
|
184
186
|
if (!lifetime && child && typeof child.unref === "function") child.unref();
|
|
185
187
|
return {
|
|
186
188
|
started: true,
|
|
@@ -542,6 +544,58 @@ async function waitForCdp(maxMs = 3e4, stepMs = 1e3) {
|
|
|
542
544
|
}
|
|
543
545
|
return false;
|
|
544
546
|
}
|
|
547
|
+
async function spawnDetachedSafely(spawnImpl, file, args, spawnOptions) {
|
|
548
|
+
let child;
|
|
549
|
+
try {
|
|
550
|
+
child = spawnImpl(file, args, spawnOptions);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
return {
|
|
553
|
+
ok: false,
|
|
554
|
+
child: null,
|
|
555
|
+
error,
|
|
556
|
+
errorCode: error && typeof error === "object" && error.code != null ? String(error.code) : null
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
if (child && typeof child.once === "function") {
|
|
560
|
+
const startup = await new Promise((resolvePromise) => {
|
|
561
|
+
let settled = false;
|
|
562
|
+
const finish = (result) => {
|
|
563
|
+
if (settled) return;
|
|
564
|
+
settled = true;
|
|
565
|
+
child.off?.("spawn", onSpawn);
|
|
566
|
+
child.off?.("error", onError);
|
|
567
|
+
resolvePromise(result);
|
|
568
|
+
};
|
|
569
|
+
const onSpawn = () => finish({ ok: true });
|
|
570
|
+
const onError = (error) => finish({ ok: false, error });
|
|
571
|
+
child.once("spawn", onSpawn);
|
|
572
|
+
child.once("error", onError);
|
|
573
|
+
if (Number.isInteger(child.pid) && child.pid > 0) queueMicrotask(onSpawn);
|
|
574
|
+
});
|
|
575
|
+
if (!startup.ok) {
|
|
576
|
+
return {
|
|
577
|
+
ok: false,
|
|
578
|
+
child,
|
|
579
|
+
error: startup.error,
|
|
580
|
+
errorCode: startup.error && typeof startup.error === "object" && startup.error.code != null ? String(startup.error.code) : null
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
child.once("error", () => {
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
587
|
+
return { ok: true, child };
|
|
588
|
+
}
|
|
589
|
+
function attachedPresentation(runtimeMode, port) {
|
|
590
|
+
if (runtimeMode !== "minimal") return null;
|
|
591
|
+
return {
|
|
592
|
+
supported: true,
|
|
593
|
+
applied: false,
|
|
594
|
+
action: "hide",
|
|
595
|
+
port,
|
|
596
|
+
reason: "attached lifecycle cannot start the PowerShell window guard under the current process policy"
|
|
597
|
+
};
|
|
598
|
+
}
|
|
545
599
|
async function ensureCursorRunningLocal(options = {}) {
|
|
546
600
|
const waitMs = Number(options.waitMs || 3e4);
|
|
547
601
|
const runtimeMode = options.runtimeMode || "normal";
|
|
@@ -553,12 +607,14 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
553
607
|
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ? resolve2(String(options.projectPath)) : null : resolveProjectPath();
|
|
554
608
|
const listCdpPageTargetsImpl = options.listCdpPageTargetsImpl || listCdpPageTargets;
|
|
555
609
|
const spawnImpl = options.spawnImpl || spawn2;
|
|
610
|
+
const allowSpawn = options.allowSpawn !== false;
|
|
611
|
+
const allowProcessControl = options.allowProcessControl !== false;
|
|
556
612
|
if (await cdpUpImpl()) {
|
|
557
613
|
const isCursor = await cdpIsCursorImpl();
|
|
558
614
|
if (isCursor) {
|
|
559
|
-
const cursorPid2 = findCursorPidByPort(CDP_PORT);
|
|
560
|
-
const windowGuard2 = effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
561
|
-
const presentation2 = effectiveRuntimeMode === "minimal" ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : null;
|
|
615
|
+
const cursorPid2 = allowProcessControl ? findCursorPidByPort(CDP_PORT) : null;
|
|
616
|
+
const windowGuard2 = allowProcessControl && effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
617
|
+
const presentation2 = effectiveRuntimeMode === "minimal" ? allowProcessControl ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : attachedPresentation(effectiveRuntimeMode, CDP_PORT) : null;
|
|
562
618
|
const currentTargets = await listCdpPageTargetsImpl();
|
|
563
619
|
const projectKey = normalizeProjectKey(projectPath);
|
|
564
620
|
let targetId2 = projectKey ? PROJECT_TARGETS.get(projectKey) || null : currentTargets[0] && currentTargets[0].id || null;
|
|
@@ -587,6 +643,22 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
587
643
|
}
|
|
588
644
|
}
|
|
589
645
|
if (projectPath && existsSync(projectPath) && !targetId2) {
|
|
646
|
+
if (!allowSpawn) {
|
|
647
|
+
return {
|
|
648
|
+
ok: false,
|
|
649
|
+
status: "workspace-not-ready",
|
|
650
|
+
port: CDP_PORT,
|
|
651
|
+
cursorPid: cursorPid2,
|
|
652
|
+
runtimeMode: effectiveRuntimeMode,
|
|
653
|
+
projectPath,
|
|
654
|
+
presentation: presentation2,
|
|
655
|
+
windowGuard: windowGuard2,
|
|
656
|
+
needsAction: "open_workspace_in_cursor",
|
|
657
|
+
retryable: true,
|
|
658
|
+
nextStep: `Open workspace ${projectPath} in the existing Cursor Agents Window, then retry the same operation.`,
|
|
659
|
+
message: `CCE connected to Cursor, but the current workspace target for ${projectPath} is not ready and the current lifecycle cannot open a new window.`
|
|
660
|
+
};
|
|
661
|
+
}
|
|
590
662
|
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
591
663
|
const exe2 = cursorExecutable2 && cursorExecutable2.path;
|
|
592
664
|
if (!exe2) {
|
|
@@ -606,12 +678,28 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
606
678
|
};
|
|
607
679
|
}
|
|
608
680
|
const beforeTargetIds = new Set(currentTargets.map((target2) => target2.id));
|
|
609
|
-
const
|
|
681
|
+
const opened = await spawnDetachedSafely(spawnImpl, exe2, ["--new-window", projectPath], {
|
|
610
682
|
detached: true,
|
|
611
683
|
stdio: "ignore",
|
|
612
684
|
windowsHide: effectiveRuntimeMode === "minimal"
|
|
613
685
|
});
|
|
614
|
-
|
|
686
|
+
if (!opened.ok) {
|
|
687
|
+
return {
|
|
688
|
+
ok: false,
|
|
689
|
+
status: "spawn-blocked",
|
|
690
|
+
port: CDP_PORT,
|
|
691
|
+
cursorPid: cursorPid2,
|
|
692
|
+
runtimeMode: effectiveRuntimeMode,
|
|
693
|
+
projectPath,
|
|
694
|
+
presentation: presentation2,
|
|
695
|
+
windowGuard: windowGuard2,
|
|
696
|
+
errorCode: opened.errorCode,
|
|
697
|
+
needsAction: "open_workspace_in_cursor",
|
|
698
|
+
retryable: true,
|
|
699
|
+
nextStep: `Open workspace ${projectPath} in Cursor, then retry the same operation.`,
|
|
700
|
+
message: `Cursor Bridge could not open a new workspace window: ${opened.error instanceof Error ? opened.error.message : String(opened.error)}`
|
|
701
|
+
};
|
|
702
|
+
}
|
|
615
703
|
workspaceAction = "opened-new-window";
|
|
616
704
|
const openedTarget2 = await waitForNewCdpTarget(beforeTargetIds, 12e3, projectPath, listCdpPageTargetsImpl);
|
|
617
705
|
if (!openedTarget2) {
|
|
@@ -660,6 +748,21 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
660
748
|
message: `CCE cannot connect to Cursor because required local port ${CDP_PORT} is occupied by another program.`
|
|
661
749
|
};
|
|
662
750
|
}
|
|
751
|
+
if (!allowSpawn) {
|
|
752
|
+
return {
|
|
753
|
+
ok: false,
|
|
754
|
+
status: "external-launch-required",
|
|
755
|
+
port: CDP_PORT,
|
|
756
|
+
cursorPid: null,
|
|
757
|
+
runtimeMode: effectiveRuntimeMode,
|
|
758
|
+
projectPath,
|
|
759
|
+
presentation: attachedPresentation(effectiveRuntimeMode, CDP_PORT),
|
|
760
|
+
needsAction: "launch_cursor_with_cdp",
|
|
761
|
+
retryable: true,
|
|
762
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, open ${projectPath || "the target workspace"}, then retry the same operation.`,
|
|
763
|
+
message: `Cursor is not reachable on the configured CDP port ${CDP_PORT}, and the current lifecycle policy cannot launch it.`
|
|
764
|
+
};
|
|
765
|
+
}
|
|
663
766
|
if (cursorRunningImpl()) {
|
|
664
767
|
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
665
768
|
return {
|
|
@@ -697,13 +800,29 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
697
800
|
);
|
|
698
801
|
}
|
|
699
802
|
if (projectPath && existsSync(projectPath)) args.push(projectPath);
|
|
700
|
-
const
|
|
803
|
+
const launched = await spawnDetachedSafely(spawnImpl, exe, args, {
|
|
701
804
|
detached: true,
|
|
702
805
|
stdio: "ignore",
|
|
703
806
|
windowsHide: effectiveRuntimeMode === "minimal"
|
|
704
807
|
});
|
|
705
|
-
|
|
706
|
-
|
|
808
|
+
if (!launched.ok) {
|
|
809
|
+
return {
|
|
810
|
+
ok: false,
|
|
811
|
+
status: "spawn-blocked",
|
|
812
|
+
exe,
|
|
813
|
+
port: CDP_PORT,
|
|
814
|
+
cursorPid: null,
|
|
815
|
+
runtimeMode: effectiveRuntimeMode,
|
|
816
|
+
projectPath,
|
|
817
|
+
errorCode: launched.errorCode,
|
|
818
|
+
needsAction: "launch_cursor_manually",
|
|
819
|
+
retryable: true,
|
|
820
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, then retry the same operation.`,
|
|
821
|
+
message: `Cursor Bridge could not launch Cursor: ${launched.error instanceof Error ? launched.error.message : String(launched.error)}`
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const child = launched.child;
|
|
825
|
+
const startupWindowGuard = effectiveRuntimeMode === "minimal" ? startMinimalWindowGuard(child && child.pid) : null;
|
|
707
826
|
const up = await waitForCdp(waitMs);
|
|
708
827
|
if (!up) {
|
|
709
828
|
return {
|
|
@@ -1048,7 +1167,8 @@ async function startSupervisor(options = {}) {
|
|
|
1048
1167
|
tryRemove(sock);
|
|
1049
1168
|
}
|
|
1050
1169
|
const ensureLocal = await loadEnsure(ensureModule);
|
|
1051
|
-
|
|
1170
|
+
const ensureInflight = /* @__PURE__ */ new Map();
|
|
1171
|
+
let ensureTail = Promise.resolve();
|
|
1052
1172
|
let ensureCount = 0;
|
|
1053
1173
|
let lastEnsure = null;
|
|
1054
1174
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -1074,14 +1194,17 @@ async function startSupervisor(options = {}) {
|
|
|
1074
1194
|
if (typeof idleTimer.unref === "function") idleTimer.unref();
|
|
1075
1195
|
};
|
|
1076
1196
|
const runEnsure = async (request = {}) => {
|
|
1077
|
-
|
|
1078
|
-
|
|
1197
|
+
const requestRuntimeMode = request.runtimeMode || "normal";
|
|
1198
|
+
const requestProjectPath = Object.hasOwn(request, "projectPath") ? request.projectPath : null;
|
|
1199
|
+
const ensureKey = JSON.stringify([requestRuntimeMode, requestProjectPath]);
|
|
1200
|
+
if (ensureInflight.has(ensureKey)) return ensureInflight.get(ensureKey);
|
|
1201
|
+
const task = ensureTail.then(async () => {
|
|
1079
1202
|
ensureCount += 1;
|
|
1080
1203
|
const waitMs = Number(request.waitMs || 3e4);
|
|
1081
1204
|
const result = await ensureLocal({
|
|
1082
1205
|
waitMs,
|
|
1083
|
-
runtimeMode:
|
|
1084
|
-
projectPath:
|
|
1206
|
+
runtimeMode: requestRuntimeMode,
|
|
1207
|
+
projectPath: requestProjectPath
|
|
1085
1208
|
});
|
|
1086
1209
|
lastEnsure = {
|
|
1087
1210
|
...result,
|
|
@@ -1089,8 +1212,8 @@ async function startSupervisor(options = {}) {
|
|
|
1089
1212
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1090
1213
|
requestReason: request.reason || null,
|
|
1091
1214
|
requestAdapterPid: request.adapterPid || null,
|
|
1092
|
-
requestRuntimeMode
|
|
1093
|
-
requestProjectPath
|
|
1215
|
+
requestRuntimeMode,
|
|
1216
|
+
requestProjectPath
|
|
1094
1217
|
};
|
|
1095
1218
|
writeSupervisorDiag(logPath, "ensure-result", {
|
|
1096
1219
|
ok: !!result.ok,
|
|
@@ -1100,11 +1223,14 @@ async function startSupervisor(options = {}) {
|
|
|
1100
1223
|
ensureCount
|
|
1101
1224
|
});
|
|
1102
1225
|
return lastEnsure;
|
|
1103
|
-
})
|
|
1226
|
+
});
|
|
1227
|
+
ensureInflight.set(ensureKey, task);
|
|
1228
|
+
ensureTail = task.catch(() => {
|
|
1229
|
+
});
|
|
1104
1230
|
try {
|
|
1105
|
-
return await
|
|
1231
|
+
return await task;
|
|
1106
1232
|
} finally {
|
|
1107
|
-
ensureInflight
|
|
1233
|
+
if (ensureInflight.get(ensureKey) === task) ensureInflight.delete(ensureKey);
|
|
1108
1234
|
}
|
|
1109
1235
|
};
|
|
1110
1236
|
const server = net.createServer((socket) => {
|
package/extensions/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
|
|
|
10
10
|
export default createStdioMcpExtension({
|
|
11
11
|
label: "Cursor Bridge",
|
|
12
12
|
clientName: "pi-cursor-bridge",
|
|
13
|
-
packageVersion: "0.1.
|
|
13
|
+
packageVersion: "0.1.6",
|
|
14
14
|
serverName: "cursor-bridge",
|
|
15
15
|
serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
|
|
16
16
|
cwd: hostCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Use Cursor Context Engine and bounded Cursor Agent execution from the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
},
|
|
48
48
|
"piPackage": {
|
|
49
49
|
"embeddedProduct": "Cursor Bridge",
|
|
50
|
-
"embeddedProductVersion": "5.6.
|
|
50
|
+
"embeddedProductVersion": "5.6.1"
|
|
51
51
|
}
|
|
52
52
|
}
|