pi-cursor-bridge 0.1.7 → 0.1.9
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 +2524 -1201
- package/dist/cursor-lifecycle-supervisor.mjs +63 -27
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
|
@@ -313,6 +313,7 @@ __export(cursor_ensure_core_exports, {
|
|
|
313
313
|
resolveProjectPath: () => resolveProjectPath,
|
|
314
314
|
selectAgentsWindowTarget: () => selectAgentsWindowTarget,
|
|
315
315
|
selectNewCdpTarget: () => selectNewCdpTarget,
|
|
316
|
+
selectReusableProjectTarget: () => selectReusableProjectTarget,
|
|
316
317
|
targetCanServeProject: () => targetCanServeProject,
|
|
317
318
|
targetTitleMatchesProject: () => targetTitleMatchesProject,
|
|
318
319
|
waitForCdp: () => waitForCdp
|
|
@@ -607,15 +608,18 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
607
608
|
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ? resolve2(String(options.projectPath)) : null : resolveProjectPath();
|
|
608
609
|
const listCdpPageTargetsImpl = options.listCdpPageTargetsImpl || listCdpPageTargets;
|
|
609
610
|
const spawnImpl = options.spawnImpl || spawn2;
|
|
611
|
+
const sleepImpl = options.sleepImpl || ((ms) => new Promise((resolveWait) => setTimeout(resolveWait, ms)));
|
|
612
|
+
const waitForCdpImpl = options.waitForCdpImpl || waitForCdp;
|
|
613
|
+
const findCursorPidByPortImpl = options.findCursorPidByPortImpl || findCursorPidByPort;
|
|
610
614
|
const allowSpawn = options.allowSpawn !== false;
|
|
611
615
|
const allowProcessControl = options.allowProcessControl !== false;
|
|
612
616
|
if (await cdpUpImpl()) {
|
|
613
617
|
const isCursor = await cdpIsCursorImpl();
|
|
614
618
|
if (isCursor) {
|
|
615
|
-
const cursorPid2 = allowProcessControl ?
|
|
619
|
+
const cursorPid2 = allowProcessControl ? findCursorPidByPortImpl(CDP_PORT) : null;
|
|
616
620
|
const windowGuard2 = allowProcessControl && effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
617
621
|
const presentation2 = effectiveRuntimeMode === "minimal" ? allowProcessControl ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : attachedPresentation(effectiveRuntimeMode, CDP_PORT) : null;
|
|
618
|
-
|
|
622
|
+
let currentTargets = await listCdpPageTargetsImpl();
|
|
619
623
|
const projectKey = normalizeProjectKey(projectPath);
|
|
620
624
|
let targetId2 = projectKey ? PROJECT_TARGETS.get(projectKey) || null : currentTargets[0] && currentTargets[0].id || null;
|
|
621
625
|
let workspaceAction = projectPath ? "reused-project-target" : "reused-last-workspace";
|
|
@@ -627,19 +631,11 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
627
631
|
workspaceAction = "reused-agents-window";
|
|
628
632
|
}
|
|
629
633
|
if (projectPath && !targetId2) {
|
|
630
|
-
const existingTarget = currentTargets
|
|
634
|
+
const existingTarget = selectReusableProjectTarget(currentTargets, projectPath);
|
|
631
635
|
if (existingTarget) {
|
|
632
636
|
targetId2 = existingTarget.id;
|
|
633
637
|
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
634
|
-
workspaceAction = "recovered-project-target";
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
if (projectPath && !targetId2) {
|
|
638
|
-
const agentsTarget = selectAgentsWindowTarget(currentTargets);
|
|
639
|
-
if (agentsTarget) {
|
|
640
|
-
targetId2 = agentsTarget.id;
|
|
641
|
-
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
642
|
-
workspaceAction = "reused-agents-window";
|
|
638
|
+
workspaceAction = isAgentsWindowTitle(existingTarget.title) ? "reused-agents-window" : "recovered-project-target";
|
|
643
639
|
}
|
|
644
640
|
}
|
|
645
641
|
if (projectPath && existsSync(projectPath) && !targetId2) {
|
|
@@ -677,13 +673,39 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
677
673
|
message: `CCE connected to Cursor but cannot open workspace ${projectPath} because the Cursor executable was not found.`
|
|
678
674
|
};
|
|
679
675
|
}
|
|
680
|
-
const
|
|
681
|
-
const
|
|
676
|
+
const settleAttempts = Math.max(1, Number(options.targetSettleAttempts ?? 8));
|
|
677
|
+
const settleDelayMs = Math.max(0, Number(options.targetSettleDelayMs ?? 250));
|
|
678
|
+
for (let attempt = 0; attempt < settleAttempts && !targetId2; attempt++) {
|
|
679
|
+
if (attempt > 0 && settleDelayMs > 0) await sleepImpl(settleDelayMs);
|
|
680
|
+
currentTargets = await listCdpPageTargetsImpl();
|
|
681
|
+
const reusable = selectReusableProjectTarget(currentTargets, projectPath);
|
|
682
|
+
if (reusable) {
|
|
683
|
+
targetId2 = reusable.id;
|
|
684
|
+
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
685
|
+
workspaceAction = isAgentsWindowTitle(reusable.title) ? "reused-agents-window" : "recovered-project-target";
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (targetId2) {
|
|
689
|
+
return {
|
|
690
|
+
ok: true,
|
|
691
|
+
status: "already",
|
|
692
|
+
port: CDP_PORT,
|
|
693
|
+
cursorPid: cursorPid2,
|
|
694
|
+
runtimeMode: effectiveRuntimeMode,
|
|
695
|
+
projectPath,
|
|
696
|
+
presentation: presentation2,
|
|
697
|
+
windowGuard: windowGuard2,
|
|
698
|
+
targetId: targetId2,
|
|
699
|
+
workspaceAction,
|
|
700
|
+
message: workspaceAction === "reused-agents-window" ? `CDP ${CDP_PORT} responded as Cursor; Agents Window ${targetId2} was reused without opening another IDE window.` : `CDP ${CDP_PORT} responded as Cursor; the target workspace is bound to CDP target ${targetId2}.`
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
const reused = await spawnDetachedSafely(spawnImpl, exe2, ["--reuse-window", projectPath], {
|
|
682
704
|
detached: true,
|
|
683
705
|
stdio: "ignore",
|
|
684
706
|
windowsHide: effectiveRuntimeMode === "minimal"
|
|
685
707
|
});
|
|
686
|
-
if (!
|
|
708
|
+
if (!reused.ok) {
|
|
687
709
|
return {
|
|
688
710
|
ok: false,
|
|
689
711
|
status: "spawn-blocked",
|
|
@@ -693,16 +715,16 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
693
715
|
projectPath,
|
|
694
716
|
presentation: presentation2,
|
|
695
717
|
windowGuard: windowGuard2,
|
|
696
|
-
errorCode:
|
|
718
|
+
errorCode: reused.errorCode,
|
|
697
719
|
needsAction: "open_workspace_in_cursor",
|
|
698
720
|
retryable: true,
|
|
699
721
|
nextStep: `Open workspace ${projectPath} in Cursor, then retry the same operation.`,
|
|
700
|
-
message: `Cursor Bridge could not
|
|
722
|
+
message: `Cursor Bridge could not reuse the existing Cursor window for the workspace: ${reused.error instanceof Error ? reused.error.message : String(reused.error)}`
|
|
701
723
|
};
|
|
702
724
|
}
|
|
703
|
-
workspaceAction = "
|
|
704
|
-
const
|
|
705
|
-
if (!
|
|
725
|
+
workspaceAction = "reused-window-for-project";
|
|
726
|
+
const reusedTarget = await waitForProjectCdpTarget(12e3, projectPath, listCdpPageTargetsImpl, sleepImpl);
|
|
727
|
+
if (!reusedTarget) {
|
|
706
728
|
return {
|
|
707
729
|
ok: false,
|
|
708
730
|
status: "workspace-not-ready",
|
|
@@ -721,7 +743,7 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
721
743
|
message: `Cursor opened the project, but CCE has not confirmed that workspace ${projectPath} is ready. Initialization stopped safely to avoid searching the wrong project.`
|
|
722
744
|
};
|
|
723
745
|
}
|
|
724
|
-
targetId2 =
|
|
746
|
+
targetId2 = reusedTarget.id;
|
|
725
747
|
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
726
748
|
}
|
|
727
749
|
return {
|
|
@@ -799,7 +821,6 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
799
821
|
"--disable-backgrounding-occluded-windows"
|
|
800
822
|
);
|
|
801
823
|
}
|
|
802
|
-
if (projectPath && existsSync(projectPath)) args.push(projectPath);
|
|
803
824
|
const launched = await spawnDetachedSafely(spawnImpl, exe, args, {
|
|
804
825
|
detached: true,
|
|
805
826
|
stdio: "ignore",
|
|
@@ -823,7 +844,7 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
823
844
|
}
|
|
824
845
|
const child = launched.child;
|
|
825
846
|
const startupWindowGuard = effectiveRuntimeMode === "minimal" ? startMinimalWindowGuard(child && child.pid) : null;
|
|
826
|
-
const up = await
|
|
847
|
+
const up = await waitForCdpImpl(waitMs);
|
|
827
848
|
if (!up) {
|
|
828
849
|
return {
|
|
829
850
|
ok: false,
|
|
@@ -842,7 +863,7 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
842
863
|
message: "Cursor started, but CCE is not ready yet. No port setting needs to be changed."
|
|
843
864
|
};
|
|
844
865
|
}
|
|
845
|
-
const cursorPid =
|
|
866
|
+
const cursorPid = findCursorPidByPortImpl(CDP_PORT) || child.pid || null;
|
|
846
867
|
const openedTarget = await waitForNewCdpTarget(/* @__PURE__ */ new Set(), 12e3, projectPath, listCdpPageTargetsImpl);
|
|
847
868
|
const targetId = openedTarget && openedTarget.id || null;
|
|
848
869
|
if (projectPath && !targetId) {
|
|
@@ -865,7 +886,8 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
865
886
|
}
|
|
866
887
|
if (projectPath && targetId) PROJECT_TARGETS.set(normalizeProjectKey(projectPath), targetId);
|
|
867
888
|
const windowGuard = effectiveRuntimeMode === "minimal" && cursorPid ? startMinimalWindowGuard(cursorPid) : null;
|
|
868
|
-
const
|
|
889
|
+
const launchedIntoAgents = !!(projectPath && openedTarget && isAgentsWindowTitle(openedTarget.title));
|
|
890
|
+
const target = launchedIntoAgents ? `restoring one Agents Window for ${projectPath}` : projectPath ? `opening ${projectPath}` : "restoring the previous workspace";
|
|
869
891
|
const presentation = effectiveRuntimeMode === "minimal" ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid }) : null;
|
|
870
892
|
return {
|
|
871
893
|
ok: true,
|
|
@@ -881,7 +903,7 @@ async function ensureCursorRunningLocal(options = {}) {
|
|
|
881
903
|
cursorExecutable: exe,
|
|
882
904
|
cursorExecutableSource: cursorExecutable.source,
|
|
883
905
|
targetId,
|
|
884
|
-
workspaceAction: projectPath ? "launched-project" : "launched-last-workspace",
|
|
906
|
+
workspaceAction: launchedIntoAgents ? "launched-agents-window" : projectPath ? "launched-project" : "launched-last-workspace",
|
|
885
907
|
message: `Cursor started (${exe}, ${target}); CDP ${CDP_PORT} is ready.`
|
|
886
908
|
};
|
|
887
909
|
}
|
|
@@ -907,6 +929,10 @@ function targetCanServeProject(title, projectPath) {
|
|
|
907
929
|
function selectAgentsWindowTarget(targets) {
|
|
908
930
|
return (Array.isArray(targets) ? targets : []).find((target) => target && target.id && isAgentsWindowTitle(target.title)) || null;
|
|
909
931
|
}
|
|
932
|
+
function selectReusableProjectTarget(targets, projectPath) {
|
|
933
|
+
const pages = Array.isArray(targets) ? targets : [];
|
|
934
|
+
return pages.find((target) => target && target.id && targetTitleMatchesProject(target.title, projectPath)) || selectAgentsWindowTarget(pages) || null;
|
|
935
|
+
}
|
|
910
936
|
async function listCdpPageTargets(timeoutMs = 1500) {
|
|
911
937
|
return new Promise((done) => {
|
|
912
938
|
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
|
|
@@ -936,7 +962,8 @@ async function listCdpPageTargets(timeoutMs = 1500) {
|
|
|
936
962
|
function selectNewCdpTarget(beforeTargetIds, targets, projectPath = "") {
|
|
937
963
|
const before = beforeTargetIds instanceof Set ? beforeTargetIds : new Set(beforeTargetIds || []);
|
|
938
964
|
const fresh = (targets || []).filter((target) => target && target.id && !before.has(target.id));
|
|
939
|
-
|
|
965
|
+
if (projectPath) return selectReusableProjectTarget(fresh, projectPath);
|
|
966
|
+
return fresh[0] || null;
|
|
940
967
|
}
|
|
941
968
|
async function waitForNewCdpTarget(beforeTargetIds, maxMs = 12e3, projectPath = "", listImpl = listCdpPageTargets) {
|
|
942
969
|
const started = Date.now();
|
|
@@ -947,6 +974,15 @@ async function waitForNewCdpTarget(beforeTargetIds, maxMs = 12e3, projectPath =
|
|
|
947
974
|
}
|
|
948
975
|
return null;
|
|
949
976
|
}
|
|
977
|
+
async function waitForProjectCdpTarget(maxMs, projectPath, listImpl = listCdpPageTargets, sleepImpl = (ms) => new Promise((resolveWait) => setTimeout(resolveWait, ms))) {
|
|
978
|
+
const started = Date.now();
|
|
979
|
+
while (Date.now() - started < maxMs) {
|
|
980
|
+
const target = selectReusableProjectTarget(await listImpl(), projectPath);
|
|
981
|
+
if (target) return target;
|
|
982
|
+
await sleepImpl(300);
|
|
983
|
+
}
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
950
986
|
var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule;
|
|
951
987
|
var init_cursor_ensure_core = __esm({
|
|
952
988
|
"cursor-ensure-core.mjs"() {
|
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.9",
|
|
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.9",
|
|
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.
|
|
50
|
+
"embeddedProductVersion": "5.7.1"
|
|
51
51
|
}
|
|
52
52
|
}
|