cursor-agent-bridge 0.1.2 → 0.1.4
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 +136 -16
- package/dist/cli.mjs +828 -5
- 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-Bk7ol2lA.mjs} +275 -95
- package/dist/server-Bk7ol2lA.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/server-CuHDT_fJ.mjs.map +0 -1
|
@@ -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,10 @@ function extractAssistantText(message) {
|
|
|
254
327
|
}).join("");
|
|
255
328
|
}
|
|
256
329
|
//#endregion
|
|
330
|
+
//#region package.json
|
|
331
|
+
var version = "0.1.4";
|
|
332
|
+
var engines = { "node": ">=22.13" };
|
|
333
|
+
//#endregion
|
|
257
334
|
//#region src/adapter/openai.ts
|
|
258
335
|
function createChatResponse(model, text) {
|
|
259
336
|
return {
|
|
@@ -305,9 +382,9 @@ function createChatDoneChunk(id, model) {
|
|
|
305
382
|
}]
|
|
306
383
|
};
|
|
307
384
|
}
|
|
308
|
-
function createResponseObject(model, text, responseId = `resp_${randomUUID().replaceAll("-", "")}`) {
|
|
385
|
+
function createResponseObject(model, text, responseId = `resp_${randomUUID().replaceAll("-", "")}`, itemId = `msg_${randomUUID().replaceAll("-", "")}`) {
|
|
309
386
|
const item = {
|
|
310
|
-
id:
|
|
387
|
+
id: itemId,
|
|
311
388
|
type: "message",
|
|
312
389
|
status: "completed",
|
|
313
390
|
role: "assistant",
|
|
@@ -331,46 +408,64 @@ function createResponseObject(model, text, responseId = `resp_${randomUUID().rep
|
|
|
331
408
|
}
|
|
332
409
|
};
|
|
333
410
|
}
|
|
334
|
-
function
|
|
335
|
-
const response = createResponseObject(model,
|
|
411
|
+
function createResponseStream(model) {
|
|
412
|
+
const response = createResponseObject(model, "");
|
|
336
413
|
const item = response.output[0];
|
|
337
414
|
/* v8 ignore next -- createResponseObject always creates one output item. */
|
|
338
415
|
if (!item) throw new Error("Responses output item was not created");
|
|
339
416
|
const part = item.content[0];
|
|
340
417
|
/* v8 ignore next -- createResponseObject always creates one output text part. */
|
|
341
418
|
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,
|
|
419
|
+
return {
|
|
420
|
+
response,
|
|
421
|
+
item,
|
|
422
|
+
part,
|
|
423
|
+
events: [
|
|
424
|
+
["response.created", {
|
|
425
|
+
...response,
|
|
353
426
|
status: "in_progress",
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
427
|
+
output: []
|
|
428
|
+
}],
|
|
429
|
+
["response.output_item.added", {
|
|
430
|
+
response_id: response.id,
|
|
431
|
+
output_index: 0,
|
|
432
|
+
item: {
|
|
433
|
+
...item,
|
|
434
|
+
status: "in_progress",
|
|
435
|
+
content: []
|
|
436
|
+
}
|
|
437
|
+
}],
|
|
438
|
+
["response.content_part.added", {
|
|
439
|
+
response_id: response.id,
|
|
440
|
+
item_id: item.id,
|
|
441
|
+
output_index: 0,
|
|
442
|
+
content_index: 0,
|
|
443
|
+
part: {
|
|
444
|
+
...part,
|
|
445
|
+
text: ""
|
|
446
|
+
}
|
|
447
|
+
}]
|
|
448
|
+
]
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function responseDeltaEvent(stream, delta) {
|
|
452
|
+
return ["response.output_text.delta", {
|
|
453
|
+
response_id: stream.response.id,
|
|
454
|
+
item_id: stream.item.id,
|
|
455
|
+
output_index: 0,
|
|
456
|
+
content_index: 0,
|
|
457
|
+
delta
|
|
458
|
+
}];
|
|
459
|
+
}
|
|
460
|
+
function responseDoneEvents(stream, model, text) {
|
|
461
|
+
const response = createResponseObject(model, text, stream.response.id, stream.item.id);
|
|
462
|
+
const item = response.output[0];
|
|
463
|
+
/* v8 ignore next -- createResponseObject always creates one output item. */
|
|
464
|
+
if (!item) throw new Error("Responses output item was not created");
|
|
465
|
+
const part = item.content[0];
|
|
466
|
+
/* v8 ignore next -- createResponseObject always creates one output text part. */
|
|
467
|
+
if (!part) throw new Error("Responses output text part was not created");
|
|
468
|
+
return [
|
|
374
469
|
["response.output_text.done", {
|
|
375
470
|
response_id: response.id,
|
|
376
471
|
item_id: item.id,
|
|
@@ -395,7 +490,17 @@ function responseTextEvents(model, text) {
|
|
|
395
490
|
}
|
|
396
491
|
//#endregion
|
|
397
492
|
//#region src/server.ts
|
|
398
|
-
const packageVersion =
|
|
493
|
+
const packageVersion = version;
|
|
494
|
+
const defaultMaxBodyBytes = 1024 * 1024;
|
|
495
|
+
var RequestError = class extends Error {
|
|
496
|
+
status;
|
|
497
|
+
code;
|
|
498
|
+
constructor(status, code, message) {
|
|
499
|
+
super(message);
|
|
500
|
+
this.status = status;
|
|
501
|
+
this.code = code;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
399
504
|
async function startServer(config = {}) {
|
|
400
505
|
const port = config.port ?? Number(process.env.PORT || 4646);
|
|
401
506
|
const host = config.host ?? process.env.HOST ?? "127.0.0.1";
|
|
@@ -403,24 +508,22 @@ async function startServer(config = {}) {
|
|
|
403
508
|
...config.agentPath ? { agentPath: config.agentPath } : {},
|
|
404
509
|
...config.defaultCwd ? { defaultCwd: config.defaultCwd } : {}
|
|
405
510
|
});
|
|
511
|
+
const maxBodyBytes = config.maxBodyBytes ?? defaultMaxBodyBytes;
|
|
406
512
|
const server = http.createServer(async (req, res) => {
|
|
407
513
|
try {
|
|
408
|
-
await route(req, res, runner);
|
|
514
|
+
await route(req, res, runner, maxBodyBytes);
|
|
409
515
|
} catch (error) {
|
|
410
|
-
|
|
411
|
-
message: error instanceof Error ? error.message : String(error),
|
|
412
|
-
type: "server_error",
|
|
413
|
-
code: "internal_error"
|
|
414
|
-
} });
|
|
516
|
+
sendError(res, error);
|
|
415
517
|
}
|
|
416
518
|
});
|
|
519
|
+
server.on("close", () => runner.abortAll());
|
|
417
520
|
await new Promise((resolve, reject) => {
|
|
418
521
|
server.listen(port, host, resolve);
|
|
419
522
|
server.once("error", reject);
|
|
420
523
|
});
|
|
421
524
|
return server;
|
|
422
525
|
}
|
|
423
|
-
async function route(req, res, runner) {
|
|
526
|
+
async function route(req, res, runner, maxBodyBytes) {
|
|
424
527
|
setCors(res);
|
|
425
528
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
|
|
426
529
|
if (req.method === "OPTIONS") {
|
|
@@ -437,16 +540,16 @@ async function route(req, res, runner) {
|
|
|
437
540
|
return;
|
|
438
541
|
}
|
|
439
542
|
if (req.method === "GET" && url.pathname === "/v1/models") {
|
|
440
|
-
const models = await runner.listModels();
|
|
543
|
+
const models = await runner.listModels({ refresh: url.searchParams.get("refresh") === "1" });
|
|
441
544
|
sendJson(res, 200, url.searchParams.has("client_version") || url.searchParams.get("format") === "codex" ? toCodexModelCatalog(models) : toOpenAIModelList(models));
|
|
442
545
|
return;
|
|
443
546
|
}
|
|
444
547
|
if (req.method === "POST" && url.pathname === "/v1/chat/completions") {
|
|
445
|
-
await handleChat(req, res, runner);
|
|
548
|
+
await handleChat(req, res, runner, maxBodyBytes);
|
|
446
549
|
return;
|
|
447
550
|
}
|
|
448
551
|
if (req.method === "POST" && url.pathname === "/v1/responses") {
|
|
449
|
-
await handleResponses(req, res, runner);
|
|
552
|
+
await handleResponses(req, res, runner, maxBodyBytes);
|
|
450
553
|
return;
|
|
451
554
|
}
|
|
452
555
|
sendJson(res, 404, { error: {
|
|
@@ -455,8 +558,8 @@ async function route(req, res, runner) {
|
|
|
455
558
|
code: "not_found"
|
|
456
559
|
} });
|
|
457
560
|
}
|
|
458
|
-
async function handleChat(req, res, runner) {
|
|
459
|
-
const body = await readJson(req);
|
|
561
|
+
async function handleChat(req, res, runner, maxBodyBytes) {
|
|
562
|
+
const body = await readJson(req, maxBodyBytes);
|
|
460
563
|
if (!Array.isArray(body.messages) || body.messages.length === 0) {
|
|
461
564
|
sendJson(res, 400, { error: {
|
|
462
565
|
message: "messages is required and must be a non-empty array",
|
|
@@ -474,21 +577,32 @@ async function handleChat(req, res, runner) {
|
|
|
474
577
|
writeSseHeaders(res);
|
|
475
578
|
let isFirst = true;
|
|
476
579
|
let lastModel = model;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
580
|
+
let streamedText = "";
|
|
581
|
+
try {
|
|
582
|
+
const result = await runner.run({
|
|
583
|
+
model,
|
|
584
|
+
prompt,
|
|
585
|
+
signal: abort.signal
|
|
586
|
+
}, {
|
|
587
|
+
onDelta: (text) => {
|
|
588
|
+
streamedText += text;
|
|
589
|
+
res.write(`data: ${JSON.stringify(createChatChunk(id, lastModel, text, isFirst))}\n\n`);
|
|
590
|
+
isFirst = false;
|
|
591
|
+
},
|
|
592
|
+
onModel: (nextModel) => {
|
|
593
|
+
lastModel = nextModel;
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
lastModel = result.model;
|
|
597
|
+
if (result.text !== streamedText) {
|
|
598
|
+
const delta = result.text.startsWith(streamedText) ? result.text.slice(streamedText.length) : result.text;
|
|
599
|
+
if (delta) res.write(`data: ${JSON.stringify(createChatChunk(id, lastModel, delta, isFirst))}\n\n`);
|
|
488
600
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
601
|
+
res.write(`data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\n\n`);
|
|
602
|
+
res.write("data: [DONE]\n\n");
|
|
603
|
+
} catch (error) {
|
|
604
|
+
writeSseError(res, error);
|
|
605
|
+
}
|
|
492
606
|
res.end();
|
|
493
607
|
return;
|
|
494
608
|
}
|
|
@@ -499,43 +613,77 @@ async function handleChat(req, res, runner) {
|
|
|
499
613
|
});
|
|
500
614
|
sendJson(res, 200, createChatResponse(result.model, result.text));
|
|
501
615
|
}
|
|
502
|
-
async function handleResponses(req, res, runner) {
|
|
503
|
-
const body = await readJson(req);
|
|
616
|
+
async function handleResponses(req, res, runner, maxBodyBytes) {
|
|
617
|
+
const body = await readJson(req, maxBodyBytes);
|
|
504
618
|
const model = normalizeModel(body.model);
|
|
505
619
|
const prompt = messagesToPrompt(responsesToMessages(body));
|
|
506
620
|
const abort = new AbortController();
|
|
507
621
|
req.on("close", () => abort.abort());
|
|
508
|
-
const result = await runner.run({
|
|
509
|
-
model,
|
|
510
|
-
prompt,
|
|
511
|
-
signal: abort.signal
|
|
512
|
-
});
|
|
513
622
|
if (body.stream === false) {
|
|
623
|
+
const result = await runner.run({
|
|
624
|
+
model,
|
|
625
|
+
prompt,
|
|
626
|
+
signal: abort.signal
|
|
627
|
+
});
|
|
514
628
|
sendJson(res, 200, createResponseObject(result.model, result.text));
|
|
515
629
|
return;
|
|
516
630
|
}
|
|
517
631
|
writeSseHeaders(res);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
632
|
+
const stream = createResponseStream(model);
|
|
633
|
+
let lastModel = model;
|
|
634
|
+
let streamedText = "";
|
|
635
|
+
for (const [event, data] of stream.events) writeSseEvent(res, event, data);
|
|
636
|
+
try {
|
|
637
|
+
const result = await runner.run({
|
|
638
|
+
model,
|
|
639
|
+
prompt,
|
|
640
|
+
signal: abort.signal
|
|
641
|
+
}, {
|
|
642
|
+
onDelta: (text) => {
|
|
643
|
+
streamedText += text;
|
|
644
|
+
const [event, data] = responseDeltaEvent(stream, text);
|
|
645
|
+
writeSseEvent(res, event, data);
|
|
646
|
+
},
|
|
647
|
+
onModel: (nextModel) => {
|
|
648
|
+
lastModel = nextModel;
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
lastModel = result.model;
|
|
652
|
+
if (result.text !== streamedText) {
|
|
653
|
+
const delta = result.text.startsWith(streamedText) ? result.text.slice(streamedText.length) : result.text;
|
|
654
|
+
if (delta) {
|
|
655
|
+
streamedText += delta;
|
|
656
|
+
const [event, data] = responseDeltaEvent(stream, delta);
|
|
657
|
+
writeSseEvent(res, event, data);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
for (const [event, data] of responseDoneEvents(stream, lastModel, result.text)) writeSseEvent(res, event, data);
|
|
661
|
+
} catch (error) {
|
|
662
|
+
writeSseError(res, error);
|
|
524
663
|
}
|
|
525
664
|
res.end();
|
|
526
665
|
}
|
|
527
|
-
function readJson(req) {
|
|
666
|
+
function readJson(req, maxBodyBytes = defaultMaxBodyBytes) {
|
|
528
667
|
return new Promise((resolve, reject) => {
|
|
529
668
|
let body = "";
|
|
669
|
+
let bytes = 0;
|
|
670
|
+
let tooLarge = false;
|
|
530
671
|
req.setEncoding("utf8");
|
|
531
672
|
req.on("data", (chunk) => {
|
|
673
|
+
if (tooLarge) return;
|
|
674
|
+
bytes += Buffer.byteLength(chunk);
|
|
675
|
+
if (bytes > maxBodyBytes) {
|
|
676
|
+
tooLarge = true;
|
|
677
|
+
reject(new RequestError(413, "payload_too_large", `Request body exceeds ${maxBodyBytes} bytes`));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
532
680
|
body += chunk;
|
|
533
681
|
});
|
|
534
682
|
req.on("end", () => {
|
|
535
683
|
try {
|
|
536
684
|
resolve(body ? JSON.parse(body) : {});
|
|
537
|
-
} catch
|
|
538
|
-
reject(
|
|
685
|
+
} catch {
|
|
686
|
+
reject(new RequestError(400, "invalid_json", "Invalid JSON"));
|
|
539
687
|
}
|
|
540
688
|
});
|
|
541
689
|
req.on("error", reject);
|
|
@@ -553,6 +701,38 @@ function writeSseHeaders(res) {
|
|
|
553
701
|
connection: "keep-alive"
|
|
554
702
|
});
|
|
555
703
|
}
|
|
704
|
+
function writeSseEvent(res, event, data) {
|
|
705
|
+
res.write(`event: ${event}\n`);
|
|
706
|
+
res.write(`data: ${JSON.stringify({
|
|
707
|
+
type: event,
|
|
708
|
+
...data
|
|
709
|
+
})}\n\n`);
|
|
710
|
+
}
|
|
711
|
+
function writeSseError(res, error) {
|
|
712
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
713
|
+
res.write("event: error\n");
|
|
714
|
+
res.write(`data: ${JSON.stringify({
|
|
715
|
+
type: "error",
|
|
716
|
+
error: {
|
|
717
|
+
message,
|
|
718
|
+
type: "server_error",
|
|
719
|
+
code: "internal_error"
|
|
720
|
+
}
|
|
721
|
+
})}\n\n`);
|
|
722
|
+
}
|
|
723
|
+
function sendError(res, error) {
|
|
724
|
+
/* v8 ignore next 4 -- last-resort guard for unexpected errors after SSE headers. */
|
|
725
|
+
if (res.headersSent) {
|
|
726
|
+
writeSseError(res, error);
|
|
727
|
+
res.end();
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
sendJson(res, error instanceof RequestError ? error.status : 500, { error: {
|
|
731
|
+
message: error instanceof Error ? error.message : String(error),
|
|
732
|
+
type: error instanceof RequestError ? "invalid_request_error" : "server_error",
|
|
733
|
+
code: error instanceof RequestError ? error.code : "internal_error"
|
|
734
|
+
} });
|
|
735
|
+
}
|
|
556
736
|
function sendJson(res, status, value) {
|
|
557
737
|
/* v8 ignore next -- this is a last-resort guard for errors after SSE headers. */
|
|
558
738
|
if (res.headersSent) return;
|
|
@@ -564,6 +744,6 @@ function sendJson(res, status, value) {
|
|
|
564
744
|
res.end(body);
|
|
565
745
|
}
|
|
566
746
|
//#endregion
|
|
567
|
-
export {
|
|
747
|
+
export { parseAgentModelList as a, messagesToPrompt as c, CursorRunner as i, normalizeModel as l, engines as n, toCodexModelCatalog as o, version as r, toOpenAIModelList as s, startServer as t, responsesToMessages as u };
|
|
568
748
|
|
|
569
|
-
//# sourceMappingURL=server-
|
|
749
|
+
//# sourceMappingURL=server-Bk7ol2lA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-Bk7ol2lA.mjs","names":["packageJson.version"],"sources":["../src/adapter/messages.ts","../src/adapter/models.ts","../src/cursor/runner.ts","../package.json","../src/adapter/openai.ts","../src/server.ts"],"sourcesContent":["import type { ChatMessage, ResponsesRequest } from \"../types.js\"\n\nfunction contentToText(content: ChatMessage[\"content\"]): string {\n if (typeof content === \"string\") return content\n\n return content\n .map((part) => part.text ?? part.input_text ?? part.output_text ?? \"\")\n .filter(Boolean)\n .join(\"\\n\")\n}\n\nexport function messagesToPrompt(messages: ChatMessage[]): string {\n const nonEmpty = messages.filter(\n (message) => contentToText(message.content).length > 0,\n )\n\n if (nonEmpty.length === 1 && nonEmpty[0]?.role === \"user\") {\n return contentToText(nonEmpty[0].content)\n }\n\n return nonEmpty\n .map((message) => {\n const label =\n message.role === \"system\"\n ? \"System\"\n : message.role === \"assistant\"\n ? \"Assistant\"\n : \"User\"\n return `[${label}]\\n${contentToText(message.content)}`\n })\n .join(\"\\n\\n\")\n}\n\nfunction responseContentToText(content: unknown): string {\n if (typeof content === \"string\") return content\n if (!Array.isArray(content)) return \"\"\n\n return content\n .map((part) => {\n if (typeof part === \"string\") return part\n if (!part || typeof part !== \"object\") return \"\"\n const record = part as Record<string, unknown>\n return String(\n record.text ?? record.input_text ?? record.output_text ?? \"\",\n )\n })\n .filter(Boolean)\n .join(\"\\n\")\n}\n\nexport function responsesToMessages(request: ResponsesRequest): ChatMessage[] {\n const messages: ChatMessage[] = []\n\n if (request.instructions) {\n messages.push({ role: \"system\", content: request.instructions })\n }\n\n const inputItems = Array.isArray(request.input)\n ? request.input\n : [{ role: \"user\", content: request.input ?? \"\" }]\n\n for (const item of inputItems) {\n if (typeof item === \"string\") {\n messages.push({ role: \"user\", content: item })\n continue\n }\n\n if (!item || typeof item !== \"object\") continue\n const record = item as Record<string, unknown>\n const role =\n record.role === \"assistant\"\n ? \"assistant\"\n : record.role === \"system\"\n ? \"system\"\n : \"user\"\n\n if (record.type === \"message\" || record.role) {\n const text = responseContentToText(record.content)\n if (text) messages.push({ role, content: text })\n continue\n }\n\n if (record.type === \"input_text\" || record.type === \"output_text\") {\n const text = responseContentToText([record])\n if (text) messages.push({ role: \"user\", content: text })\n }\n }\n\n return messages.length > 0 ? messages : [{ role: \"user\", content: \"\" }]\n}\n\nexport function normalizeModel(model: string | undefined): string {\n if (!model) return \"auto\"\n if (model.startsWith(\"cursor/\"))\n return model.slice(\"cursor/\".length) || \"auto\"\n if (model.startsWith(\"cursor-\"))\n return model.slice(\"cursor-\".length) || \"auto\"\n return model\n}\n","import type { BridgeModel } from \"../types.js\"\n\nexport function parseAgentModelList(output: string): BridgeModel[] {\n return output\n .split(\"\\n\")\n .map((line) => line.trim())\n .map((line) => {\n const match = line.match(/^([a-zA-Z0-9_.-]+)\\s+-\\s+(.+)$/)\n return match ? { id: match[1] ?? \"\", name: match[2] ?? \"\" } : null\n })\n .filter((model): model is BridgeModel => Boolean(model?.id && model.name))\n}\n\nexport function toOpenAIModelList(models: BridgeModel[]) {\n const created = Math.floor(Date.now() / 1000)\n return {\n object: \"list\",\n data: models.map((model) => ({\n id: model.id,\n object: \"model\",\n owned_by: \"cursor\",\n created,\n })),\n }\n}\n\nexport function toCodexModelCatalog(models: BridgeModel[]) {\n return {\n models: models.map((model, index) => ({\n slug: model.id,\n display_name: model.name,\n description: \"Cursor model via Cursor Agent CLI.\",\n default_reasoning_level: \"medium\",\n supported_reasoning_levels: [],\n shell_type: \"shell_command\",\n visibility: \"list\",\n supported_in_api: true,\n priority: Math.max(0, 1000 - index),\n additional_speed_tiers: [],\n service_tiers: [],\n default_service_tier: null,\n availability_nux: null,\n upgrade: null,\n base_instructions:\n \"You are Codex, a coding agent. Help the user with software engineering tasks in the current workspace.\",\n model_messages: null,\n supports_reasoning_summaries: false,\n default_reasoning_summary: \"none\",\n support_verbosity: false,\n default_verbosity: \"low\",\n apply_patch_tool_type: \"freeform\",\n web_search_tool_type: \"text_and_image\",\n truncation_policy: { mode: \"tokens\", limit: 10000 },\n supports_parallel_tool_calls: true,\n supports_image_detail_original: true,\n context_window: 128000,\n max_context_window: 128000,\n effective_context_window_percent: 95,\n experimental_supported_tools: [],\n input_modalities: [\"text\"],\n supports_search_tool: false,\n use_responses_lite: false,\n })),\n }\n}\n","import { execFile, spawn } from \"node:child_process\"\nimport { parseAgentModelList } from \"../adapter/models.js\"\nimport type {\n BridgeModel,\n CursorRunEvents,\n CursorRunOptions,\n CursorRunResult,\n} from \"../types.js\"\n\nexport interface CursorRunnerOptions {\n agentPath?: string\n defaultCwd?: string\n timeoutMs?: number\n modelListCacheMs?: number\n maxConcurrentRuns?: number\n yolo?: boolean\n}\n\ntype RunPermit = () => void\nconst defaultMaxConcurrentRuns = 1\nconst forceKillDelayMs = 1_000\n\nexport class CursorRunner {\n readonly agentPath: string\n readonly defaultCwd: string\n readonly timeoutMs: number\n readonly modelListCacheMs: number\n readonly maxConcurrentRuns: number\n readonly yolo: boolean\n private modelCache?: { expiresAt: number; models: BridgeModel[] }\n private activeRuns = 0\n private readonly runQueue: Array<() => void> = []\n private readonly activeChildren = new Set<ReturnType<typeof spawn>>()\n\n constructor(options: CursorRunnerOptions = {}) {\n this.agentPath =\n options.agentPath ?? process.env.CURSOR_AGENT_PATH ?? \"agent\"\n this.defaultCwd = options.defaultCwd ?? process.cwd()\n this.timeoutMs = options.timeoutMs ?? 300_000\n this.modelListCacheMs = options.modelListCacheMs ?? 60_000\n this.maxConcurrentRuns = parsePositiveInteger(\n options.maxConcurrentRuns ??\n Number(\n process.env.CURSOR_AGENT_MAX_CONCURRENT || defaultMaxConcurrentRuns,\n ),\n defaultMaxConcurrentRuns,\n )\n this.yolo = options.yolo ?? process.env.CURSOR_AGENT_YOLO !== \"0\"\n }\n\n async listModels(\n options: { refresh?: boolean } = {},\n ): Promise<BridgeModel[]> {\n const now = Date.now()\n if (\n !options.refresh &&\n this.modelCache &&\n this.modelCache.expiresAt > now\n ) {\n return this.modelCache.models\n }\n\n const models = await new Promise<BridgeModel[]>((resolve, reject) => {\n execFile(\n this.agentPath,\n [\"--list-models\"],\n { timeout: 30_000 },\n (error, stdout) => {\n if (error) {\n reject(error)\n return\n }\n resolve(parseAgentModelList(stdout))\n },\n )\n })\n this.modelCache = { expiresAt: now + this.modelListCacheMs, models }\n return models\n }\n\n async run(\n options: CursorRunOptions,\n events: CursorRunEvents = {},\n ): Promise<CursorRunResult> {\n const release = await this.acquireRunPermit(options.signal)\n try {\n if (options.signal?.aborted) throw new Error(\"Request aborted\")\n return await this.runWithPermit(options, events)\n } finally {\n release()\n }\n }\n\n abortAll() {\n for (const child of this.activeChildren) terminateChild(child)\n }\n\n private acquireRunPermit(signal?: AbortSignal): Promise<RunPermit> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error(\"Request aborted\"))\n return\n }\n\n let queuedAcquire: (() => void) | undefined\n const onAbort = () => {\n if (queuedAcquire) {\n this.runQueue.splice(this.runQueue.indexOf(queuedAcquire), 1)\n }\n reject(new Error(\"Request aborted\"))\n }\n const acquire = () => {\n signal?.removeEventListener(\"abort\", onAbort)\n this.activeRuns += 1\n resolve(() => {\n this.activeRuns -= 1\n const next = this.runQueue.shift()\n if (next) next()\n })\n }\n\n if (this.activeRuns < this.maxConcurrentRuns) {\n acquire()\n return\n }\n\n queuedAcquire = acquire\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n this.runQueue.push(acquire)\n })\n }\n\n private runWithPermit(\n options: CursorRunOptions,\n events: CursorRunEvents,\n ): Promise<CursorRunResult> {\n return new Promise((resolve, reject) => {\n const args = [\n \"-p\",\n \"--output-format\",\n \"stream-json\",\n \"--stream-partial-output\",\n ]\n if (this.yolo) args.push(\"--yolo\")\n if (options.model !== \"auto\") args.push(\"--model\", options.model)\n\n const child = spawn(this.agentPath, args, {\n cwd: options.cwd ?? this.defaultCwd,\n env: process.env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n this.activeChildren.add(child)\n\n let buffer = \"\"\n let lastModel = options.model\n let lastAssistantText = \"\"\n let finalText: string | undefined\n let stderr = \"\"\n let settled = false\n let forceKillTimer: NodeJS.Timeout | undefined\n\n const timer = setTimeout(() => {\n forceKillTimer = terminateChild(child)\n settle(() =>\n reject(\n new Error(\n `Cursor Agent request timed out after ${this.timeoutMs}ms`,\n ),\n ),\n )\n }, this.timeoutMs)\n\n const onAbort = () => {\n forceKillTimer = terminateChild(child)\n settle(() => reject(new Error(\"Request aborted\")))\n }\n\n const settle = (fn: () => void) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (forceKillTimer && child.exitCode !== null)\n clearTimeout(forceKillTimer)\n options.signal?.removeEventListener(\"abort\", onAbort)\n this.activeChildren.delete(child)\n fn()\n }\n\n options.signal?.addEventListener(\"abort\", onAbort, { once: true })\n\n child.stdin.write(options.prompt)\n child.stdin.end()\n\n child.stdout.on(\"data\", (chunk) => {\n buffer += chunk.toString()\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() as string\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n const message = JSON.parse(trimmed) as Record<string, unknown>\n if (typeof message.model === \"string\") {\n lastModel = message.model\n events.onModel?.(lastModel)\n }\n\n const text = extractAssistantText(message)\n if (text && text !== lastAssistantText) {\n const delta = text.startsWith(lastAssistantText)\n ? text.slice(lastAssistantText.length)\n : text\n lastAssistantText = text\n events.onDelta?.(delta)\n }\n\n if (typeof message.result === \"string\") finalText = message.result\n } catch {\n // Cursor occasionally emits non-JSON progress lines. They are not protocol data.\n }\n }\n })\n\n child.stderr.on(\"data\", (chunk) => {\n stderr += chunk.toString()\n })\n\n child.on(\"error\", (error) => {\n settle(() => reject(error))\n })\n\n child.on(\"close\", (code) => {\n settle(() => {\n if (code === 0 && (finalText !== undefined || lastAssistantText)) {\n resolve({ text: finalText ?? lastAssistantText, model: lastModel })\n return\n }\n reject(\n new Error(stderr.trim() || `Cursor Agent exited with code ${code}`),\n )\n })\n })\n })\n }\n}\n\nfunction parsePositiveInteger(value: number, fallback: number) {\n return Number.isInteger(value) && value > 0 ? value : fallback\n}\n\nfunction terminateChild(child: ReturnType<typeof spawn>) {\n child.kill(\"SIGTERM\")\n return setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) {\n child.kill(\"SIGKILL\")\n }\n }, forceKillDelayMs)\n}\n\nfunction extractAssistantText(message: Record<string, unknown>): string {\n const nested = message.message\n if (!nested || typeof nested !== \"object\") return \"\"\n const content = (nested as Record<string, unknown>).content\n if (!Array.isArray(content)) return \"\"\n return content\n .map((part) => {\n if (!part || typeof part !== \"object\") return \"\"\n const record = part as Record<string, unknown>\n return record.type === \"text\" && typeof record.text === \"string\"\n ? record.text\n : \"\"\n })\n .join(\"\")\n}\n","","import { randomUUID } from \"node:crypto\"\n\nexport function createChatResponse(model: string, text: string) {\n return {\n id: `chatcmpl-${randomUUID().replaceAll(\"-\", \"\")}`,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n message: { role: \"assistant\", content: text },\n finish_reason: \"stop\",\n },\n ],\n usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n }\n}\n\nexport function createChatChunk(\n id: string,\n model: string,\n text: string,\n isFirst: boolean,\n) {\n return {\n id,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [\n {\n index: 0,\n delta: { role: isFirst ? \"assistant\" : undefined, content: text },\n finish_reason: null,\n },\n ],\n }\n}\n\nexport function createChatDoneChunk(id: string, model: string) {\n return {\n id,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model,\n choices: [{ index: 0, delta: {}, finish_reason: \"stop\" }],\n }\n}\n\nexport function createResponseObject(\n model: string,\n text: string,\n responseId = `resp_${randomUUID().replaceAll(\"-\", \"\")}`,\n itemId = `msg_${randomUUID().replaceAll(\"-\", \"\")}`,\n) {\n const item: {\n id: string\n type: \"message\"\n status: \"completed\"\n role: \"assistant\"\n content: Array<{\n type: \"output_text\"\n text: string\n annotations: unknown[]\n }>\n } = {\n id: itemId,\n type: \"message\",\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text, annotations: [] }],\n }\n\n return {\n id: responseId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n model,\n output: [item],\n usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },\n }\n}\n\nexport function createResponseStream(model: string) {\n const response = createResponseObject(model, \"\")\n const item = response.output[0]\n /* v8 ignore next -- createResponseObject always creates one output item. */\n if (!item) throw new Error(\"Responses output item was not created\")\n const part = item.content[0]\n /* v8 ignore next -- createResponseObject always creates one output text part. */\n if (!part) throw new Error(\"Responses output text part was not created\")\n\n return {\n response,\n item,\n part,\n events: [\n [\"response.created\", { ...response, status: \"in_progress\", output: [] }],\n [\n \"response.output_item.added\",\n {\n response_id: response.id,\n output_index: 0,\n item: { ...item, status: \"in_progress\", content: [] },\n },\n ],\n [\n \"response.content_part.added\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n part: { ...part, text: \"\" },\n },\n ],\n ] as const,\n }\n}\n\nexport function responseDeltaEvent(\n stream: ReturnType<typeof createResponseStream>,\n delta: string,\n) {\n return [\n \"response.output_text.delta\",\n {\n response_id: stream.response.id,\n item_id: stream.item.id,\n output_index: 0,\n content_index: 0,\n delta,\n },\n ] as const\n}\n\nexport function responseDoneEvents(\n stream: ReturnType<typeof createResponseStream>,\n model: string,\n text: string,\n) {\n const response = createResponseObject(\n model,\n text,\n stream.response.id,\n stream.item.id,\n )\n const item = response.output[0]\n /* v8 ignore next -- createResponseObject always creates one output item. */\n if (!item) throw new Error(\"Responses output item was not created\")\n const part = item.content[0]\n /* v8 ignore next -- createResponseObject always creates one output text part. */\n if (!part) throw new Error(\"Responses output text part was not created\")\n\n return [\n [\n \"response.output_text.done\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n text,\n },\n ],\n [\n \"response.content_part.done\",\n {\n response_id: response.id,\n item_id: item.id,\n output_index: 0,\n content_index: 0,\n part,\n },\n ],\n [\n \"response.output_item.done\",\n { response_id: response.id, output_index: 0, item },\n ],\n [\"response.completed\", { response }],\n ] as const\n}\n\nexport function responseTextEvents(model: string, text: string) {\n const stream = createResponseStream(model)\n return [\n ...stream.events,\n responseDeltaEvent(stream, text),\n ...responseDoneEvents(stream, model, text),\n ] as const\n}\n","import { randomUUID } from \"node:crypto\"\nimport http, { type IncomingMessage, type ServerResponse } from \"node:http\"\nimport packageJson from \"../package.json\" with { type: \"json\" }\nimport {\n messagesToPrompt,\n normalizeModel,\n responsesToMessages,\n} from \"./adapter/messages.js\"\nimport { toCodexModelCatalog, toOpenAIModelList } from \"./adapter/models.js\"\nimport {\n createChatChunk,\n createChatDoneChunk,\n createChatResponse,\n createResponseObject,\n createResponseStream,\n responseDeltaEvent,\n responseDoneEvents,\n} from \"./adapter/openai.js\"\nimport { CursorRunner } from \"./cursor/runner.js\"\nimport type {\n ChatCompletionRequest,\n ResponsesRequest,\n ServerConfig,\n} from \"./types.js\"\n\nconst packageVersion = packageJson.version\nconst defaultMaxBodyBytes = 1024 * 1024\n\nclass RequestError extends Error {\n readonly status: number\n readonly code: string\n\n constructor(status: number, code: string, message: string) {\n super(message)\n this.status = status\n this.code = code\n }\n}\n\nexport async function startServer(config: ServerConfig = {}) {\n const port = config.port ?? Number(process.env.PORT || 4646)\n const host = config.host ?? process.env.HOST ?? \"127.0.0.1\"\n const runner = new CursorRunner({\n ...(config.agentPath ? { agentPath: config.agentPath } : {}),\n ...(config.defaultCwd ? { defaultCwd: config.defaultCwd } : {}),\n })\n const maxBodyBytes = config.maxBodyBytes ?? defaultMaxBodyBytes\n\n const server = http.createServer(async (req, res) => {\n try {\n await route(req, res, runner, maxBodyBytes)\n } catch (error) {\n sendError(res, error)\n }\n })\n server.on(\"close\", () => runner.abortAll())\n\n await new Promise<void>((resolve, reject) => {\n server.listen(port, host, resolve)\n server.once(\"error\", reject)\n })\n\n return server\n}\n\nasync function route(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n setCors(res)\n const url = new URL(\n req.url ?? \"/\",\n `http://${req.headers.host ?? \"127.0.0.1\"}`,\n )\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204)\n res.end()\n return\n }\n\n if (req.method === \"GET\" && url.pathname === \"/health\") {\n sendJson(res, 200, {\n status: \"ok\",\n provider: \"cursor-agent-bridge\",\n version: packageVersion,\n })\n return\n }\n\n if (req.method === \"GET\" && url.pathname === \"/v1/models\") {\n const models = await runner.listModels({\n refresh: url.searchParams.get(\"refresh\") === \"1\",\n })\n const wantsCodexCatalog =\n url.searchParams.has(\"client_version\") ||\n url.searchParams.get(\"format\") === \"codex\"\n sendJson(\n res,\n 200,\n wantsCodexCatalog\n ? toCodexModelCatalog(models)\n : toOpenAIModelList(models),\n )\n return\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v1/chat/completions\") {\n await handleChat(req, res, runner, maxBodyBytes)\n return\n }\n\n if (req.method === \"POST\" && url.pathname === \"/v1/responses\") {\n await handleResponses(req, res, runner, maxBodyBytes)\n return\n }\n\n sendJson(res, 404, {\n error: {\n message: \"Not found\",\n type: \"invalid_request_error\",\n code: \"not_found\",\n },\n })\n}\n\nasync function handleChat(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n const body = (await readJson(req, maxBodyBytes)) as ChatCompletionRequest\n if (!Array.isArray(body.messages) || body.messages.length === 0) {\n sendJson(res, 400, {\n error: {\n message: \"messages is required and must be a non-empty array\",\n type: \"invalid_request_error\",\n code: \"invalid_messages\",\n },\n })\n return\n }\n\n const model = normalizeModel(body.model)\n const prompt = messagesToPrompt(body.messages)\n const abort = new AbortController()\n req.on(\"close\", () => abort.abort())\n\n if (body.stream === true) {\n const id = `chatcmpl-${randomUUID().replaceAll(\"-\", \"\")}`\n writeSseHeaders(res)\n let isFirst = true\n let lastModel = model\n let streamedText = \"\"\n try {\n const result = await runner.run(\n { model, prompt, signal: abort.signal },\n {\n onDelta: (text) => {\n streamedText += text\n res.write(\n `data: ${JSON.stringify(createChatChunk(id, lastModel, text, isFirst))}\\n\\n`,\n )\n isFirst = false\n },\n onModel: (nextModel) => {\n lastModel = nextModel\n },\n },\n )\n lastModel = result.model\n if (result.text !== streamedText) {\n const delta = result.text.startsWith(streamedText)\n ? result.text.slice(streamedText.length)\n : result.text\n if (delta) {\n res.write(\n `data: ${JSON.stringify(createChatChunk(id, lastModel, delta, isFirst))}\\n\\n`,\n )\n }\n }\n res.write(\n `data: ${JSON.stringify(createChatDoneChunk(id, lastModel))}\\n\\n`,\n )\n res.write(\"data: [DONE]\\n\\n\")\n } catch (error) {\n writeSseError(res, error)\n }\n res.end()\n return\n }\n\n const result = await runner.run({ model, prompt, signal: abort.signal })\n sendJson(res, 200, createChatResponse(result.model, result.text))\n}\n\nasync function handleResponses(\n req: IncomingMessage,\n res: ServerResponse,\n runner: CursorRunner,\n maxBodyBytes: number,\n) {\n const body = (await readJson(req, maxBodyBytes)) as ResponsesRequest\n const model = normalizeModel(body.model)\n const prompt = messagesToPrompt(responsesToMessages(body))\n const abort = new AbortController()\n req.on(\"close\", () => abort.abort())\n\n if (body.stream === false) {\n const result = await runner.run({ model, prompt, signal: abort.signal })\n sendJson(res, 200, createResponseObject(result.model, result.text))\n return\n }\n\n writeSseHeaders(res)\n const stream = createResponseStream(model)\n let lastModel = model\n let streamedText = \"\"\n for (const [event, data] of stream.events) {\n writeSseEvent(res, event, data)\n }\n try {\n const result = await runner.run(\n { model, prompt, signal: abort.signal },\n {\n onDelta: (text) => {\n streamedText += text\n const [event, data] = responseDeltaEvent(stream, text)\n writeSseEvent(res, event, data)\n },\n onModel: (nextModel) => {\n lastModel = nextModel\n },\n },\n )\n lastModel = result.model\n if (result.text !== streamedText) {\n const delta = result.text.startsWith(streamedText)\n ? result.text.slice(streamedText.length)\n : result.text\n if (delta) {\n streamedText += delta\n const [event, data] = responseDeltaEvent(stream, delta)\n writeSseEvent(res, event, data)\n }\n }\n for (const [event, data] of responseDoneEvents(\n stream,\n lastModel,\n result.text,\n )) {\n writeSseEvent(res, event, data)\n }\n } catch (error) {\n writeSseError(res, error)\n }\n res.end()\n}\n\nfunction readJson(\n req: IncomingMessage,\n maxBodyBytes = defaultMaxBodyBytes,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = \"\"\n let bytes = 0\n let tooLarge = false\n req.setEncoding(\"utf8\")\n req.on(\"data\", (chunk) => {\n if (tooLarge) return\n bytes += Buffer.byteLength(chunk)\n if (bytes > maxBodyBytes) {\n tooLarge = true\n reject(\n new RequestError(\n 413,\n \"payload_too_large\",\n `Request body exceeds ${maxBodyBytes} bytes`,\n ),\n )\n return\n }\n body += chunk\n })\n req.on(\"end\", () => {\n try {\n resolve(body ? JSON.parse(body) : {})\n } catch {\n reject(new RequestError(400, \"invalid_json\", \"Invalid JSON\"))\n }\n })\n req.on(\"error\", reject)\n })\n}\n\nfunction setCors(res: ServerResponse) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET,POST,OPTIONS\")\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n}\n\nfunction writeSseHeaders(res: ServerResponse) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n })\n}\n\nfunction writeSseEvent(\n res: ServerResponse,\n event: string,\n data: Record<string, unknown>,\n) {\n res.write(`event: ${event}\\n`)\n res.write(`data: ${JSON.stringify({ type: event, ...data })}\\n\\n`)\n}\n\nfunction writeSseError(res: ServerResponse, error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n res.write(\"event: error\\n\")\n res.write(\n `data: ${JSON.stringify({\n type: \"error\",\n error: {\n message,\n type: \"server_error\",\n code: \"internal_error\",\n },\n })}\\n\\n`,\n )\n}\n\nfunction sendError(res: ServerResponse, error: unknown) {\n /* v8 ignore next 4 -- last-resort guard for unexpected errors after SSE headers. */\n if (res.headersSent) {\n writeSseError(res, error)\n res.end()\n return\n }\n const status = error instanceof RequestError ? error.status : 500\n sendJson(res, status, {\n error: {\n message: error instanceof Error ? error.message : String(error),\n type:\n error instanceof RequestError\n ? \"invalid_request_error\"\n : \"server_error\",\n code: error instanceof RequestError ? error.code : \"internal_error\",\n },\n })\n}\n\nfunction sendJson(res: ServerResponse, status: number, value: unknown) {\n /* v8 ignore next -- this is a last-resort guard for errors after SSE headers. */\n if (res.headersSent) return\n const body = JSON.stringify(value)\n res.writeHead(status, {\n \"content-type\": \"application/json; charset=utf-8\",\n \"content-length\": Buffer.byteLength(body),\n })\n res.end(body)\n}\n"],"mappings":"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"}
|