@velum-labs/cursorkit 0.1.0

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.
Files changed (142) hide show
  1. package/DISCLAIMER.md +12 -0
  2. package/README.md +157 -0
  3. package/dist/src/agentTools/diff.d.ts +11 -0
  4. package/dist/src/agentTools/diff.js +88 -0
  5. package/dist/src/agentTools/policy.d.ts +3 -0
  6. package/dist/src/agentTools/policy.js +12 -0
  7. package/dist/src/agentTools/registry.d.ts +114 -0
  8. package/dist/src/agentTools/registry.js +663 -0
  9. package/dist/src/agentTools/results.d.ts +14 -0
  10. package/dist/src/agentTools/results.js +117 -0
  11. package/dist/src/agentTools/schemas.d.ts +3 -0
  12. package/dist/src/agentTools/schemas.js +89 -0
  13. package/dist/src/agentTools/surface.d.ts +11 -0
  14. package/dist/src/agentTools/surface.js +251 -0
  15. package/dist/src/certs.d.ts +8 -0
  16. package/dist/src/certs.js +34 -0
  17. package/dist/src/ck.d.ts +2 -0
  18. package/dist/src/ck.js +6 -0
  19. package/dist/src/ckLauncher.d.ts +150 -0
  20. package/dist/src/ckLauncher.js +1496 -0
  21. package/dist/src/cli.d.ts +2 -0
  22. package/dist/src/cli.js +265 -0
  23. package/dist/src/config.d.ts +52 -0
  24. package/dist/src/config.js +210 -0
  25. package/dist/src/connectEnvelope.d.ts +16 -0
  26. package/dist/src/connectEnvelope.js +70 -0
  27. package/dist/src/desktop.d.ts +19 -0
  28. package/dist/src/desktop.js +167 -0
  29. package/dist/src/desktopConnectProxy.d.ts +26 -0
  30. package/dist/src/desktopConnectProxy.js +175 -0
  31. package/dist/src/extensions/index.d.ts +2 -0
  32. package/dist/src/extensions/index.js +1 -0
  33. package/dist/src/extensions/registry.d.ts +8 -0
  34. package/dist/src/extensions/registry.js +52 -0
  35. package/dist/src/extensions/types.d.ts +42 -0
  36. package/dist/src/extensions/types.js +1 -0
  37. package/dist/src/fixtures/modelFusion.d.ts +103 -0
  38. package/dist/src/fixtures/modelFusion.js +404 -0
  39. package/dist/src/fixtures/replay.d.ts +9 -0
  40. package/dist/src/fixtures/replay.js +41 -0
  41. package/dist/src/fixtures/sanitizer.d.ts +9 -0
  42. package/dist/src/fixtures/sanitizer.js +43 -0
  43. package/dist/src/fixtures/schema.d.ts +38 -0
  44. package/dist/src/fixtures/schema.js +33 -0
  45. package/dist/src/gen/agent/v1/agent_pb.d.ts +21577 -0
  46. package/dist/src/gen/agent/v1/agent_pb.js +5325 -0
  47. package/dist/src/gen/aiserver/v1/aiserver_pb.d.ts +135242 -0
  48. package/dist/src/gen/aiserver/v1/aiserver_pb.js +34430 -0
  49. package/dist/src/gen/anyrun/v1/anyrun_pb.d.ts +1163 -0
  50. package/dist/src/gen/anyrun/v1/anyrun_pb.js +374 -0
  51. package/dist/src/gen/google/protobuf/google_pb.d.ts +142 -0
  52. package/dist/src/gen/google/protobuf/google_pb.js +54 -0
  53. package/dist/src/gen/internapi/v1/internapi_pb.d.ts +121 -0
  54. package/dist/src/gen/internapi/v1/internapi_pb.js +79 -0
  55. package/dist/src/logger.d.ts +8 -0
  56. package/dist/src/logger.js +37 -0
  57. package/dist/src/modelFusion/cursorHarness.d.ts +146 -0
  58. package/dist/src/modelFusion/cursorHarness.js +647 -0
  59. package/dist/src/modelFusion/index.d.ts +4 -0
  60. package/dist/src/modelFusion/index.js +2 -0
  61. package/dist/src/models/registry.d.ts +22 -0
  62. package/dist/src/models/registry.js +30 -0
  63. package/dist/src/proto.d.ts +13 -0
  64. package/dist/src/proto.js +61 -0
  65. package/dist/src/providers/openai.d.ts +64 -0
  66. package/dist/src/providers/openai.js +355 -0
  67. package/dist/src/redaction.d.ts +4 -0
  68. package/dist/src/redaction.js +65 -0
  69. package/dist/src/routeInventory.d.ts +16 -0
  70. package/dist/src/routeInventory.js +39 -0
  71. package/dist/src/routes.d.ts +37 -0
  72. package/dist/src/routes.js +227 -0
  73. package/dist/src/server.d.ts +50 -0
  74. package/dist/src/server.js +1353 -0
  75. package/dist/src/services/agent.d.ts +1 -0
  76. package/dist/src/services/agent.js +7 -0
  77. package/dist/src/services/agentRun.d.ts +60 -0
  78. package/dist/src/services/agentRun.js +391 -0
  79. package/dist/src/services/chat.d.ts +11 -0
  80. package/dist/src/services/chat.js +47 -0
  81. package/dist/src/services/models.d.ts +10 -0
  82. package/dist/src/services/models.js +216 -0
  83. package/dist/src/services/serverConfig.d.ts +2 -0
  84. package/dist/src/services/serverConfig.js +19 -0
  85. package/dist/src/testing/artifacts.d.ts +14 -0
  86. package/dist/src/testing/artifacts.js +92 -0
  87. package/dist/src/testing/cli.d.ts +4 -0
  88. package/dist/src/testing/cli.js +192 -0
  89. package/dist/src/testing/localBackend.d.ts +24 -0
  90. package/dist/src/testing/localBackend.js +310 -0
  91. package/dist/src/testing/processRunner.d.ts +7 -0
  92. package/dist/src/testing/processRunner.js +74 -0
  93. package/dist/src/testing/runner.d.ts +9 -0
  94. package/dist/src/testing/runner.js +85 -0
  95. package/dist/src/testing/scenarios.d.ts +3 -0
  96. package/dist/src/testing/scenarios.js +2535 -0
  97. package/dist/src/testing/types.d.ts +66 -0
  98. package/dist/src/testing/types.js +1 -0
  99. package/dist/src/tools/baselineInventory.d.ts +12 -0
  100. package/dist/src/tools/baselineInventory.js +680 -0
  101. package/dist/src/tools/checkModelFusionProtocol.d.ts +1 -0
  102. package/dist/src/tools/checkModelFusionProtocol.js +274 -0
  103. package/dist/src/tools/checkReleasePublishConfig.d.ts +1 -0
  104. package/dist/src/tools/checkReleasePublishConfig.js +99 -0
  105. package/dist/src/tools/generateProtoInventory.d.ts +1 -0
  106. package/dist/src/tools/generateProtoInventory.js +89 -0
  107. package/dist/src/tools/normalizeGeneratedCode.d.ts +1 -0
  108. package/dist/src/tools/normalizeGeneratedCode.js +18 -0
  109. package/dist/src/tools/releaseCheck.d.ts +26 -0
  110. package/dist/src/tools/releaseCheck.js +367 -0
  111. package/dist/src/trace.d.ts +39 -0
  112. package/dist/src/trace.js +106 -0
  113. package/dist/src/translation.d.ts +6 -0
  114. package/dist/src/translation.js +22 -0
  115. package/dist/src/upstream.d.ts +20 -0
  116. package/dist/src/upstream.js +270 -0
  117. package/docs/configuration.md +55 -0
  118. package/docs/cursor-app.md +263 -0
  119. package/docs/implementation-inventory.json +609 -0
  120. package/docs/learnings.md +363 -0
  121. package/docs/model-fusion-protocol-origin.json +126 -0
  122. package/docs/model-fusion-protocol.md +110 -0
  123. package/docs/plugin-authoring.md +24 -0
  124. package/docs/proto-inventory.md +1477 -0
  125. package/docs/protocol-surface-audit.md +92 -0
  126. package/docs/protocol.md +52 -0
  127. package/docs/refreshing-protos.md +78 -0
  128. package/docs/release-gates.md +110 -0
  129. package/docs/release-summary.json +86 -0
  130. package/docs/route-contract-manifest.json +288 -0
  131. package/docs/route-policy.json +133 -0
  132. package/docs/service-manifest.json +9490 -0
  133. package/docs/test-manifest.json +155 -0
  134. package/docs/testing-harness.md +204 -0
  135. package/docs/troubleshooting.md +36 -0
  136. package/docs/type-manifest-summary.json +28927 -0
  137. package/package.json +93 -0
  138. package/proto/agent/v1/agent.proto +5371 -0
  139. package/proto/aiserver/v1/aiserver.proto +32944 -0
  140. package/proto/anyrun/v1/anyrun.proto +294 -0
  141. package/proto/google/protobuf/google.proto +37 -0
  142. package/proto/internapi/v1/internapi.proto +32 -0
@@ -0,0 +1,1496 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { lookup } from "node:dns/promises";
3
+ import fs from "node:fs";
4
+ import net from "node:net";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { loadConfig } from "./config.js";
8
+ import { startDesktopConnectProxy, } from "./desktopConnectProxy.js";
9
+ import { DESKTOP_CERT_PATH, DESKTOP_HOSTNAME, DESKTOP_HOSTNAMES, DESKTOP_KEY_PATH, desktopCertificateStatus, desktopDnsStatus, desktopEnv, desktopTrustCommand, localModelBackendStatus, upstreamReachabilityStatus, writeDesktopCertificate, } from "./desktop.js";
10
+ import { AGENT_RUN_PATH, AGENT_RUN_SSE_PATH, AVAILABLE_MODELS_PATH, BIDI_APPEND_PATH, GET_DEFAULT_MODEL_FOR_CLI_PATH, GET_USABLE_MODELS_PATH, STREAM_CHAT_WITH_TOOLS_PATH, } from "./routes.js";
11
+ export const CK_STATE_DIR = path.join(".cursor-rpc", "ck");
12
+ export const CK_STATE_PATH = path.join(CK_STATE_DIR, "state.json");
13
+ const DEFAULT_CURSOR_USER_DATA_DIR = path.join(process.env.HOME ?? "", "Library", "Application Support", "Cursor");
14
+ const CURSOR_AUTH_KEY_PATTERN = "cursorAuth/*";
15
+ const DEFAULT_ROUTE_INVENTORY_TIMEOUT_MS = 15_000;
16
+ const MODEL_ROUTE_PATHS = [
17
+ AVAILABLE_MODELS_PATH,
18
+ GET_USABLE_MODELS_PATH,
19
+ GET_DEFAULT_MODEL_FOR_CLI_PATH,
20
+ ];
21
+ const LOCAL_AGENT_CANDIDATE_PATHS = [
22
+ AGENT_RUN_PATH,
23
+ AGENT_RUN_SSE_PATH,
24
+ BIDI_APPEND_PATH,
25
+ STREAM_CHAT_WITH_TOOLS_PATH,
26
+ ];
27
+ const CK_HELP = `ck
28
+
29
+ Usage:
30
+ ck Start desktop proxy and isolated Cursor
31
+ ck test Launch, monitor route inventory, print diagnosis, then stop bridge
32
+ ck --use-default-profile Reuse your logged-in Cursor profile for auth-sensitive testing
33
+ ck --timeout-ms 30000 Set launch/test route-inventory wait time
34
+ ck --debug-port 9333 Launch Cursor with a Chromium remote debugging port
35
+ ck --instance-id name Use a fresh isolated ck state/profile subdirectory
36
+ ck --seed-auth-from-default Copy Cursor auth rows from your default profile
37
+ ck --no-seed-auth-from-default Start isolated Cursor without copying auth rows
38
+ ck --print Print commands without launching
39
+ ck doctor Check desktop launch readiness
40
+ ck cert Generate desktop proxy certificate
41
+ ck route Print manual desktop routing setup and rollback commands
42
+ ck route status Check desktop routing prerequisites and current DNS state
43
+ ck route rollback Print rollback commands only
44
+ ck route --method direct Print direct :443 routing commands instead of pf redirect
45
+ ck stop Stop ck-owned bridge process
46
+ ck --help Show this help
47
+ `;
48
+ export function parseCkArgs(argv) {
49
+ const args = argv.slice(2);
50
+ let dryRun = false;
51
+ let profileMode = "isolated";
52
+ let routeMethod = "pf";
53
+ let routeAction = "plan";
54
+ let debugPort;
55
+ let instanceId;
56
+ let seedAuthFromDefault;
57
+ let timeoutMs = DEFAULT_ROUTE_INVENTORY_TIMEOUT_MS;
58
+ const commandArgs = [];
59
+ for (let index = 0; index < args.length; index += 1) {
60
+ const arg = args[index];
61
+ if (arg === "--print") {
62
+ dryRun = true;
63
+ }
64
+ else if (arg === "--use-default-profile") {
65
+ profileMode = "default";
66
+ }
67
+ else if (arg === "--profile") {
68
+ const next = args[index + 1];
69
+ if (next !== "isolated" && next !== "default") {
70
+ throw new Error("--profile must be isolated or default");
71
+ }
72
+ profileMode = next;
73
+ index += 1;
74
+ }
75
+ else if (arg === "--timeout-ms") {
76
+ const next = args[index + 1];
77
+ const parsed = Number(next);
78
+ if (!Number.isInteger(parsed) || parsed <= 0) {
79
+ throw new Error("--timeout-ms must be a positive integer");
80
+ }
81
+ timeoutMs = parsed;
82
+ index += 1;
83
+ }
84
+ else if (arg === "--method") {
85
+ const next = args[index + 1];
86
+ if (next !== "pf" && next !== "direct") {
87
+ throw new Error("--method must be pf or direct");
88
+ }
89
+ routeMethod = next;
90
+ index += 1;
91
+ }
92
+ else if (arg === "--debug-port") {
93
+ const next = args[index + 1];
94
+ const parsed = Number(next);
95
+ if (!Number.isInteger(parsed) || parsed <= 0) {
96
+ throw new Error("--debug-port must be a positive integer");
97
+ }
98
+ debugPort = parsed;
99
+ index += 1;
100
+ }
101
+ else if (arg === "--instance-id") {
102
+ const next = args[index + 1];
103
+ if (next === undefined || !/^[A-Za-z0-9._-]+$/.test(next)) {
104
+ throw new Error("--instance-id must use only letters, numbers, dot, underscore, or dash");
105
+ }
106
+ instanceId = next;
107
+ index += 1;
108
+ }
109
+ else if (arg === "--seed-auth-from-default") {
110
+ seedAuthFromDefault = true;
111
+ }
112
+ else if (arg === "--no-seed-auth-from-default") {
113
+ seedAuthFromDefault = false;
114
+ }
115
+ else if (arg !== undefined) {
116
+ commandArgs.push(arg);
117
+ }
118
+ }
119
+ const first = commandArgs[0];
120
+ const second = commandArgs[1];
121
+ if (first === "route") {
122
+ if (second === undefined) {
123
+ routeAction = "plan";
124
+ }
125
+ else if (second === "status" || second === "rollback") {
126
+ routeAction = second;
127
+ }
128
+ else {
129
+ throw new Error("ck route subcommand must be status or rollback");
130
+ }
131
+ }
132
+ if (first === undefined) {
133
+ return compactCkArgs({
134
+ command: "launch",
135
+ dryRun,
136
+ profileMode,
137
+ timeoutMs,
138
+ routeMethod,
139
+ routeAction,
140
+ debugPort,
141
+ instanceId,
142
+ seedAuthFromDefault,
143
+ });
144
+ }
145
+ switch (first) {
146
+ case "test":
147
+ case "doctor":
148
+ case "cert":
149
+ case "route":
150
+ case "stop":
151
+ return compactCkArgs({
152
+ command: first,
153
+ dryRun,
154
+ profileMode,
155
+ timeoutMs,
156
+ routeMethod,
157
+ routeAction,
158
+ debugPort,
159
+ instanceId,
160
+ seedAuthFromDefault,
161
+ });
162
+ case "help":
163
+ case "--help":
164
+ case "-h":
165
+ return compactCkArgs({
166
+ command: "help",
167
+ dryRun,
168
+ profileMode,
169
+ timeoutMs,
170
+ routeMethod,
171
+ routeAction,
172
+ debugPort,
173
+ instanceId,
174
+ seedAuthFromDefault,
175
+ });
176
+ default:
177
+ throw new Error(`Unknown ck command: ${first}\n\n${CK_HELP}`);
178
+ }
179
+ }
180
+ function compactCkArgs(args) {
181
+ const { debugPort, instanceId, seedAuthFromDefault, ...rest } = args;
182
+ return {
183
+ ...rest,
184
+ ...(debugPort !== undefined ? { debugPort } : {}),
185
+ ...(instanceId !== undefined ? { instanceId } : {}),
186
+ ...(seedAuthFromDefault !== undefined ? { seedAuthFromDefault } : {}),
187
+ };
188
+ }
189
+ export function buildCkLaunchPlan(options) {
190
+ const cwd = options.cwd ?? process.cwd();
191
+ const profileMode = options.profileMode ?? "isolated";
192
+ const stateDir = options.instanceId === undefined
193
+ ? path.join(cwd, CK_STATE_DIR)
194
+ : path.join(cwd, CK_STATE_DIR, options.instanceId);
195
+ const userDataDir = profileMode === "isolated" ? path.join(stateDir, "user-data") : undefined;
196
+ const extensionsDir = path.join(stateDir, "extensions");
197
+ const connectProxyPort = profileMode === "isolated" ? options.connectProxyPort : undefined;
198
+ const agentHttpPort = profileMode === "isolated" ? options.agentHttpPort : undefined;
199
+ const connectProxyLogPath = connectProxyPort === undefined
200
+ ? undefined
201
+ : path.join(stateDir, "connect-proxy.log");
202
+ const bridgeEnv = desktopEnv({
203
+ ...pickDisplayableBridgeEnv(options.env),
204
+ BRIDGE_PORT: String(options.bridgePort),
205
+ ...(agentHttpPort !== undefined
206
+ ? {
207
+ BRIDGE_DESKTOP_AGENT_HTTP_PORT: String(agentHttpPort),
208
+ }
209
+ : {}),
210
+ BRIDGE_LOG_LEVEL: options.env.BRIDGE_LOG_LEVEL ?? "debug",
211
+ BRIDGE_CERT_PATH: options.env.BRIDGE_CERT_PATH ?? DESKTOP_CERT_PATH,
212
+ BRIDGE_KEY_PATH: options.env.BRIDGE_KEY_PATH ?? DESKTOP_KEY_PATH,
213
+ });
214
+ if (agentHttpPort !== undefined &&
215
+ bridgeEnv.BRIDGE_AGENT_PUBLIC_ORIGIN === undefined) {
216
+ bridgeEnv.BRIDGE_AGENT_PUBLIC_ORIGIN = `https://127.0.0.1:${options.bridgePort}`;
217
+ }
218
+ return {
219
+ bridge: {
220
+ ...bridgeCommandSpec(cwd),
221
+ env: stringEnv(bridgeEnv),
222
+ },
223
+ cursor: cursorCommandSpec(profileMode, userDataDir, extensionsDir, options.debugPort, cwd, options.bridgePort, profileMode === "isolated", connectProxyPort),
224
+ bridgePort: options.bridgePort,
225
+ ...(connectProxyPort !== undefined ? { connectProxyPort } : {}),
226
+ ...(agentHttpPort !== undefined ? { agentHttpPort } : {}),
227
+ stateDir,
228
+ statePath: path.join(stateDir, "state.json"),
229
+ logPath: path.join(stateDir, "bridge.log"),
230
+ ...(connectProxyLogPath !== undefined ? { connectProxyLogPath } : {}),
231
+ userDataDir,
232
+ extensionsDir,
233
+ workspacePath: cwd,
234
+ profileMode,
235
+ seedAuthFromDefault: profileMode === "isolated" && options.seedAuthFromDefault !== false,
236
+ authSeedStatus: "not-requested",
237
+ ...(options.debugPort !== undefined
238
+ ? { debugPort: options.debugPort }
239
+ : {}),
240
+ ...(options.instanceId !== undefined
241
+ ? { instanceId: options.instanceId }
242
+ : {}),
243
+ };
244
+ }
245
+ export function commandForDisplay(spec) {
246
+ const env = spec.env === undefined
247
+ ? []
248
+ : Object.entries(spec.env)
249
+ .sort(([left], [right]) => left.localeCompare(right))
250
+ .map(([key, value]) => `${key}=${shellQuote(displayEnvValue(key, value))}`);
251
+ const command = [spec.executable, ...spec.args].map(shellQuote);
252
+ return [...env, ...command].join(" ");
253
+ }
254
+ export async function buildCkRoutePlan(options = {}) {
255
+ const method = options.method ?? "pf";
256
+ const bridgePort = options.bridgePort ?? (method === "direct" ? 443 : 9443);
257
+ const cwd = options.cwd ?? process.cwd();
258
+ const upstreamConnectHost = options.upstreamConnectHost ?? (await detectUpstreamConnectHost());
259
+ const warnings = [];
260
+ if (upstreamConnectHost === undefined) {
261
+ warnings.push(`Could not resolve a non-local ${DESKTOP_HOSTNAME} address. Set CURSOR_UPSTREAM_CONNECT_HOST manually before redirecting DNS to localhost.`);
262
+ }
263
+ if (method === "direct") {
264
+ warnings.push("Direct mode binds the bridge to port 443, which may require elevated privileges on some machines.");
265
+ }
266
+ const hostnames = [...DESKTOP_HOSTNAMES];
267
+ const hostsLine = `127.0.0.1 ${hostnames.join(" ")}`;
268
+ const bridgeEnv = {
269
+ BRIDGE_PORT: String(bridgePort),
270
+ CURSOR_UPSTREAM_CONNECT_HOST: upstreamConnectHost ?? "<real-api2-address>",
271
+ BRIDGE_CERT_PATH: DESKTOP_CERT_PATH,
272
+ BRIDGE_KEY_PATH: DESKTOP_KEY_PATH,
273
+ };
274
+ const setupCommands = [
275
+ { executable: "pnpm", args: ["ck", "cert"] },
276
+ {
277
+ executable: "sudo",
278
+ args: desktopTrustCommand(DESKTOP_CERT_PATH).slice(1),
279
+ },
280
+ {
281
+ executable: "sudo",
282
+ args: ["sh", "-c", `printf "\\n${hostsLine}\\n" >> /etc/hosts`],
283
+ },
284
+ ];
285
+ if (method === "pf") {
286
+ setupCommands.push({ executable: "mkdir", args: ["-p", path.join(cwd, CK_STATE_DIR)] }, {
287
+ executable: "sh",
288
+ args: [
289
+ "-c",
290
+ `printf 'rdr pass on lo0 inet proto tcp from any to 127.0.0.1 port 443 -> 127.0.0.1 port ${bridgePort}\\n' > ${shellQuote(path.join(cwd, CK_STATE_DIR, "pf.conf"))}`,
291
+ ],
292
+ }, {
293
+ executable: "sudo",
294
+ args: ["pfctl", "-ef", path.join(cwd, CK_STATE_DIR, "pf.conf")],
295
+ });
296
+ }
297
+ setupCommands.push({
298
+ executable: "pnpm",
299
+ args: ["exec", "tsx", "src/cli.ts", "desktop-proxy"],
300
+ env: bridgeEnv,
301
+ });
302
+ const verificationCommands = [
303
+ {
304
+ executable: "pnpm",
305
+ args: ["ck", "test", "--use-default-profile", "--timeout-ms", "30000"],
306
+ },
307
+ ];
308
+ const rollbackCommands = [
309
+ { executable: "pnpm", args: ["ck", "stop"] },
310
+ {
311
+ executable: "sudo",
312
+ args: [
313
+ "perl",
314
+ "-0pi",
315
+ "-e",
316
+ `s/^127\\.0\\.0\\.1\\s+${hostnames.map(escapeRegExp).join("\\s+")}\\n//mg`,
317
+ "/etc/hosts",
318
+ ],
319
+ },
320
+ ];
321
+ if (method === "pf") {
322
+ rollbackCommands.push({ executable: "sudo", args: ["pfctl", "-d"] });
323
+ }
324
+ rollbackCommands.push({
325
+ executable: "pkill",
326
+ args: ["-x", "Cursor"],
327
+ });
328
+ return {
329
+ method,
330
+ hostname: DESKTOP_HOSTNAME,
331
+ hostnames,
332
+ bridgePort,
333
+ upstreamConnectHost,
334
+ warnings,
335
+ setupCommands,
336
+ verificationCommands,
337
+ rollbackCommands,
338
+ };
339
+ }
340
+ export function containsPrivilegedCommand(spec) {
341
+ const values = [spec.executable, ...spec.args];
342
+ return values.some((value) => /(^|\/)(sudo|security|pfctl)$/.test(value) ||
343
+ value.includes("/etc/hosts"));
344
+ }
345
+ export function routeInventoryTimeoutDiagnosis() {
346
+ return [
347
+ "No desktop route inventory observed yet. Per-instance routing may not affect this Cursor network path.",
348
+ "Run `pnpm ck route` to print manual fallback routing and rollback commands.",
349
+ ];
350
+ }
351
+ export function analyzeRouteInventoryLog(logText) {
352
+ const observations = parseRouteInventoryObservations(logText);
353
+ const observedPaths = uniqueSorted(observations.map((observation) => observation.path));
354
+ const routeSummary = summarizeRouteInventory(observations);
355
+ const routeCategories = categorizeRouteInventory(routeSummary);
356
+ const modelRoutesSeen = MODEL_ROUTE_PATHS.filter((path) => observedPaths.includes(path));
357
+ const missingModelRoutes = MODEL_ROUTE_PATHS.filter((path) => !observedPaths.includes(path));
358
+ const failedRoutes = observations.filter((observation) => {
359
+ return observation.status >= 400;
360
+ });
361
+ const passThroughRoutes = observations.filter((observation) => {
362
+ return observation.outcome === "pass-through";
363
+ });
364
+ return {
365
+ routeInventorySeen: observations.length > 0,
366
+ modelRoutesSeen,
367
+ missingModelRoutes,
368
+ observedPaths,
369
+ routeSummary,
370
+ routeCategories,
371
+ failedRoutes,
372
+ passThroughRoutes,
373
+ diagnosis: desktopTestDiagnosis({
374
+ routeInventorySeen: observations.length > 0,
375
+ modelRoutesSeen,
376
+ missingModelRoutes,
377
+ observedPaths,
378
+ routeSummary,
379
+ failedRoutes,
380
+ passThroughRoutes,
381
+ }),
382
+ };
383
+ }
384
+ function categorizeRouteInventory(routeSummary) {
385
+ return routeSummary.map((summary) => {
386
+ if (MODEL_ROUTE_PATHS.includes(summary.path)) {
387
+ return {
388
+ path: summary.path,
389
+ category: "model-metadata",
390
+ reason: "known model-list/default-model route",
391
+ };
392
+ }
393
+ if (LOCAL_AGENT_CANDIDATE_PATHS.includes(summary.path)) {
394
+ return {
395
+ path: summary.path,
396
+ category: "local-agent-candidate",
397
+ reason: "known route that can carry desktop prompt execution",
398
+ };
399
+ }
400
+ if (summary.policies.includes("pass-through")) {
401
+ return {
402
+ path: summary.path,
403
+ category: "pass-through",
404
+ reason: "observed but intentionally forwarded upstream",
405
+ };
406
+ }
407
+ if (summary.policies.includes("intercept")) {
408
+ return {
409
+ path: summary.path,
410
+ category: "decoded-only",
411
+ reason: "intercepted by bridge without desktop-specific acceptance proof",
412
+ };
413
+ }
414
+ if (summary.statuses.some((status) => status >= 400)) {
415
+ return {
416
+ path: summary.path,
417
+ category: "unsupported",
418
+ reason: "observed with an HTTP error status",
419
+ };
420
+ }
421
+ return {
422
+ path: summary.path,
423
+ category: "unknown",
424
+ reason: "observed route needs fixture or traffic classification",
425
+ };
426
+ });
427
+ }
428
+ function summarizeRouteInventory(observations) {
429
+ const byPath = new Map();
430
+ for (const observation of observations) {
431
+ const existing = byPath.get(observation.path) ?? [];
432
+ existing.push(observation);
433
+ byPath.set(observation.path, existing);
434
+ }
435
+ return Array.from(byPath.entries())
436
+ .map(([path, entries]) => ({
437
+ path,
438
+ count: entries.length,
439
+ methods: uniqueSorted(entries.map((entry) => entry.method)),
440
+ statuses: uniqueSortedNumbers(entries.map((entry) => entry.status)),
441
+ framings: uniqueSorted(entries.map((entry) => entry.framing)),
442
+ policies: uniqueSorted(entries.map((entry) => entry.policy)),
443
+ outcomes: uniqueSorted(entries.map((entry) => entry.outcome)),
444
+ }))
445
+ .sort((left, right) => left.path.localeCompare(right.path));
446
+ }
447
+ function parseRouteInventoryObservations(logText) {
448
+ const observations = [];
449
+ for (const line of logText.split(/\r?\n/)) {
450
+ const trimmed = line.trim();
451
+ if (trimmed.length === 0) {
452
+ continue;
453
+ }
454
+ try {
455
+ const parsed = JSON.parse(trimmed);
456
+ if (parsed.message !== "desktop route inventory") {
457
+ continue;
458
+ }
459
+ observations.push({
460
+ method: stringField(parsed.method),
461
+ path: stringField(parsed.path),
462
+ contentType: stringField(parsed.contentType),
463
+ status: numberField(parsed.status),
464
+ framing: stringField(parsed.framing),
465
+ policy: stringField(parsed.policy),
466
+ outcome: stringField(parsed.outcome),
467
+ });
468
+ }
469
+ catch {
470
+ continue;
471
+ }
472
+ }
473
+ return observations;
474
+ }
475
+ function desktopTestDiagnosis(report) {
476
+ if (!report.routeInventorySeen) {
477
+ return routeInventoryTimeoutDiagnosis();
478
+ }
479
+ const diagnosis = [];
480
+ if (report.modelRoutesSeen.length === 0) {
481
+ diagnosis.push("Desktop traffic reached the bridge, but none of the known model-list RPCs were observed. The desktop app may be using a different model route than cursor-agent.");
482
+ }
483
+ else if (report.missingModelRoutes.length > 0) {
484
+ diagnosis.push("Desktop traffic reached at least one known model-list RPC, but not the full CLI-derived model surface. Missing routes may explain a partial or empty picker.");
485
+ }
486
+ else {
487
+ diagnosis.push("All known CLI-derived model-list RPCs reached the bridge. If local-qwen is still missing, the desktop app likely needs additional app-specific model metadata or another desktop-only route.");
488
+ }
489
+ if (report.failedRoutes.length > 0) {
490
+ diagnosis.push("One or more observed routes returned HTTP errors; inspect the bridge log for status and policy details.");
491
+ }
492
+ if (report.passThroughRoutes.length > 0) {
493
+ diagnosis.push("Some observed routes passed through to upstream. This is expected for non-intercepted routes and useful for route discovery.");
494
+ }
495
+ if (report.observedPaths.length > 0) {
496
+ diagnosis.push("Use the route summary to decide the next typed interceptor instead of guessing.");
497
+ }
498
+ return diagnosis;
499
+ }
500
+ export async function runCk(argv = process.argv) {
501
+ const parsed = parseCkArgs(argv);
502
+ if (parsed.command === "help") {
503
+ console.log(CK_HELP);
504
+ return;
505
+ }
506
+ if (parsed.command === "cert") {
507
+ await printCertInstructions();
508
+ return;
509
+ }
510
+ if (parsed.command === "route") {
511
+ await printRoute(parsed.routeAction, parsed.routeMethod);
512
+ return;
513
+ }
514
+ if (parsed.command === "doctor") {
515
+ await printDoctor();
516
+ return;
517
+ }
518
+ if (parsed.command === "stop") {
519
+ await stopBridgeFromState();
520
+ return;
521
+ }
522
+ const bridgePort = await chooseBridgePort();
523
+ const connectProxyPort = parsed.profileMode === "isolated" ? await chooseFreePort() : undefined;
524
+ const agentHttpPort = parsed.profileMode === "isolated" ? await chooseFreePort() : undefined;
525
+ const plan = buildCkLaunchPlan({
526
+ env: process.env,
527
+ bridgePort,
528
+ connectProxyPort,
529
+ agentHttpPort,
530
+ profileMode: parsed.profileMode,
531
+ debugPort: parsed.debugPort,
532
+ instanceId: parsed.instanceId,
533
+ seedAuthFromDefault: parsed.seedAuthFromDefault,
534
+ });
535
+ if (parsed.dryRun) {
536
+ printPlan(plan);
537
+ return;
538
+ }
539
+ const cert = await writeDesktopCertificate();
540
+ if (cert.created) {
541
+ console.warn("Generated desktop proxy certificate.");
542
+ printTrustInstructions(cert.certPath);
543
+ }
544
+ if (parsed.command === "test") {
545
+ await testDesktopLaunch(plan, parsed.timeoutMs);
546
+ }
547
+ else {
548
+ await launch(plan, parsed.timeoutMs);
549
+ }
550
+ }
551
+ export async function chooseBridgePort() {
552
+ if (await portIsFree(9443)) {
553
+ return 9443;
554
+ }
555
+ return chooseFreePort();
556
+ }
557
+ async function chooseFreePort() {
558
+ return new Promise((resolve, reject) => {
559
+ const server = net.createServer();
560
+ server.listen(0, "127.0.0.1", () => {
561
+ const address = server.address();
562
+ if (address === null || typeof address === "string") {
563
+ server.close();
564
+ reject(new Error("Unable to allocate bridge port"));
565
+ return;
566
+ }
567
+ const port = address.port;
568
+ server.close(() => resolve(port));
569
+ });
570
+ server.on("error", reject);
571
+ });
572
+ }
573
+ async function launch(plan, timeoutMs) {
574
+ const { bridge, log, connectProxy } = await startBridge(plan);
575
+ const routeSeen = waitForRouteInventory(bridge, timeoutMs);
576
+ launchCursor(plan);
577
+ const observed = await routeSeen;
578
+ if (observed) {
579
+ console.log("Desktop route inventory observed.");
580
+ }
581
+ else {
582
+ for (const line of routeInventoryTimeoutDiagnosis()) {
583
+ console.warn(line);
584
+ }
585
+ }
586
+ await new Promise((resolve) => {
587
+ bridge.on("exit", () => {
588
+ log.end();
589
+ void connectProxy?.close();
590
+ resolve();
591
+ });
592
+ });
593
+ }
594
+ async function testDesktopLaunch(plan, timeoutMs) {
595
+ const { bridge, log, connectProxy } = await startBridge(plan);
596
+ try {
597
+ launchCursor(plan);
598
+ console.log(`Monitoring desktop route inventory for ${timeoutMs}ms`);
599
+ await delay(timeoutMs);
600
+ const logText = fs.existsSync(plan.logPath)
601
+ ? fs.readFileSync(plan.logPath, "utf8")
602
+ : "";
603
+ const report = analyzeRouteInventoryLog(logText);
604
+ writeLatestStatus(plan, report);
605
+ printDesktopTestReport(plan, report);
606
+ }
607
+ finally {
608
+ bridge.kill("SIGTERM");
609
+ await connectProxy?.close();
610
+ log.end();
611
+ if (plan.userDataDir !== undefined) {
612
+ cleanupIsolatedCursorProcesses(plan.userDataDir);
613
+ }
614
+ }
615
+ }
616
+ async function startBridge(plan) {
617
+ fs.mkdirSync(plan.stateDir, { recursive: true });
618
+ if (plan.userDataDir !== undefined) {
619
+ fs.mkdirSync(plan.userDataDir, { recursive: true });
620
+ }
621
+ fs.mkdirSync(plan.extensionsDir, { recursive: true });
622
+ plan.authSeedStatus = seedCursorAuthFromDefault(plan);
623
+ if (plan.authSeedStatus === "seeded") {
624
+ console.log("Seeded isolated Cursor profile with default auth rows");
625
+ }
626
+ else if (plan.seedAuthFromDefault &&
627
+ plan.authSeedStatus !== "not-isolated") {
628
+ console.warn(`Cursor auth seeding status: ${plan.authSeedStatus}`);
629
+ }
630
+ plan.localModelSeedStatus = seedLocalModelsIntoCursorState(plan);
631
+ if (plan.localModelSeedStatus === "seeded") {
632
+ console.log("Seeded isolated Cursor profile with local model entries");
633
+ }
634
+ else if (plan.localModelSeedStatus !== "not-isolated") {
635
+ console.warn(`Cursor local model seeding status: ${plan.localModelSeedStatus}`);
636
+ }
637
+ configureCursorNodeTlsEnv(plan);
638
+ assertSafe(plan.bridge);
639
+ assertSafe(plan.cursor);
640
+ console.log(`Starting bridge on 127.0.0.1:${plan.bridgePort}`);
641
+ console.log(`Writing bridge log to ${plan.logPath}`);
642
+ const log = fs.createWriteStream(plan.logPath, { flags: "w" });
643
+ const bridge = spawn(plan.bridge.executable, plan.bridge.args, {
644
+ env: { ...process.env, ...plan.bridge.env },
645
+ stdio: ["ignore", "pipe", "pipe"],
646
+ });
647
+ attachBridgeOutput(bridge, log);
648
+ writeState(plan, bridge);
649
+ await waitForBridgeListening(bridge);
650
+ const connectProxy = await startConnectProxy(plan);
651
+ return {
652
+ bridge,
653
+ log,
654
+ ...(connectProxy === undefined ? {} : { connectProxy }),
655
+ };
656
+ }
657
+ async function startConnectProxy(plan) {
658
+ if (plan.connectProxyPort === undefined ||
659
+ plan.connectProxyLogPath === undefined) {
660
+ return undefined;
661
+ }
662
+ fs.writeFileSync(plan.connectProxyLogPath, "");
663
+ console.log(`Starting CONNECT proxy on 127.0.0.1:${plan.connectProxyPort}`);
664
+ console.log(`Writing CONNECT proxy log to ${plan.connectProxyLogPath}`);
665
+ return startDesktopConnectProxy({
666
+ host: "127.0.0.1",
667
+ port: plan.connectProxyPort,
668
+ bridgeHost: "127.0.0.1",
669
+ bridgePort: plan.bridgePort,
670
+ logPath: plan.connectProxyLogPath,
671
+ });
672
+ }
673
+ export function seedCursorAuthFromDefault(plan) {
674
+ if (!plan.seedAuthFromDefault) {
675
+ return "not-requested";
676
+ }
677
+ if (plan.profileMode !== "isolated" || plan.userDataDir === undefined) {
678
+ return "not-isolated";
679
+ }
680
+ if (spawnSync("sqlite3", ["--version"]).status !== 0) {
681
+ return "sqlite-unavailable";
682
+ }
683
+ const sourceDb = path.join(DEFAULT_CURSOR_USER_DATA_DIR, "User", "globalStorage", "state.vscdb");
684
+ if (!fs.existsSync(sourceDb)) {
685
+ return "source-missing";
686
+ }
687
+ const count = spawnSync("sqlite3", [
688
+ sourceDb,
689
+ `select count(*) from ItemTable where key glob '${CURSOR_AUTH_KEY_PATTERN}';`,
690
+ ], { encoding: "utf8" });
691
+ if (count.status !== 0 || Number(count.stdout.trim()) <= 0) {
692
+ return "no-auth-rows";
693
+ }
694
+ const globalStorageDir = path.join(plan.userDataDir, "User", "globalStorage");
695
+ fs.mkdirSync(globalStorageDir, { recursive: true });
696
+ const targetDb = path.join(globalStorageDir, "state.vscdb");
697
+ const optionsPath = path.join(globalStorageDir, "state.vscdb.options.json");
698
+ if (!fs.existsSync(optionsPath)) {
699
+ fs.writeFileSync(optionsPath, `${JSON.stringify({ useWAL: true }, null, 2)}\n`);
700
+ }
701
+ const sourceSqlPath = sourceDb.replaceAll("'", "''");
702
+ const seed = spawnSync("sqlite3", [
703
+ targetDb,
704
+ [
705
+ "CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB);",
706
+ `ATTACH '${sourceSqlPath}' AS source_db;`,
707
+ `INSERT OR REPLACE INTO ItemTable(key, value) SELECT key, value FROM source_db.ItemTable WHERE key GLOB '${CURSOR_AUTH_KEY_PATTERN}';`,
708
+ "DETACH source_db;",
709
+ ].join(" "),
710
+ ], { encoding: "utf8" });
711
+ return seed.status === 0 ? "seeded" : "sqlite-unavailable";
712
+ }
713
+ export function seedLocalModelsIntoCursorState(plan) {
714
+ if (plan.profileMode !== "isolated" || plan.userDataDir === undefined) {
715
+ return "not-isolated";
716
+ }
717
+ if (spawnSync("sqlite3", ["--version"]).status !== 0) {
718
+ return "sqlite-unavailable";
719
+ }
720
+ const models = loadConfig(desktopEnv({ ...process.env, ...plan.bridge.env })).models;
721
+ if (models.length === 0) {
722
+ return "no-local-models";
723
+ }
724
+ const globalStorageDir = path.join(plan.userDataDir, "User", "globalStorage");
725
+ const targetDb = path.join(globalStorageDir, "state.vscdb");
726
+ if (!fs.existsSync(targetDb)) {
727
+ return "state-missing";
728
+ }
729
+ const key = "src.vs.platform.reactivestorage.browser.reactiveStorageServiceImpl.persistentStorage.applicationUser";
730
+ const applicationUserJson = readCursorStateValue(targetDb, key) ??
731
+ readCursorStateValue(defaultCursorStateDbPath(), key);
732
+ if (applicationUserJson === undefined) {
733
+ return "state-missing";
734
+ }
735
+ const applicationUser = JSON.parse(applicationUserJson);
736
+ mergeLocalDesktopModelsIntoApplicationUser(applicationUser, models);
737
+ const bridgeEnv = plan.bridge.env ?? {};
738
+ const agentPublicOrigin = bridgeEnv.BRIDGE_AGENT_PUBLIC_ORIGIN ?? bridgeEnv.BRIDGE_PUBLIC_ORIGIN;
739
+ if (typeof agentPublicOrigin === "string") {
740
+ mergeLocalAgentBackendUrlsIntoApplicationUser(applicationUser, agentPublicOrigin);
741
+ }
742
+ const seedPath = path.join(globalStorageDir, "applicationUser.seed.json");
743
+ fs.writeFileSync(seedPath, JSON.stringify(applicationUser));
744
+ const escapedSeedPath = seedPath.replaceAll("'", "''");
745
+ const seed = spawnSync("sqlite3", [
746
+ targetDb,
747
+ `insert or replace into ItemTable(key, value) values('${key}', cast(readfile('${escapedSeedPath}') as text));`,
748
+ ], { encoding: "utf8" });
749
+ if (seed.status !== 0) {
750
+ return "sqlite-unavailable";
751
+ }
752
+ return "seeded";
753
+ }
754
+ export function mergeLocalAgentBackendUrlsIntoApplicationUser(applicationUser, agentOrigin) {
755
+ const cursorCreds = ensureRecord(applicationUser, "cursorCreds");
756
+ const urls = { default: agentOrigin };
757
+ cursorCreds.agentBackendUrlPrivacy = urls;
758
+ cursorCreds.agentBackendUrlNonPrivacy = urls;
759
+ }
760
+ export function mergeLocalDesktopModelsIntoApplicationUser(applicationUser, models) {
761
+ const localModelIds = new Set(models.map((model) => model.id));
762
+ const current = Array.isArray(applicationUser.availableDefaultModels2)
763
+ ? applicationUser.availableDefaultModels2.filter((item) => {
764
+ if (!isPlainRecord(item) || typeof item.name !== "string") {
765
+ return true;
766
+ }
767
+ return !localModelIds.has(item.name);
768
+ })
769
+ : [];
770
+ applicationUser.availableDefaultModels2 = current;
771
+ const aiSettings = ensureRecord(applicationUser, "aiSettings");
772
+ for (const model of models) {
773
+ appendUnique(ensureStringArray(aiSettings, "userAddedModels"), model.id);
774
+ appendUnique(ensureStringArray(aiSettings, "modelOverrideEnabled"), model.id);
775
+ removeValue(ensureStringArray(aiSettings, "modelOverrideDisabled"), model.id);
776
+ }
777
+ const preferences = ensureRecord(aiSettings, "modelParameterPreferences");
778
+ const updatedAt = new Date().toISOString();
779
+ for (const model of models) {
780
+ preferences[model.id] = {
781
+ modelId: model.id,
782
+ parameters: localDesktopParameterValues(true),
783
+ updatedAt,
784
+ };
785
+ }
786
+ const firstModel = models[0];
787
+ if (firstModel !== undefined) {
788
+ applicationUser.useOpenAIKey = true;
789
+ applicationUser.openAIBaseUrl = firstModel.baseUrl;
790
+ applicationUser.openAIKey = firstModel.apiKey;
791
+ const modelConfig = ensureRecord(aiSettings, "modelConfig");
792
+ const selectedModel = {
793
+ modelId: firstModel.id,
794
+ parameters: localDesktopParameterValues(true),
795
+ };
796
+ for (const key of ["composer", "background-composer"]) {
797
+ modelConfig[key] = {
798
+ modelName: firstModel.id,
799
+ maxMode: true,
800
+ selectedModels: [selectedModel],
801
+ };
802
+ }
803
+ }
804
+ const featureModelConfigs = ensureRecord(applicationUser, "featureModelConfigs");
805
+ for (const value of Object.values(featureModelConfigs)) {
806
+ if (!isPlainRecord(value)) {
807
+ continue;
808
+ }
809
+ const fallbackModels = ensureStringArray(value, "fallbackModels");
810
+ for (const model of models) {
811
+ appendUnique(fallbackModels, model.id);
812
+ }
813
+ }
814
+ }
815
+ function ensureRecord(target, key) {
816
+ if (!isPlainRecord(target[key])) {
817
+ target[key] = {};
818
+ }
819
+ return target[key];
820
+ }
821
+ function ensureStringArray(target, key) {
822
+ const values = Array.isArray(target[key])
823
+ ? target[key].filter((value) => typeof value === "string")
824
+ : [];
825
+ target[key] = values;
826
+ return values;
827
+ }
828
+ function appendUnique(values, value) {
829
+ if (!values.includes(value)) {
830
+ values.push(value);
831
+ }
832
+ }
833
+ function removeValue(values, value) {
834
+ const index = values.indexOf(value);
835
+ if (index !== -1) {
836
+ values.splice(index, 1);
837
+ }
838
+ }
839
+ function defaultCursorStateDbPath() {
840
+ return path.join(DEFAULT_CURSOR_USER_DATA_DIR, "User", "globalStorage", "state.vscdb");
841
+ }
842
+ function readCursorStateValue(dbPath, key) {
843
+ if (!fs.existsSync(dbPath)) {
844
+ return undefined;
845
+ }
846
+ const escapedKey = key.replaceAll("'", "''");
847
+ const result = spawnSync("sqlite3", [dbPath, `select value from ItemTable where key='${escapedKey}';`], {
848
+ encoding: "utf8",
849
+ maxBuffer: 20 * 1024 * 1024,
850
+ });
851
+ if (result.status !== 0 || result.stdout.trim().length === 0) {
852
+ return undefined;
853
+ }
854
+ return result.stdout;
855
+ }
856
+ export function buildLocalDesktopModelEntry(model) {
857
+ const tooltipData = {
858
+ primaryText: "",
859
+ secondaryText: "",
860
+ secondaryWarningText: false,
861
+ icon: "",
862
+ tertiaryText: "",
863
+ tertiaryTextUrl: "",
864
+ markdownContent: `**${model.displayName}**<br />Local OpenAI-compatible model served by cursorkit.<br /><br />${model.contextTokenLimit.toLocaleString()} token context window`,
865
+ };
866
+ return {
867
+ name: model.id,
868
+ serverModelName: model.id,
869
+ clientDisplayName: model.displayName,
870
+ inputboxShortModelName: model.displayName,
871
+ vendorName: "local",
872
+ vendor: { displayName: "Local" },
873
+ supportsAgent: true,
874
+ supportsCmdK: false,
875
+ supportsImages: false,
876
+ supportsMaxMode: true,
877
+ supportsNonMaxMode: true,
878
+ supportsPlanMode: true,
879
+ supportsSandboxing: false,
880
+ supportsThinking: false,
881
+ cloudAgentEffortModes: [],
882
+ defaultOn: true,
883
+ degradationStatus: 0,
884
+ isRecommendedForBackgroundComposer: false,
885
+ legacySlugs: [model.id],
886
+ idAliases: [model.id, model.displayName],
887
+ parameterDefinitions: localDesktopParameterDefinitions(),
888
+ namedModelSectionIndex: 10_000,
889
+ visibleInRoutedModelView: true,
890
+ tooltipData,
891
+ tooltipDataForMaxMode: tooltipData,
892
+ variants: [
893
+ localDesktopVariantConfig(model, tooltipData, false),
894
+ localDesktopVariantConfig(model, tooltipData, true),
895
+ ],
896
+ };
897
+ }
898
+ function localDesktopVariantConfig(model, tooltipData, isMaxMode) {
899
+ return {
900
+ parameterValues: localDesktopParameterValues(isMaxMode),
901
+ displayName: model.displayName,
902
+ isMaxMode,
903
+ isDefaultMaxConfig: isMaxMode,
904
+ isDefaultNonMaxConfig: !isMaxMode,
905
+ tooltipData,
906
+ displayNameOutsidePicker: model.displayName,
907
+ variantStringRepresentation: localDesktopVariantString(model.id, isMaxMode),
908
+ legacySlug: model.id,
909
+ };
910
+ }
911
+ function localDesktopVariantString(modelId, isMaxMode) {
912
+ const context = isMaxMode ? "1m" : "272k";
913
+ return `${modelId}[context=${context},reasoning=medium,fast=false]`;
914
+ }
915
+ function localDesktopParameterValues(isMaxMode) {
916
+ return [
917
+ { id: "context", value: isMaxMode ? "1m" : "272k" },
918
+ { id: "reasoning", value: "medium" },
919
+ { id: "fast", value: "false" },
920
+ ];
921
+ }
922
+ function localDesktopParameterDefinitions() {
923
+ return [
924
+ {
925
+ id: "context",
926
+ name: "Context",
927
+ markdownTooltip: "Context size the model has available.",
928
+ parameterType: {
929
+ enumParameter: {
930
+ values: [
931
+ { value: "272k", displayName: "272K" },
932
+ { value: "1m", displayName: "1M" },
933
+ ],
934
+ },
935
+ },
936
+ },
937
+ {
938
+ id: "reasoning",
939
+ name: "Reasoning",
940
+ markdownTooltip: "Reasoning effort the model uses to generate its response.",
941
+ parameterType: {
942
+ enumParameter: {
943
+ values: [
944
+ { value: "none", displayName: "None" },
945
+ { value: "low", displayName: "Low" },
946
+ { value: "medium", displayName: "Medium" },
947
+ { value: "high", displayName: "High" },
948
+ { value: "extra-high", displayName: "Extra High" },
949
+ ],
950
+ },
951
+ },
952
+ isCycleableByHotkey: true,
953
+ },
954
+ {
955
+ id: "fast",
956
+ name: "Fast",
957
+ markdownTooltip: "Use the provider's fast lane when supported.",
958
+ parameterType: {
959
+ booleanParameter: {
960
+ values: [{ value: "false" }, { value: "true", displayName: "Fast" }],
961
+ },
962
+ },
963
+ },
964
+ ];
965
+ }
966
+ function isPlainRecord(value) {
967
+ return typeof value === "object" && value !== null && !Array.isArray(value);
968
+ }
969
+ export function cleanupIsolatedCursorProcesses(userDataDir) {
970
+ const killed = terminateIsolatedCursorProcesses(userDataDir, "SIGTERM");
971
+ spawnSync("sleep", ["1"]);
972
+ killed.push(...terminateIsolatedCursorProcesses(userDataDir, "SIGKILL"));
973
+ return Array.from(new Set(killed));
974
+ }
975
+ function terminateIsolatedCursorProcesses(userDataDir, signal) {
976
+ const pids = isolatedCursorProcessIds(userDataDir);
977
+ const killed = [];
978
+ for (const pid of pids) {
979
+ try {
980
+ process.kill(pid, signal);
981
+ killed.push(pid);
982
+ }
983
+ catch {
984
+ // The process may have exited between ps and kill.
985
+ }
986
+ }
987
+ return killed;
988
+ }
989
+ function isolatedCursorProcessIds(userDataDir) {
990
+ const result = spawnSync("ps", ["-axo", "pid=,command="], {
991
+ encoding: "utf8",
992
+ maxBuffer: 10 * 1024 * 1024,
993
+ });
994
+ if (result.status !== 0) {
995
+ return [];
996
+ }
997
+ const pids = [];
998
+ for (const line of result.stdout.split(/\r?\n/)) {
999
+ const parts = line.trim().split(/\s+/, 2);
1000
+ const pid = Number(parts[0]);
1001
+ if (!Number.isInteger(pid)) {
1002
+ continue;
1003
+ }
1004
+ const command = line.slice(line.indexOf(parts[1] ?? ""));
1005
+ if (command.includes("/Applications/Cursor.app/") &&
1006
+ cursorCommandUsesUserDataDir(command, userDataDir)) {
1007
+ pids.push(pid);
1008
+ }
1009
+ }
1010
+ return pids;
1011
+ }
1012
+ function cursorCommandUsesUserDataDir(command, userDataDir) {
1013
+ return (command.includes(`--user-data-dir=${userDataDir}`) ||
1014
+ command.includes(`--user-data-dir "${userDataDir}"`) ||
1015
+ command.includes(`--user-data-dir '${userDataDir}'`));
1016
+ }
1017
+ function launchCursor(plan) {
1018
+ console.log(plan.profileMode === "isolated"
1019
+ ? "Launching isolated Cursor instance"
1020
+ : "Launching Cursor with the default signed-in profile");
1021
+ if (plan.profileMode === "default") {
1022
+ console.warn("Default profile mode reuses your existing Cursor auth state; it is less isolated but avoids browser login callback loss.");
1023
+ }
1024
+ const cursor = spawn(plan.cursor.executable, plan.cursor.args, {
1025
+ env: { ...process.env, ...plan.cursor.env },
1026
+ stdio: "ignore",
1027
+ });
1028
+ cursor.on("error", (error) => {
1029
+ console.error(`Cursor launch failed: ${error.message}`);
1030
+ });
1031
+ return cursor;
1032
+ }
1033
+ function attachBridgeOutput(bridge, log) {
1034
+ bridge.stdout?.on("data", (chunk) => {
1035
+ log.write(chunk);
1036
+ process.stdout.write(chunk);
1037
+ });
1038
+ bridge.stderr?.on("data", (chunk) => {
1039
+ log.write(chunk);
1040
+ process.stderr.write(chunk);
1041
+ });
1042
+ }
1043
+ function writeLatestStatus(plan, report) {
1044
+ const state = fs.existsSync(plan.statePath)
1045
+ ? JSON.parse(fs.readFileSync(plan.statePath, "utf8"))
1046
+ : {};
1047
+ fs.writeFileSync(plan.statePath, JSON.stringify({
1048
+ ...state,
1049
+ latestRouteInventoryStatus: {
1050
+ routeInventorySeen: report.routeInventorySeen,
1051
+ modelRoutesSeen: report.modelRoutesSeen,
1052
+ missingModelRoutes: report.missingModelRoutes,
1053
+ observedPaths: report.observedPaths,
1054
+ routeCategories: report.routeCategories,
1055
+ failedRoutes: report.failedRoutes,
1056
+ passThroughRoutes: report.passThroughRoutes,
1057
+ authSeedStatus: plan.authSeedStatus,
1058
+ localModelSeedStatus: plan.localModelSeedStatus,
1059
+ checkedAt: new Date().toISOString(),
1060
+ },
1061
+ }, null, 2));
1062
+ }
1063
+ function printDesktopTestReport(plan, report) {
1064
+ console.log("");
1065
+ console.log("Desktop Test Report");
1066
+ console.log(`route inventory: ${report.routeInventorySeen ? "yes" : "no"}`);
1067
+ console.log(`model routes seen: ${report.modelRoutesSeen.length > 0
1068
+ ? report.modelRoutesSeen.join(", ")
1069
+ : "none"}`);
1070
+ console.log(`model routes missing: ${report.missingModelRoutes.length > 0
1071
+ ? report.missingModelRoutes.join(", ")
1072
+ : "none"}`);
1073
+ console.log(`observed paths: ${report.observedPaths.length > 0 ? report.observedPaths.join(", ") : "none"}`);
1074
+ console.log(`failed routes: ${String(report.failedRoutes.length)}`);
1075
+ console.log(`pass-through routes: ${String(report.passThroughRoutes.length)}`);
1076
+ console.log(`auth seed: ${plan.authSeedStatus ?? "not-run"}; local model seed: ${plan.localModelSeedStatus ?? "not-run"}`);
1077
+ console.log(`route categories: ${report.routeCategories.length > 0
1078
+ ? report.routeCategories
1079
+ .map((entry) => `${entry.category}:${entry.path}`)
1080
+ .join(", ")
1081
+ : "none"}`);
1082
+ console.log(`log: ${plan.logPath}`);
1083
+ console.log(`state: ${plan.statePath}`);
1084
+ console.log("diagnosis:");
1085
+ for (const line of report.diagnosis) {
1086
+ console.log(`- ${line}`);
1087
+ }
1088
+ }
1089
+ function bridgeCommandSpec(cwd) {
1090
+ const distCli = path.join(path.dirname(fileURLToPath(import.meta.url)), "cli.js");
1091
+ if (fs.existsSync(distCli)) {
1092
+ return { executable: process.execPath, args: [distCli, "desktop-proxy"] };
1093
+ }
1094
+ return {
1095
+ executable: "pnpm",
1096
+ args: ["exec", "tsx", "src/cli.ts", "desktop-proxy"],
1097
+ env: { PWD: cwd },
1098
+ };
1099
+ }
1100
+ function cursorCommandSpec(profileMode, userDataDir, extensionsDir, debugPort, workspacePath, bridgePort, allowInvalidLocalCert, connectProxyPort) {
1101
+ const userDataArgs = userDataDir === undefined ? [] : [`--user-data-dir=${userDataDir}`];
1102
+ const debugArgs = debugPort === undefined ? [] : [`--remote-debugging-port=${debugPort}`];
1103
+ const certificateArgs = allowInvalidLocalCert
1104
+ ? ["--ignore-certificate-errors"]
1105
+ : [];
1106
+ const hostResolverRules = DESKTOP_HOSTNAMES.map((hostname) => `MAP ${hostname} 127.0.0.1:${bridgePort}`).join(", ");
1107
+ if (profileMode === "isolated") {
1108
+ const proxyEnv = connectProxyPort === undefined
1109
+ ? undefined
1110
+ : proxyEnvironment(`http://127.0.0.1:${connectProxyPort}`);
1111
+ const proxyArgs = connectProxyPort === undefined
1112
+ ? [`--host-resolver-rules=${hostResolverRules}`]
1113
+ : [
1114
+ `--proxy-server=http://127.0.0.1:${connectProxyPort}`,
1115
+ `--host-resolver-rules=${hostResolverRules}`,
1116
+ "--proxy-bypass-list=<-loopback>;localhost;127.0.0.1",
1117
+ ];
1118
+ return {
1119
+ executable: "/Applications/Cursor.app/Contents/MacOS/Cursor",
1120
+ args: [
1121
+ workspacePath,
1122
+ ...userDataArgs,
1123
+ `--extensions-dir=${extensionsDir}`,
1124
+ ...debugArgs,
1125
+ ...certificateArgs,
1126
+ ...proxyArgs,
1127
+ ],
1128
+ env: proxyEnv,
1129
+ };
1130
+ }
1131
+ return {
1132
+ executable: "/usr/bin/open",
1133
+ args: [
1134
+ "-n",
1135
+ "-a",
1136
+ "Cursor",
1137
+ workspacePath,
1138
+ "--args",
1139
+ ...userDataArgs,
1140
+ `--extensions-dir=${extensionsDir}`,
1141
+ ...debugArgs,
1142
+ ...certificateArgs,
1143
+ `--host-resolver-rules=${hostResolverRules}`,
1144
+ ],
1145
+ };
1146
+ }
1147
+ function proxyEnvironment(proxyUrl) {
1148
+ return {
1149
+ HTTP_PROXY: proxyUrl,
1150
+ HTTPS_PROXY: proxyUrl,
1151
+ ALL_PROXY: proxyUrl,
1152
+ http_proxy: proxyUrl,
1153
+ https_proxy: proxyUrl,
1154
+ all_proxy: proxyUrl,
1155
+ NO_PROXY: "127.0.0.1,localhost,::1",
1156
+ no_proxy: "127.0.0.1,localhost,::1",
1157
+ };
1158
+ }
1159
+ function configureCursorNodeTlsEnv(plan) {
1160
+ if (plan.profileMode !== "isolated") {
1161
+ return;
1162
+ }
1163
+ plan.cursor.env = {
1164
+ ...plan.cursor.env,
1165
+ NODE_TLS_REJECT_UNAUTHORIZED: "0",
1166
+ };
1167
+ }
1168
+ function printPlan(plan) {
1169
+ console.log("Bridge:");
1170
+ console.log(commandForDisplay(plan.bridge));
1171
+ console.log("");
1172
+ console.log("Cursor:");
1173
+ console.log(commandForDisplay(plan.cursor));
1174
+ console.log("");
1175
+ console.log(`State: ${plan.statePath}`);
1176
+ console.log(`Log: ${plan.logPath}`);
1177
+ if (plan.connectProxyPort !== undefined) {
1178
+ console.log(`CONNECT proxy: 127.0.0.1:${plan.connectProxyPort}`);
1179
+ }
1180
+ if (plan.agentHttpPort !== undefined) {
1181
+ console.log(`Agent HTTP bridge: 127.0.0.1:${plan.agentHttpPort}`);
1182
+ }
1183
+ if (plan.connectProxyLogPath !== undefined) {
1184
+ console.log(`CONNECT proxy log: ${plan.connectProxyLogPath}`);
1185
+ }
1186
+ }
1187
+ async function printCertInstructions() {
1188
+ const cert = await writeDesktopCertificate();
1189
+ console.log(`cert: ${cert.certPath}`);
1190
+ console.log(`key: ${cert.keyPath}`);
1191
+ printTrustInstructions(cert.certPath);
1192
+ }
1193
+ function printTrustInstructions(certPath) {
1194
+ console.log("Manual macOS trust command:");
1195
+ console.log(desktopTrustCommand(certPath).map(shellQuote).join(" "));
1196
+ }
1197
+ async function printDoctor() {
1198
+ const env = desktopEnv(process.env);
1199
+ const config = loadConfig(env);
1200
+ console.log(`desktop cert: ${desktopCertificateStatus(config)}`);
1201
+ console.log(`desktop dns: ${await desktopDnsStatus(config)}`);
1202
+ console.log(`upstream reachability: ${await upstreamReachabilityStatus(config)}`);
1203
+ console.log(`local model backend: ${await localModelBackendStatus(config)}`);
1204
+ if (!fs.existsSync(DESKTOP_CERT_PATH) || !fs.existsSync(DESKTOP_KEY_PATH)) {
1205
+ console.warn("Run ck cert before launching.");
1206
+ }
1207
+ console.log("");
1208
+ console.log("manual route plan: pnpm ck route");
1209
+ }
1210
+ async function printRoute(action, method) {
1211
+ if (action === "status") {
1212
+ await printRouteStatus();
1213
+ return;
1214
+ }
1215
+ const plan = await buildCkRoutePlan({ method });
1216
+ if (action === "rollback") {
1217
+ printRouteRollback(plan);
1218
+ return;
1219
+ }
1220
+ printRoutePlan(plan);
1221
+ }
1222
+ function printRoutePlan(plan) {
1223
+ console.log("Desktop Manual Routing Plan");
1224
+ console.log(`method: ${plan.method}`);
1225
+ console.log(`primary hostname: ${plan.hostname}`);
1226
+ console.log(`hostnames: ${plan.hostnames.join(", ")}`);
1227
+ console.log(`bridge port: ${String(plan.bridgePort)}`);
1228
+ console.log(`upstream connect host: ${plan.upstreamConnectHost ?? "<set manually>"}`);
1229
+ console.log("");
1230
+ if (plan.warnings.length > 0) {
1231
+ console.log("Warnings:");
1232
+ for (const warning of plan.warnings) {
1233
+ console.log(`- ${warning}`);
1234
+ }
1235
+ console.log("");
1236
+ }
1237
+ console.log("Setup commands to run manually:");
1238
+ for (const command of plan.setupCommands) {
1239
+ console.log(commandForDisplay(command));
1240
+ }
1241
+ console.log("");
1242
+ console.log("Verification:");
1243
+ for (const command of plan.verificationCommands) {
1244
+ console.log(commandForDisplay(command));
1245
+ }
1246
+ console.log("");
1247
+ console.log("Rollback:");
1248
+ for (const command of plan.rollbackCommands) {
1249
+ console.log(commandForDisplay(command));
1250
+ }
1251
+ console.log("");
1252
+ console.log("ck prints these commands only. It does not install trust, edit hosts, configure pf, or kill Cursor for you.");
1253
+ }
1254
+ async function printRouteStatus() {
1255
+ const env = desktopEnv(process.env);
1256
+ const config = loadConfig(env);
1257
+ console.log("Desktop Routing Status");
1258
+ console.log(`desktop cert: ${desktopCertificateStatus(config)}`);
1259
+ console.log(`desktop dns: ${await desktopDnsStatus(config)}`);
1260
+ for (const hostname of DESKTOP_HOSTNAMES.filter((hostname) => hostname !== DESKTOP_HOSTNAME)) {
1261
+ console.log(`desktop dns: ${await desktopDnsStatusForHostname(hostname)}`);
1262
+ }
1263
+ console.log(`detected upstream connect host: ${(await detectUpstreamConnectHost()) ?? "<none; set CURSOR_UPSTREAM_CONNECT_HOST manually>"}`);
1264
+ console.log(`configured upstream connect: ${config.upstreamConnectHost === undefined
1265
+ ? "system DNS"
1266
+ : `${config.upstreamConnectHost}${config.upstreamConnectPort === undefined ? "" : `:${config.upstreamConnectPort}`}`}`);
1267
+ console.log(`upstream reachability: ${await upstreamReachabilityStatus(config)}`);
1268
+ console.log(`local model backend: ${await localModelBackendStatus(config)}`);
1269
+ console.log("");
1270
+ console.log("Next steps:");
1271
+ console.log("- Run `pnpm ck route` before system cutover to capture a real upstream IP.");
1272
+ console.log("- Run `pnpm ck route rollback` to print the rollback commands.");
1273
+ }
1274
+ function printRouteRollback(plan) {
1275
+ console.log("Desktop Routing Rollback");
1276
+ for (const command of plan.rollbackCommands) {
1277
+ console.log(commandForDisplay(command));
1278
+ }
1279
+ console.log("");
1280
+ console.log("ck prints rollback commands only. Review them before running; especially `pkill -x Cursor`.");
1281
+ }
1282
+ async function stopBridgeFromState() {
1283
+ if (!fs.existsSync(CK_STATE_PATH)) {
1284
+ console.log("No ck state file found.");
1285
+ return;
1286
+ }
1287
+ const state = JSON.parse(fs.readFileSync(CK_STATE_PATH, "utf8"));
1288
+ if (state.bridgePid === undefined) {
1289
+ console.log("No bridge PID recorded.");
1290
+ return;
1291
+ }
1292
+ const command = processCommandForPid(state.bridgePid);
1293
+ if (command === undefined) {
1294
+ console.warn(`No running process found for ck bridge PID ${state.bridgePid}.`);
1295
+ return;
1296
+ }
1297
+ if (!bridgeProcessMatchesState(command, state)) {
1298
+ console.warn(`Refusing to stop PID ${state.bridgePid}; it does not look like the ck-owned desktop bridge recorded in state.`);
1299
+ return;
1300
+ }
1301
+ try {
1302
+ process.kill(state.bridgePid, "SIGTERM");
1303
+ console.log(`Stopped ck bridge process ${state.bridgePid}.`);
1304
+ }
1305
+ catch (error) {
1306
+ console.warn(`Could not stop bridge process ${state.bridgePid}: ${error instanceof Error ? error.message : String(error)}`);
1307
+ }
1308
+ }
1309
+ export function bridgeProcessMatchesState(command, state = {}) {
1310
+ const normalizedCommand = command.replace(/\s+/g, " ").trim();
1311
+ const normalizedStateCommand = state.bridgeCommand
1312
+ ?.replace(/\s+/g, " ")
1313
+ .trim();
1314
+ if (normalizedStateCommand !== undefined &&
1315
+ normalizedStateCommand.length > 0 &&
1316
+ normalizedCommand.includes(normalizedStateCommand)) {
1317
+ return true;
1318
+ }
1319
+ return (/\bdesktop-proxy\b/.test(normalizedCommand) &&
1320
+ (normalizedCommand.includes("src/cli.ts") ||
1321
+ normalizedCommand.includes("dist/src/cli.js") ||
1322
+ normalizedCommand.includes("/cli.js") ||
1323
+ normalizedCommand.includes("cursorkit") ||
1324
+ normalizedCommand.includes("cursor-rpc")));
1325
+ }
1326
+ function processCommandForPid(pid) {
1327
+ const result = spawnSync("ps", ["-p", String(pid), "-o", "command="], {
1328
+ encoding: "utf8",
1329
+ maxBuffer: 1024 * 1024,
1330
+ });
1331
+ if (result.status !== 0) {
1332
+ return undefined;
1333
+ }
1334
+ const command = result.stdout.trim();
1335
+ return command.length === 0 ? undefined : command;
1336
+ }
1337
+ function writeState(plan, bridge) {
1338
+ fs.writeFileSync(plan.statePath, JSON.stringify({
1339
+ bridgePid: bridge.pid,
1340
+ bridgeCommand: commandForDisplay(plan.bridge),
1341
+ bridgePort: plan.bridgePort,
1342
+ connectProxyPort: plan.connectProxyPort,
1343
+ agentHttpPort: plan.agentHttpPort,
1344
+ logPath: plan.logPath,
1345
+ connectProxyLogPath: plan.connectProxyLogPath,
1346
+ profileMode: plan.profileMode,
1347
+ userDataDir: plan.userDataDir,
1348
+ extensionsDir: plan.extensionsDir,
1349
+ workspacePath: plan.workspacePath,
1350
+ debugPort: plan.debugPort,
1351
+ instanceId: plan.instanceId,
1352
+ seedAuthFromDefault: plan.seedAuthFromDefault,
1353
+ authSeedStatus: plan.authSeedStatus,
1354
+ localModelSeedStatus: plan.localModelSeedStatus,
1355
+ startedAt: new Date().toISOString(),
1356
+ }, null, 2));
1357
+ }
1358
+ async function waitForBridgeListening(bridge) {
1359
+ await waitForOutput(bridge, /bridge listening/, 10_000);
1360
+ }
1361
+ async function waitForRouteInventory(bridge, timeoutMs) {
1362
+ return waitForOutput(bridge, /desktop route inventory/, timeoutMs)
1363
+ .then(() => true)
1364
+ .catch(() => false);
1365
+ }
1366
+ function waitForOutput(process, pattern, timeoutMs) {
1367
+ return new Promise((resolve, reject) => {
1368
+ let settled = false;
1369
+ const timer = setTimeout(() => finish(false), timeoutMs);
1370
+ const onData = (chunk) => {
1371
+ if (pattern.test(chunk.toString("utf8"))) {
1372
+ finish(true);
1373
+ }
1374
+ };
1375
+ const onExit = () => finish(false);
1376
+ const finish = (matched) => {
1377
+ if (settled) {
1378
+ return;
1379
+ }
1380
+ settled = true;
1381
+ clearTimeout(timer);
1382
+ process.stdout?.off("data", onData);
1383
+ process.stderr?.off("data", onData);
1384
+ process.off("exit", onExit);
1385
+ if (matched) {
1386
+ resolve();
1387
+ }
1388
+ else {
1389
+ reject(new Error(`Timed out waiting for ${pattern.source}`));
1390
+ }
1391
+ };
1392
+ process.stdout?.on("data", onData);
1393
+ process.stderr?.on("data", onData);
1394
+ process.on("exit", onExit);
1395
+ });
1396
+ }
1397
+ function assertSafe(spec) {
1398
+ if (containsPrivilegedCommand(spec)) {
1399
+ throw new Error(`Refusing privileged command: ${commandForDisplay(spec)}`);
1400
+ }
1401
+ }
1402
+ async function portIsFree(port) {
1403
+ return new Promise((resolve) => {
1404
+ const server = net.createServer();
1405
+ server.once("error", () => resolve(false));
1406
+ server.listen(port, "127.0.0.1", () => {
1407
+ server.close(() => resolve(true));
1408
+ });
1409
+ });
1410
+ }
1411
+ function stringEnv(env) {
1412
+ const result = {};
1413
+ for (const [key, value] of Object.entries(env)) {
1414
+ if (value !== undefined) {
1415
+ result[key] = value;
1416
+ }
1417
+ }
1418
+ return result;
1419
+ }
1420
+ function pickDisplayableBridgeEnv(env) {
1421
+ const keys = [
1422
+ "BRIDGE_HARDCODED_RESPONSE",
1423
+ "BRIDGE_LOG_MODEL_PAYLOADS",
1424
+ "BRIDGE_MODELS_JSON",
1425
+ "BRIDGE_DESKTOP_MODE",
1426
+ "BRIDGE_USE_TLS",
1427
+ "BRIDGE_TLS_HOSTNAMES",
1428
+ "BRIDGE_PUBLIC_ORIGIN",
1429
+ "CURSOR_UPSTREAM_BASE_URL",
1430
+ "CURSOR_UPSTREAM_CONNECT_HOST",
1431
+ "CURSOR_UPSTREAM_CONNECT_PORT",
1432
+ "MODEL_API_KEY",
1433
+ "MODEL_CONTEXT_TOKEN_LIMIT",
1434
+ "MODEL_NAME",
1435
+ "MODEL_PROVIDER_MODEL",
1436
+ "MODEL_BASE_URL",
1437
+ ];
1438
+ const result = {};
1439
+ for (const key of keys) {
1440
+ if (env[key] !== undefined) {
1441
+ result[key] = env[key];
1442
+ }
1443
+ }
1444
+ return result;
1445
+ }
1446
+ function displayEnvValue(key, value) {
1447
+ if (key.includes("API_KEY") || key === "BRIDGE_MODELS_JSON") {
1448
+ return "<redacted>";
1449
+ }
1450
+ return value;
1451
+ }
1452
+ function uniqueSorted(values) {
1453
+ return Array.from(new Set(values)).sort((left, right) => left.localeCompare(right));
1454
+ }
1455
+ function uniqueSortedNumbers(values) {
1456
+ return Array.from(new Set(values)).sort((left, right) => left - right);
1457
+ }
1458
+ async function detectUpstreamConnectHost() {
1459
+ try {
1460
+ const result = await lookup(DESKTOP_HOSTNAME);
1461
+ if (result.address === "127.0.0.1" || result.address === "::1") {
1462
+ return undefined;
1463
+ }
1464
+ return result.address;
1465
+ }
1466
+ catch {
1467
+ return undefined;
1468
+ }
1469
+ }
1470
+ async function desktopDnsStatusForHostname(hostname) {
1471
+ try {
1472
+ const result = await lookup(hostname);
1473
+ return `${hostname} -> ${result.address}`;
1474
+ }
1475
+ catch (error) {
1476
+ return `${hostname} -> lookup failed: ${error instanceof Error ? error.message : String(error)}`;
1477
+ }
1478
+ }
1479
+ function stringField(value) {
1480
+ return typeof value === "string" ? value : "";
1481
+ }
1482
+ function numberField(value) {
1483
+ return typeof value === "number" ? value : 0;
1484
+ }
1485
+ async function delay(timeoutMs) {
1486
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
1487
+ }
1488
+ function shellQuote(value) {
1489
+ if (/^[A-Za-z0-9_./:=,-]+$/.test(value)) {
1490
+ return value;
1491
+ }
1492
+ return `'${value.replace(/'/g, "'\\''")}'`;
1493
+ }
1494
+ function escapeRegExp(value) {
1495
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1496
+ }