codex-copilot-dx 0.4.20 → 0.4.21

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 CHANGED
@@ -93,8 +93,8 @@ Environment variables:
93
93
  | `CCDX_UPSTREAM_RETRY_DELAY_MS` | `300` | Initial upstream retry backoff in milliseconds; capped at `5000` |
94
94
  | `CCDX_LOG_PATH` | unset | Mirror terminal logs to a file; set to `1` for `~/.local/share/codex-copilot-dx/debug.log` |
95
95
  | `CCDX_LOG_LEVEL` | `info` | Set to `debug` to include upstream request attempts, status codes, retry causes, and timings |
96
- | `CCDX_IMG_MAX_DIM` | `2048` | Max long edge in pixels for optional image downscaling |
97
- | `CCDX_IMG_QUALITY` | `85` | WebP quality used when optional image re-encoding is available |
96
+ | `CCDX_IMG_MAX_DIM` | `2048` | Max long edge in pixels for image downscaling |
97
+ | `CCDX_IMG_QUALITY` | `85` | WebP quality used when re-encoding images |
98
98
  | `CCDX_IMG_MIN_BYTES` | `100000` | Images smaller than this are left untouched |
99
99
  | `CCDX_IMG_CONCURRENCY` | `4` | Concurrent image optimization tasks; values above `12` are capped at `12` |
100
100
  | `CCDX_DISABLE_IMG_OPT` | unset | Set to `1` to disable image optimization |
@@ -127,7 +127,7 @@ codex-copilot-dx usage
127
127
 
128
128
  Long computer-use sessions can accumulate screenshots inside the conversation history. Each screenshot is shipped on later turns, and GitHub Copilot's `/responses` endpoint can reject oversized requests with `413 Payload Too Large`.
129
129
 
130
- When the optional `sharp` package is available, the adapter downsamples embedded screenshots to long-edge <= 2048 px and re-encodes them as WebP before forwarding `/v1/responses`. `sharp` is not installed by default so the CLI stays lightweight and avoids native package install stalls; install it alongside `codex-copilot-dx` if you want this optimization.
130
+ The adapter automatically downsamples embedded screenshots to long-edge <= 2048 px and re-encodes them as WebP before forwarding `/v1/responses`.
131
131
 
132
132
  ## License
133
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,5 +27,9 @@
27
27
  "license": "MIT",
28
28
  "scripts": {
29
29
  "test": "node --test"
30
+ },
31
+ "dependencies": {
32
+ "gpt-tokenizer": "^3.4.0",
33
+ "sharp": "^0.34.5"
30
34
  }
31
35
  }
package/src/anthropic.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { encode } from "gpt-tokenizer";
2
3
 
3
4
  // Anthropic Messages API to OpenAI chat/completions translation.
4
5
  // Pure functions only; network calls stay in adapter.mjs and copilot.mjs.
@@ -237,19 +238,7 @@ export async function streamAnthropicFromLines(lineIterator, emit, model, option
237
238
  await emit("message_stop", { type: "message_stop" });
238
239
  }
239
240
 
240
- function estimateTokens(text) {
241
- if (!text) return 0;
242
- let asciiChars = 0;
243
- let nonAsciiChars = 0;
244
- for (const ch of text) {
245
- if (/\s/.test(ch)) continue;
246
- if (ch.codePointAt(0) <= 0x7f) asciiChars += 1;
247
- else nonAsciiChars += 1;
248
- }
249
- return Math.max(1, Math.ceil(asciiChars / 4) + nonAsciiChars);
250
- }
251
-
252
- // Copilot has no count_tokens endpoint; estimate locally without install-time dependencies.
241
+ // Copilot has no count_tokens endpoint; estimate locally with gpt-tokenizer.
253
242
  export function countTokens(body) {
254
243
  const parts = [];
255
244
  const sys = systemToText(body.system);
@@ -274,5 +263,5 @@ export function countTokens(body) {
274
263
  }
275
264
 
276
265
  const text = parts.join("\n");
277
- return { input_tokens: estimateTokens(text) };
266
+ return { input_tokens: encode(text).length };
278
267
  }
package/src/copilot.mjs CHANGED
@@ -21,8 +21,6 @@ const IMG_CONCURRENCY = parseImageConcurrency(process.env.CCDX_IMG_CONCURRENCY);
21
21
  const UPSTREAM_RETRIES = parseUpstreamRetries(process.env.CCDX_UPSTREAM_RETRIES);
22
22
  const UPSTREAM_RETRY_DELAY_MS = parseUpstreamRetryDelayMs(process.env.CCDX_UPSTREAM_RETRY_DELAY_MS);
23
23
  let sharpImport = null;
24
- let sharpUnavailable = false;
25
- let sharpUnavailableLogged = false;
26
24
 
27
25
  export function parseImageConcurrency(value) {
28
26
  const n = Number.parseInt(value, 10);
@@ -175,16 +173,9 @@ export async function fetchCopilotUpstream(
175
173
  }
176
174
 
177
175
  async function sharp() {
178
- if (sharpUnavailable) return null;
179
- try {
180
- sharpImport ||= import("sharp");
181
- const mod = await sharpImport;
182
- return mod.default || mod;
183
- } catch {
184
- sharpUnavailable = true;
185
- sharpImport = null;
186
- return null;
187
- }
176
+ sharpImport ||= import("sharp");
177
+ const mod = await sharpImport;
178
+ return mod.default || mod;
188
179
  }
189
180
 
190
181
  export async function optimizeImageDataUrl(dataUrl) {
@@ -198,13 +189,6 @@ export async function optimizeImageDataUrl(dataUrl) {
198
189
 
199
190
  try {
200
191
  const resize = await sharp();
201
- if (!resize) {
202
- if (!sharpUnavailableLogged) {
203
- sharpUnavailableLogged = true;
204
- console.warn(status("warn", "image optimization skipped because optional dependency sharp is not installed"));
205
- }
206
- return dataUrl;
207
- }
208
192
  const out = await resize(raw, { failOn: "none" })
209
193
  .rotate()
210
194
  .resize(IMG_MAX_DIM, IMG_MAX_DIM, { fit: "inside", withoutEnlargement: true })