cursor-agent-bridge 0.1.0 → 0.1.3

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/README.md CHANGED
@@ -1,16 +1,19 @@
1
1
  # cursor-agent-bridge
2
2
 
3
- Responses-compatible API bridge for the Cursor Agent CLI.
3
+ OpenAI-compatible HTTP bridge for the Cursor Agent CLI.
4
4
 
5
- `cursor-agent-bridge` lets Codex and OpenAI-compatible clients use your local Cursor Agent CLI session through a localhost API. It is designed around Codex's current custom-provider requirement for the Responses API while still exposing Chat Completions for simpler clients.
5
+ `cursor-agent-bridge` lets Codex and other OpenAI-compatible clients talk to a
6
+ local Cursor Agent session at `http://127.0.0.1:4646/v1`. It supports the
7
+ Responses API used by Codex custom providers, plus Chat Completions for simpler
8
+ clients.
6
9
 
7
10
  ## Requirements
8
11
 
9
- - Node.js 20+
12
+ - Node.js 22.13+
10
13
  - pnpm 11+
11
14
  - Cursor Agent CLI installed and authenticated
12
15
 
13
- Install Cursor Agent and verify it first:
16
+ Check Cursor Agent first:
14
17
 
15
18
  ```bash
16
19
  agent login
@@ -19,11 +22,14 @@ agent --list-models
19
22
 
20
23
  ## Install
21
24
 
25
+ Install the package globally, then confirm the command is on your `PATH`:
26
+
22
27
  ```bash
23
28
  pnpm add -g cursor-agent-bridge
29
+ cursor-agent-bridge --version
24
30
  ```
25
31
 
26
- During local development:
32
+ For local development:
27
33
 
28
34
  ```bash
29
35
  pnpm install
@@ -31,23 +37,89 @@ pnpm build
31
37
  pnpm exec cursor-agent-bridge serve
32
38
  ```
33
39
 
40
+ ## Upgrade
41
+
42
+ Check for updates:
43
+
44
+ ```bash
45
+ cursor-agent-bridge upgrade --check
46
+ ```
47
+
48
+ Install the latest published version:
49
+
50
+ ```bash
51
+ cursor-agent-bridge upgrade
52
+ ```
53
+
54
+ Options:
55
+
56
+ ```bash
57
+ cursor-agent-bridge upgrade --target 0.1.3
58
+ cursor-agent-bridge upgrade --manager npm
59
+ ```
60
+
61
+ `--check` compares your installed version with npm and exits `1` when a newer
62
+ version is available. The default command installs through pnpm when it manages
63
+ the global package, otherwise npm.
64
+
65
+ If you use the optional macOS LaunchAgent, reinstall it after upgrading so the
66
+ service picks up the new binary:
67
+
68
+ ```bash
69
+ cursor-agent-bridge launch-agent install
70
+ ```
71
+
72
+ For local development from this repository, use `git pull`, `pnpm install`, and
73
+ `pnpm build` instead of `upgrade`.
74
+
34
75
  ## Run
35
76
 
77
+ Run the bridge in the foreground:
78
+
36
79
  ```bash
37
80
  cursor-agent-bridge serve --host 127.0.0.1 --port 4646
38
81
  ```
39
82
 
40
- Environment variables:
83
+ Configuration:
41
84
 
42
85
  ```bash
43
86
  HOST=127.0.0.1
44
87
  PORT=4646
45
88
  CURSOR_AGENT_PATH=agent
89
+ CURSOR_AGENT_MAX_CONCURRENT=1
90
+ CURSOR_AGENT_YOLO=1
46
91
  ```
47
92
 
93
+ By default, the bridge runs Cursor Agent with `--yolo` so Codex can use it as a
94
+ non-interactive provider. Set `CURSOR_AGENT_YOLO=0` to keep Cursor Agent's
95
+ normal confirmation behavior.
96
+
97
+ `CURSOR_AGENT_MAX_CONCURRENT` limits concurrent Cursor Agent subprocesses. The
98
+ default is `1`.
99
+
100
+ ## macOS Background Service
101
+
102
+ Codex connects to the configured `base_url`; it does not start provider
103
+ processes. On macOS, you can install the optional LaunchAgent to start the
104
+ bridge at login and restart it after crashes:
105
+
106
+ ```bash
107
+ cursor-agent-bridge launch-agent install
108
+ ```
109
+
110
+ Manage the service:
111
+
112
+ ```bash
113
+ cursor-agent-bridge launch-agent status
114
+ cursor-agent-bridge launch-agent uninstall
115
+ ```
116
+
117
+ The LaunchAgent listens on `127.0.0.1:4646` by default. Skip it if you prefer to
118
+ run `cursor-agent-bridge serve` yourself before starting Codex.
119
+
48
120
  ## Codex Config
49
121
 
50
- Add a Codex profile such as `~/.codex/cursor.config.toml`:
122
+ Create `~/.codex/cursor.config.toml`:
51
123
 
52
124
  ```toml
53
125
  model_provider = "cursor"
@@ -59,13 +131,14 @@ base_url = "http://127.0.0.1:4646/v1"
59
131
  wire_api = "responses"
60
132
  ```
61
133
 
62
- Start Codex:
134
+ Start Codex with the profile:
63
135
 
64
136
  ```bash
65
137
  codex --profile cursor
66
138
  ```
67
139
 
68
- Use `/model` to select a Cursor model. The model catalog is generated from `agent --list-models`.
140
+ Use `/model` in Codex to pick a Cursor model. The model catalog comes from
141
+ `agent --list-models`.
69
142
 
70
143
  ## API
71
144
 
@@ -76,7 +149,17 @@ POST /v1/responses
76
149
  POST /v1/chat/completions
77
150
  ```
78
151
 
79
- `GET /v1/models` returns the OpenAI-compatible list by default. When Codex calls it with `client_version`, it returns the Codex model catalog shape expected by `/model`.
152
+ `GET /v1/models` returns an OpenAI-style model list by default. When Codex calls
153
+ the same endpoint with `client_version`, the bridge returns the catalog shape
154
+ Codex expects for `/model`.
155
+
156
+ Model lists are cached briefly so the bridge does not spawn
157
+ `agent --list-models` for every request. Pass `refresh=1` to force a refresh.
158
+
159
+ The bridge accepts sampling and token limit fields such as `temperature`,
160
+ `top_p`, `max_tokens`, and `max_output_tokens` for OpenAI client compatibility.
161
+ Cursor Agent CLI does not expose stable equivalents for those fields, so the
162
+ bridge does not rewrite prompts or add local truncation.
80
163
 
81
164
  ## Examples
82
165
 
@@ -96,11 +179,13 @@ curl -sS http://127.0.0.1:4646/v1/chat/completions \
96
179
  -d '{"model":"auto","messages":[{"role":"user","content":"Reply OK"}],"stream":false}'
97
180
  ```
98
181
 
99
- ## Security Notes
182
+ ## Security
100
183
 
101
- This server is intended for local use. Bind to `127.0.0.1` unless you are deliberately exposing it on a trusted network.
184
+ Bind the bridge to `127.0.0.1` unless you intentionally expose it on a trusted
185
+ network.
102
186
 
103
- Client `Authorization` headers are not forwarded to Cursor Agent. Use Cursor Agent login or `CURSOR_API_KEY` in the bridge process environment.
187
+ The bridge does not forward client `Authorization` headers to Cursor Agent.
188
+ Cursor Agent uses local `agent login` state.
104
189
 
105
190
  ## Development
106
191
 
@@ -109,4 +194,4 @@ pnpm install
109
194
  pnpm run ci
110
195
  ```
111
196
 
112
- Release publishing is handled by GitHub Actions on tags named `v*`.
197
+ GitHub Actions publishes releases from `v*` tags.
package/dist/cli.mjs CHANGED
@@ -1,9 +1,287 @@
1
1
  #!/usr/bin/env node
2
- import { t as startServer } from "./server-CuHDT_fJ.mjs";
2
+ import { n as version, t as startServer } from "./server-BFgBwbF5.mjs";
3
+ import { execFile, execFileSync, spawn } from "node:child_process";
4
+ import { chmodSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import { homedir } from "node:os";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { promisify } from "node:util";
8
+ //#region src/launch-agent.ts
9
+ const defaultLabel = "com.xwartz.cursor-agent-bridge";
10
+ const defaultLogDir = join(homedir(), ".codex", "logs");
11
+ function getLaunchAgentPaths(label = defaultLabel) {
12
+ return {
13
+ label,
14
+ plistPath: join(homedir(), "Library", "LaunchAgents", `${label}.plist`),
15
+ stdoutPath: join(defaultLogDir, "cursor-agent-bridge.log"),
16
+ stderrPath: join(defaultLogDir, "cursor-agent-bridge.err.log")
17
+ };
18
+ }
19
+ function createLaunchAgentPlist(options) {
20
+ const label = options.label ?? defaultLabel;
21
+ const host = options.host ?? "127.0.0.1";
22
+ const port = options.port ?? 4646;
23
+ const paths = getLaunchAgentPaths(label);
24
+ const args = [
25
+ resolve(options.cliPath),
26
+ "serve",
27
+ "--host",
28
+ host,
29
+ "--port",
30
+ String(port)
31
+ ];
32
+ const env = { PATH: [
33
+ dirname(resolve(options.cliPath)),
34
+ join(homedir(), ".local", "bin"),
35
+ "/usr/local/bin",
36
+ "/opt/homebrew/bin",
37
+ "/usr/bin",
38
+ "/bin",
39
+ "/usr/sbin",
40
+ "/sbin"
41
+ ].join(":") };
42
+ if (options.agentPath) env.CURSOR_AGENT_PATH = options.agentPath;
43
+ return `<?xml version="1.0" encoding="UTF-8"?>
44
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
45
+ <plist version="1.0">
46
+ <dict>
47
+ <key>Label</key>
48
+ <string>${escapePlist(label)}</string>
49
+
50
+ <key>ProgramArguments</key>
51
+ <array>
52
+ ${args.map((arg) => ` <string>${escapePlist(arg)}</string>`).join("\n")}
53
+ </array>
54
+
55
+ <key>EnvironmentVariables</key>
56
+ <dict>
57
+ ${Object.entries(env).map(([key, value]) => ` <key>${escapePlist(key)}</key>\n <string>${escapePlist(value)}</string>`).join("\n")}
58
+ </dict>
59
+
60
+ <key>RunAtLoad</key>
61
+ <true/>
62
+
63
+ <key>KeepAlive</key>
64
+ <true/>
65
+
66
+ <key>StandardOutPath</key>
67
+ <string>${escapePlist(paths.stdoutPath)}</string>
68
+
69
+ <key>StandardErrorPath</key>
70
+ <string>${escapePlist(paths.stderrPath)}</string>
71
+ </dict>
72
+ </plist>
73
+ `;
74
+ }
75
+ function installLaunchAgent(options) {
76
+ ensureMacOS();
77
+ const paths = getLaunchAgentPaths(options.label ?? defaultLabel);
78
+ mkdirSync(dirname(paths.plistPath), { recursive: true });
79
+ mkdirSync(defaultLogDir, { recursive: true });
80
+ if (existsSync(paths.plistPath)) bootout(paths.plistPath);
81
+ writeFileSync(paths.plistPath, createLaunchAgentPlist(options));
82
+ chmodSync(paths.plistPath, 420);
83
+ execFileSync("launchctl", [
84
+ "bootstrap",
85
+ launchctlDomain(),
86
+ paths.plistPath
87
+ ], { stdio: "pipe" });
88
+ return paths;
89
+ }
90
+ function uninstallLaunchAgent(label = defaultLabel) {
91
+ ensureMacOS();
92
+ const paths = getLaunchAgentPaths(label);
93
+ bootout(paths.plistPath);
94
+ rmSync(paths.plistPath, { force: true });
95
+ return paths;
96
+ }
97
+ function printLaunchAgentStatus(label = defaultLabel) {
98
+ ensureMacOS();
99
+ return execFileSync("launchctl", ["print", `${launchctlDomain()}/${label}`], { encoding: "utf8" });
100
+ }
101
+ function bootout(plistPath) {
102
+ try {
103
+ execFileSync("launchctl", [
104
+ "bootout",
105
+ launchctlDomain(),
106
+ plistPath
107
+ ], { stdio: "pipe" });
108
+ } catch {}
109
+ }
110
+ function launchctlDomain() {
111
+ return `gui/${process.getuid?.() ?? execFileSync("id", ["-u"], { encoding: "utf8" }).trim()}`;
112
+ }
113
+ function ensureMacOS() {
114
+ if (process.platform !== "darwin") throw new Error("LaunchAgent management is only available on macOS.");
115
+ }
116
+ function escapePlist(value) {
117
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
118
+ }
119
+ //#endregion
120
+ //#region src/upgrade.ts
121
+ const execFileAsync = promisify(execFile);
122
+ const PACKAGE_NAME = "cursor-agent-bridge";
123
+ const DEFAULT_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
124
+ const REGISTRY_TIMEOUT_MS = 1e4;
125
+ function compareSemver(left, right) {
126
+ const toParts = (value) => {
127
+ const [major = 0, minor = 0, patch = 0] = value.split(".").map((part) => Number.parseInt(part, 10) || 0);
128
+ return [
129
+ major,
130
+ minor,
131
+ patch
132
+ ];
133
+ };
134
+ const [leftMajor, leftMinor, leftPatch] = toParts(left);
135
+ const [rightMajor, rightMinor, rightPatch] = toParts(right);
136
+ if (leftMajor !== rightMajor) return leftMajor < rightMajor ? -1 : 1;
137
+ if (leftMinor !== rightMinor) return leftMinor < rightMinor ? -1 : 1;
138
+ if (leftPatch !== rightPatch) return leftPatch < rightPatch ? -1 : 1;
139
+ return 0;
140
+ }
141
+ async function fetchLatestVersion(options) {
142
+ const registryUrl = options?.registryUrl ?? DEFAULT_REGISTRY_URL;
143
+ const fetchFn = options?.fetchFn ?? fetch;
144
+ const timeoutMs = options?.timeoutMs ?? REGISTRY_TIMEOUT_MS;
145
+ const controller = new AbortController();
146
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
147
+ try {
148
+ const response = await fetchFn(registryUrl, { signal: controller.signal });
149
+ if (!response.ok) throw new Error(`Registry returned ${response.status}`);
150
+ const payload = await response.json();
151
+ if (!payload.version) throw new Error("Registry response missing version");
152
+ return payload.version;
153
+ } finally {
154
+ clearTimeout(timeout);
155
+ }
156
+ }
157
+ async function commandExists(command, execFileFn) {
158
+ try {
159
+ await execFileFn("which", [command]);
160
+ return true;
161
+ } catch {
162
+ return false;
163
+ }
164
+ }
165
+ async function detectPackageManager(preference, execFileFn = execFileAsync) {
166
+ if (preference === "npm") return "npm";
167
+ if (preference === "pnpm") return "pnpm";
168
+ if (!await commandExists("pnpm", execFileFn)) return "npm";
169
+ try {
170
+ await execFileFn("pnpm", [
171
+ "list",
172
+ "-g",
173
+ PACKAGE_NAME,
174
+ "--json"
175
+ ], { env: process.env });
176
+ return "pnpm";
177
+ } catch {
178
+ return "npm";
179
+ }
180
+ }
181
+ function buildInstallCommand(manager, target) {
182
+ const packageSpec = `${PACKAGE_NAME}${target === "latest" ? "@latest" : `@${target.replace(/^@/, "")}`}`;
183
+ if (manager === "pnpm") return {
184
+ command: "pnpm",
185
+ args: [
186
+ "add",
187
+ "-g",
188
+ packageSpec
189
+ ]
190
+ };
191
+ return {
192
+ command: "npm",
193
+ args: [
194
+ "install",
195
+ "-g",
196
+ packageSpec
197
+ ]
198
+ };
199
+ }
200
+ function printManualUpgradeHint(errorLog) {
201
+ errorLog("Upgrade manually:");
202
+ errorLog(` pnpm add -g ${PACKAGE_NAME}@latest`);
203
+ errorLog(` npm install -g ${PACKAGE_NAME}@latest`);
204
+ }
205
+ async function resolveTargetVersion(target, registryUrl, fetchFn) {
206
+ if (target === "latest") return fetchLatestVersion({
207
+ registryUrl,
208
+ fetchFn
209
+ });
210
+ return target.replace(/^@/, "");
211
+ }
212
+ async function runInstall(manager, target, spawnFn) {
213
+ const { command, args } = buildInstallCommand(manager, target);
214
+ return new Promise((resolve, reject) => {
215
+ const child = spawnFn(command, args, {
216
+ stdio: "inherit",
217
+ env: process.env
218
+ });
219
+ child.on("error", reject);
220
+ child.on("close", (code) => resolve(code ?? 1));
221
+ });
222
+ }
223
+ async function runUpgrade(options) {
224
+ const log = options.log ?? console.log;
225
+ const errorLog = options.errorLog ?? console.error;
226
+ const checkOnly = options.checkOnly ?? false;
227
+ const target = options.target ?? "latest";
228
+ const managerPreference = options.manager ?? "auto";
229
+ const registryUrl = options.registryUrl ?? DEFAULT_REGISTRY_URL;
230
+ const fetchFn = options.fetchFn ?? fetch;
231
+ const spawnFn = options.spawnFn ?? ((command, args, spawnOptions) => spawn(command, args, spawnOptions));
232
+ const execFileFn = options.execFileFn ?? execFileAsync;
233
+ const existsFn = options.existsFn ?? existsSync;
234
+ let targetVersion;
235
+ try {
236
+ targetVersion = await resolveTargetVersion(target, registryUrl, fetchFn);
237
+ } catch (error) {
238
+ errorLog(`Failed to check for updates: ${error instanceof Error ? error.message : String(error)}`);
239
+ printManualUpgradeHint(errorLog);
240
+ return 1;
241
+ }
242
+ if (compareSemver(options.currentVersion, targetVersion) >= 0) {
243
+ log(`${PACKAGE_NAME} is up to date (${options.currentVersion})`);
244
+ return 0;
245
+ }
246
+ if (checkOnly) {
247
+ log(`Update available: ${options.currentVersion} -> ${targetVersion}`);
248
+ return 1;
249
+ }
250
+ let manager;
251
+ manager = await detectPackageManager(managerPreference, execFileFn);
252
+ log(`Installing ${PACKAGE_NAME}@${targetVersion} via ${manager}...`);
253
+ try {
254
+ const exitCode = await runInstall(manager, target, spawnFn);
255
+ if (exitCode !== 0) {
256
+ errorLog(`${manager} install failed with exit code ${exitCode}`);
257
+ printManualUpgradeHint(errorLog);
258
+ return exitCode;
259
+ }
260
+ } catch (error) {
261
+ errorLog(`Install failed: ${error instanceof Error ? error.message : String(error)}`);
262
+ printManualUpgradeHint(errorLog);
263
+ return 1;
264
+ }
265
+ log(`Installed ${PACKAGE_NAME}@${targetVersion}`);
266
+ log("Run `cursor-agent-bridge --version` to verify the upgrade.");
267
+ const launchAgentPlist = getLaunchAgentPaths().plistPath;
268
+ if (existsFn(launchAgentPlist)) log("LaunchAgent detected. Run `cursor-agent-bridge launch-agent install` to refresh the service.");
269
+ return 0;
270
+ }
271
+ //#endregion
3
272
  //#region src/cli.ts
4
273
  function readArg(name, fallback) {
5
274
  const index = process.argv.indexOf(name);
6
- return index >= 0 ? process.argv[index + 1] : fallback;
275
+ if (index < 0) return fallback;
276
+ const value = process.argv[index + 1];
277
+ if (!value || value.startsWith("-")) throw new Error(`Missing value for ${name}`);
278
+ return value;
279
+ }
280
+ function parsePort(value, fallback) {
281
+ if (value === void 0) return fallback;
282
+ const port = Number(value);
283
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`Invalid port: ${value}`);
284
+ return port;
7
285
  }
8
286
  const command = process.argv[2] && !process.argv[2]?.startsWith("-") ? process.argv[2] : "serve";
9
287
  if (command === "help" || process.argv.includes("--help") || process.argv.includes("-h")) {
@@ -11,6 +289,10 @@ if (command === "help" || process.argv.includes("--help") || process.argv.includ
11
289
 
12
290
  Usage:
13
291
  cursor-agent-bridge serve [--host 127.0.0.1] [--port 4646]
292
+ cursor-agent-bridge launch-agent install [--host 127.0.0.1] [--port 4646] [--agent-path agent]
293
+ cursor-agent-bridge launch-agent uninstall
294
+ cursor-agent-bridge launch-agent status
295
+ cursor-agent-bridge upgrade [--check] [--target latest] [--manager auto|npm|pnpm]
14
296
 
15
297
  Environment:
16
298
  HOST Listen host, default 127.0.0.1
@@ -19,12 +301,72 @@ Environment:
19
301
  `);
20
302
  process.exit(0);
21
303
  }
304
+ if (command === "version" || process.argv.includes("--version")) {
305
+ console.log(version);
306
+ process.exit(0);
307
+ }
308
+ if (command === "upgrade") {
309
+ const checkOnly = process.argv.includes("--check");
310
+ const target = readArg("--target", "latest") ?? "latest";
311
+ const manager = readArg("--manager", "auto") ?? "auto";
312
+ if (manager !== "auto" && manager !== "npm" && manager !== "pnpm") {
313
+ console.error("Invalid --manager value. Use auto, npm, or pnpm.");
314
+ process.exit(1);
315
+ }
316
+ const exitCode = await runUpgrade({
317
+ currentVersion: version,
318
+ checkOnly,
319
+ target,
320
+ manager
321
+ });
322
+ process.exit(exitCode);
323
+ }
324
+ if (command === "launch-agent") {
325
+ const action = process.argv[3] ?? "status";
326
+ try {
327
+ if (action === "install") {
328
+ const host = readArg("--host", process.env.HOST) ?? "127.0.0.1";
329
+ const port = parsePort(readArg("--port", process.env.PORT), 4646);
330
+ const agentPath = readArg("--agent-path", process.env.CURSOR_AGENT_PATH);
331
+ const paths = installLaunchAgent({
332
+ cliPath: process.argv[1] ?? "cursor-agent-bridge",
333
+ host,
334
+ port,
335
+ ...agentPath ? { agentPath } : {}
336
+ });
337
+ console.log(`Installed ${paths.label}`);
338
+ console.log(paths.plistPath);
339
+ process.exit(0);
340
+ }
341
+ if (action === "uninstall") {
342
+ const paths = uninstallLaunchAgent();
343
+ console.log(`Uninstalled ${paths.label}`);
344
+ process.exit(0);
345
+ }
346
+ if (action === "status") {
347
+ process.stdout.write(printLaunchAgentStatus());
348
+ process.exit(0);
349
+ }
350
+ } catch (error) {
351
+ console.error(error instanceof Error ? error.message : String(error));
352
+ process.exit(1);
353
+ }
354
+ console.error(`Unknown launch-agent action: ${action}`);
355
+ process.exit(1);
356
+ }
22
357
  if (command !== "serve") {
23
358
  console.error(`Unknown command: ${command}`);
24
359
  process.exit(1);
25
360
  }
26
- const host = readArg("--host", process.env.HOST) ?? "127.0.0.1";
27
- const port = Number(readArg("--port", process.env.PORT) ?? 4646);
361
+ let host;
362
+ let port;
363
+ try {
364
+ host = readArg("--host", process.env.HOST) ?? "127.0.0.1";
365
+ port = parsePort(readArg("--port", process.env.PORT), 4646);
366
+ } catch (error) {
367
+ console.error(error instanceof Error ? error.message : String(error));
368
+ process.exit(1);
369
+ }
28
370
  const server = await startServer({
29
371
  host,
30
372
  port,
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { startServer } from \"./server.js\";\n\nfunction readArg(name: string, fallback: string | undefined) {\n const index = process.argv.indexOf(name);\n return index >= 0 ? process.argv[index + 1] : fallback;\n}\n\nconst command =\n process.argv[2] && !process.argv[2]?.startsWith(\"-\")\n ? process.argv[2]\n : \"serve\";\n\nif (\n command === \"help\" ||\n process.argv.includes(\"--help\") ||\n process.argv.includes(\"-h\")\n) {\n console.log(`cursor-agent-bridge\n\nUsage:\n cursor-agent-bridge serve [--host 127.0.0.1] [--port 4646]\n\nEnvironment:\n HOST Listen host, default 127.0.0.1\n PORT Listen port, default 4646\n CURSOR_AGENT_PATH Cursor Agent CLI path, default agent\n`);\n process.exit(0);\n}\n\nif (command !== \"serve\") {\n console.error(`Unknown command: ${command}`);\n process.exit(1);\n}\n\nconst host = readArg(\"--host\", process.env.HOST) ?? \"127.0.0.1\";\nconst port = Number(readArg(\"--port\", process.env.PORT) ?? 4646);\n\nconst server = await startServer({\n host,\n port,\n ...(process.env.CURSOR_AGENT_PATH\n ? { agentPath: process.env.CURSOR_AGENT_PATH }\n : {}),\n});\nconsole.log(`cursor-agent-bridge listening on http://${host}:${port}`);\n\nfunction shutdown() {\n server.close(() => process.exit(0));\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n"],"mappings":";;;AAIA,SAAS,QAAQ,MAAc,UAA8B;CAC3D,MAAM,QAAQ,QAAQ,KAAK,QAAQ,IAAI;CACvC,OAAO,SAAS,IAAI,QAAQ,KAAK,QAAQ,KAAK;AAChD;AAEA,MAAM,UACJ,QAAQ,KAAK,MAAM,CAAC,QAAQ,KAAK,EAAE,EAAE,WAAW,GAAG,IAC/C,QAAQ,KAAK,KACb;AAEN,IACE,YAAY,UACZ,QAAQ,KAAK,SAAS,QAAQ,KAC9B,QAAQ,KAAK,SAAS,IAAI,GAC1B;CACA,QAAQ,IAAI;;;;;;;;;CASb;CACC,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,SAAS;CACvB,QAAQ,MAAM,oBAAoB,SAAS;CAC3C,QAAQ,KAAK,CAAC;AAChB;AAEA,MAAM,OAAO,QAAQ,UAAU,QAAQ,IAAI,IAAI,KAAK;AACpD,MAAM,OAAO,OAAO,QAAQ,UAAU,QAAQ,IAAI,IAAI,KAAK,IAAI;AAE/D,MAAM,SAAS,MAAM,YAAY;CAC/B;CACA;CACA,GAAI,QAAQ,IAAI,oBACZ,EAAE,WAAW,QAAQ,IAAI,kBAAkB,IAC3C,CAAC;AACP,CAAC;AACD,QAAQ,IAAI,2CAA2C,KAAK,GAAG,MAAM;AAErE,SAAS,WAAW;CAClB,OAAO,YAAY,QAAQ,KAAK,CAAC,CAAC;AACpC;AAEA,QAAQ,GAAG,UAAU,QAAQ;AAC7B,QAAQ,GAAG,WAAW,QAAQ"}
1
+ {"version":3,"file":"cli.mjs","names":["packageJson.version"],"sources":["../src/launch-agent.ts","../src/upgrade.ts","../src/cli.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nconst defaultLabel = \"com.xwartz.cursor-agent-bridge\";\nconst defaultLogDir = join(homedir(), \".codex\", \"logs\");\n\nexport type LaunchAgentOptions = {\n cliPath: string;\n host?: string;\n port?: number;\n agentPath?: string;\n label?: string;\n};\n\nexport type LaunchAgentPaths = {\n label: string;\n plistPath: string;\n stdoutPath: string;\n stderrPath: string;\n};\n\nexport function getLaunchAgentPaths(label = defaultLabel): LaunchAgentPaths {\n return {\n label,\n plistPath: join(homedir(), \"Library\", \"LaunchAgents\", `${label}.plist`),\n stdoutPath: join(defaultLogDir, \"cursor-agent-bridge.log\"),\n stderrPath: join(defaultLogDir, \"cursor-agent-bridge.err.log\"),\n };\n}\n\nexport function createLaunchAgentPlist(options: LaunchAgentOptions) {\n const label = options.label ?? defaultLabel;\n const host = options.host ?? \"127.0.0.1\";\n const port = options.port ?? 4646;\n const paths = getLaunchAgentPaths(label);\n const args = [\n resolve(options.cliPath),\n \"serve\",\n \"--host\",\n host,\n \"--port\",\n String(port),\n ];\n const env: Record<string, string> = {\n PATH: [\n dirname(resolve(options.cliPath)),\n join(homedir(), \".local\", \"bin\"),\n \"/usr/local/bin\",\n \"/opt/homebrew/bin\",\n \"/usr/bin\",\n \"/bin\",\n \"/usr/sbin\",\n \"/sbin\",\n ].join(\":\"),\n };\n\n if (options.agentPath) {\n env.CURSOR_AGENT_PATH = options.agentPath;\n }\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${escapePlist(label)}</string>\n\n <key>ProgramArguments</key>\n <array>\n${args.map((arg) => ` <string>${escapePlist(arg)}</string>`).join(\"\\n\")}\n </array>\n\n <key>EnvironmentVariables</key>\n <dict>\n${Object.entries(env)\n .map(\n ([key, value]) =>\n ` <key>${escapePlist(key)}</key>\\n <string>${escapePlist(value)}</string>`,\n )\n .join(\"\\n\")}\n </dict>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <true/>\n\n <key>StandardOutPath</key>\n <string>${escapePlist(paths.stdoutPath)}</string>\n\n <key>StandardErrorPath</key>\n <string>${escapePlist(paths.stderrPath)}</string>\n</dict>\n</plist>\n`;\n}\n\nexport function installLaunchAgent(options: LaunchAgentOptions) {\n ensureMacOS();\n const label = options.label ?? defaultLabel;\n const paths = getLaunchAgentPaths(label);\n mkdirSync(dirname(paths.plistPath), { recursive: true });\n mkdirSync(defaultLogDir, { recursive: true });\n\n if (existsSync(paths.plistPath)) {\n bootout(paths.plistPath);\n }\n\n writeFileSync(paths.plistPath, createLaunchAgentPlist(options));\n chmodSync(paths.plistPath, 0o644);\n execFileSync(\"launchctl\", [\"bootstrap\", launchctlDomain(), paths.plistPath], {\n stdio: \"pipe\",\n });\n return paths;\n}\n\nexport function uninstallLaunchAgent(label = defaultLabel) {\n ensureMacOS();\n const paths = getLaunchAgentPaths(label);\n bootout(paths.plistPath);\n rmSync(paths.plistPath, { force: true });\n return paths;\n}\n\nexport function printLaunchAgentStatus(label = defaultLabel) {\n ensureMacOS();\n return execFileSync(\"launchctl\", [\"print\", `${launchctlDomain()}/${label}`], {\n encoding: \"utf8\",\n });\n}\n\nfunction bootout(plistPath: string) {\n try {\n execFileSync(\"launchctl\", [\"bootout\", launchctlDomain(), plistPath], {\n stdio: \"pipe\",\n });\n } catch {\n // It is fine if the service is not loaded yet.\n }\n}\n\nfunction launchctlDomain() {\n return `gui/${process.getuid?.() ?? execFileSync(\"id\", [\"-u\"], { encoding: \"utf8\" }).trim()}`;\n}\n\nfunction ensureMacOS() {\n if (process.platform !== \"darwin\") {\n throw new Error(\"LaunchAgent management is only available on macOS.\");\n }\n}\n\nfunction escapePlist(value: string) {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n}\n","import { execFile, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { promisify } from \"node:util\";\nimport { getLaunchAgentPaths } from \"./launch-agent.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport const PACKAGE_NAME = \"cursor-agent-bridge\";\nexport const DEFAULT_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\nconst REGISTRY_TIMEOUT_MS = 10_000;\n\nexport type PackageManager = \"npm\" | \"pnpm\";\nexport type PackageManagerPreference = PackageManager | \"auto\";\n\nexport type ExecFileFn = (\n file: string,\n args?: readonly string[] | null,\n options?: { env?: NodeJS.ProcessEnv },\n) => Promise<unknown>;\n\nexport type SpawnFn = (\n command: string,\n args: readonly string[],\n options: { stdio: \"inherit\"; env?: NodeJS.ProcessEnv },\n) => {\n on(event: \"error\", listener: (error: Error) => void): unknown;\n on(event: \"close\", listener: (code: number | null) => void): unknown;\n};\n\nexport type UpgradeOptions = {\n currentVersion: string;\n checkOnly?: boolean;\n target?: string;\n manager?: PackageManagerPreference;\n registryUrl?: string;\n fetchFn?: typeof fetch;\n spawnFn?: SpawnFn;\n execFileFn?: ExecFileFn;\n existsFn?: typeof existsSync;\n log?: (...args: unknown[]) => void;\n errorLog?: (...args: unknown[]) => void;\n};\n\nexport function compareSemver(left: string, right: string): -1 | 0 | 1 {\n const toParts = (value: string) => {\n const [major = 0, minor = 0, patch = 0] = value\n .split(\".\")\n .map((part) => Number.parseInt(part, 10) || 0);\n return [major, minor, patch] as const;\n };\n const [leftMajor, leftMinor, leftPatch] = toParts(left);\n const [rightMajor, rightMinor, rightPatch] = toParts(right);\n\n if (leftMajor !== rightMajor) {\n return leftMajor < rightMajor ? -1 : 1;\n }\n if (leftMinor !== rightMinor) {\n return leftMinor < rightMinor ? -1 : 1;\n }\n if (leftPatch !== rightPatch) {\n return leftPatch < rightPatch ? -1 : 1;\n }\n return 0;\n}\n\nexport async function fetchLatestVersion(options?: {\n registryUrl?: string;\n fetchFn?: typeof fetch;\n timeoutMs?: number;\n}): Promise<string> {\n const registryUrl = options?.registryUrl ?? DEFAULT_REGISTRY_URL;\n const fetchFn = options?.fetchFn ?? fetch;\n const timeoutMs = options?.timeoutMs ?? REGISTRY_TIMEOUT_MS;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetchFn(registryUrl, { signal: controller.signal });\n if (!response.ok) {\n throw new Error(`Registry returned ${response.status}`);\n }\n\n const payload = (await response.json()) as { version?: string };\n if (!payload.version) {\n throw new Error(\"Registry response missing version\");\n }\n return payload.version;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function commandExists(\n command: string,\n execFileFn: ExecFileFn,\n): Promise<boolean> {\n try {\n await execFileFn(\"which\", [command]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function detectPackageManager(\n preference: PackageManagerPreference,\n execFileFn: ExecFileFn = execFileAsync,\n): Promise<PackageManager> {\n if (preference === \"npm\") return \"npm\";\n if (preference === \"pnpm\") return \"pnpm\";\n\n if (!(await commandExists(\"pnpm\", execFileFn))) {\n return \"npm\";\n }\n\n try {\n await execFileFn(\"pnpm\", [\"list\", \"-g\", PACKAGE_NAME, \"--json\"], {\n env: process.env,\n });\n return \"pnpm\";\n } catch {\n return \"npm\";\n }\n}\n\nexport function buildInstallCommand(\n manager: PackageManager,\n target: string,\n): { command: string; args: string[] } {\n const versionSpec =\n target === \"latest\" ? \"@latest\" : `@${target.replace(/^@/, \"\")}`;\n const packageSpec = `${PACKAGE_NAME}${versionSpec}`;\n\n if (manager === \"pnpm\") {\n return { command: \"pnpm\", args: [\"add\", \"-g\", packageSpec] };\n }\n\n return { command: \"npm\", args: [\"install\", \"-g\", packageSpec] };\n}\n\nfunction printManualUpgradeHint(errorLog: (...args: unknown[]) => void) {\n errorLog(\"Upgrade manually:\");\n errorLog(` pnpm add -g ${PACKAGE_NAME}@latest`);\n errorLog(` npm install -g ${PACKAGE_NAME}@latest`);\n}\n\nasync function resolveTargetVersion(\n target: string,\n registryUrl: string,\n fetchFn: typeof fetch,\n): Promise<string> {\n if (target === \"latest\") {\n return fetchLatestVersion({ registryUrl, fetchFn });\n }\n return target.replace(/^@/, \"\");\n}\n\nasync function runInstall(\n manager: PackageManager,\n target: string,\n spawnFn: SpawnFn,\n): Promise<number> {\n const { command, args } = buildInstallCommand(manager, target);\n return new Promise((resolve, reject) => {\n const child = spawnFn(command, args, {\n stdio: \"inherit\",\n env: process.env,\n });\n\n child.on(\"error\", reject);\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n\nexport async function runUpgrade(options: UpgradeOptions): Promise<number> {\n const log = options.log ?? console.log;\n const errorLog = options.errorLog ?? console.error;\n const checkOnly = options.checkOnly ?? false;\n const target = options.target ?? \"latest\";\n const managerPreference = options.manager ?? \"auto\";\n const registryUrl = options.registryUrl ?? DEFAULT_REGISTRY_URL;\n const fetchFn = options.fetchFn ?? fetch;\n const spawnFn: SpawnFn =\n options.spawnFn ??\n ((command, args, spawnOptions) => spawn(command, args, spawnOptions));\n const execFileFn = options.execFileFn ?? execFileAsync;\n const existsFn = options.existsFn ?? existsSync;\n\n let targetVersion: string;\n try {\n targetVersion = await resolveTargetVersion(target, registryUrl, fetchFn);\n } catch (error) {\n errorLog(\n `Failed to check for updates: ${error instanceof Error ? error.message : String(error)}`,\n );\n printManualUpgradeHint(errorLog);\n return 1;\n }\n\n const comparison = compareSemver(options.currentVersion, targetVersion);\n if (comparison >= 0) {\n log(`${PACKAGE_NAME} is up to date (${options.currentVersion})`);\n return 0;\n }\n\n if (checkOnly) {\n log(`Update available: ${options.currentVersion} -> ${targetVersion}`);\n return 1;\n }\n\n let manager: PackageManager;\n manager = await detectPackageManager(managerPreference, execFileFn);\n\n log(`Installing ${PACKAGE_NAME}@${targetVersion} via ${manager}...`);\n try {\n const exitCode = await runInstall(manager, target, spawnFn);\n if (exitCode !== 0) {\n errorLog(`${manager} install failed with exit code ${exitCode}`);\n printManualUpgradeHint(errorLog);\n return exitCode;\n }\n } catch (error) {\n errorLog(\n `Install failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n printManualUpgradeHint(errorLog);\n return 1;\n }\n\n log(`Installed ${PACKAGE_NAME}@${targetVersion}`);\n log(\"Run `cursor-agent-bridge --version` to verify the upgrade.\");\n\n const launchAgentPlist = getLaunchAgentPaths().plistPath;\n if (existsFn(launchAgentPlist)) {\n log(\n \"LaunchAgent detected. Run `cursor-agent-bridge launch-agent install` to refresh the service.\",\n );\n }\n\n return 0;\n}\n","#!/usr/bin/env node\n\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport {\n installLaunchAgent,\n printLaunchAgentStatus,\n uninstallLaunchAgent,\n} from \"./launch-agent.js\";\nimport { startServer } from \"./server.js\";\nimport { runUpgrade } from \"./upgrade.js\";\n\nfunction readArg(name: string, fallback: string | undefined) {\n const index = process.argv.indexOf(name);\n if (index < 0) return fallback;\n const value = process.argv[index + 1];\n if (!value || value.startsWith(\"-\"))\n throw new Error(`Missing value for ${name}`);\n return value;\n}\n\nfunction parsePort(value: string | undefined, fallback: number) {\n if (value === undefined) return fallback;\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error(`Invalid port: ${value}`);\n }\n return port;\n}\n\nconst command =\n process.argv[2] && !process.argv[2]?.startsWith(\"-\")\n ? process.argv[2]\n : \"serve\";\n\nif (\n command === \"help\" ||\n process.argv.includes(\"--help\") ||\n process.argv.includes(\"-h\")\n) {\n console.log(`cursor-agent-bridge\n\nUsage:\n cursor-agent-bridge serve [--host 127.0.0.1] [--port 4646]\n cursor-agent-bridge launch-agent install [--host 127.0.0.1] [--port 4646] [--agent-path agent]\n cursor-agent-bridge launch-agent uninstall\n cursor-agent-bridge launch-agent status\n cursor-agent-bridge upgrade [--check] [--target latest] [--manager auto|npm|pnpm]\n\nEnvironment:\n HOST Listen host, default 127.0.0.1\n PORT Listen port, default 4646\n CURSOR_AGENT_PATH Cursor Agent CLI path, default agent\n`);\n process.exit(0);\n}\n\nif (command === \"version\" || process.argv.includes(\"--version\")) {\n console.log(packageJson.version);\n process.exit(0);\n}\n\nif (command === \"upgrade\") {\n const checkOnly = process.argv.includes(\"--check\");\n const target = readArg(\"--target\", \"latest\") ?? \"latest\";\n const manager = readArg(\"--manager\", \"auto\") ?? \"auto\";\n\n if (manager !== \"auto\" && manager !== \"npm\" && manager !== \"pnpm\") {\n console.error(\"Invalid --manager value. Use auto, npm, or pnpm.\");\n process.exit(1);\n }\n\n const exitCode = await runUpgrade({\n currentVersion: packageJson.version,\n checkOnly,\n target,\n manager,\n });\n process.exit(exitCode);\n}\n\nif (command === \"launch-agent\") {\n const action = process.argv[3] ?? \"status\";\n try {\n if (action === \"install\") {\n const host = readArg(\"--host\", process.env.HOST) ?? \"127.0.0.1\";\n const port = parsePort(readArg(\"--port\", process.env.PORT), 4646);\n const agentPath = readArg(\"--agent-path\", process.env.CURSOR_AGENT_PATH);\n const paths = installLaunchAgent({\n cliPath: process.argv[1] ?? \"cursor-agent-bridge\",\n host,\n port,\n ...(agentPath ? { agentPath } : {}),\n });\n console.log(`Installed ${paths.label}`);\n console.log(paths.plistPath);\n process.exit(0);\n }\n\n if (action === \"uninstall\") {\n const paths = uninstallLaunchAgent();\n console.log(`Uninstalled ${paths.label}`);\n process.exit(0);\n }\n\n if (action === \"status\") {\n process.stdout.write(printLaunchAgentStatus());\n process.exit(0);\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n\n console.error(`Unknown launch-agent action: ${action}`);\n process.exit(1);\n}\n\nif (command !== \"serve\") {\n console.error(`Unknown command: ${command}`);\n process.exit(1);\n}\n\nlet host: string;\nlet port: number;\ntry {\n host = readArg(\"--host\", process.env.HOST) ?? \"127.0.0.1\";\n port = parsePort(readArg(\"--port\", process.env.PORT), 4646);\n} catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n}\n\nconst server = await startServer({\n host,\n port,\n ...(process.env.CURSOR_AGENT_PATH\n ? { agentPath: process.env.CURSOR_AGENT_PATH }\n : {}),\n});\nconsole.log(`cursor-agent-bridge listening on http://${host}:${port}`);\n\nfunction shutdown() {\n server.close(() => process.exit(0));\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import { EventEmitter } from "node:events";
2
1
  import http from "node:http";
3
2
 
4
3
  //#region src/types.d.ts
@@ -52,6 +51,7 @@ interface ServerConfig {
52
51
  host?: string;
53
52
  agentPath?: string;
54
53
  defaultCwd?: string;
54
+ maxBodyBytes?: number;
55
55
  }
56
56
  //#endregion
57
57
  //#region src/adapter/messages.d.ts
@@ -115,14 +115,29 @@ interface CursorRunnerOptions {
115
115
  agentPath?: string;
116
116
  defaultCwd?: string;
117
117
  timeoutMs?: number;
118
+ modelListCacheMs?: number;
119
+ maxConcurrentRuns?: number;
120
+ yolo?: boolean;
118
121
  }
119
- declare class CursorRunner extends EventEmitter {
122
+ declare class CursorRunner {
120
123
  readonly agentPath: string;
121
124
  readonly defaultCwd: string;
122
125
  readonly timeoutMs: number;
126
+ readonly modelListCacheMs: number;
127
+ readonly maxConcurrentRuns: number;
128
+ readonly yolo: boolean;
129
+ private modelCache?;
130
+ private activeRuns;
131
+ private readonly runQueue;
132
+ private readonly activeChildren;
123
133
  constructor(options?: CursorRunnerOptions);
124
- listModels(): Promise<BridgeModel[]>;
134
+ listModels(options?: {
135
+ refresh?: boolean;
136
+ }): Promise<BridgeModel[]>;
125
137
  run(options: CursorRunOptions, events?: CursorRunEvents): Promise<CursorRunResult>;
138
+ abortAll(): void;
139
+ private acquireRunPermit;
140
+ private runWithPermit;
126
141
  }
127
142
  //#endregion
128
143
  //#region src/server.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../src/server.ts"],"mappings":";;;;KAAY,QAAA;AAAA,UAEK,WAAA;EACf,IAAA,EAAM,QAAA;EACN,OAAA,WAEI,KAAK;IACH,IAAA;IACA,IAAA;IACA,UAAA;IACA,WAAA;EAAA;AAAA;AAAA,UAIS,qBAAA;EACf,KAAA;EACA,QAAA,EAAU,WAAW;EACrB,MAAA;EACA,WAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,KAAA;EACA,YAAA;EACA,MAAA;EACA,WAAA;EACA,KAAA;EACA,iBAAA;AAAA;AAAA,UAGe,WAAA;EACf,EAAA;EACA,IAAI;AAAA;AAAA,UAGW,gBAAA;EACf,KAAA;EACA,MAAA;EACA,GAAA;EACA,MAAA,GAAS,WAAW;AAAA;AAAA,UAGL,eAAA;EACf,IAAA;EACA,KAAK;AAAA;AAAA,UAGU,eAAA;EACf,OAAA,IAAW,IAAA;EACX,OAAA,IAAW,KAAA;AAAA;AAAA,UAGI,YAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,UAAA;AAAA;;;iBChDc,gBAAA,CAAiB,QAAuB,EAAb,WAAW;AAAA,iBAuCtC,mBAAA,CAAoB,OAAA,EAAS,gBAAA,GAAmB,WAAW;AAAA,iBAyC3D,cAAA,CAAe,KAAyB;;;iBCzFxC,mBAAA,CAAoB,MAAA,WAAiB,WAAW;AAAA,iBAWhD,iBAAA,CAAkB,MAAA,EAAQ,WAAW;;;;;;;;;iBAarC,mBAAA,CAAoB,MAAA,EAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UChBtC,mBAAA;EACf,SAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,cAGW,YAAA,SAAqB,YAAA;EAAA,SACvB,SAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;cAEG,OAAA,GAAS,mBAAA;EAQrB,UAAA,IAAc,OAAA,CAAQ,WAAA;EAiBtB,GAAA,CACE,OAAA,EAAS,gBAAA,EACT,MAAA,GAAQ,eAAA,GACP,OAAA,CAAQ,eAAA;AAAA;;;iBCzBS,WAAA,CAAY,MAAA,GAAQ,YAAA,GAAiB,OAAA,CAAA,IAAA,CAAA,MAAA,QAAA,IAAA,CAAA,eAAA,SAAA,IAAA,CAAA,cAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../src/server.ts"],"mappings":";;;KAAY,QAAA;AAAA,UAEK,WAAA;EACf,IAAA,EAAM,QAAA;EACN,OAAA,WAEI,KAAK;IACH,IAAA;IACA,IAAA;IACA,UAAA;IACA,WAAA;EAAA;AAAA;AAAA,UAIS,qBAAA;EACf,KAAA;EACA,QAAA,EAAU,WAAW;EACrB,MAAA;EACA,WAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,KAAA;EACA,YAAA;EACA,MAAA;EACA,WAAA;EACA,KAAA;EACA,iBAAA;AAAA;AAAA,UAGe,WAAA;EACf,EAAA;EACA,IAAI;AAAA;AAAA,UAGW,gBAAA;EACf,KAAA;EACA,MAAA;EACA,GAAA;EACA,MAAA,GAAS,WAAW;AAAA;AAAA,UAGL,eAAA;EACf,IAAA;EACA,KAAK;AAAA;AAAA,UAGU,eAAA;EACf,OAAA,IAAW,IAAA;EACX,OAAA,IAAW,KAAA;AAAA;AAAA,UAGI,YAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,UAAA;EACA,YAAA;AAAA;;;iBCjDc,gBAAA,CAAiB,QAAuB,EAAb,WAAW;AAAA,iBAuCtC,mBAAA,CAAoB,OAAA,EAAS,gBAAA,GAAmB,WAAW;AAAA,iBAyC3D,cAAA,CAAe,KAAyB;;;iBCzFxC,mBAAA,CAAoB,MAAA,WAAiB,WAAW;AAAA,iBAWhD,iBAAA,CAAkB,MAAA,EAAQ,WAAW;;;;;;;;;iBAarC,mBAAA,CAAoB,MAAA,EAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UCjBtC,mBAAA;EACf,SAAA;EACA,UAAA;EACA,SAAA;EACA,gBAAA;EACA,iBAAA;EACA,IAAA;AAAA;AAAA,cAOW,YAAA;EAAA,SACF,SAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;EAAA,SACA,gBAAA;EAAA,SACA,iBAAA;EAAA,SACA,IAAA;EAAA,QACD,UAAA;EAAA,QACA,UAAA;EAAA,iBACS,QAAA;EAAA,iBACA,cAAA;cAEL,OAAA,GAAS,mBAAA;EAgBf,UAAA,CACJ,OAAA;IAAW,OAAA;EAAA,IACV,OAAA,CAAQ,WAAA;EA4BL,GAAA,CACJ,OAAA,EAAS,gBAAA,EACT,MAAA,GAAQ,eAAA,GACP,OAAA,CAAQ,eAAA;EAUX,QAAA;EAAA,QAIQ,gBAAA;EAAA,QAmCA,aAAA;AAAA;;;iBC7FY,WAAA,CAAY,MAAA,GAAQ,YAAA,GAAiB,OAAA,CAAA,IAAA,CAAA,MAAA,QAAA,IAAA,CAAA,eAAA,SAAA,IAAA,CAAA,cAAA"}