cursor-agent-bridge 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -16
- package/dist/cli.mjs +346 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +18 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{server-CuHDT_fJ.mjs → server-BFgBwbF5.mjs} +274 -95
- package/dist/server-BFgBwbF5.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/server-CuHDT_fJ.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as toCodexModelCatalog, c as normalizeModel, i as parseAgentModelList, l as responsesToMessages, o as toOpenAIModelList, r as CursorRunner, s as messagesToPrompt, t as startServer } from "./server-BFgBwbF5.mjs";
|
|
2
2
|
export { CursorRunner, messagesToPrompt, normalizeModel, parseAgentModelList, responsesToMessages, startServer, toCodexModelCatalog, toOpenAIModelList };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
3
|
import http from "node:http";
|
|
5
4
|
//#region src/adapter/messages.ts
|
|
@@ -136,18 +135,31 @@ function toCodexModelCatalog(models) {
|
|
|
136
135
|
}
|
|
137
136
|
//#endregion
|
|
138
137
|
//#region src/cursor/runner.ts
|
|
139
|
-
|
|
138
|
+
const defaultMaxConcurrentRuns = 1;
|
|
139
|
+
const forceKillDelayMs = 1e3;
|
|
140
|
+
var CursorRunner = class {
|
|
140
141
|
agentPath;
|
|
141
142
|
defaultCwd;
|
|
142
143
|
timeoutMs;
|
|
144
|
+
modelListCacheMs;
|
|
145
|
+
maxConcurrentRuns;
|
|
146
|
+
yolo;
|
|
147
|
+
modelCache;
|
|
148
|
+
activeRuns = 0;
|
|
149
|
+
runQueue = [];
|
|
150
|
+
activeChildren = /* @__PURE__ */ new Set();
|
|
143
151
|
constructor(options = {}) {
|
|
144
|
-
super();
|
|
145
152
|
this.agentPath = options.agentPath ?? process.env.CURSOR_AGENT_PATH ?? "agent";
|
|
146
153
|
this.defaultCwd = options.defaultCwd ?? process.cwd();
|
|
147
154
|
this.timeoutMs = options.timeoutMs ?? 3e5;
|
|
155
|
+
this.modelListCacheMs = options.modelListCacheMs ?? 6e4;
|
|
156
|
+
this.maxConcurrentRuns = parsePositiveInteger(options.maxConcurrentRuns ?? Number(process.env.CURSOR_AGENT_MAX_CONCURRENT || defaultMaxConcurrentRuns), defaultMaxConcurrentRuns);
|
|
157
|
+
this.yolo = options.yolo ?? process.env.CURSOR_AGENT_YOLO !== "0";
|
|
148
158
|
}
|
|
149
|
-
listModels() {
|
|
150
|
-
|
|
159
|
+
async listModels(options = {}) {
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
if (!options.refresh && this.modelCache && this.modelCache.expiresAt > now) return this.modelCache.models;
|
|
162
|
+
const models = await new Promise((resolve, reject) => {
|
|
151
163
|
execFile(this.agentPath, ["--list-models"], { timeout: 3e4 }, (error, stdout) => {
|
|
152
164
|
if (error) {
|
|
153
165
|
reject(error);
|
|
@@ -156,16 +168,62 @@ var CursorRunner = class extends EventEmitter {
|
|
|
156
168
|
resolve(parseAgentModelList(stdout));
|
|
157
169
|
});
|
|
158
170
|
});
|
|
171
|
+
this.modelCache = {
|
|
172
|
+
expiresAt: now + this.modelListCacheMs,
|
|
173
|
+
models
|
|
174
|
+
};
|
|
175
|
+
return models;
|
|
176
|
+
}
|
|
177
|
+
async run(options, events = {}) {
|
|
178
|
+
const release = await this.acquireRunPermit(options.signal);
|
|
179
|
+
try {
|
|
180
|
+
if (options.signal?.aborted) throw new Error("Request aborted");
|
|
181
|
+
return await this.runWithPermit(options, events);
|
|
182
|
+
} finally {
|
|
183
|
+
release();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
abortAll() {
|
|
187
|
+
for (const child of this.activeChildren) terminateChild(child);
|
|
188
|
+
}
|
|
189
|
+
acquireRunPermit(signal) {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
if (signal?.aborted) {
|
|
192
|
+
reject(/* @__PURE__ */ new Error("Request aborted"));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let queuedAcquire;
|
|
196
|
+
const onAbort = () => {
|
|
197
|
+
if (queuedAcquire) this.runQueue.splice(this.runQueue.indexOf(queuedAcquire), 1);
|
|
198
|
+
reject(/* @__PURE__ */ new Error("Request aborted"));
|
|
199
|
+
};
|
|
200
|
+
const acquire = () => {
|
|
201
|
+
signal?.removeEventListener("abort", onAbort);
|
|
202
|
+
this.activeRuns += 1;
|
|
203
|
+
resolve(() => {
|
|
204
|
+
this.activeRuns -= 1;
|
|
205
|
+
const next = this.runQueue.shift();
|
|
206
|
+
if (next) next();
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
if (this.activeRuns < this.maxConcurrentRuns) {
|
|
210
|
+
acquire();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
queuedAcquire = acquire;
|
|
214
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
215
|
+
this.runQueue.push(acquire);
|
|
216
|
+
});
|
|
159
217
|
}
|
|
160
|
-
|
|
218
|
+
runWithPermit(options, events) {
|
|
161
219
|
return new Promise((resolve, reject) => {
|
|
162
220
|
const args = [
|
|
163
221
|
"-p",
|
|
164
222
|
"--output-format",
|
|
165
223
|
"stream-json",
|
|
166
|
-
"--stream-partial-output"
|
|
167
|
-
"--yolo"
|
|
224
|
+
"--stream-partial-output"
|
|
168
225
|
];
|
|
226
|
+
if (this.yolo) args.push("--yolo");
|
|
169
227
|
if (options.model !== "auto") args.push("--model", options.model);
|
|
170
228
|
const child = spawn(this.agentPath, args, {
|
|
171
229
|
cwd: options.cwd ?? this.defaultCwd,
|
|
@@ -176,32 +234,38 @@ var CursorRunner = class extends EventEmitter {
|
|
|
176
234
|
"pipe"
|
|
177
235
|
]
|
|
178
236
|
});
|
|
237
|
+
this.activeChildren.add(child);
|
|
179
238
|
let buffer = "";
|
|
180
239
|
let lastModel = options.model;
|
|
181
240
|
let lastAssistantText = "";
|
|
182
241
|
let finalText;
|
|
183
242
|
let stderr = "";
|
|
184
243
|
let settled = false;
|
|
244
|
+
let forceKillTimer;
|
|
185
245
|
const timer = setTimeout(() => {
|
|
186
|
-
child
|
|
187
|
-
reject(/* @__PURE__ */ new Error(`Cursor Agent request timed out after ${this.timeoutMs}ms`));
|
|
246
|
+
forceKillTimer = terminateChild(child);
|
|
247
|
+
settle(() => reject(/* @__PURE__ */ new Error(`Cursor Agent request timed out after ${this.timeoutMs}ms`)));
|
|
188
248
|
}, this.timeoutMs);
|
|
249
|
+
const onAbort = () => {
|
|
250
|
+
forceKillTimer = terminateChild(child);
|
|
251
|
+
settle(() => reject(/* @__PURE__ */ new Error("Request aborted")));
|
|
252
|
+
};
|
|
189
253
|
const settle = (fn) => {
|
|
190
254
|
if (settled) return;
|
|
191
255
|
settled = true;
|
|
192
256
|
clearTimeout(timer);
|
|
257
|
+
if (forceKillTimer && child.exitCode !== null) clearTimeout(forceKillTimer);
|
|
258
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
259
|
+
this.activeChildren.delete(child);
|
|
193
260
|
fn();
|
|
194
261
|
};
|
|
195
|
-
options.signal?.addEventListener("abort",
|
|
196
|
-
child.kill("SIGTERM");
|
|
197
|
-
settle(() => reject(/* @__PURE__ */ new Error("Request aborted")));
|
|
198
|
-
});
|
|
262
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
199
263
|
child.stdin.write(options.prompt);
|
|
200
264
|
child.stdin.end();
|
|
201
265
|
child.stdout.on("data", (chunk) => {
|
|
202
266
|
buffer += chunk.toString();
|
|
203
267
|
const lines = buffer.split("\n");
|
|
204
|
-
buffer = lines.pop()
|
|
268
|
+
buffer = lines.pop();
|
|
205
269
|
for (const line of lines) {
|
|
206
270
|
const trimmed = line.trim();
|
|
207
271
|
if (!trimmed) continue;
|
|
@@ -242,6 +306,15 @@ var CursorRunner = class extends EventEmitter {
|
|
|
242
306
|
});
|
|
243
307
|
}
|
|
244
308
|
};
|
|
309
|
+
function parsePositiveInteger(value, fallback) {
|
|
310
|
+
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
311
|
+
}
|
|
312
|
+
function terminateChild(child) {
|
|
313
|
+
child.kill("SIGTERM");
|
|
314
|
+
return setTimeout(() => {
|
|
315
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
316
|
+
}, forceKillDelayMs);
|
|
317
|
+
}
|
|
245
318
|
function extractAssistantText(message) {
|
|
246
319
|
const nested = message.message;
|
|
247
320
|
if (!nested || typeof nested !== "object") return "";
|
|
@@ -254,6 +327,9 @@ function extractAssistantText(message) {
|
|
|
254
327
|
}).join("");
|
|
255
328
|
}
|
|
256
329
|
//#endregion
|
|
330
|
+
//#region package.json
|
|
331
|
+
var version = "0.1.3";
|
|
332
|
+
//#endregion
|
|
257
333
|
//#region src/adapter/openai.ts
|
|
258
334
|
function createChatResponse(model, text) {
|
|
259
335
|
return {
|
|
@@ -305,9 +381,9 @@ function createChatDoneChunk(id, model) {
|
|
|
305
381
|
}]
|
|
306
382
|
};
|
|
307
383
|
}
|
|
308
|
-
function createResponseObject(model, text, responseId = `resp_${randomUUID().replaceAll("-", "")}`) {
|
|
384
|
+
function createResponseObject(model, text, responseId = `resp_${randomUUID().replaceAll("-", "")}`, itemId = `msg_${randomUUID().replaceAll("-", "")}`) {
|
|
309
385
|
const item = {
|
|
310
|
-
id:
|
|
386
|
+
id: itemId,
|
|
311
387
|
type: "message",
|
|
312
388
|
status: "completed",
|
|
313
389
|
role: "assistant",
|
|
@@ -331,46 +407,64 @@ function createResponseObject(model, text, responseId = `resp_${randomUUID().rep
|
|
|
331
407
|
}
|
|
332
408
|
};
|
|
333
409
|
}
|
|
334
|
-
function
|
|
335
|
-
const response = createResponseObject(model,
|
|
410
|
+
function createResponseStream(model) {
|
|
411
|
+
const response = createResponseObject(model, "");
|
|
336
412
|
const item = response.output[0];
|
|
337
413
|
/* v8 ignore next -- createResponseObject always creates one output item. */
|
|
338
414
|
if (!item) throw new Error("Responses output item was not created");
|
|
339
415
|
const part = item.content[0];
|
|
340
416
|
/* v8 ignore next -- createResponseObject always creates one output text part. */
|
|
341
417
|
if (!part) throw new Error("Responses output text part was not created");
|
|
342
|
-
return
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
response_id: response.id,
|
|
350
|
-
output_index: 0,
|
|
351
|
-
item: {
|
|
352
|
-
...item,
|
|
418
|
+
return {
|
|
419
|
+
response,
|
|
420
|
+
item,
|
|
421
|
+
part,
|
|
422
|
+
events: [
|
|
423
|
+
["response.created", {
|
|
424
|
+
...response,
|
|
353
425
|
status: "in_progress",
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
426
|
+
output: []
|
|
427
|
+
}],
|
|
428
|
+
["response.output_item.added", {
|
|
429
|
+
response_id: response.id,
|
|
430
|
+
output_index: 0,
|
|
431
|
+
item: {
|
|
432
|
+
...item,
|
|
433
|
+
status: "in_progress",
|
|
434
|
+
content: []
|
|
435
|
+
}
|
|
436
|
+
}],
|
|
437
|
+
["response.content_part.added", {
|
|
438
|
+
response_id: response.id,
|
|
439
|
+
item_id: item.id,
|
|
440
|
+
output_index: 0,
|
|
441
|
+
content_index: 0,
|
|
442
|
+
part: {
|
|
443
|
+
...part,
|
|
444
|
+
text: ""
|
|
445
|
+
}
|
|
446
|
+
}]
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function responseDeltaEvent(stream, delta) {
|
|
451
|
+
return ["response.output_text.delta", {
|
|
452
|
+
response_id: stream.response.id,
|
|
453
|
+
item_id: stream.item.id,
|
|
454
|
+
output_index: 0,
|
|
455
|
+
content_index: 0,
|
|
456
|
+
delta
|
|
457
|
+
}];
|
|
458
|
+
}
|
|
459
|
+
function responseDoneEvents(stream, model, text) {
|
|
460
|
+
const response = createResponseObject(model, text, stream.response.id, stream.item.id);
|
|
461
|
+
const item = response.output[0];
|
|
462
|
+
/* v8 ignore next -- createResponseObject always creates one output item. */
|
|
463
|
+
if (!item) throw new Error("Responses output item was not created");
|
|
464
|
+
const part = item.content[0];
|
|
465
|
+
/* v8 ignore next -- createResponseObject always creates one output text part. */
|
|
466
|
+
if (!part) throw new Error("Responses output text part was not created");
|
|
467
|
+
return [
|
|
374
468
|
["response.output_text.done", {
|
|
375
469
|
response_id: response.id,
|
|
376
470
|
item_id: item.id,
|
|
@@ -395,7 +489,17 @@ function responseTextEvents(model, text) {
|
|
|
395
489
|
}
|
|
396
490
|
//#endregion
|
|
397
491
|
//#region src/server.ts
|
|
398
|
-
const packageVersion =
|
|
492
|
+
const packageVersion = version;
|
|
493
|
+
const defaultMaxBodyBytes = 1024 * 1024;
|
|
494
|
+
var RequestError = class extends Error {
|
|
495
|
+
status;
|
|
496
|
+
code;
|
|
497
|
+
constructor(status, code, message) {
|
|
498
|
+
super(message);
|
|
499
|
+
this.status = status;
|
|
500
|
+
this.code = code;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
399
503
|
async function startServer(config = {}) {
|
|
400
504
|
const port = config.port ?? Number(process.env.PORT || 4646);
|
|
401
505
|
const host = config.host ?? process.env.HOST ?? "127.0.0.1";
|
|
@@ -403,24 +507,22 @@ async function startServer(config = {}) {
|
|
|
403
507
|
...config.agentPath ? { agentPath: config.agentPath } : {},
|
|
404
508
|
...config.defaultCwd ? { defaultCwd: config.defaultCwd } : {}
|
|
405
509
|
});
|
|
510
|
+
const maxBodyBytes = config.maxBodyBytes ?? defaultMaxBodyBytes;
|
|
406
511
|
const server = http.createServer(async (req, res) => {
|
|
407
512
|
try {
|
|
408
|
-
await route(req, res, runner);
|
|
513
|
+
await route(req, res, runner, maxBodyBytes);
|
|
409
514
|
} catch (error) {
|
|
410
|
-
|
|
411
|
-
message: error instanceof Error ? error.message : String(error),
|
|
412
|
-
type: "server_error",
|
|
413
|
-
code: "internal_error"
|
|
414
|
-
} });
|
|
515
|
+
sendError(res, error);
|
|
415
516
|
}
|
|
416
517
|
});
|
|
518
|
+
server.on("close", () => runner.abortAll());
|
|
417
519
|
await new Promise((resolve, reject) => {
|
|
418
520
|
server.listen(port, host, resolve);
|
|
419
521
|
server.once("error", reject);
|
|
420
522
|
});
|
|
421
523
|
return server;
|
|
422
524
|
}
|
|
423
|
-
async function route(req, res, runner) {
|
|
525
|
+
async function route(req, res, runner, maxBodyBytes) {
|
|
424
526
|
setCors(res);
|
|
425
527
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
|
|
426
528
|
if (req.method === "OPTIONS") {
|
|
@@ -437,16 +539,16 @@ async function route(req, res, runner) {
|
|
|
437
539
|
return;
|
|
438
540
|
}
|
|
439
541
|
if (req.method === "GET" && url.pathname === "/v1/models") {
|
|
440
|
-
const models = await runner.listModels();
|
|
542
|
+
const models = await runner.listModels({ refresh: url.searchParams.get("refresh") === "1" });
|
|
441
543
|
sendJson(res, 200, url.searchParams.has("client_version") || url.searchParams.get("format") === "codex" ? toCodexModelCatalog(models) : toOpenAIModelList(models));
|
|
442
544
|
return;
|
|
443
545
|
}
|
|
444
546
|
if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
|
|
445
|
-
await handleChat(req, res, runner);
|
|
547
|
+
await handleChat(req, res, runner, maxBodyBytes);
|
|
446
548
|
return;
|
|
447
549
|
}
|
|
448
550
|
if (req.method === "POST" && url.pathname === "/v1/responses") {
|
|
449
|
-
await handleResponses(req, res, runner);
|
|
551
|
+
await handleResponses(req, res, runner, maxBodyBytes);
|
|
450
552
|
return;
|
|
451
553
|
}
|
|
452
554
|
sendJson(res, 404, { error: {
|
|
@@ -455,8 +557,8 @@ async function route(req, res, runner) {
|
|
|
455
557
|
code: "not_found"
|
|
456
558
|
} });
|
|
457
559
|
}
|
|
458
|
-
async function handleChat(req, res, runner) {
|
|
459
|
-
const body = await readJson(req);
|
|
560
|
+
async function handleChat(req, res, runner, maxBodyBytes) {
|
|
561
|
+
const body = await readJson(req, maxBodyBytes);
|
|
460
562
|
if (!Array.isArray(body.messages) || body.messages.length === 0) {
|
|
461
563
|
sendJson(res, 400, { error: {
|
|
462
564
|
message: "messages is required and must be a non-empty array",
|
|
@@ -474,21 +576,32 @@ async function handleChat(req, res, runner) {
|
|
|
474
576
|
writeSseHeaders(res);
|
|
475
577
|
let isFirst = true;
|
|
476
578
|
let lastModel = model;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
579
|
+
let streamedText = "";
|
|
580
|
+
try {
|
|
581
|
+
const result = await runner.run({
|
|
582
|
+
model,
|
|
583
|
+
prompt,
|
|
584
|
+
signal: abort.signal
|
|
585
|
+
}, {
|
|
586
|
+
onDelta: (text) => {
|
|
587
|
+
streamedText += text;
|
|
588
|
+
res.write(`data: ${JSON.stringify(createChatChunk(id, lastModel, text, isFirst))}\n\n`);
|
|
589
|
+
isFirst = false;
|
|
590
|
+
},
|
|
591
|
+
onModel: (nextModel) => {
|
|
592
|
+
lastModel = nextModel;
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
lastModel = result.model;
|
|
596
|
+
if (result.text !== streamedText) {
|
|
597
|
+
const delta = result.text.startsWith(streamedText) ? result.text.slice(streamedText.length) : result.text;
|
|
598
|
+
if (delta) res.write(`data: ${JSON.stringify(createChatChunk(id, lastModel, delta, isFirst))}\n\n`);
|
|
488
599
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
600
|
+
res.write(`data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\n\n`);
|
|
601
|
+
res.write("data: [DONE]\n\n");
|
|
602
|
+
} catch (error) {
|
|
603
|
+
writeSseError(res, error);
|
|
604
|
+
}
|
|
492
605
|
res.end();
|
|
493
606
|
return;
|
|
494
607
|
}
|
|
@@ -499,43 +612,77 @@ async function handleChat(req, res, runner) {
|
|
|
499
612
|
});
|
|
500
613
|
sendJson(res, 200, createChatResponse(result.model, result.text));
|
|
501
614
|
}
|
|
502
|
-
async function handleResponses(req, res, runner) {
|
|
503
|
-
const body = await readJson(req);
|
|
615
|
+
async function handleResponses(req, res, runner, maxBodyBytes) {
|
|
616
|
+
const body = await readJson(req, maxBodyBytes);
|
|
504
617
|
const model = normalizeModel(body.model);
|
|
505
618
|
const prompt = messagesToPrompt(responsesToMessages(body));
|
|
506
619
|
const abort = new AbortController();
|
|
507
620
|
req.on("close", () => abort.abort());
|
|
508
|
-
const result = await runner.run({
|
|
509
|
-
model,
|
|
510
|
-
prompt,
|
|
511
|
-
signal: abort.signal
|
|
512
|
-
});
|
|
513
621
|
if (body.stream === false) {
|
|
622
|
+
const result = await runner.run({
|
|
623
|
+
model,
|
|
624
|
+
prompt,
|
|
625
|
+
signal: abort.signal
|
|
626
|
+
});
|
|
514
627
|
sendJson(res, 200, createResponseObject(result.model, result.text));
|
|
515
628
|
return;
|
|
516
629
|
}
|
|
517
630
|
writeSseHeaders(res);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
631
|
+
const stream = createResponseStream(model);
|
|
632
|
+
let lastModel = model;
|
|
633
|
+
let streamedText = "";
|
|
634
|
+
for (const [event, data] of stream.events) writeSseEvent(res, event, data);
|
|
635
|
+
try {
|
|
636
|
+
const result = await runner.run({
|
|
637
|
+
model,
|
|
638
|
+
prompt,
|
|
639
|
+
signal: abort.signal
|
|
640
|
+
}, {
|
|
641
|
+
onDelta: (text) => {
|
|
642
|
+
streamedText += text;
|
|
643
|
+
const [event, data] = responseDeltaEvent(stream, text);
|
|
644
|
+
writeSseEvent(res, event, data);
|
|
645
|
+
},
|
|
646
|
+
onModel: (nextModel) => {
|
|
647
|
+
lastModel = nextModel;
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
lastModel = result.model;
|
|
651
|
+
if (result.text !== streamedText) {
|
|
652
|
+
const delta = result.text.startsWith(streamedText) ? result.text.slice(streamedText.length) : result.text;
|
|
653
|
+
if (delta) {
|
|
654
|
+
streamedText += delta;
|
|
655
|
+
const [event, data] = responseDeltaEvent(stream, delta);
|
|
656
|
+
writeSseEvent(res, event, data);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
for (const [event, data] of responseDoneEvents(stream, lastModel, result.text)) writeSseEvent(res, event, data);
|
|
660
|
+
} catch (error) {
|
|
661
|
+
writeSseError(res, error);
|
|
524
662
|
}
|
|
525
663
|
res.end();
|
|
526
664
|
}
|
|
527
|
-
function readJson(req) {
|
|
665
|
+
function readJson(req, maxBodyBytes = defaultMaxBodyBytes) {
|
|
528
666
|
return new Promise((resolve, reject) => {
|
|
529
667
|
let body = "";
|
|
668
|
+
let bytes = 0;
|
|
669
|
+
let tooLarge = false;
|
|
530
670
|
req.setEncoding("utf8");
|
|
531
671
|
req.on("data", (chunk) => {
|
|
672
|
+
if (tooLarge) return;
|
|
673
|
+
bytes += Buffer.byteLength(chunk);
|
|
674
|
+
if (bytes > maxBodyBytes) {
|
|
675
|
+
tooLarge = true;
|
|
676
|
+
reject(new RequestError(413, "payload_too_large", `Request body exceeds ${maxBodyBytes} bytes`));
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
532
679
|
body += chunk;
|
|
533
680
|
});
|
|
534
681
|
req.on("end", () => {
|
|
535
682
|
try {
|
|
536
683
|
resolve(body ? JSON.parse(body) : {});
|
|
537
|
-
} catch
|
|
538
|
-
reject(
|
|
684
|
+
} catch {
|
|
685
|
+
reject(new RequestError(400, "invalid_json", "Invalid JSON"));
|
|
539
686
|
}
|
|
540
687
|
});
|
|
541
688
|
req.on("error", reject);
|
|
@@ -553,6 +700,38 @@ function writeSseHeaders(res) {
|
|
|
553
700
|
connection: "keep-alive"
|
|
554
701
|
});
|
|
555
702
|
}
|
|
703
|
+
function writeSseEvent(res, event, data) {
|
|
704
|
+
res.write(`event: ${event}\n`);
|
|
705
|
+
res.write(`data: ${JSON.stringify({
|
|
706
|
+
type: event,
|
|
707
|
+
...data
|
|
708
|
+
})}\n\n`);
|
|
709
|
+
}
|
|
710
|
+
function writeSseError(res, error) {
|
|
711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
712
|
+
res.write("event: error\n");
|
|
713
|
+
res.write(`data: ${JSON.stringify({
|
|
714
|
+
type: "error",
|
|
715
|
+
error: {
|
|
716
|
+
message,
|
|
717
|
+
type: "server_error",
|
|
718
|
+
code: "internal_error"
|
|
719
|
+
}
|
|
720
|
+
})}\n\n`);
|
|
721
|
+
}
|
|
722
|
+
function sendError(res, error) {
|
|
723
|
+
/* v8 ignore next 4 -- last-resort guard for unexpected errors after SSE headers. */
|
|
724
|
+
if (res.headersSent) {
|
|
725
|
+
writeSseError(res, error);
|
|
726
|
+
res.end();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
sendJson(res, error instanceof RequestError ? error.status : 500, { error: {
|
|
730
|
+
message: error instanceof Error ? error.message : String(error),
|
|
731
|
+
type: error instanceof RequestError ? "invalid_request_error" : "server_error",
|
|
732
|
+
code: error instanceof RequestError ? error.code : "internal_error"
|
|
733
|
+
} });
|
|
734
|
+
}
|
|
556
735
|
function sendJson(res, status, value) {
|
|
557
736
|
/* v8 ignore next -- this is a last-resort guard for errors after SSE headers. */
|
|
558
737
|
if (res.headersSent) return;
|
|
@@ -564,6 +743,6 @@ function sendJson(res, status, value) {
|
|
|
564
743
|
res.end(body);
|
|
565
744
|
}
|
|
566
745
|
//#endregion
|
|
567
|
-
export {
|
|
746
|
+
export { toCodexModelCatalog as a, normalizeModel as c, parseAgentModelList as i, responsesToMessages as l, version as n, toOpenAIModelList as o, CursorRunner as r, messagesToPrompt as s, startServer as t };
|
|
568
747
|
|
|
569
|
-
//# sourceMappingURL=server-
|
|
748
|
+
//# sourceMappingURL=server-BFgBwbF5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-BFgBwbF5.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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"}
|