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 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 (process.argv[2] === "usage") {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Use Codex Desktop app with your GitHub Copilot subscription",
5
5
  "type": "module",
6
6
  "bin": {
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
- let body = "";
282
- req.on("data", (c) => (body += c));
283
- req.on("end", async () => {
330
+ (async () => {
284
331
  try {
285
- const parsed = JSON.parse(body);
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
- let body = "";
331
- req.on("data", (c) => (body += c));
332
- req.on("end", async () => {
377
+ (async () => {
333
378
  try {
334
- const parsed = JSON.parse(body);
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
- let body = "";
358
- req.on("data", (c) => (body += c));
359
- req.on("end", () => {
402
+ (async () => {
360
403
  try {
361
- const parsed = JSON.parse(body);
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
- let body = "";
374
- req.on("data", (c) => (body += c));
375
- req.on("end", async () => {
416
+ (async () => {
376
417
  try {
377
- const parsed = JSON.parse(body);
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