pi-cursor-bridge 0.1.6 → 0.1.8
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 +3 -3
- package/dist/cursor-bridge.mjs +25785 -24444
- package/dist/cursor-lifecycle-supervisor.mjs +1452 -1416
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
- package/skills/cce-routing/SKILL.md +58 -58
- package/skills/cce-routing/agents/openai.yaml +13 -13
- package/skills/cursor-delegate/SKILL.md +16 -16
- package/skills/cursor-delegate/references/delegation-contract.md +2 -2
|
@@ -1,1416 +1,1452 @@
|
|
|
1
|
-
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// cursor-runtime.mjs
|
|
13
|
-
import { execFileSync, spawn } from "node:child_process";
|
|
14
|
-
import { mkdirSync as mkdirSync2, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
-
import { homedir as homedir2 } from "node:os";
|
|
16
|
-
import { basename, dirname, join as join2, resolve } from "node:path";
|
|
17
|
-
function normalizeCursorRuntimeMode(value, fallback = "normal") {
|
|
18
|
-
const normalized = String(value || "").trim().toLowerCase();
|
|
19
|
-
return CURSOR_RUNTIME_MODES.includes(normalized) ? normalized : fallback;
|
|
20
|
-
}
|
|
21
|
-
function resolveCursorRuntimeFile(value = process.env.CURSOR_BRIDGE_RUNTIME_FILE) {
|
|
22
|
-
const configured = String(value || "").trim();
|
|
23
|
-
if (configured) return resolve(configured);
|
|
24
|
-
const configRoot = process.platform === "win32" && process.env.APPDATA ? process.env.APPDATA : process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
25
|
-
return join2(configRoot, "cursor-bridge", "runtime.json");
|
|
26
|
-
}
|
|
27
|
-
function parseNetstatListeningPid(output, port) {
|
|
28
|
-
const expectedPort = Number(port);
|
|
29
|
-
if (!Number.isInteger(expectedPort) || expectedPort <= 0) return null;
|
|
30
|
-
for (const line of String(output || "").split(/\r?\n/)) {
|
|
31
|
-
const columns = line.trim().split(/\s+/);
|
|
32
|
-
if (columns.length < 5 || String(columns[0]).toUpperCase() !== "TCP") continue;
|
|
33
|
-
const local = columns[1] || "";
|
|
34
|
-
const state = String(columns[3] || "").toUpperCase();
|
|
35
|
-
const pid = Number(columns[4]);
|
|
36
|
-
const portMatch = local.match(/:(\d+)$/);
|
|
37
|
-
if (state === "LISTENING" && portMatch && Number(portMatch[1]) === expectedPort && Number.isInteger(pid) && pid > 0) {
|
|
38
|
-
return pid;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
function findCursorPidByPort(port, options = {}) {
|
|
44
|
-
if ((options.platform || process.platform) !== "win32") return null;
|
|
45
|
-
const run = options.execFileSyncImpl || execFileSync;
|
|
46
|
-
try {
|
|
47
|
-
const output = run("netstat.exe", ["-ano", "-p", "tcp"], {
|
|
48
|
-
encoding: "utf8",
|
|
49
|
-
windowsHide: true,
|
|
50
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
51
|
-
});
|
|
52
|
-
return parseNetstatListeningPid(output, port);
|
|
53
|
-
} catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function powershellWindowScript(options) {
|
|
58
|
-
const targetPid = Number(options.pid);
|
|
59
|
-
const loop = options.loop === true;
|
|
60
|
-
const lifetime = options.lifetime === true;
|
|
61
|
-
const show = options.action === "show" ? "$true" : "$false";
|
|
62
|
-
const iterations = Number(options.iterations || 1);
|
|
63
|
-
const intervalMs = Number(options.intervalMs || 100);
|
|
64
|
-
const showFlagPath = String(options.showFlagPath || "").replace(/'/g, "''");
|
|
65
|
-
const hideIfAllowed = `if (-not (Test-Path -LiteralPath '${showFlagPath}')) { [void][CursorBridgeWindowControl]::Apply(${targetPid}, $false) }`;
|
|
66
|
-
const mutexName = `Local\\CursorBridgeMinimalGuard-${targetPid}`;
|
|
67
|
-
const lifetimeLoop = [
|
|
68
|
-
"$createdNew = $false",
|
|
69
|
-
`$guardMutex = [System.Threading.Mutex]::new($true, '${mutexName}', [ref]$createdNew)`,
|
|
70
|
-
"if (-not $createdNew) { $guardMutex.Dispose(); exit 0 }",
|
|
71
|
-
"try {",
|
|
72
|
-
" while ($true) {",
|
|
73
|
-
` $target = Get-Process -Id ${targetPid} -ErrorAction SilentlyContinue`,
|
|
74
|
-
" if ($null -eq $target -or $target.ProcessName -ine 'Cursor') { break }",
|
|
75
|
-
` ${hideIfAllowed}`,
|
|
76
|
-
` Start-Sleep -Milliseconds ${intervalMs}`,
|
|
77
|
-
" }",
|
|
78
|
-
"} finally {",
|
|
79
|
-
" try { $guardMutex.ReleaseMutex() } catch {}",
|
|
80
|
-
" $guardMutex.Dispose()",
|
|
81
|
-
` Remove-Item -LiteralPath '${showFlagPath}' -Force -ErrorAction SilentlyContinue`,
|
|
82
|
-
"}"
|
|
83
|
-
].join("\n");
|
|
84
|
-
const apply = lifetime ? lifetimeLoop : loop ? `for ($i = 0; $i -lt ${iterations}; $i++) { ${hideIfAllowed}; Start-Sleep -Milliseconds ${intervalMs} }` : `$changed = [CursorBridgeWindowControl]::Apply(${targetPid}, ${show}); [Console]::Out.Write($changed)`;
|
|
85
|
-
return `$ErrorActionPreference = 'Stop'
|
|
86
|
-
Add-Type -TypeDefinition @'
|
|
87
|
-
${WINDOW_CONTROL_TYPE}
|
|
88
|
-
'@
|
|
89
|
-
${apply}`;
|
|
90
|
-
}
|
|
91
|
-
function encodePowerShell(script) {
|
|
92
|
-
return Buffer.from(String(script), "utf16le").toString("base64");
|
|
93
|
-
}
|
|
94
|
-
function setCursorWindowPresentation(options = {}) {
|
|
95
|
-
const platform = options.platform || process.platform;
|
|
96
|
-
const action = String(options.action || "").trim().toLowerCase();
|
|
97
|
-
if (!["hide", "show"].includes(action)) throw new Error(`unsupported Cursor window action: ${options.action}`);
|
|
98
|
-
if (platform !== "win32") {
|
|
99
|
-
return { supported: false, applied: false, action, reason: `window control is not implemented for ${platform}` };
|
|
100
|
-
}
|
|
101
|
-
const port = Number(options.port || 9223);
|
|
102
|
-
const pid = Number(options.pid || findCursorPidByPort(port, options));
|
|
103
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
104
|
-
return { supported: true, applied: false, action, port, reason: `no listening Cursor PID found on CDP ${port}` };
|
|
105
|
-
}
|
|
106
|
-
const showFlagPath = resolve(options.showFlagPath || join2(dirname(resolveCursorRuntimeFile()), `show-${pid}.flag`));
|
|
107
|
-
try {
|
|
108
|
-
if (action === "show") {
|
|
109
|
-
mkdirSync2(dirname(showFlagPath), { recursive: true });
|
|
110
|
-
writeFileSync(showFlagPath, `${pid}
|
|
111
|
-
`, { encoding: "utf8", mode: 384 });
|
|
112
|
-
} else {
|
|
113
|
-
rmSync(showFlagPath, { force: true });
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
return {
|
|
117
|
-
supported: true,
|
|
118
|
-
applied: false,
|
|
119
|
-
action,
|
|
120
|
-
port,
|
|
121
|
-
pid,
|
|
122
|
-
reason: `failed to update minimal-window override: ${error instanceof Error ? error.message : String(error)}`
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
const run = options.execFileSyncImpl || execFileSync;
|
|
126
|
-
try {
|
|
127
|
-
const script = powershellWindowScript({ pid, action });
|
|
128
|
-
const output = run("powershell.exe", [
|
|
129
|
-
"-NoLogo",
|
|
130
|
-
"-NoProfile",
|
|
131
|
-
"-NonInteractive",
|
|
132
|
-
"-ExecutionPolicy",
|
|
133
|
-
"Bypass",
|
|
134
|
-
"-EncodedCommand",
|
|
135
|
-
encodePowerShell(script)
|
|
136
|
-
], {
|
|
137
|
-
encoding: "utf8",
|
|
138
|
-
windowsHide: true,
|
|
139
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
140
|
-
timeout: Number(options.timeoutMs || 15e3)
|
|
141
|
-
});
|
|
142
|
-
const changedWindows = Number(String(output || "").trim() || 0);
|
|
143
|
-
return { supported: true, applied: true, action, port, pid, changedWindows, showFlagPath };
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (action === "show") rmSync(showFlagPath, { force: true });
|
|
146
|
-
return {
|
|
147
|
-
supported: true,
|
|
148
|
-
applied: false,
|
|
149
|
-
action,
|
|
150
|
-
port,
|
|
151
|
-
pid,
|
|
152
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function startMinimalWindowGuard(pid, options = {}) {
|
|
157
|
-
if ((options.platform || process.platform) !== "win32") return { started: false, reason: "unsupported-platform" };
|
|
158
|
-
const targetPid = Number(pid);
|
|
159
|
-
if (!Number.isInteger(targetPid) || targetPid <= 0) return { started: false, reason: "invalid-pid" };
|
|
160
|
-
const intervalMs = Math.max(50, Math.min(1e3, Number(options.intervalMs || 100)));
|
|
161
|
-
const lifetime = options.durationMs == null;
|
|
162
|
-
const durationMs = lifetime ? null : Math.max(intervalMs, Math.min(12e4, Number(options.durationMs)));
|
|
163
|
-
const iterations = lifetime ? null : Math.ceil(durationMs / intervalMs);
|
|
164
|
-
const showFlagPath = resolve(options.showFlagPath || join2(dirname(resolveCursorRuntimeFile()), `show-${targetPid}.flag`));
|
|
165
|
-
const spawnImpl = options.spawnImpl || spawn;
|
|
166
|
-
try {
|
|
167
|
-
const script = powershellWindowScript({
|
|
168
|
-
pid: targetPid,
|
|
169
|
-
loop: !lifetime,
|
|
170
|
-
lifetime,
|
|
171
|
-
iterations,
|
|
172
|
-
intervalMs,
|
|
173
|
-
showFlagPath
|
|
174
|
-
});
|
|
175
|
-
const child = spawnImpl("powershell.exe", [
|
|
176
|
-
"-NoLogo",
|
|
177
|
-
"-NoProfile",
|
|
178
|
-
"-NonInteractive",
|
|
179
|
-
"-ExecutionPolicy",
|
|
180
|
-
"Bypass",
|
|
181
|
-
"-EncodedCommand",
|
|
182
|
-
encodePowerShell(script)
|
|
183
|
-
], { detached: !lifetime, stdio: "ignore", windowsHide: true });
|
|
184
|
-
if (child && typeof child.once === "function") child.once("error", () => {
|
|
185
|
-
});
|
|
186
|
-
if (!lifetime && child && typeof child.unref === "function") child.unref();
|
|
187
|
-
return {
|
|
188
|
-
started: true,
|
|
189
|
-
pid: child && child.pid || null,
|
|
190
|
-
targetPid,
|
|
191
|
-
lifetime,
|
|
192
|
-
retainedBySupervisor: lifetime,
|
|
193
|
-
durationMs,
|
|
194
|
-
intervalMs,
|
|
195
|
-
showFlagPath
|
|
196
|
-
};
|
|
197
|
-
} catch (error) {
|
|
198
|
-
return { started: false, targetPid, reason: error instanceof Error ? error.message : String(error) };
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
var CURSOR_RUNTIME_MODES, WINDOW_CONTROL_TYPE;
|
|
202
|
-
var init_cursor_runtime = __esm({
|
|
203
|
-
"cursor-runtime.mjs"() {
|
|
204
|
-
CURSOR_RUNTIME_MODES = Object.freeze(["normal", "minimal"]);
|
|
205
|
-
WINDOW_CONTROL_TYPE = String.raw`
|
|
206
|
-
using System;
|
|
207
|
-
using System.Runtime.InteropServices;
|
|
208
|
-
using System.Text;
|
|
209
|
-
|
|
210
|
-
public static class CursorBridgeWindowControl {
|
|
211
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
212
|
-
private struct RECT {
|
|
213
|
-
public int Left;
|
|
214
|
-
public int Top;
|
|
215
|
-
public int Right;
|
|
216
|
-
public int Bottom;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
220
|
-
[DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc callback, IntPtr lParam);
|
|
221
|
-
[DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
|
|
222
|
-
[DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd);
|
|
223
|
-
[DllImport("user32.dll")] private static extern bool IsIconic(IntPtr hWnd);
|
|
224
|
-
[DllImport("user32.dll")] private static extern bool IsZoomed(IntPtr hWnd);
|
|
225
|
-
[DllImport("user32.dll", EntryPoint = "IsWindowArranged")] private static extern bool IsWindowArranged(IntPtr hWnd);
|
|
226
|
-
[DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
|
227
|
-
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextLengthW(IntPtr hWnd);
|
|
228
|
-
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassNameW(IntPtr hWnd, StringBuilder className, int maxCount);
|
|
229
|
-
[DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int command);
|
|
230
|
-
[DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr insertAfter, int x, int y, int width, int height, uint flags);
|
|
231
|
-
[DllImport("user32.dll")] private static extern bool RedrawWindow(IntPtr hWnd, IntPtr updateRect, IntPtr updateRegion, uint flags);
|
|
232
|
-
|
|
233
|
-
private const uint SWP_NOSIZE = 0x0001;
|
|
234
|
-
private const uint SWP_NOMOVE = 0x0002;
|
|
235
|
-
private const uint SWP_NOZORDER = 0x0004;
|
|
236
|
-
private const uint SWP_NOACTIVATE = 0x0010;
|
|
237
|
-
private const uint SWP_SHOWWINDOW = 0x0040;
|
|
238
|
-
private const uint SWP_NOOWNERZORDER = 0x0200;
|
|
239
|
-
private const uint SWP_ASYNCWINDOWPOS = 0x4000;
|
|
240
|
-
private const uint SHOW_NO_ACTIVATE_FLAGS = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_ASYNCWINDOWPOS;
|
|
241
|
-
private const uint COMPOSITOR_PULSE_FLAGS = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_ASYNCWINDOWPOS;
|
|
242
|
-
|
|
243
|
-
private static bool IsArrangedSafely(IntPtr hWnd) {
|
|
244
|
-
try { return IsWindowArranged(hWnd); }
|
|
245
|
-
// Older Windows versions do not export IsWindowArranged. Treat unknown as
|
|
246
|
-
// arranged so the geometry pulse fails closed and cannot disturb placement.
|
|
247
|
-
catch (EntryPointNotFoundException) { return true; }
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
public static int Apply(int expectedProcessId, bool show) {
|
|
251
|
-
int changed = 0;
|
|
252
|
-
EnumWindows((hWnd, lParam) => {
|
|
253
|
-
uint processId;
|
|
254
|
-
GetWindowThreadProcessId(hWnd, out processId);
|
|
255
|
-
if (processId != (uint)expectedProcessId || GetWindowTextLengthW(hWnd) <= 0) return true;
|
|
256
|
-
StringBuilder className = new StringBuilder(256);
|
|
257
|
-
GetClassNameW(hWnd, className, className.Capacity);
|
|
258
|
-
if (!String.Equals(className.ToString(), "Chrome_WidgetWin_1", StringComparison.Ordinal)) return true;
|
|
259
|
-
bool visible = IsWindowVisible(hWnd);
|
|
260
|
-
if (show) {
|
|
261
|
-
// SWP_SHOWWINDOW + SWP_NOACTIVATE preserves minimized/maximized/arranged
|
|
262
|
-
// placement without taking keyboard focus from the requesting app.
|
|
263
|
-
bool restored = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SHOW_NO_ACTIVATE_FLAGS);
|
|
264
|
-
bool pulsed = false;
|
|
265
|
-
RECT rect;
|
|
266
|
-
// RedrawWindow alone does not invalidate Chromium's DirectComposition
|
|
267
|
-
// surface after a long SW_HIDE. A real one-pixel resize does, while the
|
|
268
|
-
// NOACTIVATE/NOZORDER flags preserve the caller's foreground window.
|
|
269
|
-
// Do not resize minimized, maximized, or snapped windows: their placement
|
|
270
|
-
// is user-owned and a native redraw remains the safe fallback.
|
|
271
|
-
if (!IsIconic(hWnd) && !IsZoomed(hWnd) && !IsArrangedSafely(hWnd)
|
|
272
|
-
&& GetWindowRect(hWnd, out rect)) {
|
|
273
|
-
int width = rect.Right - rect.Left;
|
|
274
|
-
int height = rect.Bottom - rect.Top;
|
|
275
|
-
if (width > 1 && height > 1) {
|
|
276
|
-
bool expanded = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, width + 1, height, COMPOSITOR_PULSE_FLAGS);
|
|
277
|
-
if (expanded) System.Threading.Thread.Sleep(80);
|
|
278
|
-
bool restoredSize = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, width, height, COMPOSITOR_PULSE_FLAGS);
|
|
279
|
-
pulsed = expanded || restoredSize;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
bool redrawn = RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, 0x00000585);
|
|
283
|
-
if (restored || pulsed || redrawn) changed++;
|
|
284
|
-
}
|
|
285
|
-
if (!show && visible) { if (ShowWindowAsync(hWnd, 0)) changed++; }
|
|
286
|
-
return true;
|
|
287
|
-
}, IntPtr.Zero);
|
|
288
|
-
return changed;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
`;
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
// cursor-ensure-core.mjs
|
|
296
|
-
var cursor_ensure_core_exports = {};
|
|
297
|
-
__export(cursor_ensure_core_exports, {
|
|
298
|
-
CDP_HOST: () => CDP_HOST,
|
|
299
|
-
CDP_ORIGIN: () => CDP_ORIGIN,
|
|
300
|
-
CDP_PORT: () => CDP_PORT,
|
|
301
|
-
cdpIsCursor: () => cdpIsCursor,
|
|
302
|
-
cdpUp: () => cdpUp,
|
|
303
|
-
cursorRunning: () => cursorRunning,
|
|
304
|
-
ensureCursorRunningLocal: () => ensureCursorRunningLocal,
|
|
305
|
-
findCursorExe: () => findCursorExe,
|
|
306
|
-
findCursorExeDetails: () => findCursorExeDetails,
|
|
307
|
-
isAgentsWindowTitle: () => isAgentsWindowTitle,
|
|
308
|
-
looksLikePluginRuntimePath: () => looksLikePluginRuntimePath,
|
|
309
|
-
normalizeCodexThreadCwd: () => normalizeCodexThreadCwd,
|
|
310
|
-
normalizeCursorExeCandidate: () => normalizeCursorExeCandidate,
|
|
311
|
-
resolveCodexThreadProjectPath: () => resolveCodexThreadProjectPath,
|
|
312
|
-
resolveCursorLaunchCdpPort: () => resolveCursorLaunchCdpPort,
|
|
313
|
-
resolveProjectPath: () => resolveProjectPath,
|
|
314
|
-
selectAgentsWindowTarget: () => selectAgentsWindowTarget,
|
|
315
|
-
selectNewCdpTarget: () => selectNewCdpTarget,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
import {
|
|
322
|
-
import {
|
|
323
|
-
import {
|
|
324
|
-
import {
|
|
325
|
-
import
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (/^\\\\\?\\
|
|
339
|
-
return raw;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
["
|
|
388
|
-
["
|
|
389
|
-
["
|
|
390
|
-
["
|
|
391
|
-
["
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const
|
|
434
|
-
const
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
req.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
res.on("
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
req.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
await
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
settled
|
|
565
|
-
|
|
566
|
-
child.off?.("
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
child.once("
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
message: `Cursor
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
"
|
|
799
|
-
"
|
|
800
|
-
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
needsAction: "
|
|
840
|
-
retryable: true,
|
|
841
|
-
nextStep:
|
|
842
|
-
message:
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
849
|
-
return {
|
|
850
|
-
ok: false,
|
|
851
|
-
status: "
|
|
852
|
-
exe,
|
|
853
|
-
port: CDP_PORT,
|
|
854
|
-
cursorPid,
|
|
855
|
-
runtimeMode: effectiveRuntimeMode,
|
|
856
|
-
projectPath,
|
|
857
|
-
windowGuard: startupWindowGuard,
|
|
858
|
-
cursorExecutable: exe,
|
|
859
|
-
cursorExecutableSource: cursorExecutable.source,
|
|
860
|
-
needsAction: "retry_initialization",
|
|
861
|
-
retryable: true,
|
|
862
|
-
nextStep: "Wait
|
|
863
|
-
message:
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
return
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
return
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
if (
|
|
1066
|
-
|
|
1067
|
-
if (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
function
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
if (
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
return
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
if (
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
return
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
`);
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
socket.write(`${JSON.stringify({
|
|
1373
|
-
type: "
|
|
1374
|
-
id,
|
|
1375
|
-
ok: true,
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1
|
+
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// cursor-runtime.mjs
|
|
13
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
14
|
+
import { mkdirSync as mkdirSync2, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir as homedir2 } from "node:os";
|
|
16
|
+
import { basename, dirname, join as join2, resolve } from "node:path";
|
|
17
|
+
function normalizeCursorRuntimeMode(value, fallback = "normal") {
|
|
18
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
19
|
+
return CURSOR_RUNTIME_MODES.includes(normalized) ? normalized : fallback;
|
|
20
|
+
}
|
|
21
|
+
function resolveCursorRuntimeFile(value = process.env.CURSOR_BRIDGE_RUNTIME_FILE) {
|
|
22
|
+
const configured = String(value || "").trim();
|
|
23
|
+
if (configured) return resolve(configured);
|
|
24
|
+
const configRoot = process.platform === "win32" && process.env.APPDATA ? process.env.APPDATA : process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
|
|
25
|
+
return join2(configRoot, "cursor-bridge", "runtime.json");
|
|
26
|
+
}
|
|
27
|
+
function parseNetstatListeningPid(output, port) {
|
|
28
|
+
const expectedPort = Number(port);
|
|
29
|
+
if (!Number.isInteger(expectedPort) || expectedPort <= 0) return null;
|
|
30
|
+
for (const line of String(output || "").split(/\r?\n/)) {
|
|
31
|
+
const columns = line.trim().split(/\s+/);
|
|
32
|
+
if (columns.length < 5 || String(columns[0]).toUpperCase() !== "TCP") continue;
|
|
33
|
+
const local = columns[1] || "";
|
|
34
|
+
const state = String(columns[3] || "").toUpperCase();
|
|
35
|
+
const pid = Number(columns[4]);
|
|
36
|
+
const portMatch = local.match(/:(\d+)$/);
|
|
37
|
+
if (state === "LISTENING" && portMatch && Number(portMatch[1]) === expectedPort && Number.isInteger(pid) && pid > 0) {
|
|
38
|
+
return pid;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function findCursorPidByPort(port, options = {}) {
|
|
44
|
+
if ((options.platform || process.platform) !== "win32") return null;
|
|
45
|
+
const run = options.execFileSyncImpl || execFileSync;
|
|
46
|
+
try {
|
|
47
|
+
const output = run("netstat.exe", ["-ano", "-p", "tcp"], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
windowsHide: true,
|
|
50
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
51
|
+
});
|
|
52
|
+
return parseNetstatListeningPid(output, port);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function powershellWindowScript(options) {
|
|
58
|
+
const targetPid = Number(options.pid);
|
|
59
|
+
const loop = options.loop === true;
|
|
60
|
+
const lifetime = options.lifetime === true;
|
|
61
|
+
const show = options.action === "show" ? "$true" : "$false";
|
|
62
|
+
const iterations = Number(options.iterations || 1);
|
|
63
|
+
const intervalMs = Number(options.intervalMs || 100);
|
|
64
|
+
const showFlagPath = String(options.showFlagPath || "").replace(/'/g, "''");
|
|
65
|
+
const hideIfAllowed = `if (-not (Test-Path -LiteralPath '${showFlagPath}')) { [void][CursorBridgeWindowControl]::Apply(${targetPid}, $false) }`;
|
|
66
|
+
const mutexName = `Local\\CursorBridgeMinimalGuard-${targetPid}`;
|
|
67
|
+
const lifetimeLoop = [
|
|
68
|
+
"$createdNew = $false",
|
|
69
|
+
`$guardMutex = [System.Threading.Mutex]::new($true, '${mutexName}', [ref]$createdNew)`,
|
|
70
|
+
"if (-not $createdNew) { $guardMutex.Dispose(); exit 0 }",
|
|
71
|
+
"try {",
|
|
72
|
+
" while ($true) {",
|
|
73
|
+
` $target = Get-Process -Id ${targetPid} -ErrorAction SilentlyContinue`,
|
|
74
|
+
" if ($null -eq $target -or $target.ProcessName -ine 'Cursor') { break }",
|
|
75
|
+
` ${hideIfAllowed}`,
|
|
76
|
+
` Start-Sleep -Milliseconds ${intervalMs}`,
|
|
77
|
+
" }",
|
|
78
|
+
"} finally {",
|
|
79
|
+
" try { $guardMutex.ReleaseMutex() } catch {}",
|
|
80
|
+
" $guardMutex.Dispose()",
|
|
81
|
+
` Remove-Item -LiteralPath '${showFlagPath}' -Force -ErrorAction SilentlyContinue`,
|
|
82
|
+
"}"
|
|
83
|
+
].join("\n");
|
|
84
|
+
const apply = lifetime ? lifetimeLoop : loop ? `for ($i = 0; $i -lt ${iterations}; $i++) { ${hideIfAllowed}; Start-Sleep -Milliseconds ${intervalMs} }` : `$changed = [CursorBridgeWindowControl]::Apply(${targetPid}, ${show}); [Console]::Out.Write($changed)`;
|
|
85
|
+
return `$ErrorActionPreference = 'Stop'
|
|
86
|
+
Add-Type -TypeDefinition @'
|
|
87
|
+
${WINDOW_CONTROL_TYPE}
|
|
88
|
+
'@
|
|
89
|
+
${apply}`;
|
|
90
|
+
}
|
|
91
|
+
function encodePowerShell(script) {
|
|
92
|
+
return Buffer.from(String(script), "utf16le").toString("base64");
|
|
93
|
+
}
|
|
94
|
+
function setCursorWindowPresentation(options = {}) {
|
|
95
|
+
const platform = options.platform || process.platform;
|
|
96
|
+
const action = String(options.action || "").trim().toLowerCase();
|
|
97
|
+
if (!["hide", "show"].includes(action)) throw new Error(`unsupported Cursor window action: ${options.action}`);
|
|
98
|
+
if (platform !== "win32") {
|
|
99
|
+
return { supported: false, applied: false, action, reason: `window control is not implemented for ${platform}` };
|
|
100
|
+
}
|
|
101
|
+
const port = Number(options.port || 9223);
|
|
102
|
+
const pid = Number(options.pid || findCursorPidByPort(port, options));
|
|
103
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
104
|
+
return { supported: true, applied: false, action, port, reason: `no listening Cursor PID found on CDP ${port}` };
|
|
105
|
+
}
|
|
106
|
+
const showFlagPath = resolve(options.showFlagPath || join2(dirname(resolveCursorRuntimeFile()), `show-${pid}.flag`));
|
|
107
|
+
try {
|
|
108
|
+
if (action === "show") {
|
|
109
|
+
mkdirSync2(dirname(showFlagPath), { recursive: true });
|
|
110
|
+
writeFileSync(showFlagPath, `${pid}
|
|
111
|
+
`, { encoding: "utf8", mode: 384 });
|
|
112
|
+
} else {
|
|
113
|
+
rmSync(showFlagPath, { force: true });
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
supported: true,
|
|
118
|
+
applied: false,
|
|
119
|
+
action,
|
|
120
|
+
port,
|
|
121
|
+
pid,
|
|
122
|
+
reason: `failed to update minimal-window override: ${error instanceof Error ? error.message : String(error)}`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const run = options.execFileSyncImpl || execFileSync;
|
|
126
|
+
try {
|
|
127
|
+
const script = powershellWindowScript({ pid, action });
|
|
128
|
+
const output = run("powershell.exe", [
|
|
129
|
+
"-NoLogo",
|
|
130
|
+
"-NoProfile",
|
|
131
|
+
"-NonInteractive",
|
|
132
|
+
"-ExecutionPolicy",
|
|
133
|
+
"Bypass",
|
|
134
|
+
"-EncodedCommand",
|
|
135
|
+
encodePowerShell(script)
|
|
136
|
+
], {
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
140
|
+
timeout: Number(options.timeoutMs || 15e3)
|
|
141
|
+
});
|
|
142
|
+
const changedWindows = Number(String(output || "").trim() || 0);
|
|
143
|
+
return { supported: true, applied: true, action, port, pid, changedWindows, showFlagPath };
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (action === "show") rmSync(showFlagPath, { force: true });
|
|
146
|
+
return {
|
|
147
|
+
supported: true,
|
|
148
|
+
applied: false,
|
|
149
|
+
action,
|
|
150
|
+
port,
|
|
151
|
+
pid,
|
|
152
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function startMinimalWindowGuard(pid, options = {}) {
|
|
157
|
+
if ((options.platform || process.platform) !== "win32") return { started: false, reason: "unsupported-platform" };
|
|
158
|
+
const targetPid = Number(pid);
|
|
159
|
+
if (!Number.isInteger(targetPid) || targetPid <= 0) return { started: false, reason: "invalid-pid" };
|
|
160
|
+
const intervalMs = Math.max(50, Math.min(1e3, Number(options.intervalMs || 100)));
|
|
161
|
+
const lifetime = options.durationMs == null;
|
|
162
|
+
const durationMs = lifetime ? null : Math.max(intervalMs, Math.min(12e4, Number(options.durationMs)));
|
|
163
|
+
const iterations = lifetime ? null : Math.ceil(durationMs / intervalMs);
|
|
164
|
+
const showFlagPath = resolve(options.showFlagPath || join2(dirname(resolveCursorRuntimeFile()), `show-${targetPid}.flag`));
|
|
165
|
+
const spawnImpl = options.spawnImpl || spawn;
|
|
166
|
+
try {
|
|
167
|
+
const script = powershellWindowScript({
|
|
168
|
+
pid: targetPid,
|
|
169
|
+
loop: !lifetime,
|
|
170
|
+
lifetime,
|
|
171
|
+
iterations,
|
|
172
|
+
intervalMs,
|
|
173
|
+
showFlagPath
|
|
174
|
+
});
|
|
175
|
+
const child = spawnImpl("powershell.exe", [
|
|
176
|
+
"-NoLogo",
|
|
177
|
+
"-NoProfile",
|
|
178
|
+
"-NonInteractive",
|
|
179
|
+
"-ExecutionPolicy",
|
|
180
|
+
"Bypass",
|
|
181
|
+
"-EncodedCommand",
|
|
182
|
+
encodePowerShell(script)
|
|
183
|
+
], { detached: !lifetime, stdio: "ignore", windowsHide: true });
|
|
184
|
+
if (child && typeof child.once === "function") child.once("error", () => {
|
|
185
|
+
});
|
|
186
|
+
if (!lifetime && child && typeof child.unref === "function") child.unref();
|
|
187
|
+
return {
|
|
188
|
+
started: true,
|
|
189
|
+
pid: child && child.pid || null,
|
|
190
|
+
targetPid,
|
|
191
|
+
lifetime,
|
|
192
|
+
retainedBySupervisor: lifetime,
|
|
193
|
+
durationMs,
|
|
194
|
+
intervalMs,
|
|
195
|
+
showFlagPath
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
return { started: false, targetPid, reason: error instanceof Error ? error.message : String(error) };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
var CURSOR_RUNTIME_MODES, WINDOW_CONTROL_TYPE;
|
|
202
|
+
var init_cursor_runtime = __esm({
|
|
203
|
+
"cursor-runtime.mjs"() {
|
|
204
|
+
CURSOR_RUNTIME_MODES = Object.freeze(["normal", "minimal"]);
|
|
205
|
+
WINDOW_CONTROL_TYPE = String.raw`
|
|
206
|
+
using System;
|
|
207
|
+
using System.Runtime.InteropServices;
|
|
208
|
+
using System.Text;
|
|
209
|
+
|
|
210
|
+
public static class CursorBridgeWindowControl {
|
|
211
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
212
|
+
private struct RECT {
|
|
213
|
+
public int Left;
|
|
214
|
+
public int Top;
|
|
215
|
+
public int Right;
|
|
216
|
+
public int Bottom;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
220
|
+
[DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc callback, IntPtr lParam);
|
|
221
|
+
[DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
|
|
222
|
+
[DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd);
|
|
223
|
+
[DllImport("user32.dll")] private static extern bool IsIconic(IntPtr hWnd);
|
|
224
|
+
[DllImport("user32.dll")] private static extern bool IsZoomed(IntPtr hWnd);
|
|
225
|
+
[DllImport("user32.dll", EntryPoint = "IsWindowArranged")] private static extern bool IsWindowArranged(IntPtr hWnd);
|
|
226
|
+
[DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
|
227
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextLengthW(IntPtr hWnd);
|
|
228
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassNameW(IntPtr hWnd, StringBuilder className, int maxCount);
|
|
229
|
+
[DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int command);
|
|
230
|
+
[DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr insertAfter, int x, int y, int width, int height, uint flags);
|
|
231
|
+
[DllImport("user32.dll")] private static extern bool RedrawWindow(IntPtr hWnd, IntPtr updateRect, IntPtr updateRegion, uint flags);
|
|
232
|
+
|
|
233
|
+
private const uint SWP_NOSIZE = 0x0001;
|
|
234
|
+
private const uint SWP_NOMOVE = 0x0002;
|
|
235
|
+
private const uint SWP_NOZORDER = 0x0004;
|
|
236
|
+
private const uint SWP_NOACTIVATE = 0x0010;
|
|
237
|
+
private const uint SWP_SHOWWINDOW = 0x0040;
|
|
238
|
+
private const uint SWP_NOOWNERZORDER = 0x0200;
|
|
239
|
+
private const uint SWP_ASYNCWINDOWPOS = 0x4000;
|
|
240
|
+
private const uint SHOW_NO_ACTIVATE_FLAGS = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_ASYNCWINDOWPOS;
|
|
241
|
+
private const uint COMPOSITOR_PULSE_FLAGS = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_ASYNCWINDOWPOS;
|
|
242
|
+
|
|
243
|
+
private static bool IsArrangedSafely(IntPtr hWnd) {
|
|
244
|
+
try { return IsWindowArranged(hWnd); }
|
|
245
|
+
// Older Windows versions do not export IsWindowArranged. Treat unknown as
|
|
246
|
+
// arranged so the geometry pulse fails closed and cannot disturb placement.
|
|
247
|
+
catch (EntryPointNotFoundException) { return true; }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public static int Apply(int expectedProcessId, bool show) {
|
|
251
|
+
int changed = 0;
|
|
252
|
+
EnumWindows((hWnd, lParam) => {
|
|
253
|
+
uint processId;
|
|
254
|
+
GetWindowThreadProcessId(hWnd, out processId);
|
|
255
|
+
if (processId != (uint)expectedProcessId || GetWindowTextLengthW(hWnd) <= 0) return true;
|
|
256
|
+
StringBuilder className = new StringBuilder(256);
|
|
257
|
+
GetClassNameW(hWnd, className, className.Capacity);
|
|
258
|
+
if (!String.Equals(className.ToString(), "Chrome_WidgetWin_1", StringComparison.Ordinal)) return true;
|
|
259
|
+
bool visible = IsWindowVisible(hWnd);
|
|
260
|
+
if (show) {
|
|
261
|
+
// SWP_SHOWWINDOW + SWP_NOACTIVATE preserves minimized/maximized/arranged
|
|
262
|
+
// placement without taking keyboard focus from the requesting app.
|
|
263
|
+
bool restored = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SHOW_NO_ACTIVATE_FLAGS);
|
|
264
|
+
bool pulsed = false;
|
|
265
|
+
RECT rect;
|
|
266
|
+
// RedrawWindow alone does not invalidate Chromium's DirectComposition
|
|
267
|
+
// surface after a long SW_HIDE. A real one-pixel resize does, while the
|
|
268
|
+
// NOACTIVATE/NOZORDER flags preserve the caller's foreground window.
|
|
269
|
+
// Do not resize minimized, maximized, or snapped windows: their placement
|
|
270
|
+
// is user-owned and a native redraw remains the safe fallback.
|
|
271
|
+
if (!IsIconic(hWnd) && !IsZoomed(hWnd) && !IsArrangedSafely(hWnd)
|
|
272
|
+
&& GetWindowRect(hWnd, out rect)) {
|
|
273
|
+
int width = rect.Right - rect.Left;
|
|
274
|
+
int height = rect.Bottom - rect.Top;
|
|
275
|
+
if (width > 1 && height > 1) {
|
|
276
|
+
bool expanded = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, width + 1, height, COMPOSITOR_PULSE_FLAGS);
|
|
277
|
+
if (expanded) System.Threading.Thread.Sleep(80);
|
|
278
|
+
bool restoredSize = SetWindowPos(hWnd, IntPtr.Zero, 0, 0, width, height, COMPOSITOR_PULSE_FLAGS);
|
|
279
|
+
pulsed = expanded || restoredSize;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
bool redrawn = RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, 0x00000585);
|
|
283
|
+
if (restored || pulsed || redrawn) changed++;
|
|
284
|
+
}
|
|
285
|
+
if (!show && visible) { if (ShowWindowAsync(hWnd, 0)) changed++; }
|
|
286
|
+
return true;
|
|
287
|
+
}, IntPtr.Zero);
|
|
288
|
+
return changed;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// cursor-ensure-core.mjs
|
|
296
|
+
var cursor_ensure_core_exports = {};
|
|
297
|
+
__export(cursor_ensure_core_exports, {
|
|
298
|
+
CDP_HOST: () => CDP_HOST,
|
|
299
|
+
CDP_ORIGIN: () => CDP_ORIGIN,
|
|
300
|
+
CDP_PORT: () => CDP_PORT,
|
|
301
|
+
cdpIsCursor: () => cdpIsCursor,
|
|
302
|
+
cdpUp: () => cdpUp,
|
|
303
|
+
cursorRunning: () => cursorRunning,
|
|
304
|
+
ensureCursorRunningLocal: () => ensureCursorRunningLocal,
|
|
305
|
+
findCursorExe: () => findCursorExe,
|
|
306
|
+
findCursorExeDetails: () => findCursorExeDetails,
|
|
307
|
+
isAgentsWindowTitle: () => isAgentsWindowTitle,
|
|
308
|
+
looksLikePluginRuntimePath: () => looksLikePluginRuntimePath,
|
|
309
|
+
normalizeCodexThreadCwd: () => normalizeCodexThreadCwd,
|
|
310
|
+
normalizeCursorExeCandidate: () => normalizeCursorExeCandidate,
|
|
311
|
+
resolveCodexThreadProjectPath: () => resolveCodexThreadProjectPath,
|
|
312
|
+
resolveCursorLaunchCdpPort: () => resolveCursorLaunchCdpPort,
|
|
313
|
+
resolveProjectPath: () => resolveProjectPath,
|
|
314
|
+
selectAgentsWindowTarget: () => selectAgentsWindowTarget,
|
|
315
|
+
selectNewCdpTarget: () => selectNewCdpTarget,
|
|
316
|
+
selectReusableProjectTarget: () => selectReusableProjectTarget,
|
|
317
|
+
targetCanServeProject: () => targetCanServeProject,
|
|
318
|
+
targetTitleMatchesProject: () => targetTitleMatchesProject,
|
|
319
|
+
waitForCdp: () => waitForCdp
|
|
320
|
+
});
|
|
321
|
+
import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
|
|
322
|
+
import { existsSync } from "fs";
|
|
323
|
+
import { createRequire as createNodeRequire } from "node:module";
|
|
324
|
+
import { homedir as homedir3 } from "node:os";
|
|
325
|
+
import { basename as basename2, extname, join as join3, resolve as resolve2, win32 as winPath, posix as posixPath } from "node:path";
|
|
326
|
+
import http from "http";
|
|
327
|
+
function resolveCursorLaunchCdpPort(port = process.env.CURSOR_BRIDGE_CDP_PORT) {
|
|
328
|
+
const parsed = Number(port == null || String(port).trim() === "" ? 9223 : port);
|
|
329
|
+
if (!Number.isInteger(parsed) || parsed < 1024 || parsed > 65535) return 9223;
|
|
330
|
+
return parsed;
|
|
331
|
+
}
|
|
332
|
+
function looksLikePluginRuntimePath(candidate) {
|
|
333
|
+
const p = String(candidate || "").replace(/\//g, "\\").toLowerCase();
|
|
334
|
+
return p.includes("\\.codex\\.tmp\\marketplaces\\") || p.includes("\\.codex\\plugins\\cache\\") || p.includes("\\.claude\\plugins\\cache\\") || p.includes("\\appdata\\local\\npm-cache\\_npx\\");
|
|
335
|
+
}
|
|
336
|
+
function normalizeCodexThreadCwd(value) {
|
|
337
|
+
const raw = String(value || "").trim();
|
|
338
|
+
if (/^\\\\\?\\UNC\\/i.test(raw)) return `\\\\${raw.slice(8)}`;
|
|
339
|
+
if (/^\\\\\?\\[a-zA-Z]:\\/.test(raw)) return raw.slice(4);
|
|
340
|
+
return raw;
|
|
341
|
+
}
|
|
342
|
+
function resolveCodexThreadProjectPath(options = {}) {
|
|
343
|
+
const threadId = String(options.threadId ?? process.env.CODEX_THREAD_ID ?? "").trim();
|
|
344
|
+
if (!threadId) return null;
|
|
345
|
+
if (CODEX_THREAD_PROJECTS.has(threadId) && options.useCache !== false) {
|
|
346
|
+
return CODEX_THREAD_PROJECTS.get(threadId);
|
|
347
|
+
}
|
|
348
|
+
let database = null;
|
|
349
|
+
try {
|
|
350
|
+
const lookupThreadCwd = options.lookupThreadCwd || ((id) => {
|
|
351
|
+
const { DatabaseSync } = (options.requireImpl || loadModule)("node:sqlite");
|
|
352
|
+
const databasePath = options.databasePath || join3(homedir3(), ".codex", "state_5.sqlite");
|
|
353
|
+
database = new DatabaseSync(databasePath, { readOnly: true });
|
|
354
|
+
return database.prepare("SELECT cwd FROM threads WHERE id = ?").get(id)?.cwd || null;
|
|
355
|
+
});
|
|
356
|
+
const candidate = normalizeCodexThreadCwd(lookupThreadCwd(threadId));
|
|
357
|
+
const existsImpl = options.existsImpl || existsSync;
|
|
358
|
+
const resolved = candidate && !looksLikePluginRuntimePath(candidate) && existsImpl(candidate) ? resolve2(candidate) : null;
|
|
359
|
+
if (options.useCache !== false) CODEX_THREAD_PROJECTS.set(threadId, resolved);
|
|
360
|
+
return resolved;
|
|
361
|
+
} catch {
|
|
362
|
+
if (options.useCache !== false) CODEX_THREAD_PROJECTS.set(threadId, null);
|
|
363
|
+
return null;
|
|
364
|
+
} finally {
|
|
365
|
+
try {
|
|
366
|
+
database?.close();
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function resolveProjectPath(value = process.env.CURSOR_PROJECT_PATH, options = {}) {
|
|
372
|
+
const explicit = String(value || "").trim();
|
|
373
|
+
if (explicit) return resolve2(explicit);
|
|
374
|
+
const persisted = String(options.persistedProjectPath || "").trim();
|
|
375
|
+
if (persisted) return resolve2(normalizeCodexThreadCwd(persisted));
|
|
376
|
+
const threadProjectPath = options.threadProjectPath === void 0 ? resolveCodexThreadProjectPath(options) : options.threadProjectPath;
|
|
377
|
+
if (threadProjectPath) return resolve2(normalizeCodexThreadCwd(threadProjectPath));
|
|
378
|
+
const cwd = options.cwd ?? process.cwd();
|
|
379
|
+
if (!cwd || looksLikePluginRuntimePath(cwd)) return null;
|
|
380
|
+
return resolve2(cwd);
|
|
381
|
+
}
|
|
382
|
+
function cursorFromRegistry(options = {}) {
|
|
383
|
+
const execFileSyncImpl = options.execFileSyncImpl || execFileSync2;
|
|
384
|
+
const legacyExecSyncImpl = options.execSyncImpl;
|
|
385
|
+
const existsImpl = options.existsImpl || existsSync;
|
|
386
|
+
const queries = [
|
|
387
|
+
["HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Cursor.exe", "/ve"],
|
|
388
|
+
["HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Cursor.exe", "/ve"],
|
|
389
|
+
["HKCU\\Software\\Classes\\cursor\\shell\\open\\command", "/ve"],
|
|
390
|
+
["HKLM\\Software\\Classes\\cursor\\shell\\open\\command", "/ve"],
|
|
391
|
+
["HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Cursor (User)", "/v", "DisplayIcon"],
|
|
392
|
+
["HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Cursor", "/v", "DisplayIcon"]
|
|
393
|
+
];
|
|
394
|
+
for (const [key, ...valueArgs] of queries) {
|
|
395
|
+
try {
|
|
396
|
+
const runOptions = {
|
|
397
|
+
encoding: "utf8",
|
|
398
|
+
windowsHide: true,
|
|
399
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
400
|
+
};
|
|
401
|
+
const out = legacyExecSyncImpl && !options.execFileSyncImpl ? legacyExecSyncImpl(`reg query "${key}" ${valueArgs.join(" ")}`, runOptions) : execFileSyncImpl("reg.exe", ["query", key, ...valueArgs], runOptions);
|
|
402
|
+
const m = out.match(/([A-Za-z]:\\[^"\r\n]*?Cursor\.exe)/i);
|
|
403
|
+
if (m && existsImpl(m[1])) return m[1];
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
function normalizeCursorExeCandidate(value, options = {}) {
|
|
410
|
+
const platform = options.platform || process.platform;
|
|
411
|
+
const existsImpl = options.existsImpl || existsSync;
|
|
412
|
+
const raw = String(value || "").trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
413
|
+
if (!raw) return null;
|
|
414
|
+
let candidates;
|
|
415
|
+
if (platform === "win32") {
|
|
416
|
+
const normalized = raw.replace(/\//g, "\\");
|
|
417
|
+
candidates = /\.exe$/i.test(normalized) ? [normalized] : [winPath.join(normalized, "Cursor.exe")];
|
|
418
|
+
} else if (platform === "darwin") {
|
|
419
|
+
const normalized = raw.replace(/\\/g, "/").replace(/\/$/, "");
|
|
420
|
+
candidates = /\.app$/i.test(normalized) ? [posixPath.join(normalized, "Contents", "MacOS", "Cursor")] : /\/Contents\/MacOS$/i.test(normalized) ? [posixPath.join(normalized, "Cursor")] : [normalized];
|
|
421
|
+
} else {
|
|
422
|
+
candidates = [raw];
|
|
423
|
+
}
|
|
424
|
+
for (const candidate of candidates) {
|
|
425
|
+
try {
|
|
426
|
+
if (existsImpl(candidate)) return candidate;
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function findCursorExeDetails(options = {}) {
|
|
433
|
+
const platform = options.platform || process.platform;
|
|
434
|
+
const env = options.env || process.env;
|
|
435
|
+
const existsImpl = options.existsImpl || existsSync;
|
|
436
|
+
const override = normalizeCursorExeCandidate(env.CURSOR_EXE, { platform, existsImpl });
|
|
437
|
+
if (override) return { path: override, source: "CURSOR_EXE", platform };
|
|
438
|
+
if (platform === "win32") {
|
|
439
|
+
const fromReg = cursorFromRegistry({
|
|
440
|
+
execFileSyncImpl: options.execFileSyncImpl,
|
|
441
|
+
execSyncImpl: options.execSyncImpl,
|
|
442
|
+
existsImpl
|
|
443
|
+
});
|
|
444
|
+
if (fromReg) return { path: fromReg, source: "windows_registry", platform };
|
|
445
|
+
const localAppData = env.LOCALAPPDATA || join3(homedir3(), "AppData", "Local");
|
|
446
|
+
const programFiles = env.ProgramFiles || env.PROGRAMFILES || "C:\\Program Files";
|
|
447
|
+
const programFilesX86 = env["ProgramFiles(x86)"] || env.PROGRAMFILES_X86 || "";
|
|
448
|
+
const candidates = [
|
|
449
|
+
localAppData && winPath.join(localAppData, "Programs", "Cursor", "Cursor.exe"),
|
|
450
|
+
programFiles && winPath.join(programFiles, "Cursor", "Cursor.exe"),
|
|
451
|
+
programFilesX86 && winPath.join(programFilesX86, "Cursor", "Cursor.exe")
|
|
452
|
+
].filter(Boolean);
|
|
453
|
+
for (const candidate of candidates) {
|
|
454
|
+
try {
|
|
455
|
+
if (existsImpl(candidate)) return { path: candidate, source: "windows_standard_location", platform };
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
if (platform === "darwin") {
|
|
462
|
+
const userHome = env.HOME || homedir3();
|
|
463
|
+
const candidates = [
|
|
464
|
+
"/Applications/Cursor.app/Contents/MacOS/Cursor",
|
|
465
|
+
userHome && posixPath.join(userHome, "Applications", "Cursor.app", "Contents", "MacOS", "Cursor")
|
|
466
|
+
].filter(Boolean);
|
|
467
|
+
for (const candidate of candidates) {
|
|
468
|
+
try {
|
|
469
|
+
if (existsImpl(candidate)) return { path: candidate, source: "macos_standard_location", platform };
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
function findCursorExe(options = {}) {
|
|
478
|
+
return findCursorExeDetails(options)?.path || null;
|
|
479
|
+
}
|
|
480
|
+
function cdpUp(timeoutMs = 1500) {
|
|
481
|
+
return new Promise((resolve3) => {
|
|
482
|
+
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/version" }, (res) => {
|
|
483
|
+
res.resume();
|
|
484
|
+
resolve3(res.statusCode === 200);
|
|
485
|
+
});
|
|
486
|
+
req.on("error", () => resolve3(false));
|
|
487
|
+
req.setTimeout(timeoutMs, () => {
|
|
488
|
+
try {
|
|
489
|
+
req.destroy();
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
492
|
+
resolve3(false);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function cdpIsCursor(timeoutMs = 1500) {
|
|
497
|
+
return new Promise((resolve3) => {
|
|
498
|
+
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
|
|
499
|
+
let d = "";
|
|
500
|
+
res.on("data", (c) => d += c);
|
|
501
|
+
res.on("end", () => {
|
|
502
|
+
try {
|
|
503
|
+
if (/[\/\\](windsurf)[\/\\]/i.test(d)) return resolve3(false);
|
|
504
|
+
resolve3(/[\/\\]cursor[\/\\](resources|app)|cursor\.exe|vscode-app[^"]*[\/\\]cursor[\/\\]/i.test(d));
|
|
505
|
+
} catch {
|
|
506
|
+
resolve3(false);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
req.on("error", () => resolve3(false));
|
|
511
|
+
req.setTimeout(timeoutMs, () => {
|
|
512
|
+
try {
|
|
513
|
+
req.destroy();
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
516
|
+
resolve3(false);
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
function cursorRunning(options = {}) {
|
|
521
|
+
const platform = options.platform || process.platform;
|
|
522
|
+
const run = options.execFileSyncImpl || execFileSync2;
|
|
523
|
+
try {
|
|
524
|
+
if (platform === "win32") {
|
|
525
|
+
return /Cursor\.exe/i.test(run("tasklist.exe", ["/fi", "imagename eq Cursor.exe", "/nh"], {
|
|
526
|
+
encoding: "utf8",
|
|
527
|
+
windowsHide: true,
|
|
528
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
529
|
+
}));
|
|
530
|
+
}
|
|
531
|
+
if (platform === "darwin") {
|
|
532
|
+
run("pgrep", ["-f", "Cursor.app/Contents/MacOS/Cursor"], { stdio: "ignore" });
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
return false;
|
|
536
|
+
} catch {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function waitForCdp(maxMs = 3e4, stepMs = 1e3) {
|
|
541
|
+
const start = Date.now();
|
|
542
|
+
while (Date.now() - start < maxMs) {
|
|
543
|
+
if (await cdpUp()) return true;
|
|
544
|
+
await new Promise((r) => setTimeout(r, stepMs));
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
async function spawnDetachedSafely(spawnImpl, file, args, spawnOptions) {
|
|
549
|
+
let child;
|
|
550
|
+
try {
|
|
551
|
+
child = spawnImpl(file, args, spawnOptions);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return {
|
|
554
|
+
ok: false,
|
|
555
|
+
child: null,
|
|
556
|
+
error,
|
|
557
|
+
errorCode: error && typeof error === "object" && error.code != null ? String(error.code) : null
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
if (child && typeof child.once === "function") {
|
|
561
|
+
const startup = await new Promise((resolvePromise) => {
|
|
562
|
+
let settled = false;
|
|
563
|
+
const finish = (result) => {
|
|
564
|
+
if (settled) return;
|
|
565
|
+
settled = true;
|
|
566
|
+
child.off?.("spawn", onSpawn);
|
|
567
|
+
child.off?.("error", onError);
|
|
568
|
+
resolvePromise(result);
|
|
569
|
+
};
|
|
570
|
+
const onSpawn = () => finish({ ok: true });
|
|
571
|
+
const onError = (error) => finish({ ok: false, error });
|
|
572
|
+
child.once("spawn", onSpawn);
|
|
573
|
+
child.once("error", onError);
|
|
574
|
+
if (Number.isInteger(child.pid) && child.pid > 0) queueMicrotask(onSpawn);
|
|
575
|
+
});
|
|
576
|
+
if (!startup.ok) {
|
|
577
|
+
return {
|
|
578
|
+
ok: false,
|
|
579
|
+
child,
|
|
580
|
+
error: startup.error,
|
|
581
|
+
errorCode: startup.error && typeof startup.error === "object" && startup.error.code != null ? String(startup.error.code) : null
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
child.once("error", () => {
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
588
|
+
return { ok: true, child };
|
|
589
|
+
}
|
|
590
|
+
function attachedPresentation(runtimeMode, port) {
|
|
591
|
+
if (runtimeMode !== "minimal") return null;
|
|
592
|
+
return {
|
|
593
|
+
supported: true,
|
|
594
|
+
applied: false,
|
|
595
|
+
action: "hide",
|
|
596
|
+
port,
|
|
597
|
+
reason: "attached lifecycle cannot start the PowerShell window guard under the current process policy"
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function ensureCursorRunningLocal(options = {}) {
|
|
601
|
+
const waitMs = Number(options.waitMs || 3e4);
|
|
602
|
+
const runtimeMode = options.runtimeMode || "normal";
|
|
603
|
+
const effectiveRuntimeMode = normalizeCursorRuntimeMode(runtimeMode);
|
|
604
|
+
const cdpUpImpl = options.cdpUpImpl || cdpUp;
|
|
605
|
+
const cdpIsCursorImpl = options.cdpIsCursorImpl || cdpIsCursor;
|
|
606
|
+
const cursorRunningImpl = options.cursorRunningImpl || cursorRunning;
|
|
607
|
+
const findCursorExeDetailsImpl = options.findCursorExeDetailsImpl || findCursorExeDetails;
|
|
608
|
+
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath ? resolve2(String(options.projectPath)) : null : resolveProjectPath();
|
|
609
|
+
const listCdpPageTargetsImpl = options.listCdpPageTargetsImpl || listCdpPageTargets;
|
|
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;
|
|
614
|
+
const allowSpawn = options.allowSpawn !== false;
|
|
615
|
+
const allowProcessControl = options.allowProcessControl !== false;
|
|
616
|
+
if (await cdpUpImpl()) {
|
|
617
|
+
const isCursor = await cdpIsCursorImpl();
|
|
618
|
+
if (isCursor) {
|
|
619
|
+
const cursorPid2 = allowProcessControl ? findCursorPidByPortImpl(CDP_PORT) : null;
|
|
620
|
+
const windowGuard2 = allowProcessControl && effectiveRuntimeMode === "minimal" && cursorPid2 ? startMinimalWindowGuard(cursorPid2) : null;
|
|
621
|
+
const presentation2 = effectiveRuntimeMode === "minimal" ? allowProcessControl ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid2 }) : attachedPresentation(effectiveRuntimeMode, CDP_PORT) : null;
|
|
622
|
+
let currentTargets = await listCdpPageTargetsImpl();
|
|
623
|
+
const projectKey = normalizeProjectKey(projectPath);
|
|
624
|
+
let targetId2 = projectKey ? PROJECT_TARGETS.get(projectKey) || null : currentTargets[0] && currentTargets[0].id || null;
|
|
625
|
+
let workspaceAction = projectPath ? "reused-project-target" : "reused-last-workspace";
|
|
626
|
+
const cachedTarget = targetId2 ? currentTargets.find((target2) => target2.id === targetId2) : null;
|
|
627
|
+
if (targetId2 && (!cachedTarget || projectPath && !targetCanServeProject(cachedTarget.title, projectPath))) {
|
|
628
|
+
PROJECT_TARGETS.delete(projectKey);
|
|
629
|
+
targetId2 = null;
|
|
630
|
+
} else if (targetId2 && cachedTarget && isAgentsWindowTitle(cachedTarget.title)) {
|
|
631
|
+
workspaceAction = "reused-agents-window";
|
|
632
|
+
}
|
|
633
|
+
if (projectPath && !targetId2) {
|
|
634
|
+
const existingTarget = selectReusableProjectTarget(currentTargets, projectPath);
|
|
635
|
+
if (existingTarget) {
|
|
636
|
+
targetId2 = existingTarget.id;
|
|
637
|
+
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
638
|
+
workspaceAction = isAgentsWindowTitle(existingTarget.title) ? "reused-agents-window" : "recovered-project-target";
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (projectPath && existsSync(projectPath) && !targetId2) {
|
|
642
|
+
if (!allowSpawn) {
|
|
643
|
+
return {
|
|
644
|
+
ok: false,
|
|
645
|
+
status: "workspace-not-ready",
|
|
646
|
+
port: CDP_PORT,
|
|
647
|
+
cursorPid: cursorPid2,
|
|
648
|
+
runtimeMode: effectiveRuntimeMode,
|
|
649
|
+
projectPath,
|
|
650
|
+
presentation: presentation2,
|
|
651
|
+
windowGuard: windowGuard2,
|
|
652
|
+
needsAction: "open_workspace_in_cursor",
|
|
653
|
+
retryable: true,
|
|
654
|
+
nextStep: `Open workspace ${projectPath} in the existing Cursor Agents Window, then retry the same operation.`,
|
|
655
|
+
message: `CCE connected to Cursor, but the current workspace target for ${projectPath} is not ready and the current lifecycle cannot open a new window.`
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
659
|
+
const exe2 = cursorExecutable2 && cursorExecutable2.path;
|
|
660
|
+
if (!exe2) {
|
|
661
|
+
return {
|
|
662
|
+
ok: false,
|
|
663
|
+
status: "workspace-not-ready",
|
|
664
|
+
port: CDP_PORT,
|
|
665
|
+
cursorPid: cursorPid2,
|
|
666
|
+
runtimeMode: effectiveRuntimeMode,
|
|
667
|
+
projectPath,
|
|
668
|
+
presentation: presentation2,
|
|
669
|
+
windowGuard: windowGuard2,
|
|
670
|
+
needsAction: "install_or_locate_cursor",
|
|
671
|
+
retryable: true,
|
|
672
|
+
nextStep: "Confirm that Cursor is installed and signed in. For a portable or custom installation, set CURSOR_EXE and run the same initialization command again.",
|
|
673
|
+
message: `CCE connected to Cursor but cannot open workspace ${projectPath} because the Cursor executable was not found.`
|
|
674
|
+
};
|
|
675
|
+
}
|
|
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], {
|
|
704
|
+
detached: true,
|
|
705
|
+
stdio: "ignore",
|
|
706
|
+
windowsHide: effectiveRuntimeMode === "minimal"
|
|
707
|
+
});
|
|
708
|
+
if (!reused.ok) {
|
|
709
|
+
return {
|
|
710
|
+
ok: false,
|
|
711
|
+
status: "spawn-blocked",
|
|
712
|
+
port: CDP_PORT,
|
|
713
|
+
cursorPid: cursorPid2,
|
|
714
|
+
runtimeMode: effectiveRuntimeMode,
|
|
715
|
+
projectPath,
|
|
716
|
+
presentation: presentation2,
|
|
717
|
+
windowGuard: windowGuard2,
|
|
718
|
+
errorCode: reused.errorCode,
|
|
719
|
+
needsAction: "open_workspace_in_cursor",
|
|
720
|
+
retryable: true,
|
|
721
|
+
nextStep: `Open workspace ${projectPath} in Cursor, then retry the same operation.`,
|
|
722
|
+
message: `Cursor Bridge could not reuse the existing Cursor window for the workspace: ${reused.error instanceof Error ? reused.error.message : String(reused.error)}`
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
workspaceAction = "reused-window-for-project";
|
|
726
|
+
const reusedTarget = await waitForProjectCdpTarget(12e3, projectPath, listCdpPageTargetsImpl, sleepImpl);
|
|
727
|
+
if (!reusedTarget) {
|
|
728
|
+
return {
|
|
729
|
+
ok: false,
|
|
730
|
+
status: "workspace-not-ready",
|
|
731
|
+
port: CDP_PORT,
|
|
732
|
+
cursorPid: cursorPid2,
|
|
733
|
+
runtimeMode: effectiveRuntimeMode,
|
|
734
|
+
projectPath,
|
|
735
|
+
presentation: presentation2,
|
|
736
|
+
windowGuard: windowGuard2,
|
|
737
|
+
workspaceAction,
|
|
738
|
+
cursorExecutable: exe2,
|
|
739
|
+
cursorExecutableSource: cursorExecutable2.source,
|
|
740
|
+
needsAction: "retry_initialization",
|
|
741
|
+
retryable: true,
|
|
742
|
+
nextStep: "Wait for Cursor to finish opening the project, then run the same initialization command again.",
|
|
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.`
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
targetId2 = reusedTarget.id;
|
|
747
|
+
PROJECT_TARGETS.set(projectKey, targetId2);
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
ok: true,
|
|
751
|
+
status: "already",
|
|
752
|
+
port: CDP_PORT,
|
|
753
|
+
cursorPid: cursorPid2,
|
|
754
|
+
runtimeMode: effectiveRuntimeMode,
|
|
755
|
+
projectPath,
|
|
756
|
+
presentation: presentation2,
|
|
757
|
+
windowGuard: windowGuard2,
|
|
758
|
+
targetId: targetId2,
|
|
759
|
+
workspaceAction,
|
|
760
|
+
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 || "default"}.`
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
return {
|
|
764
|
+
ok: false,
|
|
765
|
+
status: "port-not-cursor",
|
|
766
|
+
port: CDP_PORT,
|
|
767
|
+
needsAction: "free_cce_port",
|
|
768
|
+
retryable: true,
|
|
769
|
+
nextStep: `Local port ${CDP_PORT} is in use by another program. Close that program, then run the same initialization command again.`,
|
|
770
|
+
message: `CCE cannot connect to Cursor because required local port ${CDP_PORT} is occupied by another program.`
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
if (!allowSpawn) {
|
|
774
|
+
return {
|
|
775
|
+
ok: false,
|
|
776
|
+
status: "external-launch-required",
|
|
777
|
+
port: CDP_PORT,
|
|
778
|
+
cursorPid: null,
|
|
779
|
+
runtimeMode: effectiveRuntimeMode,
|
|
780
|
+
projectPath,
|
|
781
|
+
presentation: attachedPresentation(effectiveRuntimeMode, CDP_PORT),
|
|
782
|
+
needsAction: "launch_cursor_with_cdp",
|
|
783
|
+
retryable: true,
|
|
784
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, open ${projectPath || "the target workspace"}, then retry the same operation.`,
|
|
785
|
+
message: `Cursor is not reachable on the configured CDP port ${CDP_PORT}, and the current lifecycle policy cannot launch it.`
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
if (cursorRunningImpl()) {
|
|
789
|
+
const cursorExecutable2 = findCursorExeDetailsImpl();
|
|
790
|
+
return {
|
|
791
|
+
ok: false,
|
|
792
|
+
status: "running-no-debug",
|
|
793
|
+
port: CDP_PORT,
|
|
794
|
+
cursorExecutable: cursorExecutable2 && cursorExecutable2.path || null,
|
|
795
|
+
cursorExecutableSource: cursorExecutable2 && cursorExecutable2.source || null,
|
|
796
|
+
needsAction: "close_cursor_and_retry",
|
|
797
|
+
retryable: true,
|
|
798
|
+
nextStep: projectPath ? `Save your work, exit Cursor normally once, then initialize CCE for workspace ${projectPath} again.` : "Save your work, exit Cursor normally once, then retry the previous CCE operation.",
|
|
799
|
+
message: "Cursor was already running, so CCE cannot add the required connection capability in place. Cursor Bridge will not force-close it, protecting unsaved work."
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
const cursorExecutable = findCursorExeDetailsImpl();
|
|
803
|
+
const exe = cursorExecutable && cursorExecutable.path;
|
|
804
|
+
if (!exe) {
|
|
805
|
+
return {
|
|
806
|
+
ok: false,
|
|
807
|
+
status: "no-exe",
|
|
808
|
+
port: CDP_PORT,
|
|
809
|
+
needsAction: "install_or_locate_cursor",
|
|
810
|
+
retryable: true,
|
|
811
|
+
nextStep: "Install and sign in to Cursor first. For a portable or custom installation, set CURSOR_EXE and run the same initialization command again.",
|
|
812
|
+
message: "Cursor was not found. Standard Windows and macOS installations are detected automatically and normally do not require an explicit executable path."
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
const launchPort = resolveCursorLaunchCdpPort(CDP_PORT);
|
|
816
|
+
const args = [`--remote-debugging-port=${launchPort}`, `--remote-allow-origins=http://localhost:${launchPort}`];
|
|
817
|
+
if (effectiveRuntimeMode === "minimal") {
|
|
818
|
+
args.push(
|
|
819
|
+
"--disable-background-timer-throttling",
|
|
820
|
+
"--disable-renderer-backgrounding",
|
|
821
|
+
"--disable-backgrounding-occluded-windows"
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
const launched = await spawnDetachedSafely(spawnImpl, exe, args, {
|
|
825
|
+
detached: true,
|
|
826
|
+
stdio: "ignore",
|
|
827
|
+
windowsHide: effectiveRuntimeMode === "minimal"
|
|
828
|
+
});
|
|
829
|
+
if (!launched.ok) {
|
|
830
|
+
return {
|
|
831
|
+
ok: false,
|
|
832
|
+
status: "spawn-blocked",
|
|
833
|
+
exe,
|
|
834
|
+
port: CDP_PORT,
|
|
835
|
+
cursorPid: null,
|
|
836
|
+
runtimeMode: effectiveRuntimeMode,
|
|
837
|
+
projectPath,
|
|
838
|
+
errorCode: launched.errorCode,
|
|
839
|
+
needsAction: "launch_cursor_manually",
|
|
840
|
+
retryable: true,
|
|
841
|
+
nextStep: `Start Cursor with its remote debugging connection on port ${CDP_PORT}, then retry the same operation.`,
|
|
842
|
+
message: `Cursor Bridge could not launch Cursor: ${launched.error instanceof Error ? launched.error.message : String(launched.error)}`
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
const child = launched.child;
|
|
846
|
+
const startupWindowGuard = effectiveRuntimeMode === "minimal" ? startMinimalWindowGuard(child && child.pid) : null;
|
|
847
|
+
const up = await waitForCdpImpl(waitMs);
|
|
848
|
+
if (!up) {
|
|
849
|
+
return {
|
|
850
|
+
ok: false,
|
|
851
|
+
status: "timeout",
|
|
852
|
+
exe,
|
|
853
|
+
port: CDP_PORT,
|
|
854
|
+
cursorPid: child.pid || null,
|
|
855
|
+
runtimeMode: effectiveRuntimeMode,
|
|
856
|
+
projectPath,
|
|
857
|
+
windowGuard: startupWindowGuard,
|
|
858
|
+
cursorExecutable: exe,
|
|
859
|
+
cursorExecutableSource: cursorExecutable.source,
|
|
860
|
+
needsAction: "retry_initialization",
|
|
861
|
+
retryable: true,
|
|
862
|
+
nextStep: "Wait a moment, then run the same initialization command again.",
|
|
863
|
+
message: "Cursor started, but CCE is not ready yet. No port setting needs to be changed."
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const cursorPid = findCursorPidByPortImpl(CDP_PORT) || child.pid || null;
|
|
867
|
+
const openedTarget = await waitForNewCdpTarget(/* @__PURE__ */ new Set(), 12e3, projectPath, listCdpPageTargetsImpl);
|
|
868
|
+
const targetId = openedTarget && openedTarget.id || null;
|
|
869
|
+
if (projectPath && !targetId) {
|
|
870
|
+
return {
|
|
871
|
+
ok: false,
|
|
872
|
+
status: "workspace-not-ready",
|
|
873
|
+
exe,
|
|
874
|
+
port: CDP_PORT,
|
|
875
|
+
cursorPid,
|
|
876
|
+
runtimeMode: effectiveRuntimeMode,
|
|
877
|
+
projectPath,
|
|
878
|
+
windowGuard: startupWindowGuard,
|
|
879
|
+
cursorExecutable: exe,
|
|
880
|
+
cursorExecutableSource: cursorExecutable.source,
|
|
881
|
+
needsAction: "retry_initialization",
|
|
882
|
+
retryable: true,
|
|
883
|
+
nextStep: "Wait for Cursor to finish opening the project, then run the same initialization command again.",
|
|
884
|
+
message: `Cursor started, but CCE has not confirmed that workspace ${projectPath} is ready. Initialization stopped safely to avoid searching the wrong project.`
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
if (projectPath && targetId) PROJECT_TARGETS.set(normalizeProjectKey(projectPath), targetId);
|
|
888
|
+
const windowGuard = effectiveRuntimeMode === "minimal" && cursorPid ? startMinimalWindowGuard(cursorPid) : null;
|
|
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";
|
|
891
|
+
const presentation = effectiveRuntimeMode === "minimal" ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid }) : null;
|
|
892
|
+
return {
|
|
893
|
+
ok: true,
|
|
894
|
+
status: "launched",
|
|
895
|
+
exe,
|
|
896
|
+
port: CDP_PORT,
|
|
897
|
+
cursorPid,
|
|
898
|
+
runtimeMode: effectiveRuntimeMode,
|
|
899
|
+
projectPath,
|
|
900
|
+
presentation,
|
|
901
|
+
windowGuard,
|
|
902
|
+
startupWindowGuard,
|
|
903
|
+
cursorExecutable: exe,
|
|
904
|
+
cursorExecutableSource: cursorExecutable.source,
|
|
905
|
+
targetId,
|
|
906
|
+
workspaceAction: launchedIntoAgents ? "launched-agents-window" : projectPath ? "launched-project" : "launched-last-workspace",
|
|
907
|
+
message: `Cursor started (${exe}, ${target}); CDP ${CDP_PORT} is ready.`
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function normalizeProjectKey(projectPath) {
|
|
911
|
+
return projectPath ? resolve2(String(projectPath)).replace(/\\/g, "/").toLowerCase() : "";
|
|
912
|
+
}
|
|
913
|
+
function targetTitleMatchesProject(title, projectPath) {
|
|
914
|
+
const name = basename2(String(projectPath || "")).trim().toLowerCase();
|
|
915
|
+
if (!name) return false;
|
|
916
|
+
const extension = extname(name);
|
|
917
|
+
const candidates = [...new Set([name, extension ? name.slice(0, -extension.length) : name].filter(Boolean))];
|
|
918
|
+
const normalizedTitle = String(title || "").trim().toLowerCase();
|
|
919
|
+
return candidates.some((candidate) => normalizedTitle === candidate || normalizedTitle.startsWith(candidate + " - ") || normalizedTitle.includes(" - " + candidate + " - "));
|
|
920
|
+
}
|
|
921
|
+
function isAgentsWindowTitle(title) {
|
|
922
|
+
const normalized = String(title || "").trim().toLowerCase();
|
|
923
|
+
return normalized === "cursor agents" || normalized.startsWith("cursor agents - ");
|
|
924
|
+
}
|
|
925
|
+
function targetCanServeProject(title, projectPath) {
|
|
926
|
+
if (!projectPath) return true;
|
|
927
|
+
return targetTitleMatchesProject(title, projectPath) || isAgentsWindowTitle(title);
|
|
928
|
+
}
|
|
929
|
+
function selectAgentsWindowTarget(targets) {
|
|
930
|
+
return (Array.isArray(targets) ? targets : []).find((target) => target && target.id && isAgentsWindowTitle(target.title)) || null;
|
|
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
|
+
}
|
|
936
|
+
async function listCdpPageTargets(timeoutMs = 1500) {
|
|
937
|
+
return new Promise((done) => {
|
|
938
|
+
const req = http.get({ host: CDP_HOST, port: CDP_PORT, path: "/json/list" }, (res) => {
|
|
939
|
+
let data = "";
|
|
940
|
+
res.on("data", (chunk) => {
|
|
941
|
+
data += chunk;
|
|
942
|
+
});
|
|
943
|
+
res.on("end", () => {
|
|
944
|
+
try {
|
|
945
|
+
const targets = JSON.parse(data);
|
|
946
|
+
done(Array.isArray(targets) ? targets.filter((target) => target && target.type === "page" && target.id) : []);
|
|
947
|
+
} catch {
|
|
948
|
+
done([]);
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
req.on("error", () => done([]));
|
|
953
|
+
req.setTimeout(timeoutMs, () => {
|
|
954
|
+
try {
|
|
955
|
+
req.destroy();
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
done([]);
|
|
959
|
+
});
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
function selectNewCdpTarget(beforeTargetIds, targets, projectPath = "") {
|
|
963
|
+
const before = beforeTargetIds instanceof Set ? beforeTargetIds : new Set(beforeTargetIds || []);
|
|
964
|
+
const fresh = (targets || []).filter((target) => target && target.id && !before.has(target.id));
|
|
965
|
+
if (projectPath) return selectReusableProjectTarget(fresh, projectPath);
|
|
966
|
+
return fresh[0] || null;
|
|
967
|
+
}
|
|
968
|
+
async function waitForNewCdpTarget(beforeTargetIds, maxMs = 12e3, projectPath = "", listImpl = listCdpPageTargets) {
|
|
969
|
+
const started = Date.now();
|
|
970
|
+
while (Date.now() - started < maxMs) {
|
|
971
|
+
const target = selectNewCdpTarget(beforeTargetIds, await listImpl(), projectPath);
|
|
972
|
+
if (target) return target;
|
|
973
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 300));
|
|
974
|
+
}
|
|
975
|
+
return null;
|
|
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
|
+
}
|
|
986
|
+
var CDP_PORT, CDP_ORIGIN, CDP_HOST, PROJECT_TARGETS, CODEX_THREAD_PROJECTS, loadModule;
|
|
987
|
+
var init_cursor_ensure_core = __esm({
|
|
988
|
+
"cursor-ensure-core.mjs"() {
|
|
989
|
+
init_cursor_runtime();
|
|
990
|
+
CDP_PORT = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
991
|
+
CDP_ORIGIN = `http://localhost:${CDP_PORT}`;
|
|
992
|
+
CDP_HOST = "127.0.0.1";
|
|
993
|
+
PROJECT_TARGETS = /* @__PURE__ */ new Map();
|
|
994
|
+
CODEX_THREAD_PROJECTS = /* @__PURE__ */ new Map();
|
|
995
|
+
loadModule = createNodeRequire(import.meta.url);
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// cursor-lifecycle-supervisor.mjs
|
|
1000
|
+
import net from "node:net";
|
|
1001
|
+
import {
|
|
1002
|
+
writeFileSync as writeFileSync2,
|
|
1003
|
+
unlinkSync,
|
|
1004
|
+
existsSync as existsSync2,
|
|
1005
|
+
openSync,
|
|
1006
|
+
closeSync,
|
|
1007
|
+
readFileSync as readFileSync2,
|
|
1008
|
+
appendFileSync,
|
|
1009
|
+
renameSync as renameSync2,
|
|
1010
|
+
statSync
|
|
1011
|
+
} from "node:fs";
|
|
1012
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
1013
|
+
|
|
1014
|
+
// lifecycle-paths.mjs
|
|
1015
|
+
import { createHash } from "node:crypto";
|
|
1016
|
+
import { homedir } from "node:os";
|
|
1017
|
+
import { join } from "node:path";
|
|
1018
|
+
import { mkdirSync } from "node:fs";
|
|
1019
|
+
function defaultLifecycleDir() {
|
|
1020
|
+
if (process.env.CURSOR_BRIDGE_LIFECYCLE_DIR) return process.env.CURSOR_BRIDGE_LIFECYCLE_DIR;
|
|
1021
|
+
if (process.platform === "win32") {
|
|
1022
|
+
const root2 = process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local");
|
|
1023
|
+
return join(root2, "cursor-bridge", "lifecycle");
|
|
1024
|
+
}
|
|
1025
|
+
const root = process.env.XDG_RUNTIME_DIR || process.env.XDG_STATE_HOME || join(homedir(), ".local", "state");
|
|
1026
|
+
return join(root, "cursor-bridge", "lifecycle");
|
|
1027
|
+
}
|
|
1028
|
+
function ensureLifecycleDir(dir = defaultLifecycleDir()) {
|
|
1029
|
+
mkdirSync(dir, { recursive: true });
|
|
1030
|
+
return dir;
|
|
1031
|
+
}
|
|
1032
|
+
function lifecycleEndpointTag(dir) {
|
|
1033
|
+
return createHash("sha256").update(String(dir), "utf8").digest("hex").slice(0, 24);
|
|
1034
|
+
}
|
|
1035
|
+
function supervisorSockPath(dir = defaultLifecycleDir()) {
|
|
1036
|
+
if (process.env.CURSOR_BRIDGE_SUPERVISOR_SOCK) return process.env.CURSOR_BRIDGE_SUPERVISOR_SOCK;
|
|
1037
|
+
if (process.platform === "win32") {
|
|
1038
|
+
return `\\\\.\\pipe\\cursor-bridge-lifecycle-${lifecycleEndpointTag(dir)}`;
|
|
1039
|
+
}
|
|
1040
|
+
return join(dir, "supervisor.sock");
|
|
1041
|
+
}
|
|
1042
|
+
function supervisorPidPath(dir = defaultLifecycleDir()) {
|
|
1043
|
+
return join(dir, "supervisor.pid");
|
|
1044
|
+
}
|
|
1045
|
+
function supervisorLockPath(dir = defaultLifecycleDir()) {
|
|
1046
|
+
return join(dir, "supervisor.lock");
|
|
1047
|
+
}
|
|
1048
|
+
function supervisorLogPath(dir = defaultLifecycleDir()) {
|
|
1049
|
+
return join(dir, "supervisor.log");
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// cursor-lifecycle-supervisor.mjs
|
|
1053
|
+
var LOG_MAX_BYTES = 256 * 1024;
|
|
1054
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
1055
|
+
const out = {};
|
|
1056
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
1057
|
+
const a = argv[i];
|
|
1058
|
+
if (a === "--lifecycle-supervisor") continue;
|
|
1059
|
+
if (a.startsWith("--lifecycle-dir=")) out.dir = a.slice("--lifecycle-dir=".length);
|
|
1060
|
+
else if (a === "--lifecycle-dir") out.dir = argv[++i];
|
|
1061
|
+
else if (a.startsWith("--sock=")) out.sock = a.slice("--sock=".length);
|
|
1062
|
+
else if (a === "--sock") out.sock = argv[++i];
|
|
1063
|
+
else if (a.startsWith("--boot-env=")) out.bootEnv = a.slice("--boot-env=".length);
|
|
1064
|
+
else if (a === "--boot-env") out.bootEnv = argv[++i];
|
|
1065
|
+
else if (a.startsWith("--ensure-module=")) out.ensureModule = a.slice("--ensure-module=".length);
|
|
1066
|
+
else if (a === "--ensure-module") out.ensureModule = argv[++i];
|
|
1067
|
+
else if (a.startsWith("--idle-ms=")) out.idleMs = Number(a.slice("--idle-ms=".length));
|
|
1068
|
+
else if (a === "--idle-ms") out.idleMs = Number(argv[++i]);
|
|
1069
|
+
else if (a.startsWith("--runtime-fingerprint=")) out.runtimeFingerprint = a.slice("--runtime-fingerprint=".length);
|
|
1070
|
+
else if (a === "--runtime-fingerprint") out.runtimeFingerprint = argv[++i];
|
|
1071
|
+
}
|
|
1072
|
+
return out;
|
|
1073
|
+
}
|
|
1074
|
+
function tryRemove(path) {
|
|
1075
|
+
try {
|
|
1076
|
+
if (path && existsSync2(path)) unlinkSync(path);
|
|
1077
|
+
} catch {
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
function applyBootEnv(bootEnvPath) {
|
|
1081
|
+
if (!bootEnvPath || !existsSync2(bootEnvPath)) return { applied: false, deleted: false };
|
|
1082
|
+
try {
|
|
1083
|
+
const parsed = JSON.parse(readFileSync2(bootEnvPath, "utf8"));
|
|
1084
|
+
if (parsed && typeof parsed === "object") {
|
|
1085
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
1086
|
+
if (value == null) continue;
|
|
1087
|
+
if (process.env[key] == null || process.env[key] === "") {
|
|
1088
|
+
process.env[key] = String(value);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
tryRemove(bootEnvPath);
|
|
1093
|
+
return { applied: true, deleted: !existsSync2(bootEnvPath) };
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
console.error("[cursor-lifecycle-supervisor] boot-env read failed:", error instanceof Error ? error.message : error);
|
|
1096
|
+
return { applied: false, deleted: false, error: error instanceof Error ? error.message : String(error) };
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function rotateLogIfNeeded(logPath) {
|
|
1100
|
+
try {
|
|
1101
|
+
if (!existsSync2(logPath)) return;
|
|
1102
|
+
const size = statSync(logPath).size;
|
|
1103
|
+
if (size < LOG_MAX_BYTES) return;
|
|
1104
|
+
const rotated = `${logPath}.1`;
|
|
1105
|
+
tryRemove(rotated);
|
|
1106
|
+
renameSync2(logPath, rotated);
|
|
1107
|
+
} catch {
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function writeSupervisorDiag(logPath, event, fields = {}) {
|
|
1111
|
+
if (!logPath || !event) return;
|
|
1112
|
+
try {
|
|
1113
|
+
rotateLogIfNeeded(logPath);
|
|
1114
|
+
const safe = {
|
|
1115
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1116
|
+
event: String(event),
|
|
1117
|
+
supervisorPid: process.pid
|
|
1118
|
+
};
|
|
1119
|
+
if (fields.adapterPid != null) safe.adapterPid = fields.adapterPid;
|
|
1120
|
+
if (fields.reason != null) safe.reason = String(fields.reason).slice(0, 200);
|
|
1121
|
+
if (fields.status != null) safe.status = String(fields.status);
|
|
1122
|
+
if (fields.ok != null) safe.ok = !!fields.ok;
|
|
1123
|
+
if (fields.clients != null) safe.clients = Number(fields.clients);
|
|
1124
|
+
if (fields.ensureCount != null) safe.ensureCount = Number(fields.ensureCount);
|
|
1125
|
+
if (fields.sock != null) safe.sock = String(fields.sock).slice(0, 260);
|
|
1126
|
+
if (fields.dir != null) safe.dir = String(fields.dir).slice(0, 260);
|
|
1127
|
+
if (fields.error != null) safe.error = String(fields.error).slice(0, 400);
|
|
1128
|
+
if (fields.code != null) safe.code = fields.code;
|
|
1129
|
+
appendFileSync(logPath, `${JSON.stringify(safe)}
|
|
1130
|
+
`, { encoding: "utf8" });
|
|
1131
|
+
} catch {
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
function log(...args) {
|
|
1135
|
+
console.error("[cursor-lifecycle-supervisor]", ...args);
|
|
1136
|
+
}
|
|
1137
|
+
async function loadEnsure(ensureModule) {
|
|
1138
|
+
const override = ensureModule || process.env.CURSOR_BRIDGE_ENSURE_MODULE;
|
|
1139
|
+
if (override) {
|
|
1140
|
+
const mod = await import(pathToFileURL(override).href);
|
|
1141
|
+
if (typeof mod.ensureCursorRunningLocal !== "function") {
|
|
1142
|
+
throw new Error(`ensure module missing ensureCursorRunningLocal: ${override}`);
|
|
1143
|
+
}
|
|
1144
|
+
return mod.ensureCursorRunningLocal;
|
|
1145
|
+
}
|
|
1146
|
+
const { ensureCursorRunningLocal: ensureCursorRunningLocal2 } = await Promise.resolve().then(() => (init_cursor_ensure_core(), cursor_ensure_core_exports));
|
|
1147
|
+
return ensureCursorRunningLocal2;
|
|
1148
|
+
}
|
|
1149
|
+
function writePid(pidPath) {
|
|
1150
|
+
writeFileSync2(pidPath, `${process.pid}
|
|
1151
|
+
`, { encoding: "utf8" });
|
|
1152
|
+
}
|
|
1153
|
+
function acquireOrExit(lockPath, diagLog) {
|
|
1154
|
+
try {
|
|
1155
|
+
const fd = openSync(lockPath, "wx");
|
|
1156
|
+
closeSync(fd);
|
|
1157
|
+
writeFileSync2(lockPath, `${process.pid}
|
|
1158
|
+
`, { encoding: "utf8" });
|
|
1159
|
+
return true;
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
if (error && error.code === "EEXIST") {
|
|
1162
|
+
try {
|
|
1163
|
+
const existing = Number(String(readFileSync2(lockPath, "utf8")).trim());
|
|
1164
|
+
if (existing && existing !== process.pid) {
|
|
1165
|
+
try {
|
|
1166
|
+
process.kill(existing, 0);
|
|
1167
|
+
log(`another supervisor holds lock pid=${existing}; exiting`);
|
|
1168
|
+
writeSupervisorDiag(diagLog, "fatal", { reason: "lock-held", error: `pid=${existing}` });
|
|
1169
|
+
return false;
|
|
1170
|
+
} catch {
|
|
1171
|
+
tryRemove(lockPath);
|
|
1172
|
+
return acquireOrExit(lockPath, diagLog);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
} catch {
|
|
1176
|
+
}
|
|
1177
|
+
log("lock busy; exiting");
|
|
1178
|
+
writeSupervisorDiag(diagLog, "fatal", { reason: "lock-busy" });
|
|
1179
|
+
return false;
|
|
1180
|
+
}
|
|
1181
|
+
throw error;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
async function startSupervisor(options = {}) {
|
|
1185
|
+
const cli = parseArgs(options.argv || process.argv.slice(2));
|
|
1186
|
+
if (cli.bootEnv || options.bootEnv) applyBootEnv(cli.bootEnv || options.bootEnv);
|
|
1187
|
+
const dir = ensureLifecycleDir(options.dir || cli.dir || defaultLifecycleDir());
|
|
1188
|
+
const sock = options.sock || cli.sock || process.env.CURSOR_BRIDGE_SUPERVISOR_SOCK || supervisorSockPath(dir);
|
|
1189
|
+
const pidPath = options.pidPath || supervisorPidPath(dir);
|
|
1190
|
+
const lockPath = options.lockPath || supervisorLockPath(dir);
|
|
1191
|
+
const logPath = options.logPath || supervisorLogPath(dir);
|
|
1192
|
+
const idleMs = Number(
|
|
1193
|
+
options.idleMs ?? cli.idleMs ?? process.env.CURSOR_BRIDGE_SUPERVISOR_IDLE_MS ?? 5 * 60 * 1e3
|
|
1194
|
+
);
|
|
1195
|
+
const ensureModule = options.ensureModule || cli.ensureModule || process.env.CURSOR_BRIDGE_ENSURE_MODULE;
|
|
1196
|
+
const runtimeFingerprint = String(options.runtimeFingerprint || cli.runtimeFingerprint || "unknown");
|
|
1197
|
+
const runtimeScript = fileURLToPath(import.meta.url);
|
|
1198
|
+
writeSupervisorDiag(logPath, "start", { dir, sock, reason: "startSupervisor" });
|
|
1199
|
+
if (!acquireOrExit(lockPath, logPath)) {
|
|
1200
|
+
return { started: false, reason: "lock-held" };
|
|
1201
|
+
}
|
|
1202
|
+
if (process.platform !== "win32" && existsSync2(sock)) {
|
|
1203
|
+
tryRemove(sock);
|
|
1204
|
+
}
|
|
1205
|
+
const ensureLocal = await loadEnsure(ensureModule);
|
|
1206
|
+
const ensureInflight = /* @__PURE__ */ new Map();
|
|
1207
|
+
let ensureTail = Promise.resolve();
|
|
1208
|
+
let ensureCount = 0;
|
|
1209
|
+
let lastEnsure = null;
|
|
1210
|
+
const clients = /* @__PURE__ */ new Set();
|
|
1211
|
+
let idleTimer = null;
|
|
1212
|
+
let shuttingDown = false;
|
|
1213
|
+
const scheduleIdle = () => {
|
|
1214
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
1215
|
+
if (lastEnsure && lastEnsure.ok && lastEnsure.runtimeMode === "minimal") {
|
|
1216
|
+
writeSupervisorDiag(logPath, "idle-suppressed", {
|
|
1217
|
+
reason: "minimal-runtime-owns-window-guard",
|
|
1218
|
+
clients: clients.size,
|
|
1219
|
+
ensureCount
|
|
1220
|
+
});
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
if (!(idleMs > 0)) return;
|
|
1224
|
+
idleTimer = setTimeout(() => {
|
|
1225
|
+
if (clients.size > 0 || shuttingDown) return;
|
|
1226
|
+
log(`idle ${idleMs}ms with 0 clients; exiting without stopping Cursor`);
|
|
1227
|
+
writeSupervisorDiag(logPath, "idle", { reason: `idle-${idleMs}ms`, clients: 0, ensureCount });
|
|
1228
|
+
shutdown(0);
|
|
1229
|
+
}, idleMs);
|
|
1230
|
+
if (typeof idleTimer.unref === "function") idleTimer.unref();
|
|
1231
|
+
};
|
|
1232
|
+
const runEnsure = async (request = {}) => {
|
|
1233
|
+
const requestRuntimeMode = request.runtimeMode || "normal";
|
|
1234
|
+
const requestProjectPath = Object.hasOwn(request, "projectPath") ? request.projectPath : null;
|
|
1235
|
+
const ensureKey = JSON.stringify([requestRuntimeMode, requestProjectPath]);
|
|
1236
|
+
if (ensureInflight.has(ensureKey)) return ensureInflight.get(ensureKey);
|
|
1237
|
+
const task = ensureTail.then(async () => {
|
|
1238
|
+
ensureCount += 1;
|
|
1239
|
+
const waitMs = Number(request.waitMs || 3e4);
|
|
1240
|
+
const result = await ensureLocal({
|
|
1241
|
+
waitMs,
|
|
1242
|
+
runtimeMode: requestRuntimeMode,
|
|
1243
|
+
projectPath: requestProjectPath
|
|
1244
|
+
});
|
|
1245
|
+
lastEnsure = {
|
|
1246
|
+
...result,
|
|
1247
|
+
ensureCount,
|
|
1248
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1249
|
+
requestReason: request.reason || null,
|
|
1250
|
+
requestAdapterPid: request.adapterPid || null,
|
|
1251
|
+
requestRuntimeMode,
|
|
1252
|
+
requestProjectPath
|
|
1253
|
+
};
|
|
1254
|
+
writeSupervisorDiag(logPath, "ensure-result", {
|
|
1255
|
+
ok: !!result.ok,
|
|
1256
|
+
status: result.status,
|
|
1257
|
+
reason: request.reason || null,
|
|
1258
|
+
adapterPid: request.adapterPid || null,
|
|
1259
|
+
ensureCount
|
|
1260
|
+
});
|
|
1261
|
+
return lastEnsure;
|
|
1262
|
+
});
|
|
1263
|
+
ensureInflight.set(ensureKey, task);
|
|
1264
|
+
ensureTail = task.catch(() => {
|
|
1265
|
+
});
|
|
1266
|
+
try {
|
|
1267
|
+
return await task;
|
|
1268
|
+
} finally {
|
|
1269
|
+
if (ensureInflight.get(ensureKey) === task) ensureInflight.delete(ensureKey);
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
const server = net.createServer((socket) => {
|
|
1273
|
+
clients.add(socket);
|
|
1274
|
+
if (idleTimer) {
|
|
1275
|
+
clearTimeout(idleTimer);
|
|
1276
|
+
idleTimer = null;
|
|
1277
|
+
}
|
|
1278
|
+
let buffer = "";
|
|
1279
|
+
socket.setEncoding("utf8");
|
|
1280
|
+
socket.on("data", (chunk) => {
|
|
1281
|
+
buffer += chunk;
|
|
1282
|
+
let idx;
|
|
1283
|
+
while ((idx = buffer.indexOf("\n")) >= 0) {
|
|
1284
|
+
const line = buffer.slice(0, idx).trim();
|
|
1285
|
+
buffer = buffer.slice(idx + 1);
|
|
1286
|
+
if (!line) continue;
|
|
1287
|
+
Promise.resolve().then(() => handleLine(socket, line, {
|
|
1288
|
+
runEnsure,
|
|
1289
|
+
ensureCount: () => ensureCount,
|
|
1290
|
+
lastEnsure: () => lastEnsure,
|
|
1291
|
+
clients,
|
|
1292
|
+
isEnsureInflight: () => Boolean(ensureInflight),
|
|
1293
|
+
shutdown,
|
|
1294
|
+
runtimeFingerprint,
|
|
1295
|
+
runtimeScript
|
|
1296
|
+
})).catch((error) => {
|
|
1297
|
+
try {
|
|
1298
|
+
socket.write(`${JSON.stringify({
|
|
1299
|
+
type: "error",
|
|
1300
|
+
ok: false,
|
|
1301
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1302
|
+
})}
|
|
1303
|
+
`);
|
|
1304
|
+
} catch {
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
socket.on("close", () => {
|
|
1310
|
+
clients.delete(socket);
|
|
1311
|
+
scheduleIdle();
|
|
1312
|
+
});
|
|
1313
|
+
socket.on("error", () => {
|
|
1314
|
+
clients.delete(socket);
|
|
1315
|
+
scheduleIdle();
|
|
1316
|
+
});
|
|
1317
|
+
});
|
|
1318
|
+
const shutdown = (code = 0) => {
|
|
1319
|
+
if (shuttingDown) return;
|
|
1320
|
+
shuttingDown = true;
|
|
1321
|
+
writeSupervisorDiag(logPath, "cleanup", { reason: "shutdown", code, clients: clients.size, ensureCount });
|
|
1322
|
+
try {
|
|
1323
|
+
server.close();
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
tryRemove(pidPath);
|
|
1327
|
+
tryRemove(lockPath);
|
|
1328
|
+
if (process.platform !== "win32") tryRemove(sock);
|
|
1329
|
+
process.exit(code);
|
|
1330
|
+
};
|
|
1331
|
+
process.on("SIGINT", () => shutdown(0));
|
|
1332
|
+
process.on("SIGTERM", () => shutdown(0));
|
|
1333
|
+
await new Promise((resolve3, reject) => {
|
|
1334
|
+
server.once("error", reject);
|
|
1335
|
+
server.listen(sock, () => {
|
|
1336
|
+
server.removeListener("error", reject);
|
|
1337
|
+
resolve3();
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
writePid(pidPath);
|
|
1341
|
+
log(`listening sock=${sock} pid=${process.pid} dir=${dir}`);
|
|
1342
|
+
writeSupervisorDiag(logPath, "listen", { sock, dir, reason: "listening" });
|
|
1343
|
+
scheduleIdle();
|
|
1344
|
+
return { started: true, sock, pid: process.pid, dir, server, shutdown, logPath };
|
|
1345
|
+
}
|
|
1346
|
+
async function handleLine(socket, line, ctx) {
|
|
1347
|
+
let msg;
|
|
1348
|
+
try {
|
|
1349
|
+
msg = JSON.parse(line);
|
|
1350
|
+
} catch {
|
|
1351
|
+
socket.write(`${JSON.stringify({ type: "error", ok: false, error: "invalid-json" })}
|
|
1352
|
+
`);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
const id = msg.id;
|
|
1356
|
+
try {
|
|
1357
|
+
if (msg.type === "ping") {
|
|
1358
|
+
socket.write(`${JSON.stringify({
|
|
1359
|
+
type: "pong",
|
|
1360
|
+
id,
|
|
1361
|
+
ok: true,
|
|
1362
|
+
supervisorPid: process.pid,
|
|
1363
|
+
clients: ctx.clients.size,
|
|
1364
|
+
ensureCount: ctx.ensureCount(),
|
|
1365
|
+
runtimeFingerprint: ctx.runtimeFingerprint,
|
|
1366
|
+
runtimeScript: ctx.runtimeScript
|
|
1367
|
+
})}
|
|
1368
|
+
`);
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (msg.type === "status") {
|
|
1372
|
+
socket.write(`${JSON.stringify({
|
|
1373
|
+
type: "status-result",
|
|
1374
|
+
id,
|
|
1375
|
+
ok: true,
|
|
1376
|
+
supervisorPid: process.pid,
|
|
1377
|
+
clients: ctx.clients.size,
|
|
1378
|
+
ensureCount: ctx.ensureCount(),
|
|
1379
|
+
lastEnsure: ctx.lastEnsure(),
|
|
1380
|
+
runtimeFingerprint: ctx.runtimeFingerprint,
|
|
1381
|
+
runtimeScript: ctx.runtimeScript
|
|
1382
|
+
})}
|
|
1383
|
+
`);
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
if (msg.type === "ensure") {
|
|
1387
|
+
const result = await ctx.runEnsure(msg);
|
|
1388
|
+
socket.write(`${JSON.stringify({
|
|
1389
|
+
type: "ensure-result",
|
|
1390
|
+
id,
|
|
1391
|
+
supervisorPid: process.pid,
|
|
1392
|
+
reusedSupervisor: true,
|
|
1393
|
+
launchReason: result.status === "launched" ? "supervisor-spawned-cursor" : result.status === "already" ? "supervisor-cursor-already" : `supervisor-${result.status}`,
|
|
1394
|
+
...result,
|
|
1395
|
+
runtimeFingerprint: ctx.runtimeFingerprint,
|
|
1396
|
+
runtimeScript: ctx.runtimeScript
|
|
1397
|
+
})}
|
|
1398
|
+
`);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
if (msg.type === "shutdown_if_idle") {
|
|
1402
|
+
if (msg.confirmation !== "ROLL_CURSOR_LIFECYCLE_SUPERVISOR") {
|
|
1403
|
+
socket.write(`${JSON.stringify({ type: "error", id, ok: false, error: "invalid-shutdown-confirmation" })}
|
|
1404
|
+
`);
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
const busy = ctx.isEnsureInflight() || ctx.clients.size > 1;
|
|
1408
|
+
socket.write(`${JSON.stringify({
|
|
1409
|
+
type: "shutdown-result",
|
|
1410
|
+
id,
|
|
1411
|
+
ok: true,
|
|
1412
|
+
restarting: !busy,
|
|
1413
|
+
busy,
|
|
1414
|
+
runtimeFingerprint: ctx.runtimeFingerprint,
|
|
1415
|
+
targetRuntimeFingerprint: msg.targetRuntimeFingerprint || null
|
|
1416
|
+
})}
|
|
1417
|
+
`);
|
|
1418
|
+
if (!busy) setTimeout(() => ctx.shutdown(0), 25);
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
socket.write(`${JSON.stringify({ type: "error", id, ok: false, error: `unknown-type:${msg.type}` })}
|
|
1422
|
+
`);
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
socket.write(`${JSON.stringify({
|
|
1425
|
+
type: "error",
|
|
1426
|
+
id,
|
|
1427
|
+
ok: false,
|
|
1428
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1429
|
+
})}
|
|
1430
|
+
`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
var isMain = import.meta.url === pathToFileURL(process.argv[1] || "").href || process.argv.includes("--lifecycle-supervisor") || process.env.CURSOR_BRIDGE_ROLE === "supervisor";
|
|
1434
|
+
if (isMain) {
|
|
1435
|
+
startSupervisor().catch((error) => {
|
|
1436
|
+
log("fatal", error);
|
|
1437
|
+
try {
|
|
1438
|
+
const dir = ensureLifecycleDir(process.env.CURSOR_BRIDGE_LIFECYCLE_DIR || defaultLifecycleDir());
|
|
1439
|
+
writeSupervisorDiag(supervisorLogPath(dir), "fatal", {
|
|
1440
|
+
reason: "start-failed",
|
|
1441
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1442
|
+
});
|
|
1443
|
+
} catch {
|
|
1444
|
+
}
|
|
1445
|
+
process.exit(1);
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
export {
|
|
1449
|
+
applyBootEnv,
|
|
1450
|
+
startSupervisor,
|
|
1451
|
+
writeSupervisorDiag
|
|
1452
|
+
};
|