cursor-agent-bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xwartz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # cursor-agent-bridge
2
+
3
+ Responses-compatible API bridge for the Cursor Agent CLI.
4
+
5
+ `cursor-agent-bridge` lets Codex and OpenAI-compatible clients use your local Cursor Agent CLI session through a localhost API. It is designed around Codex's current custom-provider requirement for the Responses API while still exposing Chat Completions for simpler clients.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 20+
10
+ - pnpm 11+
11
+ - Cursor Agent CLI installed and authenticated
12
+
13
+ Install Cursor Agent and verify it first:
14
+
15
+ ```bash
16
+ agent login
17
+ agent --list-models
18
+ ```
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pnpm add -g cursor-agent-bridge
24
+ ```
25
+
26
+ During local development:
27
+
28
+ ```bash
29
+ pnpm install
30
+ pnpm build
31
+ pnpm exec cursor-agent-bridge serve
32
+ ```
33
+
34
+ ## Run
35
+
36
+ ```bash
37
+ cursor-agent-bridge serve --host 127.0.0.1 --port 4646
38
+ ```
39
+
40
+ Environment variables:
41
+
42
+ ```bash
43
+ HOST=127.0.0.1
44
+ PORT=4646
45
+ CURSOR_AGENT_PATH=agent
46
+ ```
47
+
48
+ ## Codex Config
49
+
50
+ Add a Codex profile such as `~/.codex/cursor.config.toml`:
51
+
52
+ ```toml
53
+ model_provider = "cursor"
54
+ model = "auto"
55
+
56
+ [model_providers.cursor]
57
+ name = "Cursor Agent Bridge"
58
+ base_url = "http://127.0.0.1:4646/v1"
59
+ wire_api = "responses"
60
+ ```
61
+
62
+ Start Codex:
63
+
64
+ ```bash
65
+ codex --profile cursor
66
+ ```
67
+
68
+ Use `/model` to select a Cursor model. The model catalog is generated from `agent --list-models`.
69
+
70
+ ## API
71
+
72
+ ```text
73
+ GET /health
74
+ GET /v1/models
75
+ POST /v1/responses
76
+ POST /v1/chat/completions
77
+ ```
78
+
79
+ `GET /v1/models` returns the OpenAI-compatible list by default. When Codex calls it with `client_version`, it returns the Codex model catalog shape expected by `/model`.
80
+
81
+ ## Examples
82
+
83
+ Responses API:
84
+
85
+ ```bash
86
+ curl -sS http://127.0.0.1:4646/v1/responses \
87
+ -H 'Content-Type: application/json' \
88
+ -d '{"model":"auto","input":"Reply OK","stream":false}'
89
+ ```
90
+
91
+ Chat Completions:
92
+
93
+ ```bash
94
+ curl -sS http://127.0.0.1:4646/v1/chat/completions \
95
+ -H 'Content-Type: application/json' \
96
+ -d '{"model":"auto","messages":[{"role":"user","content":"Reply OK"}],"stream":false}'
97
+ ```
98
+
99
+ ## Security Notes
100
+
101
+ This server is intended for local use. Bind to `127.0.0.1` unless you are deliberately exposing it on a trusted network.
102
+
103
+ Client `Authorization` headers are not forwarded to Cursor Agent. Use Cursor Agent login or `CURSOR_API_KEY` in the bridge process environment.
104
+
105
+ ## Development
106
+
107
+ ```bash
108
+ pnpm install
109
+ pnpm run ci
110
+ ```
111
+
112
+ Release publishing is handled by GitHub Actions on tags named `v*`.
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { t as startServer } from "./server-CuHDT_fJ.mjs";
3
+ //#region src/cli.ts
4
+ function readArg(name, fallback) {
5
+ const index = process.argv.indexOf(name);
6
+ return index >= 0 ? process.argv[index + 1] : fallback;
7
+ }
8
+ const command = process.argv[2] && !process.argv[2]?.startsWith("-") ? process.argv[2] : "serve";
9
+ if (command === "help" || process.argv.includes("--help") || process.argv.includes("-h")) {
10
+ console.log(`cursor-agent-bridge
11
+
12
+ Usage:
13
+ cursor-agent-bridge serve [--host 127.0.0.1] [--port 4646]
14
+
15
+ Environment:
16
+ HOST Listen host, default 127.0.0.1
17
+ PORT Listen port, default 4646
18
+ CURSOR_AGENT_PATH Cursor Agent CLI path, default agent
19
+ `);
20
+ process.exit(0);
21
+ }
22
+ if (command !== "serve") {
23
+ console.error(`Unknown command: ${command}`);
24
+ process.exit(1);
25
+ }
26
+ const host = readArg("--host", process.env.HOST) ?? "127.0.0.1";
27
+ const port = Number(readArg("--port", process.env.PORT) ?? 4646);
28
+ const server = await startServer({
29
+ host,
30
+ port,
31
+ ...process.env.CURSOR_AGENT_PATH ? { agentPath: process.env.CURSOR_AGENT_PATH } : {}
32
+ });
33
+ console.log(`cursor-agent-bridge listening on http://${host}:${port}`);
34
+ function shutdown() {
35
+ server.close(() => process.exit(0));
36
+ }
37
+ process.on("SIGINT", shutdown);
38
+ process.on("SIGTERM", shutdown);
39
+ //#endregion
40
+ export {};
41
+
42
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { startServer } from \"./server.js\";\n\nfunction readArg(name: string, fallback: string | undefined) {\n const index = process.argv.indexOf(name);\n return index >= 0 ? process.argv[index + 1] : fallback;\n}\n\nconst command =\n process.argv[2] && !process.argv[2]?.startsWith(\"-\")\n ? process.argv[2]\n : \"serve\";\n\nif (\n command === \"help\" ||\n process.argv.includes(\"--help\") ||\n process.argv.includes(\"-h\")\n) {\n console.log(`cursor-agent-bridge\n\nUsage:\n cursor-agent-bridge serve [--host 127.0.0.1] [--port 4646]\n\nEnvironment:\n HOST Listen host, default 127.0.0.1\n PORT Listen port, default 4646\n CURSOR_AGENT_PATH Cursor Agent CLI path, default agent\n`);\n process.exit(0);\n}\n\nif (command !== \"serve\") {\n console.error(`Unknown command: ${command}`);\n process.exit(1);\n}\n\nconst host = readArg(\"--host\", process.env.HOST) ?? \"127.0.0.1\";\nconst port = Number(readArg(\"--port\", process.env.PORT) ?? 4646);\n\nconst server = await startServer({\n host,\n port,\n ...(process.env.CURSOR_AGENT_PATH\n ? { agentPath: process.env.CURSOR_AGENT_PATH }\n : {}),\n});\nconsole.log(`cursor-agent-bridge listening on http://${host}:${port}`);\n\nfunction shutdown() {\n server.close(() => process.exit(0));\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n"],"mappings":";;;AAIA,SAAS,QAAQ,MAAc,UAA8B;CAC3D,MAAM,QAAQ,QAAQ,KAAK,QAAQ,IAAI;CACvC,OAAO,SAAS,IAAI,QAAQ,KAAK,QAAQ,KAAK;AAChD;AAEA,MAAM,UACJ,QAAQ,KAAK,MAAM,CAAC,QAAQ,KAAK,EAAE,EAAE,WAAW,GAAG,IAC/C,QAAQ,KAAK,KACb;AAEN,IACE,YAAY,UACZ,QAAQ,KAAK,SAAS,QAAQ,KAC9B,QAAQ,KAAK,SAAS,IAAI,GAC1B;CACA,QAAQ,IAAI;;;;;;;;;CASb;CACC,QAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,SAAS;CACvB,QAAQ,MAAM,oBAAoB,SAAS;CAC3C,QAAQ,KAAK,CAAC;AAChB;AAEA,MAAM,OAAO,QAAQ,UAAU,QAAQ,IAAI,IAAI,KAAK;AACpD,MAAM,OAAO,OAAO,QAAQ,UAAU,QAAQ,IAAI,IAAI,KAAK,IAAI;AAE/D,MAAM,SAAS,MAAM,YAAY;CAC/B;CACA;CACA,GAAI,QAAQ,IAAI,oBACZ,EAAE,WAAW,QAAQ,IAAI,kBAAkB,IAC3C,CAAC;AACP,CAAC;AACD,QAAQ,IAAI,2CAA2C,KAAK,GAAG,MAAM;AAErE,SAAS,WAAW;CAClB,OAAO,YAAY,QAAQ,KAAK,CAAC,CAAC;AACpC;AAEA,QAAQ,GAAG,UAAU,QAAQ;AAC7B,QAAQ,GAAG,WAAW,QAAQ"}
@@ -0,0 +1,132 @@
1
+ import { EventEmitter } from "node:events";
2
+ import http from "node:http";
3
+
4
+ //#region src/types.d.ts
5
+ type ChatRole = "system" | "user" | "assistant";
6
+ interface ChatMessage {
7
+ role: ChatRole;
8
+ content: string | Array<{
9
+ type: string;
10
+ text?: string;
11
+ input_text?: string;
12
+ output_text?: string;
13
+ }>;
14
+ }
15
+ interface ChatCompletionRequest {
16
+ model?: string;
17
+ messages: ChatMessage[];
18
+ stream?: boolean;
19
+ temperature?: number;
20
+ top_p?: number;
21
+ max_tokens?: number;
22
+ }
23
+ interface ResponsesRequest {
24
+ model?: string;
25
+ input?: unknown;
26
+ instructions?: string;
27
+ stream?: boolean;
28
+ temperature?: number;
29
+ top_p?: number;
30
+ max_output_tokens?: number;
31
+ }
32
+ interface BridgeModel {
33
+ id: string;
34
+ name: string;
35
+ }
36
+ interface CursorRunOptions {
37
+ model: string;
38
+ prompt: string;
39
+ cwd?: string;
40
+ signal?: AbortSignal;
41
+ }
42
+ interface CursorRunResult {
43
+ text: string;
44
+ model: string;
45
+ }
46
+ interface CursorRunEvents {
47
+ onDelta?: (text: string) => void;
48
+ onModel?: (model: string) => void;
49
+ }
50
+ interface ServerConfig {
51
+ port?: number;
52
+ host?: string;
53
+ agentPath?: string;
54
+ defaultCwd?: string;
55
+ }
56
+ //#endregion
57
+ //#region src/adapter/messages.d.ts
58
+ declare function messagesToPrompt(messages: ChatMessage[]): string;
59
+ declare function responsesToMessages(request: ResponsesRequest): ChatMessage[];
60
+ declare function normalizeModel(model: string | undefined): string;
61
+ //#endregion
62
+ //#region src/adapter/models.d.ts
63
+ declare function parseAgentModelList(output: string): BridgeModel[];
64
+ declare function toOpenAIModelList(models: BridgeModel[]): {
65
+ object: string;
66
+ data: {
67
+ id: string;
68
+ object: string;
69
+ owned_by: string;
70
+ created: number;
71
+ }[];
72
+ };
73
+ declare function toCodexModelCatalog(models: BridgeModel[]): {
74
+ models: {
75
+ slug: string;
76
+ display_name: string;
77
+ description: string;
78
+ default_reasoning_level: string;
79
+ supported_reasoning_levels: never[];
80
+ shell_type: string;
81
+ visibility: string;
82
+ supported_in_api: boolean;
83
+ priority: number;
84
+ additional_speed_tiers: never[];
85
+ service_tiers: never[];
86
+ default_service_tier: null;
87
+ availability_nux: null;
88
+ upgrade: null;
89
+ base_instructions: string;
90
+ model_messages: null;
91
+ supports_reasoning_summaries: boolean;
92
+ default_reasoning_summary: string;
93
+ support_verbosity: boolean;
94
+ default_verbosity: string;
95
+ apply_patch_tool_type: string;
96
+ web_search_tool_type: string;
97
+ truncation_policy: {
98
+ mode: string;
99
+ limit: number;
100
+ };
101
+ supports_parallel_tool_calls: boolean;
102
+ supports_image_detail_original: boolean;
103
+ context_window: number;
104
+ max_context_window: number;
105
+ effective_context_window_percent: number;
106
+ experimental_supported_tools: never[];
107
+ input_modalities: string[];
108
+ supports_search_tool: boolean;
109
+ use_responses_lite: boolean;
110
+ }[];
111
+ };
112
+ //#endregion
113
+ //#region src/cursor/runner.d.ts
114
+ interface CursorRunnerOptions {
115
+ agentPath?: string;
116
+ defaultCwd?: string;
117
+ timeoutMs?: number;
118
+ }
119
+ declare class CursorRunner extends EventEmitter {
120
+ readonly agentPath: string;
121
+ readonly defaultCwd: string;
122
+ readonly timeoutMs: number;
123
+ constructor(options?: CursorRunnerOptions);
124
+ listModels(): Promise<BridgeModel[]>;
125
+ run(options: CursorRunOptions, events?: CursorRunEvents): Promise<CursorRunResult>;
126
+ }
127
+ //#endregion
128
+ //#region src/server.d.ts
129
+ declare function startServer(config?: ServerConfig): Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
130
+ //#endregion
131
+ 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 };
132
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../src/server.ts"],"mappings":";;;;KAAY,QAAA;AAAA,UAEK,WAAA;EACf,IAAA,EAAM,QAAA;EACN,OAAA,WAEI,KAAK;IACH,IAAA;IACA,IAAA;IACA,UAAA;IACA,WAAA;EAAA;AAAA;AAAA,UAIS,qBAAA;EACf,KAAA;EACA,QAAA,EAAU,WAAW;EACrB,MAAA;EACA,WAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,KAAA;EACA,YAAA;EACA,MAAA;EACA,WAAA;EACA,KAAA;EACA,iBAAA;AAAA;AAAA,UAGe,WAAA;EACf,EAAA;EACA,IAAI;AAAA;AAAA,UAGW,gBAAA;EACf,KAAA;EACA,MAAA;EACA,GAAA;EACA,MAAA,GAAS,WAAW;AAAA;AAAA,UAGL,eAAA;EACf,IAAA;EACA,KAAK;AAAA;AAAA,UAGU,eAAA;EACf,OAAA,IAAW,IAAA;EACX,OAAA,IAAW,KAAA;AAAA;AAAA,UAGI,YAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,UAAA;AAAA;;;iBChDc,gBAAA,CAAiB,QAAuB,EAAb,WAAW;AAAA,iBAuCtC,mBAAA,CAAoB,OAAA,EAAS,gBAAA,GAAmB,WAAW;AAAA,iBAyC3D,cAAA,CAAe,KAAyB;;;iBCzFxC,mBAAA,CAAoB,MAAA,WAAiB,WAAW;AAAA,iBAWhD,iBAAA,CAAkB,MAAA,EAAQ,WAAW;;;;;;;;;iBAarC,mBAAA,CAAoB,MAAA,EAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UChBtC,mBAAA;EACf,SAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,cAGW,YAAA,SAAqB,YAAA;EAAA,SACvB,SAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;cAEG,OAAA,GAAS,mBAAA;EAQrB,UAAA,IAAc,OAAA,CAAQ,WAAA;EAiBtB,GAAA,CACE,OAAA,EAAS,gBAAA,EACT,MAAA,GAAQ,eAAA,GACP,OAAA,CAAQ,eAAA;AAAA;;;iBCzBS,WAAA,CAAY,MAAA,GAAQ,YAAA,GAAiB,OAAA,CAAA,IAAA,CAAA,MAAA,QAAA,IAAA,CAAA,eAAA,SAAA,IAAA,CAAA,cAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as toOpenAIModelList, c as responsesToMessages, i as toCodexModelCatalog, n as CursorRunner, o as messagesToPrompt, r as parseAgentModelList, s as normalizeModel, t as startServer } from "./server-CuHDT_fJ.mjs";
2
+ export { CursorRunner, messagesToPrompt, normalizeModel, parseAgentModelList, responsesToMessages, startServer, toCodexModelCatalog, toOpenAIModelList };
@@ -0,0 +1,569 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { EventEmitter } from "node:events";
3
+ import { randomUUID } from "node:crypto";
4
+ import http from "node:http";
5
+ //#region src/adapter/messages.ts
6
+ function contentToText(content) {
7
+ if (typeof content === "string") return content;
8
+ return content.map((part) => part.text ?? part.input_text ?? part.output_text ?? "").filter(Boolean).join("\n");
9
+ }
10
+ function messagesToPrompt(messages) {
11
+ const nonEmpty = messages.filter((message) => contentToText(message.content).length > 0);
12
+ if (nonEmpty.length === 1 && nonEmpty[0]?.role === "user") return contentToText(nonEmpty[0].content);
13
+ return nonEmpty.map((message) => {
14
+ return `[${message.role === "system" ? "System" : message.role === "assistant" ? "Assistant" : "User"}]\n${contentToText(message.content)}`;
15
+ }).join("\n\n");
16
+ }
17
+ function responseContentToText(content) {
18
+ if (typeof content === "string") return content;
19
+ if (!Array.isArray(content)) return "";
20
+ return content.map((part) => {
21
+ if (typeof part === "string") return part;
22
+ if (!part || typeof part !== "object") return "";
23
+ const record = part;
24
+ return String(record.text ?? record.input_text ?? record.output_text ?? "");
25
+ }).filter(Boolean).join("\n");
26
+ }
27
+ function responsesToMessages(request) {
28
+ const messages = [];
29
+ if (request.instructions) messages.push({
30
+ role: "system",
31
+ content: request.instructions
32
+ });
33
+ const inputItems = Array.isArray(request.input) ? request.input : [{
34
+ role: "user",
35
+ content: request.input ?? ""
36
+ }];
37
+ for (const item of inputItems) {
38
+ if (typeof item === "string") {
39
+ messages.push({
40
+ role: "user",
41
+ content: item
42
+ });
43
+ continue;
44
+ }
45
+ if (!item || typeof item !== "object") continue;
46
+ const record = item;
47
+ const role = record.role === "assistant" ? "assistant" : record.role === "system" ? "system" : "user";
48
+ if (record.type === "message" || record.role) {
49
+ const text = responseContentToText(record.content);
50
+ if (text) messages.push({
51
+ role,
52
+ content: text
53
+ });
54
+ continue;
55
+ }
56
+ if (record.type === "input_text" || record.type === "output_text") {
57
+ const text = responseContentToText([record]);
58
+ if (text) messages.push({
59
+ role: "user",
60
+ content: text
61
+ });
62
+ }
63
+ }
64
+ return messages.length > 0 ? messages : [{
65
+ role: "user",
66
+ content: ""
67
+ }];
68
+ }
69
+ function normalizeModel(model) {
70
+ if (!model) return "auto";
71
+ if (model.startsWith("cursor/")) return model.slice(7) || "auto";
72
+ if (model.startsWith("cursor-")) return model.slice(7) || "auto";
73
+ return model;
74
+ }
75
+ //#endregion
76
+ //#region src/adapter/models.ts
77
+ function parseAgentModelList(output) {
78
+ return output.split("\n").map((line) => line.trim()).map((line) => {
79
+ const match = line.match(/^([a-zA-Z0-9_.-]+)\s+-\s+(.+)$/);
80
+ return match ? {
81
+ id: match[1] ?? "",
82
+ name: match[2] ?? ""
83
+ } : null;
84
+ }).filter((model) => Boolean(model?.id && model.name));
85
+ }
86
+ function toOpenAIModelList(models) {
87
+ const created = Math.floor(Date.now() / 1e3);
88
+ return {
89
+ object: "list",
90
+ data: models.map((model) => ({
91
+ id: model.id,
92
+ object: "model",
93
+ owned_by: "cursor",
94
+ created
95
+ }))
96
+ };
97
+ }
98
+ function toCodexModelCatalog(models) {
99
+ return { models: models.map((model, index) => ({
100
+ slug: model.id,
101
+ display_name: model.name,
102
+ description: "Cursor model via Cursor Agent CLI.",
103
+ default_reasoning_level: "medium",
104
+ supported_reasoning_levels: [],
105
+ shell_type: "shell_command",
106
+ visibility: "list",
107
+ supported_in_api: true,
108
+ priority: Math.max(0, 1e3 - index),
109
+ additional_speed_tiers: [],
110
+ service_tiers: [],
111
+ default_service_tier: null,
112
+ availability_nux: null,
113
+ upgrade: null,
114
+ base_instructions: "You are Codex, a coding agent. Help the user with software engineering tasks in the current workspace.",
115
+ model_messages: null,
116
+ supports_reasoning_summaries: false,
117
+ default_reasoning_summary: "none",
118
+ support_verbosity: false,
119
+ default_verbosity: "low",
120
+ apply_patch_tool_type: "freeform",
121
+ web_search_tool_type: "text_and_image",
122
+ truncation_policy: {
123
+ mode: "tokens",
124
+ limit: 1e4
125
+ },
126
+ supports_parallel_tool_calls: true,
127
+ supports_image_detail_original: true,
128
+ context_window: 128e3,
129
+ max_context_window: 128e3,
130
+ effective_context_window_percent: 95,
131
+ experimental_supported_tools: [],
132
+ input_modalities: ["text"],
133
+ supports_search_tool: false,
134
+ use_responses_lite: false
135
+ })) };
136
+ }
137
+ //#endregion
138
+ //#region src/cursor/runner.ts
139
+ var CursorRunner = class extends EventEmitter {
140
+ agentPath;
141
+ defaultCwd;
142
+ timeoutMs;
143
+ constructor(options = {}) {
144
+ super();
145
+ this.agentPath = options.agentPath ?? process.env.CURSOR_AGENT_PATH ?? "agent";
146
+ this.defaultCwd = options.defaultCwd ?? process.cwd();
147
+ this.timeoutMs = options.timeoutMs ?? 3e5;
148
+ }
149
+ listModels() {
150
+ return new Promise((resolve, reject) => {
151
+ execFile(this.agentPath, ["--list-models"], { timeout: 3e4 }, (error, stdout) => {
152
+ if (error) {
153
+ reject(error);
154
+ return;
155
+ }
156
+ resolve(parseAgentModelList(stdout));
157
+ });
158
+ });
159
+ }
160
+ run(options, events = {}) {
161
+ return new Promise((resolve, reject) => {
162
+ const args = [
163
+ "-p",
164
+ "--output-format",
165
+ "stream-json",
166
+ "--stream-partial-output",
167
+ "--yolo"
168
+ ];
169
+ if (options.model !== "auto") args.push("--model", options.model);
170
+ const child = spawn(this.agentPath, args, {
171
+ cwd: options.cwd ?? this.defaultCwd,
172
+ env: process.env,
173
+ stdio: [
174
+ "pipe",
175
+ "pipe",
176
+ "pipe"
177
+ ]
178
+ });
179
+ let buffer = "";
180
+ let lastModel = options.model;
181
+ let lastAssistantText = "";
182
+ let finalText;
183
+ let stderr = "";
184
+ let settled = false;
185
+ const timer = setTimeout(() => {
186
+ child.kill("SIGTERM");
187
+ reject(/* @__PURE__ */ new Error(`Cursor Agent request timed out after ${this.timeoutMs}ms`));
188
+ }, this.timeoutMs);
189
+ const settle = (fn) => {
190
+ if (settled) return;
191
+ settled = true;
192
+ clearTimeout(timer);
193
+ fn();
194
+ };
195
+ options.signal?.addEventListener("abort", () => {
196
+ child.kill("SIGTERM");
197
+ settle(() => reject(/* @__PURE__ */ new Error("Request aborted")));
198
+ });
199
+ child.stdin.write(options.prompt);
200
+ child.stdin.end();
201
+ child.stdout.on("data", (chunk) => {
202
+ buffer += chunk.toString();
203
+ const lines = buffer.split("\n");
204
+ buffer = lines.pop() ?? "";
205
+ for (const line of lines) {
206
+ const trimmed = line.trim();
207
+ if (!trimmed) continue;
208
+ try {
209
+ const message = JSON.parse(trimmed);
210
+ if (typeof message.model === "string") {
211
+ lastModel = message.model;
212
+ events.onModel?.(lastModel);
213
+ }
214
+ const text = extractAssistantText(message);
215
+ if (text && text !== lastAssistantText) {
216
+ const delta = text.startsWith(lastAssistantText) ? text.slice(lastAssistantText.length) : text;
217
+ lastAssistantText = text;
218
+ events.onDelta?.(delta);
219
+ }
220
+ if (typeof message.result === "string") finalText = message.result;
221
+ } catch {}
222
+ }
223
+ });
224
+ child.stderr.on("data", (chunk) => {
225
+ stderr += chunk.toString();
226
+ });
227
+ child.on("error", (error) => {
228
+ settle(() => reject(error));
229
+ });
230
+ child.on("close", (code) => {
231
+ settle(() => {
232
+ if (code === 0 && (finalText !== void 0 || lastAssistantText)) {
233
+ resolve({
234
+ text: finalText ?? lastAssistantText,
235
+ model: lastModel
236
+ });
237
+ return;
238
+ }
239
+ reject(new Error(stderr.trim() || `Cursor Agent exited with code ${code}`));
240
+ });
241
+ });
242
+ });
243
+ }
244
+ };
245
+ function extractAssistantText(message) {
246
+ const nested = message.message;
247
+ if (!nested || typeof nested !== "object") return "";
248
+ const content = nested.content;
249
+ if (!Array.isArray(content)) return "";
250
+ return content.map((part) => {
251
+ if (!part || typeof part !== "object") return "";
252
+ const record = part;
253
+ return record.type === "text" && typeof record.text === "string" ? record.text : "";
254
+ }).join("");
255
+ }
256
+ //#endregion
257
+ //#region src/adapter/openai.ts
258
+ function createChatResponse(model, text) {
259
+ return {
260
+ id: `chatcmpl-${randomUUID().replaceAll("-", "")}`,
261
+ object: "chat.completion",
262
+ created: Math.floor(Date.now() / 1e3),
263
+ model,
264
+ choices: [{
265
+ index: 0,
266
+ message: {
267
+ role: "assistant",
268
+ content: text
269
+ },
270
+ finish_reason: "stop"
271
+ }],
272
+ usage: {
273
+ prompt_tokens: 0,
274
+ completion_tokens: 0,
275
+ total_tokens: 0
276
+ }
277
+ };
278
+ }
279
+ function createChatChunk(id, model, text, isFirst) {
280
+ return {
281
+ id,
282
+ object: "chat.completion.chunk",
283
+ created: Math.floor(Date.now() / 1e3),
284
+ model,
285
+ choices: [{
286
+ index: 0,
287
+ delta: {
288
+ role: isFirst ? "assistant" : void 0,
289
+ content: text
290
+ },
291
+ finish_reason: null
292
+ }]
293
+ };
294
+ }
295
+ function createChatDoneChunk(id, model) {
296
+ return {
297
+ id,
298
+ object: "chat.completion.chunk",
299
+ created: Math.floor(Date.now() / 1e3),
300
+ model,
301
+ choices: [{
302
+ index: 0,
303
+ delta: {},
304
+ finish_reason: "stop"
305
+ }]
306
+ };
307
+ }
308
+ function createResponseObject(model, text, responseId = `resp_${randomUUID().replaceAll("-", "")}`) {
309
+ const item = {
310
+ id: `msg_${randomUUID().replaceAll("-", "")}`,
311
+ type: "message",
312
+ status: "completed",
313
+ role: "assistant",
314
+ content: [{
315
+ type: "output_text",
316
+ text,
317
+ annotations: []
318
+ }]
319
+ };
320
+ return {
321
+ id: responseId,
322
+ object: "response",
323
+ created_at: Math.floor(Date.now() / 1e3),
324
+ status: "completed",
325
+ model,
326
+ output: [item],
327
+ usage: {
328
+ input_tokens: 0,
329
+ output_tokens: 0,
330
+ total_tokens: 0
331
+ }
332
+ };
333
+ }
334
+ function responseTextEvents(model, text) {
335
+ const response = createResponseObject(model, text);
336
+ const item = response.output[0];
337
+ /* v8 ignore next -- createResponseObject always creates one output item. */
338
+ if (!item) throw new Error("Responses output item was not created");
339
+ const part = item.content[0];
340
+ /* v8 ignore next -- createResponseObject always creates one output text part. */
341
+ if (!part) throw new Error("Responses output text part was not created");
342
+ return [
343
+ ["response.created", {
344
+ ...response,
345
+ status: "in_progress",
346
+ output: []
347
+ }],
348
+ ["response.output_item.added", {
349
+ response_id: response.id,
350
+ output_index: 0,
351
+ item: {
352
+ ...item,
353
+ status: "in_progress",
354
+ content: []
355
+ }
356
+ }],
357
+ ["response.content_part.added", {
358
+ response_id: response.id,
359
+ item_id: item.id,
360
+ output_index: 0,
361
+ content_index: 0,
362
+ part: {
363
+ ...part,
364
+ text: ""
365
+ }
366
+ }],
367
+ ["response.output_text.delta", {
368
+ response_id: response.id,
369
+ item_id: item.id,
370
+ output_index: 0,
371
+ content_index: 0,
372
+ delta: text
373
+ }],
374
+ ["response.output_text.done", {
375
+ response_id: response.id,
376
+ item_id: item.id,
377
+ output_index: 0,
378
+ content_index: 0,
379
+ text
380
+ }],
381
+ ["response.content_part.done", {
382
+ response_id: response.id,
383
+ item_id: item.id,
384
+ output_index: 0,
385
+ content_index: 0,
386
+ part
387
+ }],
388
+ ["response.output_item.done", {
389
+ response_id: response.id,
390
+ output_index: 0,
391
+ item
392
+ }],
393
+ ["response.completed", { response }]
394
+ ];
395
+ }
396
+ //#endregion
397
+ //#region src/server.ts
398
+ const packageVersion = "0.1.0";
399
+ async function startServer(config = {}) {
400
+ const port = config.port ?? Number(process.env.PORT || 4646);
401
+ const host = config.host ?? process.env.HOST ?? "127.0.0.1";
402
+ const runner = new CursorRunner({
403
+ ...config.agentPath ? { agentPath: config.agentPath } : {},
404
+ ...config.defaultCwd ? { defaultCwd: config.defaultCwd } : {}
405
+ });
406
+ const server = http.createServer(async (req, res) => {
407
+ try {
408
+ await route(req, res, runner);
409
+ } catch (error) {
410
+ sendJson(res, 500, { error: {
411
+ message: error instanceof Error ? error.message : String(error),
412
+ type: "server_error",
413
+ code: "internal_error"
414
+ } });
415
+ }
416
+ });
417
+ await new Promise((resolve, reject) => {
418
+ server.listen(port, host, resolve);
419
+ server.once("error", reject);
420
+ });
421
+ return server;
422
+ }
423
+ async function route(req, res, runner) {
424
+ setCors(res);
425
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
426
+ if (req.method === "OPTIONS") {
427
+ res.writeHead(204);
428
+ res.end();
429
+ return;
430
+ }
431
+ if (req.method === "GET" && url.pathname === "/health") {
432
+ sendJson(res, 200, {
433
+ status: "ok",
434
+ provider: "cursor-agent-bridge",
435
+ version: packageVersion
436
+ });
437
+ return;
438
+ }
439
+ if (req.method === "GET" && url.pathname === "/v1/models") {
440
+ const models = await runner.listModels();
441
+ sendJson(res, 200, url.searchParams.has("client_version") || url.searchParams.get("format") === "codex" ? toCodexModelCatalog(models) : toOpenAIModelList(models));
442
+ return;
443
+ }
444
+ if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
445
+ await handleChat(req, res, runner);
446
+ return;
447
+ }
448
+ if (req.method === "POST" && url.pathname === "/v1/responses") {
449
+ await handleResponses(req, res, runner);
450
+ return;
451
+ }
452
+ sendJson(res, 404, { error: {
453
+ message: "Not found",
454
+ type: "invalid_request_error",
455
+ code: "not_found"
456
+ } });
457
+ }
458
+ async function handleChat(req, res, runner) {
459
+ const body = await readJson(req);
460
+ if (!Array.isArray(body.messages) || body.messages.length === 0) {
461
+ sendJson(res, 400, { error: {
462
+ message: "messages is required and must be a non-empty array",
463
+ type: "invalid_request_error",
464
+ code: "invalid_messages"
465
+ } });
466
+ return;
467
+ }
468
+ const model = normalizeModel(body.model);
469
+ const prompt = messagesToPrompt(body.messages);
470
+ const abort = new AbortController();
471
+ req.on("close", () => abort.abort());
472
+ if (body.stream === true) {
473
+ const id = `chatcmpl-${randomUUID().replaceAll("-", "")}`;
474
+ writeSseHeaders(res);
475
+ let isFirst = true;
476
+ let lastModel = model;
477
+ await runner.run({
478
+ model,
479
+ prompt,
480
+ signal: abort.signal
481
+ }, {
482
+ onDelta: (text) => {
483
+ res.write(`data: ${JSON.stringify(createChatChunk(id, lastModel, text, isFirst))}\n\n`);
484
+ isFirst = false;
485
+ },
486
+ onModel: (nextModel) => {
487
+ lastModel = nextModel;
488
+ }
489
+ });
490
+ res.write(`data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\n\n`);
491
+ res.write("data: [DONE]\n\n");
492
+ res.end();
493
+ return;
494
+ }
495
+ const result = await runner.run({
496
+ model,
497
+ prompt,
498
+ signal: abort.signal
499
+ });
500
+ sendJson(res, 200, createChatResponse(result.model, result.text));
501
+ }
502
+ async function handleResponses(req, res, runner) {
503
+ const body = await readJson(req);
504
+ const model = normalizeModel(body.model);
505
+ const prompt = messagesToPrompt(responsesToMessages(body));
506
+ const abort = new AbortController();
507
+ req.on("close", () => abort.abort());
508
+ const result = await runner.run({
509
+ model,
510
+ prompt,
511
+ signal: abort.signal
512
+ });
513
+ if (body.stream === false) {
514
+ sendJson(res, 200, createResponseObject(result.model, result.text));
515
+ return;
516
+ }
517
+ writeSseHeaders(res);
518
+ for (const [event, data] of responseTextEvents(result.model, result.text)) {
519
+ res.write(`event: ${event}\n`);
520
+ res.write(`data: ${JSON.stringify({
521
+ type: event,
522
+ ...data
523
+ })}\n\n`);
524
+ }
525
+ res.end();
526
+ }
527
+ function readJson(req) {
528
+ return new Promise((resolve, reject) => {
529
+ let body = "";
530
+ req.setEncoding("utf8");
531
+ req.on("data", (chunk) => {
532
+ body += chunk;
533
+ });
534
+ req.on("end", () => {
535
+ try {
536
+ resolve(body ? JSON.parse(body) : {});
537
+ } catch (error) {
538
+ reject(error);
539
+ }
540
+ });
541
+ req.on("error", reject);
542
+ });
543
+ }
544
+ function setCors(res) {
545
+ res.setHeader("Access-Control-Allow-Origin", "*");
546
+ res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
547
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
548
+ }
549
+ function writeSseHeaders(res) {
550
+ res.writeHead(200, {
551
+ "content-type": "text/event-stream; charset=utf-8",
552
+ "cache-control": "no-cache",
553
+ connection: "keep-alive"
554
+ });
555
+ }
556
+ function sendJson(res, status, value) {
557
+ /* v8 ignore next -- this is a last-resort guard for errors after SSE headers. */
558
+ if (res.headersSent) return;
559
+ const body = JSON.stringify(value);
560
+ res.writeHead(status, {
561
+ "content-type": "application/json; charset=utf-8",
562
+ "content-length": Buffer.byteLength(body)
563
+ });
564
+ res.end(body);
565
+ }
566
+ //#endregion
567
+ export { toOpenAIModelList as a, responsesToMessages as c, toCodexModelCatalog as i, CursorRunner as n, messagesToPrompt as o, parseAgentModelList as r, normalizeModel as s, startServer as t };
568
+
569
+ //# sourceMappingURL=server-CuHDT_fJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-CuHDT_fJ.mjs","names":[],"sources":["../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../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 { EventEmitter } from \"node:events\";\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}\n\nexport class CursorRunner extends EventEmitter {\n readonly agentPath: string;\n readonly defaultCwd: string;\n readonly timeoutMs: number;\n\n constructor(options: CursorRunnerOptions = {}) {\n super();\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 }\n\n listModels(): Promise<BridgeModel[]> {\n return new Promise((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 }\n\n run(\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 \"--yolo\",\n ];\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\n let buffer = \"\";\n let lastModel = options.model;\n let lastAssistantText = \"\";\n let finalText: string | undefined;\n let stderr = \"\";\n let settled = false;\n\n const timer = setTimeout(() => {\n child.kill(\"SIGTERM\");\n reject(\n new Error(`Cursor Agent request timed out after ${this.timeoutMs}ms`),\n );\n }, this.timeoutMs);\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n fn();\n };\n\n options.signal?.addEventListener(\"abort\", () => {\n child.kill(\"SIGTERM\");\n settle(() => reject(new Error(\"Request aborted\")));\n });\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() ?? \"\";\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 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) {\n const itemId = `msg_${randomUUID().replaceAll(\"-\", \"\")}`;\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 responseTextEvents(model: string, text: string) {\n const response = createResponseObject(model, text);\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.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 [\n \"response.output_text.delta\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n delta: text,\n },\n ],\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","import { randomUUID } from \"node:crypto\";\nimport http, { type IncomingMessage, type ServerResponse } from \"node:http\";\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 responseTextEvents,\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 = \"0.1.0\";\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\n const server = http.createServer(async (req, res) => {\n try {\n await route(req, res, runner);\n } catch (error) {\n sendJson(res, 500, {\n error: {\n message: error instanceof Error ? error.message : String(error),\n type: \"server_error\",\n code: \"internal_error\",\n },\n });\n }\n });\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) {\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 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);\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v1/responses\") {\n await handleResponses(req, res, runner);\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) {\n const body = (await readJson(req)) 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 await runner.run(\n { model, prompt, signal: abort.signal },\n {\n onDelta: (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 res.write(\n `data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\\n\\n`,\n );\n res.write(\"data: [DONE]\\n\\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) {\n const body = (await readJson(req)) 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 const result = await runner.run({ model, prompt, signal: abort.signal });\n\n if (body.stream === false) {\n sendJson(res, 200, createResponseObject(result.model, result.text));\n return;\n }\n\n writeSseHeaders(res);\n for (const [event, data] of responseTextEvents(result.model, result.text)) {\n res.write(`event: ${event}\\n`);\n res.write(`data: ${JSON.stringify({ type: event, ...data })}\\n\\n`);\n }\n res.end();\n}\n\nfunction readJson(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n req.setEncoding(\"utf8\");\n req.on(\"data\", (chunk) => {\n body += chunk;\n });\n req.on(\"end\", () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch (error) {\n reject(error);\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 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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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "cursor-agent-bridge",
3
+ "version": "0.1.0",
4
+ "description": "Responses-compatible API bridge for Cursor Agent CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "cursor-agent-bridge": "./dist/cli.mjs"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.mts",
12
+ "import": "./dist/index.mjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsdown",
22
+ "check": "biome check .",
23
+ "check:fix": "biome check --write .",
24
+ "test": "vitest run",
25
+ "test:coverage": "vitest run --coverage",
26
+ "typecheck": "tsc --noEmit",
27
+ "ci": "pnpm check && pnpm typecheck && pnpm test:coverage && pnpm build"
28
+ },
29
+ "keywords": [
30
+ "cursor",
31
+ "codex",
32
+ "responses-api",
33
+ "openai",
34
+ "proxy",
35
+ "agent"
36
+ ],
37
+ "author": "xwartz",
38
+ "license": "MIT",
39
+ "engines": {
40
+ "node": ">=20"
41
+ },
42
+ "packageManager": "pnpm@11.9.0",
43
+ "devDependencies": {
44
+ "@biomejs/biome": "^2.5.1",
45
+ "@types/node": "^26.0.1",
46
+ "@vitest/coverage-v8": "^4.1.9",
47
+ "tsdown": "^0.22.3",
48
+ "typescript": "^6.0.3",
49
+ "unrun": "^0.3.1",
50
+ "vite": "8.1.0",
51
+ "vitest": "^4.1.9"
52
+ }
53
+ }