codex-copilot-dx 0.4.2 → 0.4.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/bin/cli.mjs +7 -1
- package/package.json +1 -1
- package/src/adapter.mjs +61 -20
package/bin/cli.mjs
CHANGED
|
@@ -11,8 +11,14 @@ import { checkForUpdate, localPackageVersion } from "../src/version.mjs";
|
|
|
11
11
|
|
|
12
12
|
const ADAPTER_PORT = parseInt(process.env.ADAPTER_PORT || "2026");
|
|
13
13
|
const LOCAL_VERSION = localPackageVersion();
|
|
14
|
+
const command = process.argv[2];
|
|
14
15
|
|
|
15
|
-
if (
|
|
16
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
17
|
+
console.log(`codex-copilot-dx v${LOCAL_VERSION}`);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (command === "usage") {
|
|
16
22
|
await printUsageSummary();
|
|
17
23
|
process.exit(0);
|
|
18
24
|
}
|
package/package.json
CHANGED
package/src/adapter.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import * as zlib from "node:zlib";
|
|
3
5
|
import { chatCompletions, listModels, responses as copilotResponses, responsesCompact as copilotResponsesCompact } from "./copilot.mjs";
|
|
4
6
|
import { webStreamLines } from "./stream.mjs";
|
|
5
7
|
import { anthropicToChat, chatToAnthropic, streamAnthropicFromLines, countTokens } from "./anthropic.mjs";
|
|
@@ -19,6 +21,10 @@ const RESPONSES_ONLY = new Set([
|
|
|
19
21
|
|
|
20
22
|
const RESPONSE_HISTORY_LIMIT = 64;
|
|
21
23
|
const responseHistories = new Map();
|
|
24
|
+
const gunzipAsync = promisify(zlib.gunzip);
|
|
25
|
+
const inflateAsync = promisify(zlib.inflate);
|
|
26
|
+
const brotliDecompressAsync = promisify(zlib.brotliDecompress);
|
|
27
|
+
const zstdDecompressAsync = zlib.zstdDecompress ? promisify(zlib.zstdDecompress) : null;
|
|
22
28
|
|
|
23
29
|
export function requestPath(reqUrl) {
|
|
24
30
|
return new URL(reqUrl || "/", "http://localhost").pathname;
|
|
@@ -28,6 +34,45 @@ function cloneJson(value) {
|
|
|
28
34
|
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
async function readRequestBuffer(req) {
|
|
38
|
+
const chunks = [];
|
|
39
|
+
for await (const chunk of req) {
|
|
40
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
41
|
+
}
|
|
42
|
+
return Buffer.concat(chunks);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function decodeRequestBuffer(buffer, contentEncoding) {
|
|
46
|
+
const encodings = String(contentEncoding || "identity")
|
|
47
|
+
.split(",")
|
|
48
|
+
.map((v) => v.trim().toLowerCase())
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
|
|
51
|
+
let decoded = buffer;
|
|
52
|
+
for (const encoding of encodings.reverse()) {
|
|
53
|
+
if (encoding === "identity") continue;
|
|
54
|
+
if (encoding === "gzip" || encoding === "x-gzip") {
|
|
55
|
+
decoded = await gunzipAsync(decoded);
|
|
56
|
+
} else if (encoding === "deflate") {
|
|
57
|
+
decoded = await inflateAsync(decoded);
|
|
58
|
+
} else if (encoding === "br") {
|
|
59
|
+
decoded = await brotliDecompressAsync(decoded);
|
|
60
|
+
} else if (encoding === "zstd") {
|
|
61
|
+
if (!zstdDecompressAsync) throw new Error("Unsupported Content-Encoding: zstd");
|
|
62
|
+
decoded = await zstdDecompressAsync(decoded);
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error(`Unsupported Content-Encoding: ${encoding}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return decoded;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function readJsonBody(req) {
|
|
71
|
+
const buffer = await readRequestBuffer(req);
|
|
72
|
+
const decoded = await decodeRequestBuffer(buffer, req.headers?.["content-encoding"]);
|
|
73
|
+
return JSON.parse(decoded.toString("utf8"));
|
|
74
|
+
}
|
|
75
|
+
|
|
31
76
|
function responsesInputItems(input) {
|
|
32
77
|
if (input === undefined || input === null) return [];
|
|
33
78
|
if (typeof input === "string") {
|
|
@@ -64,6 +109,10 @@ export function prepareResponsesRequest(reqBody) {
|
|
|
64
109
|
|
|
65
110
|
delete body.previous_response_id;
|
|
66
111
|
delete body.store;
|
|
112
|
+
if (Array.isArray(body.tools)) {
|
|
113
|
+
body.tools = body.tools.filter((tool) => tool?.type !== "image_generation");
|
|
114
|
+
if (!body.tools.length) delete body.tools;
|
|
115
|
+
}
|
|
67
116
|
|
|
68
117
|
return { body, inputItems: body.input };
|
|
69
118
|
}
|
|
@@ -278,11 +327,9 @@ export function startAdapter(port = 2026) {
|
|
|
278
327
|
const pathname = requestPath(req.url);
|
|
279
328
|
|
|
280
329
|
if (req.method === "POST" && pathname === "/v1/responses") {
|
|
281
|
-
|
|
282
|
-
req.on("data", (c) => (body += c));
|
|
283
|
-
req.on("end", async () => {
|
|
330
|
+
(async () => {
|
|
284
331
|
try {
|
|
285
|
-
const parsed =
|
|
332
|
+
const parsed = await readJsonBody(req);
|
|
286
333
|
const prepared = prepareResponsesRequest(parsed);
|
|
287
334
|
prepared.surface = "responses";
|
|
288
335
|
const model = parsed.model || "unknown";
|
|
@@ -322,16 +369,14 @@ export function startAdapter(port = 2026) {
|
|
|
322
369
|
if (!res.headersSent) res.writeHead(400);
|
|
323
370
|
res.end(JSON.stringify({ error: e.message }));
|
|
324
371
|
}
|
|
325
|
-
});
|
|
372
|
+
})();
|
|
326
373
|
return;
|
|
327
374
|
}
|
|
328
375
|
|
|
329
376
|
if (req.method === "POST" && pathname === "/v1/responses/compact") {
|
|
330
|
-
|
|
331
|
-
req.on("data", (c) => (body += c));
|
|
332
|
-
req.on("end", async () => {
|
|
377
|
+
(async () => {
|
|
333
378
|
try {
|
|
334
|
-
const parsed =
|
|
379
|
+
const parsed = await readJsonBody(req);
|
|
335
380
|
const prepared = prepareResponsesRequest(parsed);
|
|
336
381
|
prepared.surface = "responses_compact";
|
|
337
382
|
const model = parsed.model || "unknown";
|
|
@@ -342,7 +387,7 @@ export function startAdapter(port = 2026) {
|
|
|
342
387
|
if (!res.headersSent) res.writeHead(400);
|
|
343
388
|
res.end(JSON.stringify({ error: e.message }));
|
|
344
389
|
}
|
|
345
|
-
});
|
|
390
|
+
})();
|
|
346
391
|
return;
|
|
347
392
|
}
|
|
348
393
|
|
|
@@ -354,27 +399,23 @@ export function startAdapter(port = 2026) {
|
|
|
354
399
|
}
|
|
355
400
|
|
|
356
401
|
if (req.method === "POST" && pathname === "/v1/messages/count_tokens") {
|
|
357
|
-
|
|
358
|
-
req.on("data", (c) => (body += c));
|
|
359
|
-
req.on("end", () => {
|
|
402
|
+
(async () => {
|
|
360
403
|
try {
|
|
361
|
-
const parsed =
|
|
404
|
+
const parsed = await readJsonBody(req);
|
|
362
405
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
363
406
|
res.end(JSON.stringify(countTokens(parsed)));
|
|
364
407
|
} catch (e) {
|
|
365
408
|
if (!res.headersSent) res.writeHead(400);
|
|
366
409
|
res.end(JSON.stringify({ error: e.message }));
|
|
367
410
|
}
|
|
368
|
-
});
|
|
411
|
+
})();
|
|
369
412
|
return;
|
|
370
413
|
}
|
|
371
414
|
|
|
372
415
|
if (req.method === "POST" && pathname === "/v1/messages") {
|
|
373
|
-
|
|
374
|
-
req.on("data", (c) => (body += c));
|
|
375
|
-
req.on("end", async () => {
|
|
416
|
+
(async () => {
|
|
376
417
|
try {
|
|
377
|
-
const parsed =
|
|
418
|
+
const parsed = await readJsonBody(req);
|
|
378
419
|
const model = parsed.model || "unknown";
|
|
379
420
|
console.log(status("info", `messages model=${model} stream=${!!parsed.stream}`));
|
|
380
421
|
const chatReq = anthropicToChat(parsed);
|
|
@@ -412,7 +453,7 @@ export function startAdapter(port = 2026) {
|
|
|
412
453
|
if (!res.headersSent) res.writeHead(400);
|
|
413
454
|
res.end(JSON.stringify({ error: e.message }));
|
|
414
455
|
}
|
|
415
|
-
});
|
|
456
|
+
})();
|
|
416
457
|
return;
|
|
417
458
|
}
|
|
418
459
|
|