cursor-agent-bridge 0.1.4 → 0.1.5
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 +34 -2
- package/dist/cli.mjs +167 -7
- package/dist/index.d.mts +1 -2
- package/dist/index.mjs +1 -1
- package/dist/{server-Bk7ol2lA.mjs → server-DBFhIonT.mjs} +8 -4
- package/package.json +1 -1
- package/dist/cli.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/server-Bk7ol2lA.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -23,7 +23,8 @@ agent --list-models
|
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
26
|
-
Install the package globally, then confirm the command is on your
|
|
26
|
+
Install the package globally, then confirm the installed command is on your
|
|
27
|
+
`PATH`:
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
30
|
pnpm add -g cursor-agent-bridge
|
|
@@ -120,7 +121,7 @@ run `cursor-agent-bridge serve` yourself before starting Codex.
|
|
|
120
121
|
|
|
121
122
|
## Codex Config
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
For Codex CLI profile usage, create or update `~/.codex/cursor.config.toml`:
|
|
124
125
|
|
|
125
126
|
```bash
|
|
126
127
|
cursor-agent-bridge config write
|
|
@@ -159,6 +160,37 @@ codex --profile cursor
|
|
|
159
160
|
Use `/model` in Codex to pick a Cursor model. The model catalog comes from
|
|
160
161
|
`agent --list-models`.
|
|
161
162
|
|
|
163
|
+
## Codex IDE Switching
|
|
164
|
+
|
|
165
|
+
The Codex IDE extension reads the user-level `~/.codex/config.toml`. Switch that file when you want the IDE to use Cursor Agent Bridge:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
cursor-agent-bridge config switch cursor
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Switch back to Codex's default provider:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
cursor-agent-bridge config switch openai
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Check the active provider:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
cursor-agent-bridge config switch status
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The switch command backs up the previous `~/.codex/config.toml` to
|
|
184
|
+
`~/.codex/config.toml.bak.cursor-agent-bridge` before enabling Cursor. Restore
|
|
185
|
+
that backup with:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cursor-agent-bridge config switch restore
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Reload the Codex IDE window or start a new session after switching. Codex does
|
|
192
|
+
not reliably hot-reload provider changes while a session is running.
|
|
193
|
+
|
|
162
194
|
## Troubleshooting
|
|
163
195
|
|
|
164
196
|
Run a full preflight before starting Codex:
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as CursorRunner, n as engines, r as version, s as toOpenAIModelList, t as startServer } from "./server-
|
|
2
|
+
import { i as CursorRunner, n as engines, r as version, s as toOpenAIModelList, t as startServer } from "./server-DBFhIonT.mjs";
|
|
3
3
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
4
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { copyFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { dirname, join, resolve } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
@@ -29,10 +29,19 @@ function readHostAndPort(defaultHost = "127.0.0.1", defaultPort = 4646) {
|
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/codex-config.ts
|
|
31
31
|
const DEFAULT_CODEX_PROFILE = "cursor";
|
|
32
|
+
const OPENAI_PROVIDER = "openai";
|
|
33
|
+
const USER_CODEX_CONFIG_FILE = "config.toml";
|
|
34
|
+
const SWITCH_BACKUP_SUFFIX = ".bak.cursor-agent-bridge";
|
|
32
35
|
function resolveCodexConfigPath(profile = DEFAULT_CODEX_PROFILE, homeDir = homedir()) {
|
|
33
36
|
assertValidProfile(profile);
|
|
34
37
|
return join(homeDir, ".codex", `${profile}.config.toml`);
|
|
35
38
|
}
|
|
39
|
+
function resolveCodexUserConfigPath(homeDir = homedir()) {
|
|
40
|
+
return join(homeDir, ".codex", USER_CODEX_CONFIG_FILE);
|
|
41
|
+
}
|
|
42
|
+
function resolveCodexSwitchBackupPath(filePath) {
|
|
43
|
+
return `${filePath}${SWITCH_BACKUP_SUFFIX}`;
|
|
44
|
+
}
|
|
36
45
|
function buildBaseUrl(host, port) {
|
|
37
46
|
return `http://${host}:${port}/v1`;
|
|
38
47
|
}
|
|
@@ -64,7 +73,7 @@ function parseCodexConfig(content, profile = DEFAULT_CODEX_PROFILE) {
|
|
|
64
73
|
}
|
|
65
74
|
const assignment = line.match(/^([A-Za-z0-9_.-]+)\s*=\s*(.+)$/);
|
|
66
75
|
if (!assignment) continue;
|
|
67
|
-
const key = assignment[1]
|
|
76
|
+
const key = assignment[1];
|
|
68
77
|
const value = parseTomlValue(assignment[2] ?? "");
|
|
69
78
|
if (section === providerSection) {
|
|
70
79
|
if (key === "name") fields.providerName = value;
|
|
@@ -121,6 +130,86 @@ async function writeCodexConfig(options) {
|
|
|
121
130
|
updated: true
|
|
122
131
|
};
|
|
123
132
|
}
|
|
133
|
+
async function getCodexProviderStatus(filePath) {
|
|
134
|
+
const content = await readCodexConfigIfExists(filePath);
|
|
135
|
+
return content === void 0 ? {
|
|
136
|
+
path: filePath,
|
|
137
|
+
exists: false,
|
|
138
|
+
modelProvider: void 0
|
|
139
|
+
} : {
|
|
140
|
+
path: filePath,
|
|
141
|
+
exists: true,
|
|
142
|
+
modelProvider: parseCodexConfig(content).modelProvider
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async function switchCodexProvider(options) {
|
|
146
|
+
const existing = await readCodexConfigIfExists(options.filePath);
|
|
147
|
+
const backupPath = resolveCodexSwitchBackupPath(options.filePath);
|
|
148
|
+
if (options.mode === "cursor") {
|
|
149
|
+
await mkdir(dirname(options.filePath), { recursive: true });
|
|
150
|
+
const existingProvider = existing === void 0 ? void 0 : parseCodexConfig(existing).modelProvider;
|
|
151
|
+
if (existing !== void 0 && existingProvider !== "cursor") await copyFile(options.filePath, backupPath);
|
|
152
|
+
const merged = existing === void 0 ? {
|
|
153
|
+
content: buildCodexConfigToml(options),
|
|
154
|
+
changed: true
|
|
155
|
+
} : mergeCodexConfig(existing, {
|
|
156
|
+
...options,
|
|
157
|
+
force: true
|
|
158
|
+
});
|
|
159
|
+
if ("error" in merged) throw new Error(merged.error);
|
|
160
|
+
if (!merged.changed && existing !== void 0) return {
|
|
161
|
+
path: options.filePath,
|
|
162
|
+
mode: "cursor",
|
|
163
|
+
changed: false,
|
|
164
|
+
...existingProvider !== "cursor" && existing !== void 0 ? { backupPath } : {}
|
|
165
|
+
};
|
|
166
|
+
await writeFileAtomic(options.filePath, merged.content);
|
|
167
|
+
return {
|
|
168
|
+
path: options.filePath,
|
|
169
|
+
mode: "cursor",
|
|
170
|
+
changed: true,
|
|
171
|
+
...existingProvider !== "cursor" && existing !== void 0 ? { backupPath } : {}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const backup = await readCodexConfigIfExists(backupPath);
|
|
175
|
+
if (backup !== void 0) {
|
|
176
|
+
await writeFileAtomic(options.filePath, backup);
|
|
177
|
+
return {
|
|
178
|
+
path: options.filePath,
|
|
179
|
+
mode: OPENAI_PROVIDER,
|
|
180
|
+
changed: normalizeToml(existing ?? "") !== normalizeToml(backup),
|
|
181
|
+
backupPath,
|
|
182
|
+
restoredBackup: true
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const content = removeCursorProviderSelection(existing ?? "");
|
|
186
|
+
if (normalizeToml(content) === normalizeToml(existing ?? "")) return {
|
|
187
|
+
path: options.filePath,
|
|
188
|
+
mode: OPENAI_PROVIDER,
|
|
189
|
+
changed: false,
|
|
190
|
+
restoredBackup: false
|
|
191
|
+
};
|
|
192
|
+
await mkdir(dirname(options.filePath), { recursive: true });
|
|
193
|
+
await writeFileAtomic(options.filePath, content);
|
|
194
|
+
return {
|
|
195
|
+
path: options.filePath,
|
|
196
|
+
mode: OPENAI_PROVIDER,
|
|
197
|
+
changed: true,
|
|
198
|
+
restoredBackup: false
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function restoreCodexProviderBackup(filePath) {
|
|
202
|
+
const backupPath = resolveCodexSwitchBackupPath(filePath);
|
|
203
|
+
const backup = await readCodexConfigIfExists(backupPath);
|
|
204
|
+
if (backup === void 0) throw new Error(`No switch backup found at ${backupPath}`);
|
|
205
|
+
const existing = await readCodexConfigIfExists(filePath);
|
|
206
|
+
await writeFileAtomic(filePath, backup);
|
|
207
|
+
return {
|
|
208
|
+
path: filePath,
|
|
209
|
+
backupPath,
|
|
210
|
+
changed: normalizeToml(existing ?? "") !== normalizeToml(backup)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
124
213
|
function mergeCodexConfig(existing, options) {
|
|
125
214
|
const profile = options.profile ?? "cursor";
|
|
126
215
|
assertValidProfile(profile);
|
|
@@ -206,6 +295,44 @@ function mergeCodexConfig(existing, options) {
|
|
|
206
295
|
if (!handledProviderKeys.has("wire_api")) output.push("wire_api = \"responses\"");
|
|
207
296
|
}
|
|
208
297
|
}
|
|
298
|
+
async function readCodexConfigIfExists(filePath) {
|
|
299
|
+
try {
|
|
300
|
+
return await readFile(filePath, "utf8");
|
|
301
|
+
} catch (error) {
|
|
302
|
+
if (error.code !== "ENOENT") throw error;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function writeFileAtomic(filePath, content) {
|
|
307
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
308
|
+
const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
309
|
+
await writeFile(tmpPath, content, "utf8");
|
|
310
|
+
await rename(tmpPath, filePath);
|
|
311
|
+
}
|
|
312
|
+
function removeCursorProviderSelection(existing) {
|
|
313
|
+
const lines = existing.split("\n");
|
|
314
|
+
const output = [];
|
|
315
|
+
let section = "";
|
|
316
|
+
for (const rawLine of lines) {
|
|
317
|
+
const trimmed = rawLine.trim();
|
|
318
|
+
const sectionMatch = trimmed.match(/^\[(.+)\]$/);
|
|
319
|
+
if (sectionMatch) {
|
|
320
|
+
section = sectionMatch[1];
|
|
321
|
+
output.push(rawLine);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const assignment = trimmed.match(/^([A-Za-z0-9_.-]+)\s*=\s*(.+)$/);
|
|
325
|
+
if (!section && assignment) {
|
|
326
|
+
const key = assignment[1];
|
|
327
|
+
const value = parseTomlValue(assignment[2]);
|
|
328
|
+
if (key === "model_provider" && value === "cursor") continue;
|
|
329
|
+
if (key === "model" && value === "auto") continue;
|
|
330
|
+
}
|
|
331
|
+
output.push(rawLine);
|
|
332
|
+
}
|
|
333
|
+
const content = output.join("\n").trimEnd();
|
|
334
|
+
return content ? `${content}\n` : "";
|
|
335
|
+
}
|
|
209
336
|
function assertValidProfile(profile) {
|
|
210
337
|
if (/^[A-Za-z0-9_-]+$/.test(profile)) return;
|
|
211
338
|
throw new Error("Invalid Codex profile. Use only letters, numbers, underscores, or hyphens.");
|
|
@@ -223,7 +350,7 @@ function parseTomlValue(raw) {
|
|
|
223
350
|
return trimmed;
|
|
224
351
|
}
|
|
225
352
|
function unescapeTomlString(value) {
|
|
226
|
-
|
|
353
|
+
const escapes = {
|
|
227
354
|
"\"": "\"",
|
|
228
355
|
"\\": "\\",
|
|
229
356
|
b: "\b",
|
|
@@ -231,7 +358,8 @@ function unescapeTomlString(value) {
|
|
|
231
358
|
n: "\n",
|
|
232
359
|
f: "\f",
|
|
233
360
|
r: "\r"
|
|
234
|
-
}
|
|
361
|
+
};
|
|
362
|
+
return value.replace(/\\(["\\btnfr])/g, (_match, escaped) => escapes[escaped]);
|
|
235
363
|
}
|
|
236
364
|
function stripInlineTomlComment(raw) {
|
|
237
365
|
let quote;
|
|
@@ -690,6 +818,7 @@ Usage:
|
|
|
690
818
|
cursor-agent-bridge config print [--host 127.0.0.1] [--port 4646] [--profile cursor]
|
|
691
819
|
cursor-agent-bridge config check [--file ~/.codex/cursor.config.toml] [--host 127.0.0.1] [--port 4646] [--profile cursor]
|
|
692
820
|
cursor-agent-bridge config write [--file ~/.codex/cursor.config.toml] [--host 127.0.0.1] [--port 4646] [--profile cursor] [--force]
|
|
821
|
+
cursor-agent-bridge config switch cursor|openai|status|restore [--file ~/.codex/config.toml] [--host 127.0.0.1] [--port 4646]
|
|
693
822
|
cursor-agent-bridge models [--json] [--refresh]
|
|
694
823
|
cursor-agent-bridge launch-agent install [--host 127.0.0.1] [--port 4646] [--agent-path agent]
|
|
695
824
|
cursor-agent-bridge launch-agent uninstall
|
|
@@ -783,6 +912,39 @@ if (command === "config") {
|
|
|
783
912
|
console.log(`Start Codex with: codex --profile ${profile}`);
|
|
784
913
|
process.exit(0);
|
|
785
914
|
}
|
|
915
|
+
if (action === "switch") {
|
|
916
|
+
const mode = process.argv[4] ?? "status";
|
|
917
|
+
const switchFilePath = readArg("--file", void 0) ?? resolveCodexUserConfigPath();
|
|
918
|
+
if (mode === "status") {
|
|
919
|
+
const status = await getCodexProviderStatus(switchFilePath);
|
|
920
|
+
const provider = status.modelProvider ?? "default";
|
|
921
|
+
console.log(`Codex config: ${status.path}`);
|
|
922
|
+
console.log(status.exists ? `Current model_provider: ${provider}` : "Current model_provider: default (config file not found)");
|
|
923
|
+
console.log(`Switch backup: ${resolveCodexSwitchBackupPath(status.path)}`);
|
|
924
|
+
process.exit(0);
|
|
925
|
+
}
|
|
926
|
+
if (mode === "restore") {
|
|
927
|
+
const result = await restoreCodexProviderBackup(switchFilePath);
|
|
928
|
+
console.log(result.changed ? `Restored Codex config from ${result.backupPath}` : `Codex config already matches ${result.backupPath}`);
|
|
929
|
+
console.log("Reload Codex IDE or start a new session to apply it.");
|
|
930
|
+
process.exit(0);
|
|
931
|
+
}
|
|
932
|
+
if (mode !== "cursor" && mode !== "openai") {
|
|
933
|
+
console.error("Unknown config switch target. Use cursor, openai, status, or restore.");
|
|
934
|
+
process.exit(1);
|
|
935
|
+
}
|
|
936
|
+
const result = await switchCodexProvider({
|
|
937
|
+
filePath: switchFilePath,
|
|
938
|
+
host,
|
|
939
|
+
port,
|
|
940
|
+
mode
|
|
941
|
+
});
|
|
942
|
+
console.log(result.changed ? `Switched Codex config to ${mode}: ${result.path}` : `Codex config already uses ${mode}: ${result.path}`);
|
|
943
|
+
if (result.backupPath) console.log(`Backup: ${result.backupPath}`);
|
|
944
|
+
if (mode === "cursor") console.log(`Make sure the bridge is running: cursor-agent-bridge serve --host ${host} --port ${port}`);
|
|
945
|
+
console.log("Reload Codex IDE or start a new session to apply it.");
|
|
946
|
+
process.exit(0);
|
|
947
|
+
}
|
|
786
948
|
} catch (error) {
|
|
787
949
|
console.error(error instanceof Error ? error.message : String(error));
|
|
788
950
|
process.exit(1);
|
|
@@ -861,5 +1023,3 @@ process.on("SIGINT", shutdown);
|
|
|
861
1023
|
process.on("SIGTERM", shutdown);
|
|
862
1024
|
//#endregion
|
|
863
1025
|
export {};
|
|
864
|
-
|
|
865
|
-
//# sourceMappingURL=cli.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -143,5 +143,4 @@ declare class CursorRunner {
|
|
|
143
143
|
//#region src/server.d.ts
|
|
144
144
|
declare function startServer(config?: ServerConfig): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
145
145
|
//#endregion
|
|
146
|
-
export { type BridgeModel, type ChatCompletionRequest, type ChatMessage, type ChatRole, type CursorRunEvents, type CursorRunOptions, type CursorRunResult, CursorRunner, type ResponsesRequest, type ServerConfig, messagesToPrompt, normalizeModel, parseAgentModelList, responsesToMessages, startServer, toCodexModelCatalog, toOpenAIModelList };
|
|
147
|
-
//# sourceMappingURL=index.d.mts.map
|
|
146
|
+
export { type BridgeModel, type ChatCompletionRequest, type ChatMessage, type ChatRole, type CursorRunEvents, type CursorRunOptions, type CursorRunResult, CursorRunner, type ResponsesRequest, type ServerConfig, messagesToPrompt, normalizeModel, parseAgentModelList, responsesToMessages, startServer, toCodexModelCatalog, toOpenAIModelList };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as parseAgentModelList, c as messagesToPrompt, i as CursorRunner, l as normalizeModel, o as toCodexModelCatalog, s as toOpenAIModelList, t as startServer, u as responsesToMessages } from "./server-
|
|
1
|
+
import { a as parseAgentModelList, c as messagesToPrompt, i as CursorRunner, l as normalizeModel, o as toCodexModelCatalog, s as toOpenAIModelList, t as startServer, u as responsesToMessages } from "./server-DBFhIonT.mjs";
|
|
2
2
|
export { CursorRunner, messagesToPrompt, normalizeModel, parseAgentModelList, responsesToMessages, startServer, toCodexModelCatalog, toOpenAIModelList };
|
|
@@ -328,7 +328,7 @@ function extractAssistantText(message) {
|
|
|
328
328
|
}
|
|
329
329
|
//#endregion
|
|
330
330
|
//#region package.json
|
|
331
|
-
var version = "0.1.
|
|
331
|
+
var version = "0.1.5";
|
|
332
332
|
var engines = { "node": ">=22.13" };
|
|
333
333
|
//#endregion
|
|
334
334
|
//#region src/adapter/openai.ts
|
|
@@ -541,7 +541,7 @@ async function route(req, res, runner, maxBodyBytes) {
|
|
|
541
541
|
}
|
|
542
542
|
if (req.method === "GET" && url.pathname === "/v1/models") {
|
|
543
543
|
const models = await runner.listModels({ refresh: url.searchParams.get("refresh") === "1" });
|
|
544
|
-
sendJson(res, 200,
|
|
544
|
+
sendJson(res, 200, shouldReturnCodexModelCatalog(req, url) ? toCodexModelCatalog(models) : toOpenAIModelList(models));
|
|
545
545
|
return;
|
|
546
546
|
}
|
|
547
547
|
if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
|
|
@@ -558,6 +558,12 @@ async function route(req, res, runner, maxBodyBytes) {
|
|
|
558
558
|
code: "not_found"
|
|
559
559
|
} });
|
|
560
560
|
}
|
|
561
|
+
function shouldReturnCodexModelCatalog(req, url) {
|
|
562
|
+
if (url.searchParams.has("client_version")) return true;
|
|
563
|
+
if (url.searchParams.get("format") === "codex") return true;
|
|
564
|
+
const userAgent = req.headers["user-agent"];
|
|
565
|
+
return typeof userAgent === "string" && /\bCodex\b/i.test(userAgent);
|
|
566
|
+
}
|
|
561
567
|
async function handleChat(req, res, runner, maxBodyBytes) {
|
|
562
568
|
const body = await readJson(req, maxBodyBytes);
|
|
563
569
|
if (!Array.isArray(body.messages) || body.messages.length === 0) {
|
|
@@ -745,5 +751,3 @@ function sendJson(res, status, value) {
|
|
|
745
751
|
}
|
|
746
752
|
//#endregion
|
|
747
753
|
export { parseAgentModelList as a, messagesToPrompt as c, CursorRunner as i, normalizeModel as l, engines as n, toCodexModelCatalog as o, version as r, toOpenAIModelList as s, startServer as t, responsesToMessages as u };
|
|
748
|
-
|
|
749
|
-
//# sourceMappingURL=server-Bk7ol2lA.mjs.map
|
package/package.json
CHANGED
package/dist/cli.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["execFileAsync","packageJson.version","packageJson.version"],"sources":["../src/cli-args.ts","../src/codex-config.ts","../src/doctor.ts","../src/launch-agent.ts","../src/upgrade.ts","../src/cli.ts"],"sourcesContent":["export function 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\nexport function 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\nexport function readHostAndPort(defaultHost = \"127.0.0.1\", defaultPort = 4646) {\n const host = readArg(\"--host\", process.env.HOST) ?? defaultHost\n const port = parsePort(readArg(\"--port\", process.env.PORT), defaultPort)\n return { host, port }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { dirname, join } from \"node:path\"\n\nexport const DEFAULT_CODEX_PROFILE = \"cursor\"\n\nexport type CodexConfigOptions = {\n host: string\n port: number\n profile?: string\n}\n\nexport type CodexConfigFields = {\n modelProvider?: string\n model?: string\n providerName?: string\n baseUrl?: string\n wireApi?: string\n}\n\nexport type CodexConfigCheckResult = {\n ok: boolean\n issues: string[]\n}\n\nexport type WriteCodexConfigResult = {\n path: string\n created: boolean\n updated: boolean\n}\n\nexport function resolveCodexConfigPath(\n profile = DEFAULT_CODEX_PROFILE,\n homeDir = homedir(),\n) {\n assertValidProfile(profile)\n return join(homeDir, \".codex\", `${profile}.config.toml`)\n}\n\nexport function buildBaseUrl(host: string, port: number) {\n return `http://${host}:${port}/v1`\n}\n\nexport function buildCodexConfigToml(options: CodexConfigOptions) {\n const profile = options.profile ?? DEFAULT_CODEX_PROFILE\n assertValidProfile(profile)\n const baseUrl = buildBaseUrl(options.host, options.port)\n return `model_provider = ${formatTomlString(profile)}\nmodel = \"auto\"\n\n[model_providers.${profile}]\nname = \"Cursor Agent Bridge\"\nbase_url = ${formatTomlString(baseUrl)}\nwire_api = \"responses\"\n`\n}\n\nexport function parseCodexConfig(\n content: string,\n profile = DEFAULT_CODEX_PROFILE,\n) {\n assertValidProfile(profile)\n const fields: CodexConfigFields = {}\n const providerSection = `model_providers.${profile}`\n let section = \"\"\n\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim()\n if (!line || line.startsWith(\"#\")) continue\n\n const sectionMatch = line.match(/^\\[(.+)\\]$/)\n if (sectionMatch) {\n section = sectionMatch[1] ?? \"\"\n continue\n }\n\n const assignment = line.match(/^([A-Za-z0-9_.-]+)\\s*=\\s*(.+)$/)\n if (!assignment) continue\n\n const key = assignment[1] ?? \"\"\n const value = parseTomlValue(assignment[2] ?? \"\")\n\n if (section === providerSection) {\n if (key === \"name\") fields.providerName = value\n if (key === \"base_url\") fields.baseUrl = value\n if (key === \"wire_api\") fields.wireApi = value\n continue\n }\n\n if (section) continue\n if (key === \"model_provider\") fields.modelProvider = value\n if (key === \"model\") fields.model = value\n }\n\n return fields\n}\n\nexport function checkCodexConfig(\n content: string,\n options: CodexConfigOptions,\n): CodexConfigCheckResult {\n const profile = options.profile ?? DEFAULT_CODEX_PROFILE\n const expectedBaseUrl = buildBaseUrl(options.host, options.port)\n const fields = parseCodexConfig(content, profile)\n const issues: string[] = []\n\n if (fields.modelProvider !== profile) {\n issues.push(\n `model_provider should be \"${profile}\"${\n fields.modelProvider\n ? `, found \"${fields.modelProvider}\"`\n : \", but it is missing\"\n }`,\n )\n }\n\n if (fields.baseUrl !== expectedBaseUrl) {\n issues.push(\n `base_url should be \"${expectedBaseUrl}\"${\n fields.baseUrl ? `, found \"${fields.baseUrl}\"` : \", but it is missing\"\n }`,\n )\n }\n\n if (fields.wireApi !== \"responses\") {\n issues.push(\n `wire_api should be \"responses\"${\n fields.wireApi ? `, found \"${fields.wireApi}\"` : \", but it is missing\"\n }`,\n )\n }\n\n return { ok: issues.length === 0, issues }\n}\n\nexport async function writeCodexConfig(\n options: CodexConfigOptions & {\n filePath: string\n force?: boolean\n },\n): Promise<WriteCodexConfigResult> {\n let existing: string | undefined\n\n try {\n existing = await readFile(options.filePath, \"utf8\")\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error\n }\n\n if (existing === undefined) {\n await mkdir(dirname(options.filePath), { recursive: true })\n await writeFile(options.filePath, buildCodexConfigToml(options), \"utf8\")\n return { path: options.filePath, created: true, updated: false }\n }\n\n const merged = mergeCodexConfig(existing, options)\n if (\"error\" in merged) throw new Error(merged.error)\n\n if (!merged.changed) {\n return { path: options.filePath, created: false, updated: false }\n }\n\n await writeFile(options.filePath, merged.content, \"utf8\")\n return { path: options.filePath, created: false, updated: true }\n}\n\nfunction mergeCodexConfig(\n existing: string,\n options: CodexConfigOptions & { force?: boolean },\n) {\n const profile = options.profile ?? DEFAULT_CODEX_PROFILE\n assertValidProfile(profile)\n const providerSection = `model_providers.${profile}`\n const expected = buildCodexConfigToml(options)\n const parsed = parseCodexConfig(existing, profile)\n\n if (\n parsed.modelProvider &&\n parsed.modelProvider !== profile &&\n !options.force\n ) {\n return {\n error: `model_provider is \"${parsed.modelProvider}\". Re-run with --force to switch it to \"${profile}\".`,\n }\n }\n\n const lines = existing.split(\"\\n\")\n const output: string[] = []\n let section = \"\"\n let inProviderSection = false\n let sawModelProvider = false\n let sawModel = false\n let sawProviderSection = false\n const handledProviderKeys = new Set<string>()\n\n for (const rawLine of lines) {\n const trimmed = rawLine.trim()\n const sectionMatch = trimmed.match(/^\\[(.+)\\]$/)\n if (sectionMatch) {\n if (inProviderSection) appendMissingProviderKeys()\n section = sectionMatch[1] ?? \"\"\n inProviderSection = section === providerSection\n if (inProviderSection) sawProviderSection = true\n output.push(rawLine)\n continue\n }\n\n const assignment = trimmed.match(/^([A-Za-z0-9_.-]+)\\s*=\\s*(.+)$/)\n if (!assignment) {\n output.push(rawLine)\n continue\n }\n\n const key = assignment[1] ?? \"\"\n\n if (!section) {\n if (key === \"model_provider\") {\n sawModelProvider = true\n output.push(`model_provider = ${formatTomlString(profile)}`)\n continue\n }\n if (key === \"model\") {\n sawModel = true\n if (parsed.model === undefined || options.force) {\n output.push('model = \"auto\"')\n } else {\n output.push(rawLine)\n }\n continue\n }\n }\n\n if (inProviderSection) {\n if (key === \"name\") {\n handledProviderKeys.add(\"name\")\n output.push('name = \"Cursor Agent Bridge\"')\n continue\n }\n if (key === \"base_url\") {\n handledProviderKeys.add(\"base_url\")\n output.push(\n `base_url = ${formatTomlString(buildBaseUrl(options.host, options.port))}`,\n )\n continue\n }\n if (key === \"wire_api\") {\n handledProviderKeys.add(\"wire_api\")\n output.push('wire_api = \"responses\"')\n continue\n }\n }\n\n output.push(rawLine)\n }\n\n if (inProviderSection) appendMissingProviderKeys()\n\n const missingTopLevel: string[] = []\n if (!sawModelProvider)\n missingTopLevel.push(`model_provider = ${formatTomlString(profile)}`)\n if (!sawModel) missingTopLevel.push('model = \"auto\"')\n\n let content = output.join(\"\\n\").trimEnd()\n if (missingTopLevel.length > 0) {\n content = `${missingTopLevel.join(\"\\n\")}\\n${content}`\n }\n\n if (!sawProviderSection) {\n const providerBlock = expected.split(\"\\n\").slice(3).join(\"\\n\")\n content = `${content}\\n\\n${providerBlock}\\n`\n }\n\n const changed = normalizeToml(content) !== normalizeToml(existing)\n return { content: `${content.trimEnd()}\\n`, changed }\n\n function appendMissingProviderKeys() {\n if (!handledProviderKeys.has(\"name\")) {\n output.push('name = \"Cursor Agent Bridge\"')\n }\n if (!handledProviderKeys.has(\"base_url\")) {\n output.push(\n `base_url = ${formatTomlString(buildBaseUrl(options.host, options.port))}`,\n )\n }\n if (!handledProviderKeys.has(\"wire_api\")) {\n output.push('wire_api = \"responses\"')\n }\n }\n}\n\nfunction assertValidProfile(profile: string) {\n if (/^[A-Za-z0-9_-]+$/.test(profile)) return\n throw new Error(\n \"Invalid Codex profile. Use only letters, numbers, underscores, or hyphens.\",\n )\n}\n\nfunction formatTomlString(value: string) {\n return `\"${value\n .replaceAll(\"\\\\\", \"\\\\\\\\\")\n .replaceAll('\"', '\\\\\"')\n .replaceAll(\"\\b\", \"\\\\b\")\n .replaceAll(\"\\t\", \"\\\\t\")\n .replaceAll(\"\\n\", \"\\\\n\")\n .replaceAll(\"\\f\", \"\\\\f\")\n .replaceAll(\"\\r\", \"\\\\r\")}\"`\n}\n\nfunction normalizeToml(content: string) {\n return content.replace(/\\r\\n/g, \"\\n\").trimEnd()\n}\n\nfunction parseTomlValue(raw: string) {\n const trimmed = stripInlineTomlComment(raw).trim()\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) {\n return unescapeTomlString(trimmed.slice(1, -1))\n }\n if (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\")) {\n return trimmed.slice(1, -1)\n }\n return trimmed\n}\n\nfunction unescapeTomlString(value: string) {\n return value.replace(\n /\\\\([\"\\\\btnfr])/g,\n (_match, escaped: string) =>\n ({\n '\"': '\"',\n \"\\\\\": \"\\\\\",\n b: \"\\b\",\n t: \"\\t\",\n n: \"\\n\",\n f: \"\\f\",\n r: \"\\r\",\n })[escaped] ?? escaped,\n )\n}\n\nfunction stripInlineTomlComment(raw: string) {\n let quote: string | undefined\n for (let index = 0; index < raw.length; index += 1) {\n const char = raw[index]\n if ((char === '\"' || char === \"'\") && raw[index - 1] !== \"\\\\\") {\n quote = quote === char ? undefined : (quote ?? char)\n continue\n }\n\n if (!quote && char === \"#\") {\n return raw.slice(0, index)\n }\n }\n\n return raw\n}\n","import { execFile } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport { promisify } from \"node:util\"\nimport packageJson from \"../package.json\" with { type: \"json\" }\nimport {\n checkCodexConfig,\n DEFAULT_CODEX_PROFILE,\n resolveCodexConfigPath,\n} from \"./codex-config.js\"\nimport { CursorRunner } from \"./cursor/runner.js\"\n\nconst execFileAsync = promisify(execFile)\n\nexport type DoctorCheck = {\n name: string\n ok: boolean\n message: string\n hint?: string\n}\n\nexport type ExecFileFn = (\n file: string,\n args?: readonly string[] | null,\n options?: { timeout?: number },\n) => Promise<unknown>\n\nexport type DoctorOptions = {\n host: string\n port: number\n agentPath?: string\n profile?: string\n nodeVersionRange?: string\n codexConfigPath?: string\n skipCodexConfig?: boolean\n runner?: CursorRunner\n fetchFn?: typeof fetch\n execFileFn?: ExecFileFn\n readFileFn?: typeof readFile\n}\n\nexport async function runDoctor(options: DoctorOptions) {\n const checks: DoctorCheck[] = []\n const fetchFn = options.fetchFn ?? fetch\n const execFileFn = options.execFileFn ?? execFileAsync\n const readFileFn = options.readFileFn ?? readFile\n const agentPath =\n options.agentPath ?? process.env.CURSOR_AGENT_PATH ?? \"agent\"\n const profile = options.profile ?? DEFAULT_CODEX_PROFILE\n const codexConfigPath =\n options.codexConfigPath ?? resolveCodexConfigPath(profile)\n\n checks.push(\n checkNodeVersion(options.nodeVersionRange ?? packageJson.engines.node),\n )\n checks.push({\n name: \"bridge-version\",\n ok: true,\n message: `cursor-agent-bridge ${packageJson.version}`,\n })\n\n const agentCheck = await checkAgentExecutable(agentPath, execFileFn)\n checks.push(agentCheck)\n\n if (agentCheck.ok) {\n checks.push(await checkAgentLogin(agentPath, options.runner))\n }\n\n checks.push(await checkBridgeHealth(options.host, options.port, fetchFn))\n\n if (!options.skipCodexConfig) {\n checks.push(\n await checkCodexConfigFile({\n codexConfigPath,\n host: options.host,\n port: options.port,\n profile,\n readFileFn,\n }),\n )\n }\n\n return {\n ok: checks.every((check) => check.ok),\n checks,\n }\n}\n\nexport function formatDoctorReport(result: {\n ok: boolean\n checks: DoctorCheck[]\n}) {\n const lines = result.checks.map((check) => {\n const prefix = check.ok ? \"✓\" : \"✗\"\n const hint = check.hint ? `\\n → ${check.hint}` : \"\"\n return `${prefix} ${check.name}: ${check.message}${hint}`\n })\n\n lines.push(\"\")\n lines.push(\n result.ok\n ? \"All checks passed. Codex can use Cursor Agent through the bridge.\"\n : \"Some checks failed. Fix the items above before starting Codex.\",\n )\n\n return `${lines.join(\"\\n\")}\\n`\n}\n\nfunction checkNodeVersion(requiredRange: string): DoctorCheck {\n const current = process.version.slice(1)\n const minimum = parseMinimumNodeVersion(requiredRange)\n const ok = compareNodeVersion(current, minimum) >= 0\n\n return ok\n ? {\n name: \"node-version\",\n ok,\n message: `Node ${current} satisfies ${requiredRange}`,\n }\n : {\n name: \"node-version\",\n ok,\n message: `Node ${current} does not satisfy ${requiredRange}`,\n hint: `Install Node ${minimum} or newer.`,\n }\n}\n\nasync function checkAgentExecutable(\n agentPath: string,\n execFileFn: ExecFileFn,\n): Promise<DoctorCheck> {\n try {\n await execFileFn(agentPath, [\"--help\"], { timeout: 5_000 })\n return {\n name: \"agent-cli\",\n ok: true,\n message: `Cursor Agent CLI found at ${agentPath}`,\n }\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Cursor Agent CLI not found\"\n return {\n name: \"agent-cli\",\n ok: false,\n message,\n hint: \"Install the Cursor Agent CLI and ensure `agent` is on PATH, or set CURSOR_AGENT_PATH.\",\n }\n }\n}\n\nasync function checkAgentLogin(\n agentPath: string,\n runner = new CursorRunner({ agentPath }),\n): Promise<DoctorCheck> {\n try {\n const models = await runner.listModels({\n refresh: true,\n })\n if (models.length === 0) {\n return {\n name: \"agent-login\",\n ok: false,\n message: \"Cursor Agent responded, but returned no models\",\n hint: \"Run `agent login` and confirm `agent --list-models` returns models.\",\n }\n }\n\n return {\n name: \"agent-login\",\n ok: true,\n message: `Cursor Agent is logged in (${models.length} models available)`,\n }\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Cursor Agent login check failed\"\n return {\n name: \"agent-login\",\n ok: false,\n message,\n hint: \"Run `agent login` and retry `cursor-agent-bridge doctor`.\",\n }\n }\n}\n\nasync function checkBridgeHealth(\n host: string,\n port: number,\n fetchFn: typeof fetch,\n): Promise<DoctorCheck> {\n const url = `http://${host}:${port}/health`\n try {\n const response = await fetchFn(url, { signal: AbortSignal.timeout(3_000) })\n if (!response.ok) {\n return {\n name: \"bridge-health\",\n ok: false,\n message: `${url} returned HTTP ${response.status}`,\n hint: \"Start the bridge with `cursor-agent-bridge serve` or `cursor-agent-bridge launch-agent install`.\",\n }\n }\n\n const payload = (await response.json()) as { version?: string }\n return {\n name: \"bridge-health\",\n ok: true,\n message: payload.version\n ? `Bridge is listening on ${url} (version ${payload.version})`\n : `Bridge is listening on ${url}`,\n }\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Bridge health check failed\"\n return {\n name: \"bridge-health\",\n ok: false,\n message,\n hint: \"Start the bridge with `cursor-agent-bridge serve` or `cursor-agent-bridge launch-agent install`.\",\n }\n }\n}\n\nasync function checkCodexConfigFile(options: {\n codexConfigPath: string\n host: string\n port: number\n profile: string\n readFileFn: typeof readFile\n}): Promise<DoctorCheck> {\n try {\n const content = await options.readFileFn(options.codexConfigPath, \"utf8\")\n const result = checkCodexConfig(content, {\n host: options.host,\n port: options.port,\n profile: options.profile,\n })\n\n if (result.ok) {\n return {\n name: \"codex-config\",\n ok: true,\n message: `Codex config looks correct at ${options.codexConfigPath}`,\n }\n }\n\n return {\n name: \"codex-config\",\n ok: false,\n message: result.issues.join(\"; \"),\n hint: \"Run `cursor-agent-bridge config write` or `cursor-agent-bridge config print`.\",\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n name: \"codex-config\",\n ok: false,\n message: `Codex config not found at ${options.codexConfigPath}`,\n hint: \"Run `cursor-agent-bridge config write` to create it.\",\n }\n }\n\n const message =\n error instanceof Error ? error.message : \"Codex config check failed\"\n return {\n name: \"codex-config\",\n ok: false,\n message,\n hint: \"Verify the Codex config path and file permissions.\",\n }\n }\n}\n\nfunction parseMinimumNodeVersion(range: string) {\n const match = range.match(/(\\d+)\\.(\\d+)(?:\\.(\\d+))?/)\n if (!match) return \"0.0.0\"\n return `${match[1]}.${match[2]}.${match[3] ?? 0}`\n}\n\nfunction compareNodeVersion(left: string, right: string) {\n const toParts = (value: string) =>\n value.split(\".\").map((part) => Number.parseInt(part, 10) || 0)\n const [leftMajor = 0, leftMinor = 0, leftPatch = 0] = toParts(left)\n const [rightMajor = 0, rightMinor = 0, rightPatch = 0] = toParts(right)\n\n if (leftMajor !== rightMajor) return leftMajor - rightMajor\n if (leftMinor !== rightMinor) return leftMinor - rightMinor\n return leftPatch - rightPatch\n}\n","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(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\")\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 { readFile } from \"node:fs/promises\"\nimport packageJson from \"../package.json\" with { type: \"json\" }\nimport { toOpenAIModelList } from \"./adapter/models.js\"\nimport { readArg, readHostAndPort } from \"./cli-args.js\"\nimport {\n buildCodexConfigToml,\n checkCodexConfig,\n DEFAULT_CODEX_PROFILE,\n resolveCodexConfigPath,\n writeCodexConfig,\n} from \"./codex-config.js\"\nimport { CursorRunner } from \"./cursor/runner.js\"\nimport { formatDoctorReport, runDoctor } from \"./doctor.js\"\nimport {\n installLaunchAgent,\n printLaunchAgentStatus,\n uninstallLaunchAgent,\n} from \"./launch-agent.js\"\nimport { startServer } from \"./server.js\"\nimport { runUpgrade } from \"./upgrade.js\"\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 doctor [--host 127.0.0.1] [--port 4646] [--profile cursor] [--file ~/.codex/cursor.config.toml] [--skip-codex-config]\n cursor-agent-bridge config print [--host 127.0.0.1] [--port 4646] [--profile cursor]\n cursor-agent-bridge config check [--file ~/.codex/cursor.config.toml] [--host 127.0.0.1] [--port 4646] [--profile cursor]\n cursor-agent-bridge config write [--file ~/.codex/cursor.config.toml] [--host 127.0.0.1] [--port 4646] [--profile cursor] [--force]\n cursor-agent-bridge models [--json] [--refresh]\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 === \"doctor\") {\n try {\n const { host, port } = readHostAndPort()\n const profile =\n readArg(\"--profile\", DEFAULT_CODEX_PROFILE) ?? DEFAULT_CODEX_PROFILE\n const codexConfigPath = readArg(\"--file\", undefined)\n const result = await runDoctor({\n host,\n port,\n profile,\n skipCodexConfig: process.argv.includes(\"--skip-codex-config\"),\n ...(process.env.CURSOR_AGENT_PATH\n ? { agentPath: process.env.CURSOR_AGENT_PATH }\n : {}),\n ...(codexConfigPath ? { codexConfigPath } : {}),\n })\n process.stdout.write(formatDoctorReport(result))\n process.exit(result.ok ? 0 : 1)\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n}\n\nif (command === \"config\") {\n const action = process.argv[3] ?? \"print\"\n try {\n const { host, port } = readHostAndPort()\n const profile =\n readArg(\"--profile\", DEFAULT_CODEX_PROFILE) ?? DEFAULT_CODEX_PROFILE\n const filePath =\n readArg(\"--file\", undefined) ?? resolveCodexConfigPath(profile)\n\n if (action === \"print\") {\n process.stdout.write(buildCodexConfigToml({ host, port, profile }))\n console.error(`Start Codex with: codex --profile ${profile}`)\n process.exit(0)\n }\n\n if (action === \"check\") {\n const content = await readFile(filePath, \"utf8\")\n const result = checkCodexConfig(content, { host, port, profile })\n if (result.ok) {\n console.log(`Codex config looks correct: ${filePath}`)\n process.exit(0)\n }\n\n for (const issue of result.issues) {\n console.error(issue)\n }\n process.exit(1)\n }\n\n if (action === \"write\") {\n const result = await writeCodexConfig({\n filePath,\n host,\n port,\n profile,\n force: process.argv.includes(\"--force\"),\n })\n\n if (result.created) {\n console.log(`Created Codex config at ${result.path}`)\n } else if (result.updated) {\n console.log(`Updated Codex config at ${result.path}`)\n } else {\n console.log(`Codex config already up to date at ${result.path}`)\n }\n\n console.log(`Start Codex with: codex --profile ${profile}`)\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 config action: ${action}`)\n process.exit(1)\n}\n\nif (command === \"models\") {\n try {\n const runner = new CursorRunner({\n ...(process.env.CURSOR_AGENT_PATH\n ? { agentPath: process.env.CURSOR_AGENT_PATH }\n : {}),\n })\n const models = await runner.listModels({\n refresh: process.argv.includes(\"--refresh\"),\n })\n\n if (process.argv.includes(\"--json\")) {\n console.log(JSON.stringify(toOpenAIModelList(models), null, 2))\n process.exit(0)\n }\n\n for (const model of models) {\n console.log(model.id)\n }\n process.exit(0)\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n}\n\nif (command === \"launch-agent\") {\n const action = process.argv[3] ?? \"status\"\n try {\n if (action === \"install\") {\n const { host, port } = readHostAndPort()\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 const options = readHostAndPort()\n host = options.host\n port = options.port\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":";;;;;;;;;AAAA,SAAgB,QAAQ,MAAc,UAA8B;CAClE,MAAM,QAAQ,QAAQ,KAAK,QAAQ,IAAI;CACvC,IAAI,QAAQ,GAAG,OAAO;CACtB,MAAM,QAAQ,QAAQ,KAAK,QAAQ;CACnC,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,GAChC,MAAM,IAAI,MAAM,qBAAqB,MAAM;CAC7C,OAAO;AACT;AAEA,SAAgB,UAAU,OAA2B,UAAkB;CACrE,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,OAAO,OAAO,KAAK;CACzB,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,OAChD,MAAM,IAAI,MAAM,iBAAiB,OAAO;CAE1C,OAAO;AACT;AAEA,SAAgB,gBAAgB,cAAc,aAAa,cAAc,MAAM;CAG7E,OAAO;EAAE,MAFI,QAAQ,UAAU,QAAQ,IAAI,IAAI,KAAK;EAErC,MADF,UAAU,QAAQ,UAAU,QAAQ,IAAI,IAAI,GAAG,WAC1C;CAAE;AACtB;;;AClBA,MAAa,wBAAwB;AA2BrC,SAAgB,uBACd,UAAU,uBACV,UAAU,QAAQ,GAClB;CACA,mBAAmB,OAAO;CAC1B,OAAO,KAAK,SAAS,UAAU,GAAG,QAAQ,aAAa;AACzD;AAEA,SAAgB,aAAa,MAAc,MAAc;CACvD,OAAO,UAAU,KAAK,GAAG,KAAK;AAChC;AAEA,SAAgB,qBAAqB,SAA6B;CAChE,MAAM,UAAU,QAAQ,WAAA;CACxB,mBAAmB,OAAO;CAC1B,MAAM,UAAU,aAAa,QAAQ,MAAM,QAAQ,IAAI;CACvD,OAAO,oBAAoB,iBAAiB,OAAO,EAAE;;;mBAGpC,QAAQ;;aAEd,iBAAiB,OAAO,EAAE;;;AAGvC;AAEA,SAAgB,iBACd,SACA,UAAU,uBACV;CACA,mBAAmB,OAAO;CAC1B,MAAM,SAA4B,CAAC;CACnC,MAAM,kBAAkB,mBAAmB;CAC3C,IAAI,UAAU;CAEd,KAAK,MAAM,WAAW,QAAQ,MAAM,IAAI,GAAG;EACzC,MAAM,OAAO,QAAQ,KAAK;EAC1B,IAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,GAAG;EAEnC,MAAM,eAAe,KAAK,MAAM,YAAY;EAC5C,IAAI,cAAc;GAChB,UAAU,aAAa,MAAM;GAC7B;EACF;EAEA,MAAM,aAAa,KAAK,MAAM,gCAAgC;EAC9D,IAAI,CAAC,YAAY;EAEjB,MAAM,MAAM,WAAW,MAAM;EAC7B,MAAM,QAAQ,eAAe,WAAW,MAAM,EAAE;EAEhD,IAAI,YAAY,iBAAiB;GAC/B,IAAI,QAAQ,QAAQ,OAAO,eAAe;GAC1C,IAAI,QAAQ,YAAY,OAAO,UAAU;GACzC,IAAI,QAAQ,YAAY,OAAO,UAAU;GACzC;EACF;EAEA,IAAI,SAAS;EACb,IAAI,QAAQ,kBAAkB,OAAO,gBAAgB;EACrD,IAAI,QAAQ,SAAS,OAAO,QAAQ;CACtC;CAEA,OAAO;AACT;AAEA,SAAgB,iBACd,SACA,SACwB;CACxB,MAAM,UAAU,QAAQ,WAAA;CACxB,MAAM,kBAAkB,aAAa,QAAQ,MAAM,QAAQ,IAAI;CAC/D,MAAM,SAAS,iBAAiB,SAAS,OAAO;CAChD,MAAM,SAAmB,CAAC;CAE1B,IAAI,OAAO,kBAAkB,SAC3B,OAAO,KACL,6BAA6B,QAAQ,GACnC,OAAO,gBACH,YAAY,OAAO,cAAc,KACjC,uBAER;CAGF,IAAI,OAAO,YAAY,iBACrB,OAAO,KACL,uBAAuB,gBAAgB,GACrC,OAAO,UAAU,YAAY,OAAO,QAAQ,KAAK,uBAErD;CAGF,IAAI,OAAO,YAAY,aACrB,OAAO,KACL,iCACE,OAAO,UAAU,YAAY,OAAO,QAAQ,KAAK,uBAErD;CAGF,OAAO;EAAE,IAAI,OAAO,WAAW;EAAG;CAAO;AAC3C;AAEA,eAAsB,iBACpB,SAIiC;CACjC,IAAI;CAEJ,IAAI;EACF,WAAW,MAAM,SAAS,QAAQ,UAAU,MAAM;CACpD,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,MAAM;CAChE;CAEA,IAAI,aAAa,KAAA,GAAW;EAC1B,MAAM,MAAM,QAAQ,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EAC1D,MAAM,UAAU,QAAQ,UAAU,qBAAqB,OAAO,GAAG,MAAM;EACvE,OAAO;GAAE,MAAM,QAAQ;GAAU,SAAS;GAAM,SAAS;EAAM;CACjE;CAEA,MAAM,SAAS,iBAAiB,UAAU,OAAO;CACjD,IAAI,WAAW,QAAQ,MAAM,IAAI,MAAM,OAAO,KAAK;CAEnD,IAAI,CAAC,OAAO,SACV,OAAO;EAAE,MAAM,QAAQ;EAAU,SAAS;EAAO,SAAS;CAAM;CAGlE,MAAM,UAAU,QAAQ,UAAU,OAAO,SAAS,MAAM;CACxD,OAAO;EAAE,MAAM,QAAQ;EAAU,SAAS;EAAO,SAAS;CAAK;AACjE;AAEA,SAAS,iBACP,UACA,SACA;CACA,MAAM,UAAU,QAAQ,WAAA;CACxB,mBAAmB,OAAO;CAC1B,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,WAAW,qBAAqB,OAAO;CAC7C,MAAM,SAAS,iBAAiB,UAAU,OAAO;CAEjD,IACE,OAAO,iBACP,OAAO,kBAAkB,WACzB,CAAC,QAAQ,OAET,OAAO,EACL,OAAO,sBAAsB,OAAO,cAAc,0CAA0C,QAAQ,IACtG;CAGF,MAAM,QAAQ,SAAS,MAAM,IAAI;CACjC,MAAM,SAAmB,CAAC;CAC1B,IAAI,UAAU;CACd,IAAI,oBAAoB;CACxB,IAAI,mBAAmB;CACvB,IAAI,WAAW;CACf,IAAI,qBAAqB;CACzB,MAAM,sCAAsB,IAAI,IAAY;CAE5C,KAAK,MAAM,WAAW,OAAO;EAC3B,MAAM,UAAU,QAAQ,KAAK;EAC7B,MAAM,eAAe,QAAQ,MAAM,YAAY;EAC/C,IAAI,cAAc;GAChB,IAAI,mBAAmB,0BAA0B;GACjD,UAAU,aAAa,MAAM;GAC7B,oBAAoB,YAAY;GAChC,IAAI,mBAAmB,qBAAqB;GAC5C,OAAO,KAAK,OAAO;GACnB;EACF;EAEA,MAAM,aAAa,QAAQ,MAAM,gCAAgC;EACjE,IAAI,CAAC,YAAY;GACf,OAAO,KAAK,OAAO;GACnB;EACF;EAEA,MAAM,MAAM,WAAW,MAAM;EAE7B,IAAI,CAAC,SAAS;GACZ,IAAI,QAAQ,kBAAkB;IAC5B,mBAAmB;IACnB,OAAO,KAAK,oBAAoB,iBAAiB,OAAO,GAAG;IAC3D;GACF;GACA,IAAI,QAAQ,SAAS;IACnB,WAAW;IACX,IAAI,OAAO,UAAU,KAAA,KAAa,QAAQ,OACxC,OAAO,KAAK,kBAAgB;SAE5B,OAAO,KAAK,OAAO;IAErB;GACF;EACF;EAEA,IAAI,mBAAmB;GACrB,IAAI,QAAQ,QAAQ;IAClB,oBAAoB,IAAI,MAAM;IAC9B,OAAO,KAAK,gCAA8B;IAC1C;GACF;GACA,IAAI,QAAQ,YAAY;IACtB,oBAAoB,IAAI,UAAU;IAClC,OAAO,KACL,cAAc,iBAAiB,aAAa,QAAQ,MAAM,QAAQ,IAAI,CAAC,GACzE;IACA;GACF;GACA,IAAI,QAAQ,YAAY;IACtB,oBAAoB,IAAI,UAAU;IAClC,OAAO,KAAK,0BAAwB;IACpC;GACF;EACF;EAEA,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,mBAAmB,0BAA0B;CAEjD,MAAM,kBAA4B,CAAC;CACnC,IAAI,CAAC,kBACH,gBAAgB,KAAK,oBAAoB,iBAAiB,OAAO,GAAG;CACtE,IAAI,CAAC,UAAU,gBAAgB,KAAK,kBAAgB;CAEpD,IAAI,UAAU,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ;CACxC,IAAI,gBAAgB,SAAS,GAC3B,UAAU,GAAG,gBAAgB,KAAK,IAAI,EAAE,IAAI;CAG9C,IAAI,CAAC,oBAAoB;EACvB,MAAM,gBAAgB,SAAS,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI;EAC7D,UAAU,GAAG,QAAQ,MAAM,cAAc;CAC3C;CAEA,MAAM,UAAU,cAAc,OAAO,MAAM,cAAc,QAAQ;CACjE,OAAO;EAAE,SAAS,GAAG,QAAQ,QAAQ,EAAE;EAAK;CAAQ;CAEpD,SAAS,4BAA4B;EACnC,IAAI,CAAC,oBAAoB,IAAI,MAAM,GACjC,OAAO,KAAK,gCAA8B;EAE5C,IAAI,CAAC,oBAAoB,IAAI,UAAU,GACrC,OAAO,KACL,cAAc,iBAAiB,aAAa,QAAQ,MAAM,QAAQ,IAAI,CAAC,GACzE;EAEF,IAAI,CAAC,oBAAoB,IAAI,UAAU,GACrC,OAAO,KAAK,0BAAwB;CAExC;AACF;AAEA,SAAS,mBAAmB,SAAiB;CAC3C,IAAI,mBAAmB,KAAK,OAAO,GAAG;CACtC,MAAM,IAAI,MACR,4EACF;AACF;AAEA,SAAS,iBAAiB,OAAe;CACvC,OAAO,IAAI,MACR,WAAW,MAAM,MAAM,CAAC,CACxB,WAAW,MAAK,MAAK,CAAC,CACtB,WAAW,MAAM,KAAK,CAAC,CACvB,WAAW,KAAM,KAAK,CAAC,CACvB,WAAW,MAAM,KAAK,CAAC,CACvB,WAAW,MAAM,KAAK,CAAC,CACvB,WAAW,MAAM,KAAK,EAAE;AAC7B;AAEA,SAAS,cAAc,SAAiB;CACtC,OAAO,QAAQ,QAAQ,SAAS,IAAI,CAAC,CAAC,QAAQ;AAChD;AAEA,SAAS,eAAe,KAAa;CACnC,MAAM,UAAU,uBAAuB,GAAG,CAAC,CAAC,KAAK;CACjD,IAAI,QAAQ,WAAW,IAAG,KAAK,QAAQ,SAAS,IAAG,GACjD,OAAO,mBAAmB,QAAQ,MAAM,GAAG,EAAE,CAAC;CAEhD,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GACjD,OAAO,QAAQ,MAAM,GAAG,EAAE;CAE5B,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAe;CACzC,OAAO,MAAM,QACX,oBACC,QAAQ,aACN;EACC,MAAK;EACL,MAAM;EACN,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL,EAAA,CAAG,YAAY,OACnB;AACF;AAEA,SAAS,uBAAuB,KAAa;CAC3C,IAAI;CACJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,IAAI,QAAQ,SAAS,GAAG;EAClD,MAAM,OAAO,IAAI;EACjB,KAAK,SAAS,QAAO,SAAS,QAAQ,IAAI,QAAQ,OAAO,MAAM;GAC7D,QAAQ,UAAU,OAAO,KAAA,IAAa,SAAS;GAC/C;EACF;EAEA,IAAI,CAAC,SAAS,SAAS,KACrB,OAAO,IAAI,MAAM,GAAG,KAAK;CAE7B;CAEA,OAAO;AACT;;;ACvVA,MAAMA,kBAAgB,UAAU,QAAQ;AA6BxC,eAAsB,UAAU,SAAwB;CACtD,MAAM,SAAwB,CAAC;CAC/B,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,aAAa,QAAQ,cAAcA;CACzC,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,YACJ,QAAQ,aAAa,QAAQ,IAAI,qBAAqB;CACxD,MAAM,UAAU,QAAQ,WAAA;CACxB,MAAM,kBACJ,QAAQ,mBAAmB,uBAAuB,OAAO;CAE3D,OAAO,KACL,iBAAiB,QAAQ,oBAAA,QAAwC,IAAI,CACvE;CACA,OAAO,KAAK;EACV,MAAM;EACN,IAAI;EACJ,SAAS,uBAAuBC;CAClC,CAAC;CAED,MAAM,aAAa,MAAM,qBAAqB,WAAW,UAAU;CACnE,OAAO,KAAK,UAAU;CAEtB,IAAI,WAAW,IACb,OAAO,KAAK,MAAM,gBAAgB,WAAW,QAAQ,MAAM,CAAC;CAG9D,OAAO,KAAK,MAAM,kBAAkB,QAAQ,MAAM,QAAQ,MAAM,OAAO,CAAC;CAExE,IAAI,CAAC,QAAQ,iBACX,OAAO,KACL,MAAM,qBAAqB;EACzB;EACA,MAAM,QAAQ;EACd,MAAM,QAAQ;EACd;EACA;CACF,CAAC,CACH;CAGF,OAAO;EACL,IAAI,OAAO,OAAO,UAAU,MAAM,EAAE;EACpC;CACF;AACF;AAEA,SAAgB,mBAAmB,QAGhC;CACD,MAAM,QAAQ,OAAO,OAAO,KAAK,UAAU;EACzC,MAAM,SAAS,MAAM,KAAK,MAAM;EAChC,MAAM,OAAO,MAAM,OAAO,SAAS,MAAM,SAAS;EAClD,OAAO,GAAG,OAAO,GAAG,MAAM,KAAK,IAAI,MAAM,UAAU;CACrD,CAAC;CAED,MAAM,KAAK,EAAE;CACb,MAAM,KACJ,OAAO,KACH,sEACA,gEACN;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;AAEA,SAAS,iBAAiB,eAAoC;CAC5D,MAAM,UAAU,QAAQ,QAAQ,MAAM,CAAC;CACvC,MAAM,UAAU,wBAAwB,aAAa;CACrD,MAAM,KAAK,mBAAmB,SAAS,OAAO,KAAK;CAEnD,OAAO,KACH;EACE,MAAM;EACN;EACA,SAAS,QAAQ,QAAQ,aAAa;CACxC,IACA;EACE,MAAM;EACN;EACA,SAAS,QAAQ,QAAQ,oBAAoB;EAC7C,MAAM,gBAAgB,QAAQ;CAChC;AACN;AAEA,eAAe,qBACb,WACA,YACsB;CACtB,IAAI;EACF,MAAM,WAAW,WAAW,CAAC,QAAQ,GAAG,EAAE,SAAS,IAAM,CAAC;EAC1D,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,6BAA6B;EACxC;CACF,SAAS,OAAO;EAGd,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAJA,iBAAiB,QAAQ,MAAM,UAAU;GAKzC,MAAM;EACR;CACF;AACF;AAEA,eAAe,gBACb,WACA,SAAS,IAAI,aAAa,EAAE,UAAU,CAAC,GACjB;CACtB,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAAW,EACrC,SAAS,KACX,CAAC;EACD,IAAI,OAAO,WAAW,GACpB,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS;GACT,MAAM;EACR;EAGF,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,8BAA8B,OAAO,OAAO;EACvD;CACF,SAAS,OAAO;EAGd,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAJA,iBAAiB,QAAQ,MAAM,UAAU;GAKzC,MAAM;EACR;CACF;AACF;AAEA,eAAe,kBACb,MACA,MACA,SACsB;CACtB,MAAM,MAAM,UAAU,KAAK,GAAG,KAAK;CACnC,IAAI;EACF,MAAM,WAAW,MAAM,QAAQ,KAAK,EAAE,QAAQ,YAAY,QAAQ,GAAK,EAAE,CAAC;EAC1E,IAAI,CAAC,SAAS,IACZ,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,GAAG,IAAI,iBAAiB,SAAS;GAC1C,MAAM;EACR;EAGF,MAAM,UAAW,MAAM,SAAS,KAAK;EACrC,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,QAAQ,UACb,0BAA0B,IAAI,YAAY,QAAQ,QAAQ,KAC1D,0BAA0B;EAChC;CACF,SAAS,OAAO;EAGd,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAJA,iBAAiB,QAAQ,MAAM,UAAU;GAKzC,MAAM;EACR;CACF;AACF;AAEA,eAAe,qBAAqB,SAMX;CACvB,IAAI;EAEF,MAAM,SAAS,iBAAiB,MADV,QAAQ,WAAW,QAAQ,iBAAiB,MAAM,GAC/B;GACvC,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,SAAS,QAAQ;EACnB,CAAC;EAED,IAAI,OAAO,IACT,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,iCAAiC,QAAQ;EACpD;EAGF,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,OAAO,OAAO,KAAK,IAAI;GAChC,MAAM;EACR;CACF,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAC5C,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAAS,6BAA6B,QAAQ;GAC9C,MAAM;EACR;EAKF,OAAO;GACL,MAAM;GACN,IAAI;GACJ,SAJA,iBAAiB,QAAQ,MAAM,UAAU;GAKzC,MAAM;EACR;CACF;AACF;AAEA,SAAS,wBAAwB,OAAe;CAC9C,MAAM,QAAQ,MAAM,MAAM,0BAA0B;CACpD,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM,MAAM;AAChD;AAEA,SAAS,mBAAmB,MAAc,OAAe;CACvD,MAAM,WAAW,UACf,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,OAAO,SAAS,MAAM,EAAE,KAAK,CAAC;CAC/D,MAAM,CAAC,YAAY,GAAG,YAAY,GAAG,YAAY,KAAK,QAAQ,IAAI;CAClE,MAAM,CAAC,aAAa,GAAG,aAAa,GAAG,aAAa,KAAK,QAAQ,KAAK;CAEtE,IAAI,cAAc,YAAY,OAAO,YAAY;CACjD,IAAI,cAAc,YAAY,OAAO,YAAY;CACjD,OAAO,YAAY;AACrB;;;AClRA,MAAM,eAAe;AACrB,MAAM,gBAAgB,KAAK,QAAQ,GAAG,UAAU,MAAM;AAiBtD,SAAgB,oBAAoB,QAAQ,cAAgC;CAC1E,OAAO;EACL;EACA,WAAW,KAAK,QAAQ,GAAG,WAAW,gBAAgB,GAAG,MAAM,OAAO;EACtE,YAAY,KAAK,eAAe,yBAAyB;EACzD,YAAY,KAAK,eAAe,6BAA6B;CAC/D;AACF;AAEA,SAAgB,uBAAuB,SAA6B;CAClE,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,oBAAoB,KAAK;CACvC,MAAM,OAAO;EACX,QAAQ,QAAQ,OAAO;EACvB;EACA;EACA;EACA;EACA,OAAO,IAAI;CACb;CACA,MAAM,MAA8B,EAClC,MAAM;EACJ,QAAQ,QAAQ,QAAQ,OAAO,CAAC;EAChC,KAAK,QAAQ,GAAG,UAAU,KAAK;EAC/B;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG,EACZ;CAEA,IAAI,QAAQ,WACV,IAAI,oBAAoB,QAAQ;CAGlC,OAAO;;;;;YAKG,YAAY,KAAK,EAAE;;;;EAI7B,KAAK,KAAK,QAAQ,eAAe,YAAY,GAAG,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,EAAE;;;;;EAKzE,OAAO,QAAQ,GAAG,CAAC,CAClB,KACE,CAAC,KAAK,WACL,YAAY,YAAY,GAAG,EAAE,sBAAsB,YAAY,KAAK,EAAE,UAC1E,CAAC,CACA,KAAK,IAAI,EAAE;;;;;;;;;;YAUF,YAAY,MAAM,UAAU,EAAE;;;YAG9B,YAAY,MAAM,UAAU,EAAE;;;;AAI1C;AAEA,SAAgB,mBAAmB,SAA6B;CAC9D,YAAY;CAEZ,MAAM,QAAQ,oBADA,QAAQ,SAAS,YACQ;CACvC,UAAU,QAAQ,MAAM,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;CACvD,UAAU,eAAe,EAAE,WAAW,KAAK,CAAC;CAE5C,IAAI,WAAW,MAAM,SAAS,GAC5B,QAAQ,MAAM,SAAS;CAGzB,cAAc,MAAM,WAAW,uBAAuB,OAAO,CAAC;CAC9D,UAAU,MAAM,WAAW,GAAK;CAChC,aAAa,aAAa;EAAC;EAAa,gBAAgB;EAAG,MAAM;CAAS,GAAG,EAC3E,OAAO,OACT,CAAC;CACD,OAAO;AACT;AAEA,SAAgB,qBAAqB,QAAQ,cAAc;CACzD,YAAY;CACZ,MAAM,QAAQ,oBAAoB,KAAK;CACvC,QAAQ,MAAM,SAAS;CACvB,OAAO,MAAM,WAAW,EAAE,OAAO,KAAK,CAAC;CACvC,OAAO;AACT;AAEA,SAAgB,uBAAuB,QAAQ,cAAc;CAC3D,YAAY;CACZ,OAAO,aAAa,aAAa,CAAC,SAAS,GAAG,gBAAgB,EAAE,GAAG,OAAO,GAAG,EAC3E,UAAU,OACZ,CAAC;AACH;AAEA,SAAS,QAAQ,WAAmB;CAClC,IAAI;EACF,aAAa,aAAa;GAAC;GAAW,gBAAgB;GAAG;EAAS,GAAG,EACnE,OAAO,OACT,CAAC;CACH,QAAQ,CAER;AACF;AAEA,SAAS,kBAAkB;CACzB,OAAO,OAAO,QAAQ,SAAS,KAAK,aAAa,MAAM,CAAC,IAAI,GAAG,EAAE,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK;AAC5F;AAEA,SAAS,cAAc;CACrB,IAAI,QAAQ,aAAa,UACvB,MAAM,IAAI,MAAM,oDAAoD;AAExE;AAEA,SAAS,YAAY,OAAe;CAClC,OAAO,MACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,MAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,QAAQ;AAC7B;;;AClKA,MAAM,gBAAgB,UAAU,QAAQ;AAExC,MAAa,eAAe;AAC5B,MAAa,uBAAuB,8BAA8B,aAAa;AAC/E,MAAM,sBAAsB;AAkC5B,SAAgB,cAAc,MAAc,OAA2B;CACrE,MAAM,WAAW,UAAkB;EACjC,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG,QAAQ,KAAK,MACvC,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,OAAO,SAAS,MAAM,EAAE,KAAK,CAAC;EAC/C,OAAO;GAAC;GAAO;GAAO;EAAK;CAC7B;CACA,MAAM,CAAC,WAAW,WAAW,aAAa,QAAQ,IAAI;CACtD,MAAM,CAAC,YAAY,YAAY,cAAc,QAAQ,KAAK;CAE1D,IAAI,cAAc,YAChB,OAAO,YAAY,aAAa,KAAK;CAEvC,IAAI,cAAc,YAChB,OAAO,YAAY,aAAa,KAAK;CAEvC,IAAI,cAAc,YAChB,OAAO,YAAY,aAAa,KAAK;CAEvC,OAAO;AACT;AAEA,eAAsB,mBAAmB,SAIrB;CAClB,MAAM,cAAc,SAAS,eAAe;CAC5C,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,YAAY,SAAS,aAAa;CACxC,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,SAAS;CAE9D,IAAI;EACF,MAAM,WAAW,MAAM,QAAQ,aAAa,EAAE,QAAQ,WAAW,OAAO,CAAC;EACzE,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,qBAAqB,SAAS,QAAQ;EAGxD,MAAM,UAAW,MAAM,SAAS,KAAK;EACrC,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,MAAM,mCAAmC;EAErD,OAAO,QAAQ;CACjB,UAAU;EACR,aAAa,OAAO;CACtB;AACF;AAEA,eAAe,cACb,SACA,YACkB;CAClB,IAAI;EACF,MAAM,WAAW,SAAS,CAAC,OAAO,CAAC;EACnC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,qBACpB,YACA,aAAyB,eACA;CACzB,IAAI,eAAe,OAAO,OAAO;CACjC,IAAI,eAAe,QAAQ,OAAO;CAElC,IAAI,CAAE,MAAM,cAAc,QAAQ,UAAU,GAC1C,OAAO;CAGT,IAAI;EACF,MAAM,WAAW,QAAQ;GAAC;GAAQ;GAAM;GAAc;EAAQ,GAAG,EAC/D,KAAK,QAAQ,IACf,CAAC;EACD,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,oBACd,SACA,QACqC;CAGrC,MAAM,cAAc,GAAG,eADrB,WAAW,WAAW,YAAY,IAAI,OAAO,QAAQ,MAAM,EAAE;CAG/D,IAAI,YAAY,QACd,OAAO;EAAE,SAAS;EAAQ,MAAM;GAAC;GAAO;GAAM;EAAW;CAAE;CAG7D,OAAO;EAAE,SAAS;EAAO,MAAM;GAAC;GAAW;GAAM;EAAW;CAAE;AAChE;AAEA,SAAS,uBAAuB,UAAwC;CACtE,SAAS,mBAAmB;CAC5B,SAAS,iBAAiB,aAAa,QAAQ;CAC/C,SAAS,oBAAoB,aAAa,QAAQ;AACpD;AAEA,eAAe,qBACb,QACA,aACA,SACiB;CACjB,IAAI,WAAW,UACb,OAAO,mBAAmB;EAAE;EAAa;CAAQ,CAAC;CAEpD,OAAO,OAAO,QAAQ,MAAM,EAAE;AAChC;AAEA,eAAe,WACb,SACA,QACA,SACiB;CACjB,MAAM,EAAE,SAAS,SAAS,oBAAoB,SAAS,MAAM;CAC7D,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,QAAQ,QAAQ,SAAS,MAAM;GACnC,OAAO;GACP,KAAK,QAAQ;EACf,CAAC;EAED,MAAM,GAAG,SAAS,MAAM;EACxB,MAAM,GAAG,UAAU,SAAS,QAAQ,QAAQ,CAAC,CAAC;CAChD,CAAC;AACH;AAEA,eAAsB,WAAW,SAA0C;CACzE,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAC7C,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,oBAAoB,QAAQ,WAAW;CAC7C,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,UACJ,QAAQ,aACN,SAAS,MAAM,iBAAiB,MAAM,SAAS,MAAM,YAAY;CACrE,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,WAAW,QAAQ,YAAY;CAErC,IAAI;CACJ,IAAI;EACF,gBAAgB,MAAM,qBAAqB,QAAQ,aAAa,OAAO;CACzE,SAAS,OAAO;EACd,SACE,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACvF;EACA,uBAAuB,QAAQ;EAC/B,OAAO;CACT;CAGA,IADmB,cAAc,QAAQ,gBAAgB,aAC5C,KAAK,GAAG;EACnB,IAAI,GAAG,aAAa,kBAAkB,QAAQ,eAAe,EAAE;EAC/D,OAAO;CACT;CAEA,IAAI,WAAW;EACb,IAAI,qBAAqB,QAAQ,eAAe,MAAM,eAAe;EACrE,OAAO;CACT;CAEA,IAAI;CACJ,UAAU,MAAM,qBAAqB,mBAAmB,UAAU;CAElE,IAAI,cAAc,aAAa,GAAG,cAAc,OAAO,QAAQ,IAAI;CACnE,IAAI;EACF,MAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,OAAO;EAC1D,IAAI,aAAa,GAAG;GAClB,SAAS,GAAG,QAAQ,iCAAiC,UAAU;GAC/D,uBAAuB,QAAQ;GAC/B,OAAO;EACT;CACF,SAAS,OAAO;EACd,SACE,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,uBAAuB,QAAQ;EAC/B,OAAO;CACT;CAEA,IAAI,aAAa,aAAa,GAAG,eAAe;CAChD,IAAI,4DAA4D;CAEhE,MAAM,mBAAmB,oBAAoB,CAAC,CAAC;CAC/C,IAAI,SAAS,gBAAgB,GAC3B,IACE,8FACF;CAGF,OAAO;AACT;;;ACzNA,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;;;;;;;;;;;;;;;;;;CAkBb;CACC,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,aAAa,QAAQ,KAAK,SAAS,WAAW,GAAG;CAC/D,QAAQ,IAAIC,OAAmB;CAC/B,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,WAAW;CACzB,MAAM,YAAY,QAAQ,KAAK,SAAS,SAAS;CACjD,MAAM,SAAS,QAAQ,YAAY,QAAQ,KAAK;CAChD,MAAM,UAAU,QAAQ,aAAa,MAAM,KAAK;CAEhD,IAAI,YAAY,UAAU,YAAY,SAAS,YAAY,QAAQ;EACjE,QAAQ,MAAM,kDAAkD;EAChE,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,WAAW,MAAM,WAAW;EAChC,gBAAgBA;EAChB;EACA;EACA;CACF,CAAC;CACD,QAAQ,KAAK,QAAQ;AACvB;AAEA,IAAI,YAAY,UACd,IAAI;CACF,MAAM,EAAE,MAAM,SAAS,gBAAgB;CACvC,MAAM,UACJ,QAAQ,aAAA,QAAkC,KAAA;CAC5C,MAAM,kBAAkB,QAAQ,UAAU,KAAA,CAAS;CACnD,MAAM,SAAS,MAAM,UAAU;EAC7B;EACA;EACA;EACA,iBAAiB,QAAQ,KAAK,SAAS,qBAAqB;EAC5D,GAAI,QAAQ,IAAI,oBACZ,EAAE,WAAW,QAAQ,IAAI,kBAAkB,IAC3C,CAAC;EACL,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;CAC/C,CAAC;CACD,QAAQ,OAAO,MAAM,mBAAmB,MAAM,CAAC;CAC/C,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC;AAChC,SAAS,OAAO;CACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACpE,QAAQ,KAAK,CAAC;AAChB;AAGF,IAAI,YAAY,UAAU;CACxB,MAAM,SAAS,QAAQ,KAAK,MAAM;CAClC,IAAI;EACF,MAAM,EAAE,MAAM,SAAS,gBAAgB;EACvC,MAAM,UACJ,QAAQ,aAAA,QAAkC,KAAA;EAC5C,MAAM,WACJ,QAAQ,UAAU,KAAA,CAAS,KAAK,uBAAuB,OAAO;EAEhE,IAAI,WAAW,SAAS;GACtB,QAAQ,OAAO,MAAM,qBAAqB;IAAE;IAAM;IAAM;GAAQ,CAAC,CAAC;GAClE,QAAQ,MAAM,qCAAqC,SAAS;GAC5D,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,WAAW,SAAS;GAEtB,MAAM,SAAS,iBAAiB,MADV,SAAS,UAAU,MAAM,GACN;IAAE;IAAM;IAAM;GAAQ,CAAC;GAChE,IAAI,OAAO,IAAI;IACb,QAAQ,IAAI,+BAA+B,UAAU;IACrD,QAAQ,KAAK,CAAC;GAChB;GAEA,KAAK,MAAM,SAAS,OAAO,QACzB,QAAQ,MAAM,KAAK;GAErB,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,WAAW,SAAS;GACtB,MAAM,SAAS,MAAM,iBAAiB;IACpC;IACA;IACA;IACA;IACA,OAAO,QAAQ,KAAK,SAAS,SAAS;GACxC,CAAC;GAED,IAAI,OAAO,SACT,QAAQ,IAAI,2BAA2B,OAAO,MAAM;QAC/C,IAAI,OAAO,SAChB,QAAQ,IAAI,2BAA2B,OAAO,MAAM;QAEpD,QAAQ,IAAI,sCAAsC,OAAO,MAAM;GAGjE,QAAQ,IAAI,qCAAqC,SAAS;GAC1D,QAAQ,KAAK,CAAC;EAChB;CACF,SAAS,OAAO;EACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACpE,QAAQ,KAAK,CAAC;CAChB;CAEA,QAAQ,MAAM,0BAA0B,QAAQ;CAChD,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,UACd,IAAI;CAMF,MAAM,SAAS,MAAM,IALF,aAAa,EAC9B,GAAI,QAAQ,IAAI,oBACZ,EAAE,WAAW,QAAQ,IAAI,kBAAkB,IAC3C,CAAC,EACP,CAC0B,CAAC,CAAC,WAAW,EACrC,SAAS,QAAQ,KAAK,SAAS,WAAW,EAC5C,CAAC;CAED,IAAI,QAAQ,KAAK,SAAS,QAAQ,GAAG;EACnC,QAAQ,IAAI,KAAK,UAAU,kBAAkB,MAAM,GAAG,MAAM,CAAC,CAAC;EAC9D,QAAQ,KAAK,CAAC;CAChB;CAEA,KAAK,MAAM,SAAS,QAClB,QAAQ,IAAI,MAAM,EAAE;CAEtB,QAAQ,KAAK,CAAC;AAChB,SAAS,OAAO;CACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACpE,QAAQ,KAAK,CAAC;AAChB;AAGF,IAAI,YAAY,gBAAgB;CAC9B,MAAM,SAAS,QAAQ,KAAK,MAAM;CAClC,IAAI;EACF,IAAI,WAAW,WAAW;GACxB,MAAM,EAAE,MAAM,SAAS,gBAAgB;GACvC,MAAM,YAAY,QAAQ,gBAAgB,QAAQ,IAAI,iBAAiB;GACvE,MAAM,QAAQ,mBAAmB;IAC/B,SAAS,QAAQ,KAAK,MAAM;IAC5B;IACA;IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;GACnC,CAAC;GACD,QAAQ,IAAI,aAAa,MAAM,OAAO;GACtC,QAAQ,IAAI,MAAM,SAAS;GAC3B,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,WAAW,aAAa;GAC1B,MAAM,QAAQ,qBAAqB;GACnC,QAAQ,IAAI,eAAe,MAAM,OAAO;GACxC,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,WAAW,UAAU;GACvB,QAAQ,OAAO,MAAM,uBAAuB,CAAC;GAC7C,QAAQ,KAAK,CAAC;EAChB;CACF,SAAS,OAAO;EACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACpE,QAAQ,KAAK,CAAC;CAChB;CAEA,QAAQ,MAAM,gCAAgC,QAAQ;CACtD,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,SAAS;CACvB,QAAQ,MAAM,oBAAoB,SAAS;CAC3C,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI;AACJ,IAAI;AACJ,IAAI;CACF,MAAM,UAAU,gBAAgB;CAChC,OAAO,QAAQ;CACf,OAAO,QAAQ;AACjB,SAAS,OAAO;CACd,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACpE,QAAQ,KAAK,CAAC;AAChB;AAEA,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"}
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-Bk7ol2lA.mjs","names":["packageJson.version"],"sources":["../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../package.json","../src/adapter/openai.ts","../src/server.ts"],"sourcesContent":["import type { ChatMessage, ResponsesRequest } from \"../types.js\"\n\nfunction contentToText(content: ChatMessage[\"content\"]): string {\n if (typeof content === \"string\") return content\n\n return content\n .map((part) => part.text ?? part.input_text ?? part.output_text ?? \"\")\n .filter(Boolean)\n .join(\"\\n\")\n}\n\nexport function messagesToPrompt(messages: ChatMessage[]): string {\n const nonEmpty = messages.filter(\n (message) => contentToText(message.content).length > 0,\n )\n\n if (nonEmpty.length === 1 && nonEmpty[0]?.role === \"user\") {\n return contentToText(nonEmpty[0].content)\n }\n\n return nonEmpty\n .map((message) => {\n const label =\n message.role === \"system\"\n ? \"System\"\n : message.role === \"assistant\"\n ? \"Assistant\"\n : \"User\"\n return `[${label}]\\n${contentToText(message.content)}`\n })\n .join(\"\\n\\n\")\n}\n\nfunction responseContentToText(content: unknown): string {\n if (typeof content === \"string\") return content\n if (!Array.isArray(content)) return \"\"\n\n return content\n .map((part) => {\n if (typeof part === \"string\") return part\n if (!part || typeof part !== \"object\") return \"\"\n const record = part as Record<string, unknown>\n return String(\n record.text ?? record.input_text ?? record.output_text ?? \"\",\n )\n })\n .filter(Boolean)\n .join(\"\\n\")\n}\n\nexport function responsesToMessages(request: ResponsesRequest): ChatMessage[] {\n const messages: ChatMessage[] = []\n\n if (request.instructions) {\n messages.push({ role: \"system\", content: request.instructions })\n }\n\n const inputItems = Array.isArray(request.input)\n ? request.input\n : [{ role: \"user\", content: request.input ?? \"\" }]\n\n for (const item of inputItems) {\n if (typeof item === \"string\") {\n messages.push({ role: \"user\", content: item })\n continue\n }\n\n if (!item || typeof item !== \"object\") continue\n const record = item as Record<string, unknown>\n const role =\n record.role === \"assistant\"\n ? \"assistant\"\n : record.role === \"system\"\n ? \"system\"\n : \"user\"\n\n if (record.type === \"message\" || record.role) {\n const text = responseContentToText(record.content)\n if (text) messages.push({ role, content: text })\n continue\n }\n\n if (record.type === \"input_text\" || record.type === \"output_text\") {\n const text = responseContentToText([record])\n if (text) messages.push({ role: \"user\", content: text })\n }\n }\n\n return messages.length > 0 ? messages : [{ role: \"user\", content: \"\" }]\n}\n\nexport function normalizeModel(model: string | undefined): string {\n if (!model) return \"auto\"\n if (model.startsWith(\"cursor/\"))\n return model.slice(\"cursor/\".length) || \"auto\"\n if (model.startsWith(\"cursor-\"))\n return model.slice(\"cursor-\".length) || \"auto\"\n return model\n}\n","import type { BridgeModel } from \"../types.js\"\n\nexport function parseAgentModelList(output: string): BridgeModel[] {\n return output\n .split(\"\\n\")\n .map((line) => line.trim())\n .map((line) => {\n const match = line.match(/^([a-zA-Z0-9_.-]+)\\s+-\\s+(.+)$/)\n return match ? { id: match[1] ?? \"\", name: match[2] ?? \"\" } : null\n })\n .filter((model): model is BridgeModel => Boolean(model?.id && model.name))\n}\n\nexport function toOpenAIModelList(models: BridgeModel[]) {\n const created = Math.floor(Date.now() / 1000)\n return {\n object: \"list\",\n data: models.map((model) => ({\n id: model.id,\n object: \"model\",\n owned_by: \"cursor\",\n created,\n })),\n }\n}\n\nexport function toCodexModelCatalog(models: BridgeModel[]) {\n return {\n models: models.map((model, index) => ({\n slug: model.id,\n display_name: model.name,\n description: \"Cursor model via Cursor Agent CLI.\",\n default_reasoning_level: \"medium\",\n supported_reasoning_levels: [],\n shell_type: \"shell_command\",\n visibility: \"list\",\n supported_in_api: true,\n priority: Math.max(0, 1000 - index),\n additional_speed_tiers: [],\n service_tiers: [],\n default_service_tier: null,\n availability_nux: null,\n upgrade: null,\n base_instructions:\n \"You are Codex, a coding agent. Help the user with software engineering tasks in the current workspace.\",\n model_messages: null,\n supports_reasoning_summaries: false,\n default_reasoning_summary: \"none\",\n support_verbosity: false,\n default_verbosity: \"low\",\n apply_patch_tool_type: \"freeform\",\n web_search_tool_type: \"text_and_image\",\n truncation_policy: { mode: \"tokens\", limit: 10000 },\n supports_parallel_tool_calls: true,\n supports_image_detail_original: true,\n context_window: 128000,\n max_context_window: 128000,\n effective_context_window_percent: 95,\n experimental_supported_tools: [],\n input_modalities: [\"text\"],\n supports_search_tool: false,\n use_responses_lite: false,\n })),\n }\n}\n","import { execFile, spawn } from \"node:child_process\"\nimport { parseAgentModelList } from \"../adapter/models.js\"\nimport type {\n BridgeModel,\n CursorRunEvents,\n CursorRunOptions,\n CursorRunResult,\n} from \"../types.js\"\n\nexport interface CursorRunnerOptions {\n agentPath?: string\n defaultCwd?: string\n timeoutMs?: number\n modelListCacheMs?: number\n maxConcurrentRuns?: number\n yolo?: boolean\n}\n\ntype RunPermit = () => void\nconst defaultMaxConcurrentRuns = 1\nconst forceKillDelayMs = 1_000\n\nexport class CursorRunner {\n readonly agentPath: string\n readonly defaultCwd: string\n readonly timeoutMs: number\n readonly modelListCacheMs: number\n readonly maxConcurrentRuns: number\n readonly yolo: boolean\n private modelCache?: { expiresAt: number; models: BridgeModel[] }\n private activeRuns = 0\n private readonly runQueue: Array<() => void> = []\n private readonly activeChildren = new Set<ReturnType<typeof spawn>>()\n\n constructor(options: CursorRunnerOptions = {}) {\n this.agentPath =\n options.agentPath ?? process.env.CURSOR_AGENT_PATH ?? \"agent\"\n this.defaultCwd = options.defaultCwd ?? process.cwd()\n this.timeoutMs = options.timeoutMs ?? 300_000\n this.modelListCacheMs = options.modelListCacheMs ?? 60_000\n this.maxConcurrentRuns = parsePositiveInteger(\n options.maxConcurrentRuns ??\n Number(\n process.env.CURSOR_AGENT_MAX_CONCURRENT || defaultMaxConcurrentRuns,\n ),\n defaultMaxConcurrentRuns,\n )\n this.yolo = options.yolo ?? process.env.CURSOR_AGENT_YOLO !== \"0\"\n }\n\n async listModels(\n options: { refresh?: boolean } = {},\n ): Promise<BridgeModel[]> {\n const now = Date.now()\n if (\n !options.refresh &&\n this.modelCache &&\n this.modelCache.expiresAt > now\n ) {\n return this.modelCache.models\n }\n\n const models = await new Promise<BridgeModel[]>((resolve, reject) => {\n execFile(\n this.agentPath,\n [\"--list-models\"],\n { timeout: 30_000 },\n (error, stdout) => {\n if (error) {\n reject(error)\n return\n }\n resolve(parseAgentModelList(stdout))\n },\n )\n })\n this.modelCache = { expiresAt: now + this.modelListCacheMs, models }\n return models\n }\n\n async run(\n options: CursorRunOptions,\n events: CursorRunEvents = {},\n ): Promise<CursorRunResult> {\n const release = await this.acquireRunPermit(options.signal)\n try {\n if (options.signal?.aborted) throw new Error(\"Request aborted\")\n return await this.runWithPermit(options, events)\n } finally {\n release()\n }\n }\n\n abortAll() {\n for (const child of this.activeChildren) terminateChild(child)\n }\n\n private acquireRunPermit(signal?: AbortSignal): Promise<RunPermit> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error(\"Request aborted\"))\n return\n }\n\n let queuedAcquire: (() => void) | undefined\n const onAbort = () => {\n if (queuedAcquire) {\n this.runQueue.splice(this.runQueue.indexOf(queuedAcquire), 1)\n }\n reject(new Error(\"Request aborted\"))\n }\n const acquire = () => {\n signal?.removeEventListener(\"abort\", onAbort)\n this.activeRuns += 1\n resolve(() => {\n this.activeRuns -= 1\n const next = this.runQueue.shift()\n if (next) next()\n })\n }\n\n if (this.activeRuns < this.maxConcurrentRuns) {\n acquire()\n return\n }\n\n queuedAcquire = acquire\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n this.runQueue.push(acquire)\n })\n }\n\n private runWithPermit(\n options: CursorRunOptions,\n events: CursorRunEvents,\n ): Promise<CursorRunResult> {\n return new Promise((resolve, reject) => {\n const args = [\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--stream-partial-output\",\n ]\n if (this.yolo) args.push(\"--yolo\")\n if (options.model !== \"auto\") args.push(\"--model\", options.model)\n\n const child = spawn(this.agentPath, args, {\n cwd: options.cwd ?? this.defaultCwd,\n env: process.env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n this.activeChildren.add(child)\n\n let buffer = \"\"\n let lastModel = options.model\n let lastAssistantText = \"\"\n let finalText: string | undefined\n let stderr = \"\"\n let settled = false\n let forceKillTimer: NodeJS.Timeout | undefined\n\n const timer = setTimeout(() => {\n forceKillTimer = terminateChild(child)\n settle(() =>\n reject(\n new Error(\n `Cursor Agent request timed out after ${this.timeoutMs}ms`,\n ),\n ),\n )\n }, this.timeoutMs)\n\n const onAbort = () => {\n forceKillTimer = terminateChild(child)\n settle(() => reject(new Error(\"Request aborted\")))\n }\n\n const settle = (fn: () => void) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (forceKillTimer && child.exitCode !== null)\n clearTimeout(forceKillTimer)\n options.signal?.removeEventListener(\"abort\", onAbort)\n this.activeChildren.delete(child)\n fn()\n }\n\n options.signal?.addEventListener(\"abort\", onAbort, { once: true })\n\n child.stdin.write(options.prompt)\n child.stdin.end()\n\n child.stdout.on(\"data\", (chunk) => {\n buffer += chunk.toString()\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() as string\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n const message = JSON.parse(trimmed) as Record<string, unknown>\n if (typeof message.model === \"string\") {\n lastModel = message.model\n events.onModel?.(lastModel)\n }\n\n const text = extractAssistantText(message)\n if (text && text !== lastAssistantText) {\n const delta = text.startsWith(lastAssistantText)\n ? text.slice(lastAssistantText.length)\n : text\n lastAssistantText = text\n events.onDelta?.(delta)\n }\n\n if (typeof message.result === \"string\") finalText = message.result\n } catch {\n // Cursor occasionally emits non-JSON progress lines. They are not protocol data.\n }\n }\n })\n\n child.stderr.on(\"data\", (chunk) => {\n stderr += chunk.toString()\n })\n\n child.on(\"error\", (error) => {\n settle(() => reject(error))\n })\n\n child.on(\"close\", (code) => {\n settle(() => {\n if (code === 0 && (finalText !== undefined || lastAssistantText)) {\n resolve({ text: finalText ?? lastAssistantText, model: lastModel })\n return\n }\n reject(\n new Error(stderr.trim() || `Cursor Agent exited with code ${code}`),\n )\n })\n })\n })\n }\n}\n\nfunction parsePositiveInteger(value: number, fallback: number) {\n return Number.isInteger(value) && value > 0 ? value : fallback\n}\n\nfunction terminateChild(child: ReturnType<typeof spawn>) {\n child.kill(\"SIGTERM\")\n return setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) {\n child.kill(\"SIGKILL\")\n }\n }, forceKillDelayMs)\n}\n\nfunction extractAssistantText(message: Record<string, unknown>): string {\n const nested = message.message\n if (!nested || typeof nested !== \"object\") return \"\"\n const content = (nested as Record<string, unknown>).content\n if (!Array.isArray(content)) return \"\"\n return content\n .map((part) => {\n if (!part || typeof part !== \"object\") return \"\"\n const record = part as Record<string, unknown>\n return record.type === \"text\" && typeof record.text === \"string\"\n ? record.text\n : \"\"\n })\n .join(\"\")\n}\n","","import { randomUUID } from \"node:crypto\"\n\nexport function createChatResponse(model: string, text: string) {\n return {\n id: `chatcmpl-${randomUUID().replaceAll(\"-\", \"\")}`,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n message: { role: \"assistant\", content: text },\n finish_reason: \"stop\",\n },\n ],\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n }\n}\n\nexport function createChatChunk(\n id: string,\n model: string,\n text: string,\n isFirst: boolean,\n) {\n return {\n id,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n delta: { role: isFirst ? \"assistant\" : undefined, content: text },\n finish_reason: null,\n },\n ],\n }\n}\n\nexport function createChatDoneChunk(id: string, model: string) {\n return {\n id,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [{ index: 0, delta: {}, finish_reason: \"stop\" }],\n }\n}\n\nexport function createResponseObject(\n model: string,\n text: string,\n responseId = `resp_${randomUUID().replaceAll(\"-\", \"\")}`,\n itemId = `msg_${randomUUID().replaceAll(\"-\", \"\")}`,\n) {\n const item: {\n id: string\n type: \"message\"\n status: \"completed\"\n role: \"assistant\"\n content: Array<{\n type: \"output_text\"\n text: string\n annotations: unknown[]\n }>\n } = {\n id: itemId,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text, annotations: [] }],\n }\n\n return {\n id: responseId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n model,\n output: [item],\n usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },\n }\n}\n\nexport function createResponseStream(model: string) {\n const response = createResponseObject(model, \"\")\n const item = response.output[0]\n /* v8 ignore next -- createResponseObject always creates one output item. */\n if (!item) throw new Error(\"Responses output item was not created\")\n const part = item.content[0]\n /* v8 ignore next -- createResponseObject always creates one output text part. */\n if (!part) throw new Error(\"Responses output text part was not created\")\n\n return {\n response,\n item,\n part,\n events: [\n [\"response.created\", { ...response, status: \"in_progress\", output: [] }],\n [\n \"response.output_item.added\",\n {\n response_id: response.id,\n output_index: 0,\n item: { ...item, status: \"in_progress\", content: [] },\n },\n ],\n [\n \"response.content_part.added\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n part: { ...part, text: \"\" },\n },\n ],\n ] as const,\n }\n}\n\nexport function responseDeltaEvent(\n stream: ReturnType<typeof createResponseStream>,\n delta: string,\n) {\n return [\n \"response.output_text.delta\",\n {\n response_id: stream.response.id,\n item_id: stream.item.id,\n output_index: 0,\n content_index: 0,\n delta,\n },\n ] as const\n}\n\nexport function responseDoneEvents(\n stream: ReturnType<typeof createResponseStream>,\n model: string,\n text: string,\n) {\n const response = createResponseObject(\n model,\n text,\n stream.response.id,\n stream.item.id,\n )\n const item = response.output[0]\n /* v8 ignore next -- createResponseObject always creates one output item. */\n if (!item) throw new Error(\"Responses output item was not created\")\n const part = item.content[0]\n /* v8 ignore next -- createResponseObject always creates one output text part. */\n if (!part) throw new Error(\"Responses output text part was not created\")\n\n return [\n [\n \"response.output_text.done\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n text,\n },\n ],\n [\n \"response.content_part.done\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n part,\n },\n ],\n [\n \"response.output_item.done\",\n { response_id: response.id, output_index: 0, item },\n ],\n [\"response.completed\", { response }],\n ] as const\n}\n\nexport function responseTextEvents(model: string, text: string) {\n const stream = createResponseStream(model)\n return [\n ...stream.events,\n responseDeltaEvent(stream, text),\n ...responseDoneEvents(stream, model, text),\n ] as const\n}\n","import { randomUUID } from \"node:crypto\"\nimport http, { type IncomingMessage, type ServerResponse } from \"node:http\"\nimport packageJson from \"../package.json\" with { type: \"json\" }\nimport {\n messagesToPrompt,\n normalizeModel,\n responsesToMessages,\n} from \"./adapter/messages.js\"\nimport { toCodexModelCatalog, toOpenAIModelList } from \"./adapter/models.js\"\nimport {\n createChatChunk,\n createChatDoneChunk,\n createChatResponse,\n createResponseObject,\n createResponseStream,\n responseDeltaEvent,\n responseDoneEvents,\n} from \"./adapter/openai.js\"\nimport { CursorRunner } from \"./cursor/runner.js\"\nimport type {\n ChatCompletionRequest,\n ResponsesRequest,\n ServerConfig,\n} from \"./types.js\"\n\nconst packageVersion = packageJson.version\nconst defaultMaxBodyBytes = 1024 * 1024\n\nclass RequestError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(status: number, code: string, message: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n\nexport async function startServer(config: ServerConfig = {}) {\n const port = config.port ?? Number(process.env.PORT || 4646)\n const host = config.host ?? process.env.HOST ?? \"127.0.0.1\"\n const runner = new CursorRunner({\n ...(config.agentPath ? { agentPath: config.agentPath } : {}),\n ...(config.defaultCwd ? { defaultCwd: config.defaultCwd } : {}),\n })\n const maxBodyBytes = config.maxBodyBytes ?? defaultMaxBodyBytes\n\n const server = http.createServer(async (req, res) => {\n try {\n await route(req, res, runner, maxBodyBytes)\n } catch (error) {\n sendError(res, error)\n }\n })\n server.on(\"close\", () => runner.abortAll())\n\n await new Promise<void>((resolve, reject) => {\n server.listen(port, host, resolve)\n server.once(\"error\", reject)\n })\n\n return server\n}\n\nasync function route(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n setCors(res)\n const url = new URL(\n req.url ?? \"/\",\n `http://${req.headers.host ?? \"127.0.0.1\"}`,\n )\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (req.method === \"GET\" && url.pathname === \"/health\") {\n sendJson(res, 200, {\n status: \"ok\",\n provider: \"cursor-agent-bridge\",\n version: packageVersion,\n })\n return\n }\n\n if (req.method === \"GET\" && url.pathname === \"/v1/models\") {\n const models = await runner.listModels({\n refresh: url.searchParams.get(\"refresh\") === \"1\",\n })\n const wantsCodexCatalog =\n url.searchParams.has(\"client_version\") ||\n url.searchParams.get(\"format\") === \"codex\"\n sendJson(\n res,\n 200,\n wantsCodexCatalog\n ? toCodexModelCatalog(models)\n : toOpenAIModelList(models),\n )\n return\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v1/chat/completions\") {\n await handleChat(req, res, runner, maxBodyBytes)\n return\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v1/responses\") {\n await handleResponses(req, res, runner, maxBodyBytes)\n return\n }\n\n sendJson(res, 404, {\n error: {\n message: \"Not found\",\n type: \"invalid_request_error\",\n code: \"not_found\",\n },\n })\n}\n\nasync function handleChat(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n const body = (await readJson(req, maxBodyBytes)) as ChatCompletionRequest\n if (!Array.isArray(body.messages) || body.messages.length === 0) {\n sendJson(res, 400, {\n error: {\n message: \"messages is required and must be a non-empty array\",\n type: \"invalid_request_error\",\n code: \"invalid_messages\",\n },\n })\n return\n }\n\n const model = normalizeModel(body.model)\n const prompt = messagesToPrompt(body.messages)\n const abort = new AbortController()\n req.on(\"close\", () => abort.abort())\n\n if (body.stream === true) {\n const id = `chatcmpl-${randomUUID().replaceAll(\"-\", \"\")}`\n writeSseHeaders(res)\n let isFirst = true\n let lastModel = model\n let streamedText = \"\"\n try {\n const result = await runner.run(\n { model, prompt, signal: abort.signal },\n {\n onDelta: (text) => {\n streamedText += text\n res.write(\n `data: ${JSON.stringify(createChatChunk(id, lastModel, text, isFirst))}\\n\\n`,\n )\n isFirst = false\n },\n onModel: (nextModel) => {\n lastModel = nextModel\n },\n },\n )\n lastModel = result.model\n if (result.text !== streamedText) {\n const delta = result.text.startsWith(streamedText)\n ? result.text.slice(streamedText.length)\n : result.text\n if (delta) {\n res.write(\n `data: ${JSON.stringify(createChatChunk(id, lastModel, delta, isFirst))}\\n\\n`,\n )\n }\n }\n res.write(\n `data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\\n\\n`,\n )\n res.write(\"data: [DONE]\\n\\n\")\n } catch (error) {\n writeSseError(res, error)\n }\n res.end()\n return\n }\n\n const result = await runner.run({ model, prompt, signal: abort.signal })\n sendJson(res, 200, createChatResponse(result.model, result.text))\n}\n\nasync function handleResponses(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n const body = (await readJson(req, maxBodyBytes)) as ResponsesRequest\n const model = normalizeModel(body.model)\n const prompt = messagesToPrompt(responsesToMessages(body))\n const abort = new AbortController()\n req.on(\"close\", () => abort.abort())\n\n if (body.stream === false) {\n const result = await runner.run({ model, prompt, signal: abort.signal })\n sendJson(res, 200, createResponseObject(result.model, result.text))\n return\n }\n\n writeSseHeaders(res)\n const stream = createResponseStream(model)\n let lastModel = model\n let streamedText = \"\"\n for (const [event, data] of stream.events) {\n writeSseEvent(res, event, data)\n }\n try {\n const result = await runner.run(\n { model, prompt, signal: abort.signal },\n {\n onDelta: (text) => {\n streamedText += text\n const [event, data] = responseDeltaEvent(stream, text)\n writeSseEvent(res, event, data)\n },\n onModel: (nextModel) => {\n lastModel = nextModel\n },\n },\n )\n lastModel = result.model\n if (result.text !== streamedText) {\n const delta = result.text.startsWith(streamedText)\n ? result.text.slice(streamedText.length)\n : result.text\n if (delta) {\n streamedText += delta\n const [event, data] = responseDeltaEvent(stream, delta)\n writeSseEvent(res, event, data)\n }\n }\n for (const [event, data] of responseDoneEvents(\n stream,\n lastModel,\n result.text,\n )) {\n writeSseEvent(res, event, data)\n }\n } catch (error) {\n writeSseError(res, error)\n }\n res.end()\n}\n\nfunction readJson(\n req: IncomingMessage,\n maxBodyBytes = defaultMaxBodyBytes,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = \"\"\n let bytes = 0\n let tooLarge = false\n req.setEncoding(\"utf8\")\n req.on(\"data\", (chunk) => {\n if (tooLarge) return\n bytes += Buffer.byteLength(chunk)\n if (bytes > maxBodyBytes) {\n tooLarge = true\n reject(\n new RequestError(\n 413,\n \"payload_too_large\",\n `Request body exceeds ${maxBodyBytes} bytes`,\n ),\n )\n return\n }\n body += chunk\n })\n req.on(\"end\", () => {\n try {\n resolve(body ? JSON.parse(body) : {})\n } catch {\n reject(new RequestError(400, \"invalid_json\", \"Invalid JSON\"))\n }\n })\n req.on(\"error\", reject)\n })\n}\n\nfunction setCors(res: ServerResponse) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET,POST,OPTIONS\")\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n}\n\nfunction writeSseHeaders(res: ServerResponse) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n })\n}\n\nfunction writeSseEvent(\n res: ServerResponse,\n event: string,\n data: Record<string, unknown>,\n) {\n res.write(`event: ${event}\\n`)\n res.write(`data: ${JSON.stringify({ type: event, ...data })}\\n\\n`)\n}\n\nfunction writeSseError(res: ServerResponse, error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n res.write(\"event: error\\n\")\n res.write(\n `data: ${JSON.stringify({\n type: \"error\",\n error: {\n message,\n type: \"server_error\",\n code: \"internal_error\",\n },\n })}\\n\\n`,\n )\n}\n\nfunction sendError(res: ServerResponse, error: unknown) {\n /* v8 ignore next 4 -- last-resort guard for unexpected errors after SSE headers. */\n if (res.headersSent) {\n writeSseError(res, error)\n res.end()\n return\n }\n const status = error instanceof RequestError ? error.status : 500\n sendJson(res, status, {\n error: {\n message: error instanceof Error ? error.message : String(error),\n type:\n error instanceof RequestError\n ? \"invalid_request_error\"\n : \"server_error\",\n code: error instanceof RequestError ? error.code : \"internal_error\",\n },\n })\n}\n\nfunction sendJson(res: ServerResponse, status: number, value: unknown) {\n /* v8 ignore next -- this is a last-resort guard for errors after SSE headers. */\n if (res.headersSent) return\n const body = JSON.stringify(value)\n res.writeHead(status, {\n \"content-type\": \"application/json; charset=utf-8\",\n \"content-length\": Buffer.byteLength(body),\n })\n res.end(body)\n}\n"],"mappings":"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"}
|