codex-copilot-dx 0.6.20 → 0.7.0

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
@@ -2,11 +2,8 @@
2
2
 
3
3
  import { ensureAuth, openCodex } from "../src/launcher.mjs";
4
4
  import { ensureCodexConfig } from "../src/config.mjs";
5
- import { ensureClaudeConfig } from "../src/claude-config.mjs";
6
- import { applyClaudeDesktopConfig, formatClaudeDesktopApplyResult, generatedClaudeDesktopApiKey, loadManagedClaudeDesktopApiKey, syncManagedClaudeDesktopModels } from "../src/claude-desktop-config.mjs";
7
5
  import { startAdapter } from "../src/adapter.mjs";
8
6
  import { defaultCopilotClient, refreshVSCodeVersion } from "../src/copilot.mjs";
9
- import { claudeDesktopModelIds } from "../src/models.mjs";
10
7
  import { status } from "../src/status.mjs";
11
8
  import { configureLogging } from "../src/log.mjs";
12
9
  import { flushUsageWrites, printUsageSummary } from "../src/usage.mjs";
@@ -17,7 +14,6 @@ import { assertSafeAdapterHost, isLoopbackHost } from "../src/security.mjs";
17
14
  import { runInBackground } from "../src/startup.mjs";
18
15
  import { cliCommandName, cliHelp, parseAdapterProbeOptions, parseCliArgs, parseRuntimeOptions } from "../src/cli-options.mjs";
19
16
  import { formatAdapterStatus, readAdapterStatus } from "../src/cli-status.mjs";
20
- import { formatCliError } from "../src/cli-error.mjs";
21
17
  import { closeHttpServer } from "../src/shutdown.mjs";
22
18
  import { runAutoReviewModelCommand } from "../src/auto-review-model.mjs";
23
19
  import { autoReviewModelPreference } from "../src/user-settings.mjs";
@@ -54,6 +50,15 @@ if (CLI.command === "usage") {
54
50
  });
55
51
  process.exit(0);
56
52
  }
53
+ if (CLI.command === "retired") {
54
+ console.error(status("err", `${CLI.integration} integration was retired in ccdx 0.7.0.`));
55
+ if (CLI.integration === "PM Studio") {
56
+ console.error(status("info", "Update or reinstall PM Studio to remove an older CCDX app patch."));
57
+ } else {
58
+ console.error(status("info", "Remove any old CCDX endpoint from the client before using a different provider."));
59
+ }
60
+ process.exit(1);
61
+ }
57
62
  if (CLI.command === "auth") {
58
63
  try {
59
64
  const result = await runAuthCommand({
@@ -67,12 +72,6 @@ if (CLI.command === "auth") {
67
72
  output: process.stdout,
68
73
  });
69
74
  if (result.output) console.log(result.output);
70
- if (result.action === "login") {
71
- if (!result.changed) {
72
- console.log(status("ok", `Claude profile is already configured for ${result.identity?.login || result.identity?.id || "the saved account"}`));
73
- }
74
- console.log(status("warn", `Restart the running ${CLI_NAME} adapter to activate the Claude profile`));
75
- }
76
75
  process.exit(0);
77
76
  } catch (e) {
78
77
  console.error(status("err", e.message));
@@ -133,31 +132,6 @@ if (CLI.command === "update") {
133
132
  process.exit(1);
134
133
  }
135
134
  }
136
- if (CLI.command === "pms") {
137
- try {
138
- if (CLI.action === "status") {
139
- const { runPmStudioStatus } = await import("../src/pm-studio-status.mjs");
140
- const result = await runPmStudioStatus({
141
- commandName: CLI_NAME,
142
- format: CLI.outputFormat || "auto",
143
- output: process.stdout,
144
- });
145
- process.exit(result.ok ? 0 : 1);
146
- }
147
- if (CLI.action === "restore") {
148
- const { runPmStudioRestore } = await import("../src/pm-studio-setup.mjs");
149
- await runPmStudioRestore({ commandName: CLI_NAME });
150
- process.exit(0);
151
- }
152
- const { runPmStudioSetup } = await import("../src/pm-studio-setup.mjs");
153
- await runPmStudioSetup({ commandName: CLI_NAME });
154
- process.exit(0);
155
- } catch (e) {
156
- console.error(formatCliError(e));
157
- process.exit(1);
158
- }
159
- }
160
-
161
135
  let RUNTIME;
162
136
  try {
163
137
  RUNTIME = parseRuntimeOptions(process.env);
@@ -170,7 +144,6 @@ const ADAPTER_PORT = RUNTIME.adapterPort;
170
144
  const ADAPTER_HOST = RUNTIME.adapterHost;
171
145
  const MODEL_REFRESH_TIMEOUT_MS = RUNTIME.modelRefreshTimeoutMs;
172
146
  const EXISTING_ADAPTER_TIMEOUT_MS = RUNTIME.existingAdapterTimeoutMs;
173
- const CONFIGURE_CLAUDE_DESKTOP = CLI.configureClaudeDesktop || process.env.CCDX_CONFIGURE_CLAUDE_DESKTOP === "1";
174
147
  const LOGGING = configureLogging();
175
148
  let activeServer = null;
176
149
  let modelRefreshTimer = null;
@@ -181,24 +154,6 @@ if (LOGGING.filePath) {
181
154
  if (LOGGING.level === "debug") console.log(status("debug", "Debug logging enabled"));
182
155
  }
183
156
 
184
- function currentClaudeDesktopApiKey() {
185
- return String(process.env.CCDX_CLAUDE_DESKTOP_API_KEY || process.env.CCDX_PROXY_API_KEY || "").trim();
186
- }
187
-
188
- function syncClaudeDesktopProfileModels(modelDefs) {
189
- const result = syncManagedClaudeDesktopModels({
190
- port: ADAPTER_PORT,
191
- host: ADAPTER_HOST,
192
- modelIds: claudeDesktopModelIds(process.env, { modelDefs }),
193
- });
194
- if (result.updated) {
195
- console.log(status("ok", `Updated Claude App profile models: ${result.modelIds.join(", ")}`));
196
- } else if (result.error) {
197
- console.log(status("warn", `Could not update Claude App profile models (${result.error.message})`));
198
- }
199
- return result;
200
- }
201
-
202
157
  async function reuseRunningAdapterIfAvailable() {
203
158
  const running = await checkRunningAdapter({
204
159
  port: ADAPTER_PORT,
@@ -213,25 +168,6 @@ async function reuseRunningAdapterIfAvailable() {
213
168
 
214
169
  console.log(status("ok", `Using existing adapter at ${running.baseUrl}`));
215
170
  ensureCodexConfig(ADAPTER_PORT, { host: ADAPTER_HOST });
216
- ensureClaudeConfig(ADAPTER_PORT, { host: ADAPTER_HOST });
217
-
218
- if (CONFIGURE_CLAUDE_DESKTOP) {
219
- const claudeDesktopApiKey = currentClaudeDesktopApiKey();
220
- if (claudeDesktopApiKey) {
221
- const result = applyClaudeDesktopConfig({
222
- port: ADAPTER_PORT,
223
- host: ADAPTER_HOST,
224
- gatewayApiKey: claudeDesktopApiKey,
225
- modelIds: claudeDesktopModelIds(process.env),
226
- });
227
- console.log(status("ok", `Configured Claude App gateway profile at ${result.baseUrl}`));
228
- console.log(formatClaudeDesktopApplyResult(result));
229
- } else {
230
- console.log(status("warn", "Existing adapter is running; skip Claude App profile update unless CCDX_CLAUDE_DESKTOP_API_KEY or CCDX_PROXY_API_KEY is set"));
231
- }
232
- } else {
233
- console.log(status("ok", "Claude App support available with --configure-claude-app"));
234
- }
235
171
 
236
172
  await openCodex();
237
173
  console.log(`
@@ -249,14 +185,13 @@ if (CLI.command === "doctor") {
249
185
  host: ADAPTER_HOST,
250
186
  online: CLI.online,
251
187
  compat: CLI.compat,
252
- profile: CLI.profile,
253
188
  });
254
189
  process.exit(checks.some((check) => check.kind === "err") ? 1 : 0);
255
190
  }
256
191
 
257
192
  console.log(`
258
193
  ${CLI_BANNER}
259
- Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot
194
+ Use Codex Desktop and ChatGPT App with GitHub Copilot
260
195
  `);
261
196
 
262
197
  async function printUpdateNotice() {
@@ -279,84 +214,35 @@ try {
279
214
  await ensureAuth();
280
215
 
281
216
  const profileRuntime = createProfileRuntime({ codexClient: defaultCopilotClient });
282
- if (profileRuntime.claudeMode === "isolated") {
283
- if (profileRuntime.claudeProfile.valid) {
284
- const account = profileRuntime.claudeProfile.identity?.login
285
- || profileRuntime.claudeProfile.identity?.id
286
- || "the saved account";
287
- console.log(status("ok", `Claude requests will use the isolated GitHub account ${account}`));
288
- } else {
289
- console.log(status("warn", `Claude profile is configured but invalid (${profileRuntime.claudeProfile.reason}); Codex remains available, but Claude requests will fail until you run ${CLI_NAME} auth login claude --reauth and restart`));
290
- }
291
- }
292
-
293
217
  const profileModels = createProfileModelRuntime({
294
218
  codexClient: profileRuntime.codexClient,
295
- claudeClient: profileRuntime.claudeClient,
296
- claudeMode: profileRuntime.claudeMode,
297
219
  codexCredentialFingerprint: profileRuntime.codexCredentialFingerprint,
298
- claudeCredentialFingerprint: profileRuntime.claudeCredentialFingerprint,
299
220
  timeoutMs: MODEL_REFRESH_TIMEOUT_MS,
300
221
  commandName: CLI_NAME,
301
222
  autoReviewModelResolver: () => autoReviewModelPreference().model,
302
- onClaudeModelsChanged: syncClaudeDesktopProfileModels,
303
223
  });
304
224
 
305
225
  // Refresh the VS Code version in the background; fallback is non-blocking.
306
226
  void refreshVSCodeVersion();
307
- const modelInitialization = await profileModels.initialize();
308
- const claudeDesktopModelDefs = modelInitialization.claude.modelDefs;
309
- syncClaudeDesktopProfileModels(claudeDesktopModelDefs);
227
+ await profileModels.initialize();
310
228
 
311
229
  if (!isLoopbackHost(ADAPTER_HOST)) {
312
230
  console.log(status("warn", `ADAPTER_HOST=${ADAPTER_HOST} exposes the adapter beyond loopback because CCDX_ALLOW_LAN=1 is set. Use only on trusted networks.`));
313
231
  }
314
232
 
315
- const configuredClaudeDesktopApiKey = currentClaudeDesktopApiKey();
316
- const restoredClaudeDesktopApiKey = configuredClaudeDesktopApiKey ? "" : loadManagedClaudeDesktopApiKey({
317
- port: ADAPTER_PORT,
318
- host: ADAPTER_HOST,
319
- });
320
- if (restoredClaudeDesktopApiKey) {
321
- console.log(status("ok", "Restored Claude App gateway key from the managed profile"));
322
- }
323
- const claudeDesktopApiKey = configuredClaudeDesktopApiKey
324
- || restoredClaudeDesktopApiKey
325
- || (CONFIGURE_CLAUDE_DESKTOP ? generatedClaudeDesktopApiKey() : "");
326
-
327
233
  // Start the in-process adapter.
328
234
  activeServer = await startAdapter(ADAPTER_PORT, ADAPTER_HOST, {
329
- claudeDesktopApiKey,
330
- claudeDesktopModelDefs,
331
235
  autoReviewModelResolver: () => autoReviewModelPreference().model,
332
236
  codexClient: profileRuntime.codexClient,
333
- claudeClient: profileRuntime.claudeClient,
334
- claudeProfile: profileRuntime.claudeProfile,
335
237
  codexModelRegistry: profileModels.codexRegistry,
336
- claudeModelRegistry: profileModels.claudeRegistry,
337
- claudeMode: profileRuntime.claudeMode,
338
- isClaudeProfileCurrent: profileRuntime.isClaudeProfileCurrent,
339
238
  showRequestId: CLI.showRequestId,
340
239
  upstreamTimeoutMs: RUNTIME.upstreamTimeoutMs,
341
240
  streamHandshakeTimeoutMs: RUNTIME.streamHandshakeTimeoutMs,
342
241
  streamIdleTimeoutMs: RUNTIME.streamIdleTimeoutMs,
343
242
  });
344
243
 
345
- // Point Codex and Claude Code at the adapter.
244
+ // Point Codex and compatible ChatGPT App builds at the adapter.
346
245
  ensureCodexConfig(ADAPTER_PORT, { host: ADAPTER_HOST });
347
- ensureClaudeConfig(ADAPTER_PORT, { host: ADAPTER_HOST });
348
- if (CONFIGURE_CLAUDE_DESKTOP) {
349
- const result = applyClaudeDesktopConfig({
350
- port: ADAPTER_PORT,
351
- host: ADAPTER_HOST,
352
- gatewayApiKey: claudeDesktopApiKey,
353
- modelIds: claudeDesktopModelIds(process.env, { modelDefs: claudeDesktopModelDefs }),
354
- });
355
- console.log(status("ok", `Configured Claude App gateway profile at ${result.baseUrl}`));
356
- console.log(formatClaudeDesktopApplyResult(result));
357
- } else {
358
- console.log(status("ok", "Claude App support available with --configure-claude-app"));
359
- }
360
246
 
361
247
  // Launch Codex when available.
362
248
  await openCodex();
@@ -371,10 +257,8 @@ try {
371
257
  modelRefreshTimer.unref?.();
372
258
  }
373
259
 
374
- const readyClients = ["Codex App", "Claude Code"];
375
- if (claudeDesktopApiKey) readyClients.splice(1, 0, "Claude App");
376
260
  console.log(`
377
- ${status("ok", `Ready, ${readyClients.join(", ")} ${readyClients.length === 1 ? "is" : "are"} ready to use`)}
261
+ ${status("ok", "Ready, Codex App and ChatGPT App are ready to use")}
378
262
 
379
263
  Adapter: ${adapterBaseUrl(ADAPTER_HOST, ADAPTER_PORT)}
380
264
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.6.20",
4
- "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
3
+ "version": "0.7.0",
4
+ "description": "Use Codex Desktop and ChatGPT App with GitHub Copilot",
5
5
  "author": "Dale Xiao",
6
6
  "type": "module",
7
7
  "bin": {
@@ -18,11 +18,6 @@
18
18
  "codex",
19
19
  "copilot",
20
20
  "github",
21
- "claude",
22
- "claude-app",
23
- "claude-code",
24
- "claude-desktop",
25
- "anthropic",
26
21
  "openai",
27
22
  "proxy",
28
23
  "responses-api"
@@ -51,7 +46,6 @@
51
46
  "prepublishOnly": "npm run verify"
52
47
  },
53
48
  "dependencies": {
54
- "gpt-tokenizer": "^3.4.0",
55
49
  "sharp": "^0.35.3"
56
50
  }
57
51
  }
@@ -8,7 +8,6 @@ import { setTimeout as delay } from "node:timers/promises";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import sharp from "sharp";
10
10
  import { optimizeImagesInBody, prepareResponsesPayload } from "../src/image-optimization.mjs";
11
- import { createPmStudioModelRouter } from "../src/profile-routing.mjs";
12
11
 
13
12
  const packageRoot = fileURLToPath(new URL("../", import.meta.url));
14
13
 
@@ -23,14 +22,6 @@ function runProbe(source) {
23
22
  return JSON.parse(result.stdout.trim());
24
23
  }
25
24
 
26
- function median(values) {
27
- const ordered = [...values].sort((left, right) => left - right);
28
- const middle = Math.floor(ordered.length / 2);
29
- return ordered.length % 2 === 0
30
- ? (ordered[middle - 1] + ordered[middle]) / 2
31
- : ordered[middle];
32
- }
33
-
34
25
  function sseLinearityProbe() {
35
26
  return runProbe(`
36
27
  import { webStreamLines } from "./src/stream.mjs";
@@ -220,38 +211,6 @@ function largePayloadProbe(targetMiB, concurrency) {
220
211
  }
221
212
  }
222
213
 
223
- function largeTokenCountProbe(mode) {
224
- const samples = Array.from({ length: 3 }, () => runProbe(`
225
- const text = "a ".repeat(2 * 1024 * 1024);
226
- const started = performance.now();
227
- let tokens;
228
- if (${JSON.stringify(mode)} === "proxy") {
229
- const { countTokens } = await import("./src/anthropic.mjs");
230
- tokens = (await countTokens({ messages: [{ role: "user", content: text }] })).input_tokens;
231
- } else {
232
- const { encode } = await import("gpt-tokenizer");
233
- const encoded = encode(text);
234
- tokens = encoded.length;
235
- }
236
- const memory = process.memoryUsage();
237
- process.stdout.write(JSON.stringify({
238
- input_mib: +(Buffer.byteLength(text) / 1048576).toFixed(1),
239
- tokens,
240
- elapsed_ms: +(performance.now() - started).toFixed(1),
241
- rss_mib: +(memory.rss / 1048576).toFixed(1),
242
- heap_used_mib: +(memory.heapUsed / 1048576).toFixed(1),
243
- }));
244
- `));
245
- return {
246
- input_mib: samples[0].input_mib,
247
- tokens: samples[0].tokens,
248
- elapsed_ms: +median(samples.map((sample) => sample.elapsed_ms)).toFixed(1),
249
- rss_mib: +median(samples.map((sample) => sample.rss_mib)).toFixed(1),
250
- heap_used_mib: +median(samples.map((sample) => sample.heap_used_mib)).toFixed(1),
251
- samples: samples.length,
252
- };
253
- }
254
-
255
214
  function toolOutputParseCacheProbe() {
256
215
  return runProbe(`
257
216
  import { enforceResponsesImageLimit } from "./src/responses-image-limit.mjs";
@@ -355,29 +314,6 @@ const adapterImport = runProbe(`
355
314
  }));
356
315
  `);
357
316
 
358
- const tokenCount = runProbe(`
359
- const importStarted = performance.now();
360
- const { countTokens } = await import("./src/anthropic.mjs");
361
- const importMs = performance.now() - importStarted;
362
- const body = { messages: [{ role: "user", content: "a ".repeat(50000) }] };
363
- const firstStarted = performance.now();
364
- const first = await countTokens(body);
365
- const firstMs = performance.now() - firstStarted;
366
- const warmStarted = performance.now();
367
- const warm = await countTokens(body);
368
- const warmMs = performance.now() - warmStarted;
369
- globalThis.gc?.();
370
- const memory = process.memoryUsage();
371
- console.log(JSON.stringify({
372
- module_import_ms: +importMs.toFixed(1),
373
- first_call_ms: +firstMs.toFixed(1),
374
- warm_call_ms: +warmMs.toFixed(1),
375
- input_tokens: first.input_tokens,
376
- deterministic: first.input_tokens === warm.input_tokens,
377
- rss_mib: +(memory.rss / 1048576).toFixed(1),
378
- }));
379
- `);
380
-
381
317
  const repeatedImage = "data:image/png;base64,QUJDRA==";
382
318
  const duplicateBody = {
383
319
  input: [{
@@ -402,34 +338,6 @@ const duplicateImages = {
402
338
  optimizer_calls: duplicateCalls,
403
339
  };
404
340
 
405
- const routingCatalog = {
406
- data: Array.from({ length: 1000 }, (_, index) => index % 5 === 0
407
- ? {
408
- id: `claude-benchmark-${index}`,
409
- vendor: "Anthropic",
410
- model_picker_enabled: true,
411
- supported_endpoints: ["/chat/completions"],
412
- }
413
- : { id: `gpt-benchmark-${index}`, vendor: "OpenAI", supported_endpoints: ["/responses"] }),
414
- };
415
- const modelRouter = createPmStudioModelRouter({
416
- getCatalog: () => routingCatalog,
417
- isClaudeEnabled: () => true,
418
- });
419
- const routingIterations = 100000;
420
- const routingStarted = performance.now();
421
- let claudeClassifications = 0;
422
- for (let index = 0; index < routingIterations; index += 1) {
423
- if (modelRouter.classify("claude-benchmark-0") === "claude") claudeClassifications += 1;
424
- }
425
- const pmModelRouting = {
426
- catalog_models: routingCatalog.data.length,
427
- iterations: routingIterations,
428
- elapsed_ms: +(performance.now() - routingStarted).toFixed(1),
429
- claude_classifications: claudeClassifications,
430
- availability_rebuilds: modelRouter.diagnostics().rebuilds,
431
- };
432
-
433
341
  const pixels = deterministicPixels(1024 * 1024 * 3, 0x87654321);
434
342
  const webp = await sharp(pixels, { raw: { width: 1024, height: 1024, channels: 3 } })
435
343
  .webp({ quality: 100 })
@@ -474,18 +382,12 @@ const report = {
474
382
  ? "Relative performance and resource checks; absolute timings vary by machine."
475
383
  : "Report-only benchmark; timings vary by machine and are not pass/fail thresholds.",
476
384
  adapter_import: adapterImport,
477
- token_count_100kb: tokenCount,
478
385
  duplicate_images: duplicateImages,
479
- pm_model_routing: pmModelRouting,
480
386
  oversized_payload: oversizedPayload,
481
387
  };
482
388
 
483
389
  if (checkMode) {
484
390
  report.sse_linearity = sseLinearityProbe();
485
- report.large_token_count = {
486
- proxy: largeTokenCountProbe("proxy"),
487
- materialized_array: largeTokenCountProbe("array"),
488
- };
489
391
  report.tool_output_parse_cache = toolOutputParseCacheProbe();
490
392
  report.large_payload_peak = [
491
393
  largePayloadProbe(5, 4),
@@ -507,22 +409,6 @@ console.log(JSON.stringify(report, null, 2));
507
409
 
508
410
  if (checkMode) {
509
411
  const failures = [];
510
- if (report.pm_model_routing.availability_rebuilds !== 1
511
- || report.pm_model_routing.claude_classifications !== report.pm_model_routing.iterations) {
512
- failures.push("PM Studio model routing did not reuse the memoized catalog classification");
513
- }
514
- const proxy = report.large_token_count.proxy;
515
- const array = report.large_token_count.materialized_array;
516
- if (proxy.tokens !== array.tokens) failures.push(`token counts differ: proxy=${proxy.tokens} array=${array.tokens}`);
517
- if (proxy.elapsed_ms > array.elapsed_ms * 1.25) {
518
- failures.push(`token counter is materially slower: proxy=${proxy.elapsed_ms}ms array=${array.elapsed_ms}ms`);
519
- }
520
- if (proxy.rss_mib > array.rss_mib * 0.9) {
521
- failures.push(`token counter RSS regression: proxy=${proxy.rss_mib}MiB array=${array.rss_mib}MiB`);
522
- }
523
- if (proxy.heap_used_mib > array.heap_used_mib * 0.75) {
524
- failures.push(`token counter heap regression: proxy=${proxy.heap_used_mib}MiB array=${array.heap_used_mib}MiB`);
525
- }
526
412
  if (report.tool_output_parse_cache.parse_calls !== 1) {
527
413
  failures.push(`stringified tool output parsed ${report.tool_output_parse_cache.parse_calls} times instead of once`);
528
414
  }
package/src/adapter.mjs CHANGED
@@ -1,8 +1,6 @@
1
1
  import http from "node:http";
2
- import { createAnthropicCountTokensHandler, createAnthropicMessagesHandler } from "./anthropic-handler.mjs";
3
2
  import { defaultCopilotClient } from "./copilot.mjs";
4
3
  import { isValidModelList } from "./model-cache.mjs";
5
- import { claudeDesktopModelsResponse } from "./models.mjs";
6
4
  import {
7
5
  ADAPTER_STATUS_PATH,
8
6
  classifyAdapterRoute,
@@ -11,11 +9,9 @@ import {
11
9
  runtimeStatusPayload,
12
10
  } from "./observability.mjs";
13
11
  import { createRequestId, runWithRequestContext } from "./request-context.mjs";
14
- import { createPmStudioRelayHandler, PM_STUDIO_RELAY_PREFIX } from "./pm-studio-relay.mjs";
15
12
  import { createStreamPerformanceMetrics } from "./stream-performance.mjs";
16
13
  import { status } from "./status.mjs";
17
14
  import { createTerminalActivityIndicator } from "./terminal-activity.mjs";
18
- import { createTokenCounterService } from "./token-counter-service.mjs";
19
15
  import { ADAPTER_HEALTH_PATH, adapterHealthPayload } from "./running-adapter.mjs";
20
16
  import { createResponsesCompactHandler, createResponsesHandler } from "./responses-handler.mjs";
21
17
  import { createResponsesImagePressureController } from "./responses-image-pressure.mjs";
@@ -63,17 +59,19 @@ export function requestPath(reqUrl) {
63
59
  return new URL(reqUrl || "/", "http://localhost").pathname;
64
60
  }
65
61
 
66
- function bearerToken(headers = {}) {
67
- const value = headers.authorization || headers.Authorization || "";
68
- const match = /^Bearer\s+(.+)$/i.exec(String(value));
69
- return match?.[1]?.trim() || "";
70
- }
71
-
72
- export function shouldServeClaudeDesktopModels(req, claudeDesktopApiKey) {
73
- if (!claudeDesktopApiKey || claudeDesktopApiKey === "dummy") return false;
74
- const token = bearerToken(req.headers);
75
- const xApiKey = req.headers?.["x-api-key"] || req.headers?.["X-Api-Key"] || req.headers?.["X-API-Key"] || "";
76
- return token === claudeDesktopApiKey || xApiKey === claudeDesktopApiKey;
62
+ function sendRetiredClaudeSurface(res) {
63
+ res.writeHead(410, {
64
+ "Content-Type": "application/json",
65
+ "Cache-Control": "no-store",
66
+ });
67
+ res.end(JSON.stringify({
68
+ type: "error",
69
+ error: {
70
+ type: "not_found_error",
71
+ code: "ccdx_claude_retired",
72
+ message: "Claude App and Claude Code support was retired in ccdx 0.7.0.",
73
+ },
74
+ }));
77
75
  }
78
76
 
79
77
  function modelListCanUseLastKnownGood(statusCode) {
@@ -93,50 +91,18 @@ function sendLastKnownGoodModels(res, modelRegistry) {
93
91
  // Shared request handler. Keeping this separate from the listener makes the
94
92
  // complete HTTP routing layer testable without opening a local port.
95
93
  export function createAdapterHandler(options = {}) {
96
- const claudeDesktopApiKey = options.claudeDesktopApiKey
97
- || process.env.CCDX_CLAUDE_DESKTOP_API_KEY
98
- || process.env.CCDX_PROXY_API_KEY
99
- || "";
100
94
  const codexClient = options.codexClient || defaultCopilotClient;
101
- const hasExplicitClaudeMode = Object.hasOwn(options, "claudeMode");
102
- if (hasExplicitClaudeMode && !["inherited", "isolated"].includes(options.claudeMode)) {
103
- throw new Error(`Unsupported Claude mode: ${options.claudeMode}`);
104
- }
105
- if (options.claudeMode === "isolated"
106
- && (!options.claudeClient || options.claudeClient === codexClient)) {
107
- throw new Error("Isolated Claude mode requires a distinct Claude client");
108
- }
109
- if (options.claudeMode === "inherited"
110
- && options.claudeClient && options.claudeClient !== codexClient) {
111
- throw new Error("Inherited Claude mode requires the Codex client");
112
- }
113
- const claudeClient = options.claudeClient || codexClient;
114
- // chatCompletionsFn is retained as a compatibility injection and continues to
115
- // override both surfaces in existing tests and embedders. New dual-profile
116
- // callers should pass codexClient/claudeClient instead.
117
- const codexChatCompletionsFn = options.codexChatCompletionsFn
95
+ // Keep the established injection names because Responses may still use the
96
+ // internal Chat Completions compatibility path for upstream models that need it.
97
+ const chatCompletionsFn = options.codexChatCompletionsFn
118
98
  || options.chatCompletionsFn
119
99
  || codexClient.chatCompletions;
120
- const claudeChatCompletionsFn = options.claudeChatCompletionsFn
121
- || options.chatCompletionsFn
122
- || claudeClient.chatCompletions;
123
100
  const responsesFn = options.responsesFn || codexClient.responses;
124
101
  const responsesCompactFn = options.responsesCompactFn || codexClient.responsesCompact;
125
102
  const listModelsFn = options.listModelsFn || codexClient.listModels;
126
103
  const getCachedModelEndpointsFn = options.getCachedModelEndpointsFn
127
104
  || codexClient.getCachedModelEndpoints;
128
- const claudeMode = hasExplicitClaudeMode
129
- ? options.claudeMode
130
- : claudeClient === codexClient ? "inherited" : "isolated";
131
- if (claudeMode === "isolated" && options.chatCompletionsFn) {
132
- throw new Error("Isolated Claude mode cannot use the shared chatCompletionsFn override; pass codexChatCompletionsFn or claudeChatCompletionsFn explicitly");
133
- }
134
105
  const codexModelRegistry = options.codexModelRegistry || options.modelRegistry;
135
- const claudeModelRegistry = options.claudeModelRegistry || options.modelRegistry || codexModelRegistry;
136
- if (claudeMode === "isolated"
137
- && (!options.claudeModelRegistry || claudeModelRegistry === codexModelRegistry)) {
138
- throw new Error("Isolated Claude mode requires a distinct Claude model registry");
139
- }
140
106
  const openAIModelEnv = options.openAIModelEnv || process.env;
141
107
  const upstreamTimeoutMs = parsePositiveInteger(options.upstreamTimeoutMs, ADAPTER_RUNTIME_CONFIG.upstreamTimeoutMs);
142
108
  const streamHandshakeTimeoutMs = parsePositiveInteger(options.streamHandshakeTimeoutMs, ADAPTER_RUNTIME_CONFIG.streamHandshakeTimeoutMs);
@@ -146,19 +112,10 @@ export function createAdapterHandler(options = {}) {
146
112
  const requestMetrics = options.requestMetrics || createRequestMetrics();
147
113
  const streamPerformanceMetrics = options.streamPerformanceMetrics || createStreamPerformanceMetrics();
148
114
  const imagePressure = options.imagePressure || createResponsesImagePressureController();
149
- const ownsTokenCounterService = !options.tokenCounterService;
150
- const tokenCounterService = options.tokenCounterService || createTokenCounterService({
151
- timeoutMs: options.countTokensTimeoutMs,
152
- maxQueued: options.countTokensMaxQueued,
153
- });
154
- const claudeDesktopModelOptions = () => {
155
- const modelDefs = claudeModelRegistry?.modelDefs || options.claudeDesktopModelDefs;
156
- return Array.isArray(modelDefs) ? { modelDefs } : {};
157
- };
158
115
  const responsesHandler = createResponsesHandler({
159
116
  acquireRequest,
160
117
  autoReviewModelResolver: options.autoReviewModelResolver,
161
- chatCompletionsFn: codexChatCompletionsFn,
118
+ chatCompletionsFn,
162
119
  getCachedModelEndpointsFn,
163
120
  imagePressure,
164
121
  modelRegistry: codexModelRegistry,
@@ -184,34 +141,6 @@ export function createAdapterHandler(options = {}) {
184
141
  streamIdleTimeoutMs,
185
142
  upstreamTimeoutMs,
186
143
  });
187
- const anthropicCountTokensHandler = createAnthropicCountTokensHandler({
188
- acquireRequest,
189
- requestBodyTimeoutMs,
190
- countTokensFn: (body, countOptions) => tokenCounterService.count(body, countOptions),
191
- });
192
- const anthropicMessagesHandler = createAnthropicMessagesHandler({
193
- acquireRequest,
194
- chatCompletionsFn: claudeChatCompletionsFn,
195
- environment: process.env,
196
- modelOptions: claudeDesktopModelOptions,
197
- requestBodyTimeoutMs,
198
- streamHandshakeTimeoutMs,
199
- streamIdleTimeoutMs,
200
- upstreamTimeoutMs,
201
- });
202
- const pmStudioRelayHandler = createPmStudioRelayHandler({
203
- acquireRequest,
204
- claudeClient,
205
- claudeMode,
206
- claudeProfile: options.claudeProfile,
207
- claudeModelRegistry,
208
- fetchImpl: options.pmStudioFetchImpl,
209
- requestBodyTimeoutMs,
210
- streamHandshakeTimeoutMs,
211
- streamIdleTimeoutMs,
212
- upstreamTimeoutMs,
213
- });
214
-
215
144
  const dispatch = (req, res, pathname) => {
216
145
  if (req.method === "GET" && pathname === ADAPTER_STATUS_PATH) {
217
146
  if (!isLoopbackAddress(req.socket?.remoteAddress)) {
@@ -227,11 +156,7 @@ export function createAdapterHandler(options = {}) {
227
156
  imagePressure,
228
157
  modelRegistry: codexModelRegistry,
229
158
  codexClient,
230
- claudeClient,
231
159
  codexModelRegistry,
232
- claudeModelRegistry,
233
- claudeMode,
234
- isClaudeProfileCurrent: options.isClaudeProfileCurrent,
235
160
  })));
236
161
  return;
237
162
  }
@@ -242,10 +167,6 @@ export function createAdapterHandler(options = {}) {
242
167
  return;
243
168
  }
244
169
 
245
- if (pathname.startsWith(`${PM_STUDIO_RELAY_PREFIX}/`)) {
246
- return pmStudioRelayHandler(req, res, pathname);
247
- }
248
-
249
170
  if (req.method === "POST" && pathname === "/v1/responses") {
250
171
  return responsesHandler(req, res);
251
172
  }
@@ -255,11 +176,6 @@ export function createAdapterHandler(options = {}) {
255
176
  }
256
177
 
257
178
  if (req.method === "GET" && pathname === "/v1/models") {
258
- if (shouldServeClaudeDesktopModels(req, claudeDesktopApiKey)) {
259
- res.writeHead(200, { "Content-Type": "application/json" });
260
- res.end(JSON.stringify(claudeDesktopModelsResponse(process.env, claudeDesktopModelOptions())));
261
- return;
262
- }
263
179
  const abort = createRequestAbort(req, res);
264
180
  abort.setTimeout(upstreamTimeoutMs);
265
181
  listModelsFn({ signal: abort.signal })
@@ -296,11 +212,11 @@ export function createAdapterHandler(options = {}) {
296
212
  }
297
213
 
298
214
  if (req.method === "POST" && pathname === "/v1/messages/count_tokens") {
299
- return anthropicCountTokensHandler(req, res);
215
+ return sendRetiredClaudeSurface(res);
300
216
  }
301
217
 
302
218
  if (req.method === "POST" && pathname === "/v1/messages") {
303
- return anthropicMessagesHandler(req, res);
219
+ return sendRetiredClaudeSurface(res);
304
220
  }
305
221
 
306
222
  res.writeHead(404, { "Content-Type": "application/json" });
@@ -388,9 +304,6 @@ export function createAdapterHandler(options = {}) {
388
304
  containUnexpectedError(error);
389
305
  }
390
306
  };
391
- handler.cleanup = () => {
392
- if (ownsTokenCounterService) tokenCounterService.close();
393
- };
394
307
  return handler;
395
308
  }
396
309