codex-copilot-dx 0.6.21 → 0.7.1

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.
Files changed (42) hide show
  1. package/README.md +142 -323
  2. package/bin/cli.mjs +26 -130
  3. package/package.json +2 -8
  4. package/scripts/benchmark.mjs +0 -114
  5. package/src/adapter.mjs +22 -107
  6. package/src/auth-profile.mjs +18 -130
  7. package/src/cli-animation.mjs +108 -0
  8. package/src/cli-auth.mjs +44 -398
  9. package/src/cli-models.mjs +21 -49
  10. package/src/cli-options.mjs +53 -75
  11. package/src/cli-status.mjs +6 -23
  12. package/src/config.mjs +2 -8
  13. package/src/copilot.mjs +2 -2
  14. package/src/doctor.mjs +44 -291
  15. package/src/model-cache.mjs +4 -11
  16. package/src/models.mjs +0 -184
  17. package/src/observability.mjs +1 -38
  18. package/src/profile-model-runtime.mjs +61 -189
  19. package/src/profile-routing.mjs +2 -102
  20. package/src/profile-runtime.mjs +15 -68
  21. package/src/responses-handler.mjs +1 -1
  22. package/src/responses-proxy.mjs +1 -1
  23. package/src/running-adapter.mjs +2 -4
  24. package/src/stream-errors.mjs +3 -6
  25. package/src/stream-performance.mjs +1 -1
  26. package/src/terminal-activity.mjs +40 -34
  27. package/src/terminal-animation.mjs +249 -0
  28. package/src/usage.mjs +0 -31
  29. package/src/user-settings.mjs +50 -0
  30. package/src/anthropic-handler.mjs +0 -195
  31. package/src/anthropic.mjs +0 -387
  32. package/src/claude-config.mjs +0 -57
  33. package/src/claude-desktop-config.mjs +0 -317
  34. package/src/cli-error.mjs +0 -50
  35. package/src/pm-studio-asar.mjs +0 -1244
  36. package/src/pm-studio-bundle-content.mjs +0 -165
  37. package/src/pm-studio-relay.mjs +0 -563
  38. package/src/pm-studio-setup-lock.mjs +0 -336
  39. package/src/pm-studio-setup.mjs +0 -1581
  40. package/src/pm-studio-status.mjs +0 -369
  41. package/src/token-counter-service.mjs +0 -211
  42. package/src/token-counter-worker.mjs +0 -16
@@ -1,15 +1,10 @@
1
1
  import os from "node:os";
2
2
  import { validateGithubToken } from "./auth.mjs";
3
3
  import {
4
- AUTH_PROFILE_CLAUDE,
5
4
  AUTH_PROFILE_CODEX,
6
5
  readAuthProfileCredentials,
7
6
  } from "./auth-profile.mjs";
8
7
  import { buildHeaders, FALLBACK_VSCODE_VERSION, parseApiBase } from "./copilot.mjs";
9
- import {
10
- isClaudeCopilotCatalogEntry,
11
- isClaudeCopilotModel,
12
- } from "./models.mjs";
13
8
  import { status } from "./status.mjs";
14
9
  import {
15
10
  cliOutputFormat,
@@ -22,7 +17,6 @@ const DEFAULT_MODELS_TIMEOUT_MS = 10000;
22
17
  const SUPPORTED_ENDPOINTS = [
23
18
  ["responses", new Set(["/responses", "/v1/responses"])],
24
19
  ["chat", new Set(["/chat/completions"])],
25
- ["messages", new Set(["/v1/messages"])],
26
20
  ];
27
21
 
28
22
  function safeText(value, fallback = "") {
@@ -47,19 +41,15 @@ export function selectableCopilotModels(payload) {
47
41
  const models = [];
48
42
  const seen = new Set();
49
43
  for (const model of data) {
50
- if (isClaudeCopilotCatalogEntry(model)) {
51
- if (!isClaudeCopilotModel(model)) continue;
52
- } else {
53
- if (model?.model_picker_enabled !== true) continue;
54
- const policyState = safeText(model?.policy?.state).toLowerCase();
55
- if (policyState && policyState !== "enabled") continue;
56
- const capabilityType = safeText(model?.capabilities?.type).toLowerCase();
57
- if (capabilityType && capabilityType !== "chat") continue;
58
- }
44
+ if (model?.model_picker_enabled !== true) continue;
45
+ const policyState = safeText(model?.policy?.state).toLowerCase();
46
+ if (policyState && policyState !== "enabled") continue;
47
+ const capabilityType = safeText(model?.capabilities?.type).toLowerCase();
48
+ if (capabilityType && capabilityType !== "chat") continue;
59
49
 
60
50
  const id = safeText(model?.id);
61
51
  const endpoints = endpointLabels(model);
62
- if (!id || id === "codex-auto-review" || !endpoints.length || seen.has(id)) continue;
52
+ if (!id.startsWith("gpt-") || id === "codex-auto-review" || !endpoints.length || seen.has(id)) continue;
63
53
  seen.add(id);
64
54
  models.push({
65
55
  id,
@@ -87,27 +77,14 @@ async function responseText(response) {
87
77
  }
88
78
  }
89
79
 
90
- function readModelProfile(profile, home) {
91
- const requested = String(profile || AUTH_PROFILE_CODEX).trim().toLowerCase();
92
- if (requested !== AUTH_PROFILE_CODEX && requested !== AUTH_PROFILE_CLAUDE) {
93
- throw new Error(`Unsupported models profile: ${profile}`);
94
- }
95
-
80
+ function readModelCredentials(home) {
96
81
  let credentials;
97
82
  try {
98
- credentials = readAuthProfileCredentials(requested, { home });
83
+ credentials = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
99
84
  } catch (error) {
100
- const label = requested === AUTH_PROFILE_CLAUDE ? "Claude GitHub profile" : "GitHub token";
101
- throw new Error(`Could not read the ${label}: ${error.message}`);
85
+ throw new Error(`Could not read the GitHub token: ${error.message}`);
102
86
  }
103
87
  if (credentials.valid) return credentials;
104
-
105
- if (requested === AUTH_PROFILE_CLAUDE) {
106
- if (!credentials.configured) {
107
- throw new Error("Claude GitHub profile is not configured. Run ccdx auth login claude.");
108
- }
109
- throw new Error(`Claude GitHub profile is invalid (${credentials.reason || "unknown"}). Run ccdx auth login claude --reauth.`);
110
- }
111
88
  if (!credentials.configured) throw new Error("GitHub token not found. Start ccdx once to log in.");
112
89
  if (credentials.reason === "empty_token") throw new Error("GitHub token file is empty. Start ccdx again to log in.");
113
90
  throw new Error(`GitHub token is unavailable (${credentials.reason || "unknown"}). Start ccdx again to log in.`);
@@ -115,18 +92,15 @@ function readModelProfile(profile, home) {
115
92
 
116
93
  export async function fetchLiveCopilotModels({
117
94
  home = os.homedir(),
118
- profile = AUTH_PROFILE_CODEX,
119
95
  fetchImpl = fetch,
120
96
  timeoutMs = DEFAULT_MODELS_TIMEOUT_MS,
121
97
  vscodeVersion = FALLBACK_VSCODE_VERSION,
122
98
  } = {}) {
123
- const credentials = readModelProfile(profile, home);
124
- const githubToken = credentials.token;
125
-
99
+ const credentials = readModelCredentials(home);
126
100
  const controller = new AbortController();
127
101
  const timer = setTimeout(() => controller.abort(), timeoutMs);
128
102
  try {
129
- const validation = await validateGithubToken(githubToken, {
103
+ const validation = await validateGithubToken(credentials.token, {
130
104
  fetchImpl,
131
105
  signal: controller.signal,
132
106
  });
@@ -161,7 +135,7 @@ export async function fetchLiveCopilotModels({
161
135
  const catalog = selectableCopilotModels(payload);
162
136
  let upstreamHost = "GitHub Copilot";
163
137
  try { upstreamHost = new URL(apiBase).hostname; } catch {}
164
- return { ...catalog, profile: credentials.profile, upstreamHost: safeText(upstreamHost, "GitHub Copilot") };
138
+ return { ...catalog, profile: AUTH_PROFILE_CODEX, upstreamHost: safeText(upstreamHost, "GitHub Copilot") };
165
139
  } catch (error) {
166
140
  if (controller.signal.aborted && !/timed out/.test(error?.message || "")) {
167
141
  throw new Error(`Live model lookup timed out after ${timeoutMs}ms`);
@@ -172,21 +146,20 @@ export async function fetchLiveCopilotModels({
172
146
  }
173
147
  }
174
148
 
175
- function liveCatalogHeader(catalog, { commandName, profile, models }) {
149
+ function liveCatalogHeader(catalog, { commandName, models }) {
176
150
  const responses = models.filter((model) => model.endpoints.includes("responses")).length;
177
151
  const chat = models.filter((model) => model.endpoints.includes("chat")).length;
178
- const claude = models.filter(isClaudeCopilotCatalogEntry).length;
179
152
  return [
180
- `${commandName} models${profile === AUTH_PROFILE_CLAUDE ? " --profile claude" : ""}`,
181
- status("ok", `Live catalog from ${safeText(catalog?.upstreamHost, "GitHub Copilot")}: ${models.length} selectable of ${Number(catalog?.advertised) || 0} advertised`),
182
- status("info", `Responses: ${responses}; Chat: ${chat}; Claude/Anthropic: ${claude}`),
153
+ `${commandName} models`,
154
+ status("ok", `Live catalog from ${safeText(catalog?.upstreamHost, "GitHub Copilot")}: ${models.length} selectable GPT models of ${Number(catalog?.advertised) || 0} advertised`),
155
+ status("info", `Responses: ${responses}; Chat: ${chat}`),
183
156
  ];
184
157
  }
185
158
 
186
- function formatLiveCopilotModelsPlain(catalog, { commandName, profile, models }, { sanitize = false } = {}) {
187
- const lines = liveCatalogHeader(catalog, { commandName, profile, models });
159
+ function formatLiveCopilotModelsPlain(catalog, { commandName, models }, { sanitize = false } = {}) {
160
+ const lines = liveCatalogHeader(catalog, { commandName, models });
188
161
  if (!models.length) {
189
- lines.push(status("warn", "No selectable models were advertised for this account"));
162
+ lines.push(status("warn", "No selectable GPT models were advertised for this account"));
190
163
  return (sanitize ? lines.map((line) => terminalCell(line, { fallback: "" })) : lines).join("\n");
191
164
  }
192
165
 
@@ -209,15 +182,14 @@ export function formatLiveCopilotModels(catalog, {
209
182
  width = cliOutputWidth(output),
210
183
  } = {}) {
211
184
  const models = Array.isArray(catalog?.models) ? catalog.models : [];
212
- const profile = catalog?.profile === AUTH_PROFILE_CLAUDE ? AUTH_PROFILE_CLAUDE : AUTH_PROFILE_CODEX;
213
- const context = { commandName, profile, models };
185
+ const context = { commandName, models };
214
186
  if (cliOutputFormat(format, output) === "plain") {
215
187
  return formatLiveCopilotModelsPlain(catalog, context);
216
188
  }
217
189
 
218
190
  const lines = liveCatalogHeader(catalog, context);
219
191
  if (!models.length) {
220
- lines.push(status("warn", "No selectable models were advertised for this account"));
192
+ lines.push(status("warn", "No selectable GPT models were advertised for this account"));
221
193
  return lines.join("\n");
222
194
  }
223
195
 
@@ -3,25 +3,23 @@ import { MAX_TIMER_DELAY_MS } from "./runtime-config.mjs";
3
3
  const HELP_COMMANDS = new Set(["help", "--help", "-h"]);
4
4
  const VERSION_COMMANDS = new Set(["version", "--version", "-v"]);
5
5
  const START_OPTIONS = new Map([
6
- ["--configure-claude-desktop", "configure-claude-app"],
7
- ["--configure-claude-app", "configure-claude-app"],
8
6
  ["--show-request-id", "show-request-id"],
9
7
  ]);
8
+ const RETIRED_CLAUDE_OPTIONS = new Set([
9
+ "--configure-claude-desktop",
10
+ "--configure-claude-app",
11
+ ]);
10
12
  const PM_COMMANDS = new Set(["pms", "pm-studio"]);
11
13
  const HELP_TOPICS = new Set([
12
14
  "",
13
15
  "start",
14
16
  "auth",
15
17
  "auth status",
16
- "auth login",
17
18
  "doctor",
18
19
  "status",
19
20
  "models",
20
- "pms",
21
- "pms setup",
22
- "pms status",
23
- "pms restore",
24
21
  "usage",
22
+ "animation",
25
23
  "auto-review-model",
26
24
  "update",
27
25
  "version",
@@ -54,15 +52,6 @@ function parseOptions(args, schema) {
54
52
  return parsed;
55
53
  }
56
54
 
57
- function checkedProfile(value, { allowAll = false } = {}) {
58
- const profile = value || "codex";
59
- const allowed = allowAll ? new Set(["codex", "claude", "all"]) : new Set(["codex", "claude"]);
60
- if (!allowed.has(profile)) {
61
- throw new Error(`Profile must be ${allowAll ? "codex, claude, or all" : "codex or claude"}: ${profile}`);
62
- }
63
- return profile;
64
- }
65
-
66
55
  function checkedOutputFormat(value) {
67
56
  const format = value || "";
68
57
  if (!new Set(["table", "plain"]).has(format)) {
@@ -78,7 +67,6 @@ function outputFormatOption(value) {
78
67
  function baseCommand(command, extra = {}) {
79
68
  return {
80
69
  command,
81
- configureClaudeDesktop: false,
82
70
  showRequestId: false,
83
71
  online: false,
84
72
  compat: false,
@@ -88,7 +76,6 @@ function baseCommand(command, extra = {}) {
88
76
 
89
77
  function checkedHelpTopic(parts = []) {
90
78
  const normalized = parts.map((part, index) => {
91
- if (index === 0 && part === "pm-studio") return "pms";
92
79
  return part;
93
80
  }).join(" ");
94
81
  if (!HELP_TOPICS.has(normalized)) throw new Error(`Unknown help topic: ${parts.join(" ")}`);
@@ -102,14 +89,7 @@ function helpCommand(parts = []) {
102
89
  function nestedHelp(command, rest) {
103
90
  if (!rest.length || !HELP_COMMANDS.has(rest.at(-1))) return null;
104
91
  const args = rest.slice(0, -1);
105
- if (PM_COMMANDS.has(command)) {
106
- if (args.length > 1) unexpectedArgs(args.slice(1));
107
- return helpCommand(["pms", ...args]);
108
- }
109
92
  if (command === "auth") {
110
- if (args[0] === "login" && args[1] === "claude" && args.length === 2) {
111
- return helpCommand(["auth", "login"]);
112
- }
113
93
  if (args.length > 1) unexpectedArgs(args.slice(1));
114
94
  return helpCommand(["auth", ...args]);
115
95
  }
@@ -118,6 +98,9 @@ function nestedHelp(command, rest) {
118
98
  }
119
99
 
120
100
  function parseStartOptions(args) {
101
+ if (args.some((option) => RETIRED_CLAUDE_OPTIONS.has(option))) {
102
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
103
+ }
121
104
  const seen = new Set();
122
105
  for (const option of args) {
123
106
  const kind = START_OPTIONS.get(option);
@@ -125,7 +108,6 @@ function parseStartOptions(args) {
125
108
  seen.add(kind);
126
109
  }
127
110
  return baseCommand("start", {
128
- configureClaudeDesktop: seen.has("configure-claude-app"),
129
111
  showRequestId: seen.has("show-request-id"),
130
112
  });
131
113
  }
@@ -134,12 +116,21 @@ export function parseCliArgs(args = []) {
134
116
  const [command, ...rest] = args;
135
117
  if (!command) return baseCommand("start");
136
118
  if (HELP_COMMANDS.has(command)) {
137
- const topic = rest[0] === "auth" && rest[1] === "login" && rest[2] === "claude"
138
- ? ["auth", "login"]
139
- : rest;
119
+ const topic = rest;
140
120
  if (topic.length > 2) unexpectedArgs(topic.slice(2));
141
121
  return helpCommand(topic);
142
122
  }
123
+ const profileOptionIndex = rest.indexOf("--profile");
124
+ const requestedProfile = profileOptionIndex >= 0 ? rest[profileOptionIndex + 1] : "";
125
+ const retiredClaudeInvocation = RETIRED_CLAUDE_OPTIONS.has(command)
126
+ || (command === "start" && rest.some((option) => RETIRED_CLAUDE_OPTIONS.has(option)))
127
+ || (command === "auth" && rest[0] === "login" && rest[1] === "claude")
128
+ || (command === "models" && requestedProfile === "claude")
129
+ || (command === "doctor" && ["claude", "all"].includes(requestedProfile));
130
+ if (PM_COMMANDS.has(command)) return baseCommand("retired", { integration: "PM Studio" });
131
+ if (retiredClaudeInvocation) {
132
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
133
+ }
143
134
  const requestedHelp = nestedHelp(command, rest);
144
135
  if (requestedHelp) return requestedHelp;
145
136
  if (VERSION_COMMANDS.has(command)) {
@@ -156,27 +147,20 @@ export function parseCliArgs(args = []) {
156
147
  return baseCommand("usage", outputFormatOption(options["--format"]));
157
148
  }
158
149
  if (PM_COMMANDS.has(command)) {
159
- const [action, ...pmsArgs] = rest;
160
- if (!action) throw new Error("Missing pms action: expected setup, status, or restore");
161
- if (!new Set(["setup", "status", "restore"]).has(action)) throw new Error(`Unknown pms action: ${action}`);
162
- if (action === "setup" || action === "restore") {
163
- if (pmsArgs.length) unexpectedArgs(pmsArgs);
164
- return baseCommand("pms", { action });
165
- }
166
- const options = parseOptions(pmsArgs, new Map([["--format", "value"]]));
167
- return baseCommand("pms", {
168
- action,
169
- ...outputFormatOption(options["--format"]),
170
- });
150
+ return baseCommand("retired", { integration: "PM Studio" });
171
151
  }
172
152
  if (command === "models") {
173
153
  const options = parseOptions(rest, new Map([
174
154
  ["--profile", "value"],
175
155
  ["--format", "value"],
176
156
  ]));
157
+ const profile = options["--profile"];
158
+ if (profile === "claude") {
159
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
160
+ }
161
+ if (profile && profile !== "codex") throw new Error(`Profile must be codex: ${profile}`);
177
162
  return {
178
163
  ...baseCommand("models"),
179
- profile: checkedProfile(options["--profile"]),
180
164
  ...outputFormatOption(options["--format"]),
181
165
  };
182
166
  }
@@ -202,20 +186,14 @@ export function parseCliArgs(args = []) {
202
186
  const [profile, ...loginArgs] = authArgs;
203
187
  if (!profile) throw new Error("Missing auth login profile: expected claude");
204
188
  if (profile !== "claude") throw new Error(`Auth login profile must be claude: ${profile}`);
205
- const options = parseOptions(loginArgs, new Map([
206
- ["--github-login", "value"],
207
- ["--reauth", "flag"],
208
- ]));
209
- return {
210
- ...baseCommand("auth"),
211
- action: "login",
212
- profile: "claude",
213
- expectedLogin: options["--github-login"] || "",
214
- reauth: Boolean(options["--reauth"]),
215
- };
189
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
216
190
  }
217
191
  throw new Error(`Unknown auth action: ${action}`);
218
192
  }
193
+ if (command === "animation") {
194
+ if (rest.length) unexpectedArgs(rest);
195
+ return baseCommand("animation");
196
+ }
219
197
  if (command === "auto-review-model") {
220
198
  if (rest.length) unexpectedArgs(rest);
221
199
  return baseCommand("auto-review-model");
@@ -237,13 +215,20 @@ export function parseCliArgs(args = []) {
237
215
  ["--compat", "flag"],
238
216
  ["--profile", "value"],
239
217
  ]));
218
+ const profile = options["--profile"];
219
+ if (profile === "claude" || profile === "all") {
220
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
221
+ }
222
+ if (profile && profile !== "codex") throw new Error(`Profile must be codex: ${profile}`);
240
223
  return {
241
224
  ...baseCommand("doctor"),
242
225
  online: Boolean(options["--online"]),
243
226
  compat: Boolean(options["--compat"]),
244
- profile: checkedProfile(options["--profile"], { allowAll: true }),
245
227
  };
246
228
  }
229
+ if (RETIRED_CLAUDE_OPTIONS.has(command)) {
230
+ return baseCommand("retired", { integration: "Claude App and Claude Code" });
231
+ }
247
232
  if (START_OPTIONS.has(command)) return parseStartOptions([command, ...rest]);
248
233
  throw new Error(`Unknown command or option: ${command}`);
249
234
  }
@@ -288,18 +273,14 @@ export function cliCommandName() {
288
273
 
289
274
  function topicHelp(name, topic) {
290
275
  const sections = {
291
- start: `Usage:\n ${name} [start] [--configure-claude-app] [--show-request-id]\n\nStarts or reuses the local adapter, updates Codex and Claude Code configuration, and attempts to open Codex or ChatGPT when auto-launch is enabled and supported.\n--configure-claude-app also creates or updates the managed Claude App gateway profile.`,
292
- auth: `Usage:\n ${name} auth status [--online] [--format table|plain]\n ${name} auth login claude [--github-login <login>] [--reauth]\n\nShows the two account profiles or configures the isolated Claude account. Device login starts only when no reusable local Copilot credential is available or --reauth is used.`,
293
- "auth status": `Usage:\n ${name} auth status [--online] [--format table|plain]\n\nShows account routing without exposing credentials. --online also verifies both configured Copilot entitlements and model catalogs. Interactive terminals use a table by default.`,
294
- "auth login": `Usage:\n ${name} auth login claude [--github-login <login>] [--reauth]\n\nReuses a compatible local Copilot credential when possible. --reauth forces GitHub Device Flow.`,
295
- doctor: `Usage:\n ${name} doctor [--online] [--compat] [--profile codex|claude|all]\n\nChecks local credentials, client configuration, PM Studio state, and the adapter. --online validates account entitlement; --compat sends minimal inference requests and consumes a small amount of Copilot usage.`,
296
- status: `Usage:\n ${name} status\n\nShows bounded runtime, routing, performance, cache, queue, and PM relay metrics from a running adapter.`,
297
- models: `Usage:\n ${name} models [--profile codex|claude] [--format table|plain]\n\nPerforms a fresh, read-only Copilot model-directory lookup for one saved profile. Interactive terminals use a table by default.`,
298
- pms: `Usage:\n ${name} pms status [--format table|plain]\n ${name} pms setup\n ${name} pms restore\n ${name} pm-studio status [--format table|plain]\n ${name} pm-studio setup\n ${name} pm-studio restore\n\nInspects, installs, or restores the fail-closed PM Studio split-origin patch. setup and restore modify the installed app only after source-bound compatibility and backup checks pass.`,
299
- "pms setup": `Usage:\n ${name} pms setup\n\nBacks up, patches, verifies, and ad-hoc signs an exact or structurally compatible PM Studio bundle. Unverifiable versions and drift are never modified.`,
300
- "pms status": `Usage:\n ${name} pms status [--format table|plain]\n\nRead-only inspection of the installed PM Studio version, patch integrity, Claude profile, and relay availability. Interactive terminals use a table by default.`,
301
- "pms restore": `Usage:\n ${name} pms restore\n\nRestores the installed PM Studio bundle from its exact source-bound verified backup. The explicit command authorizes the change; no confirmation prompt is shown. Missing, ambiguous, version-mismatched, or drifted records are never restored.`,
276
+ start: `Usage:\n ${name} [start] [--show-request-id]\n\nStarts or reuses the local adapter, updates Codex configuration, and attempts to open Codex or ChatGPT when auto-launch is enabled and supported.`,
277
+ auth: `Usage:\n ${name} auth status [--online] [--format table|plain]\n\nShows the saved GitHub Copilot account without exposing credentials. --online also verifies its entitlement and model catalog.`,
278
+ "auth status": `Usage:\n ${name} auth status [--online] [--format table|plain]\n\nShows the saved GitHub Copilot account without exposing credentials. --online also verifies its entitlement and model catalog. Interactive terminals use a table by default.`,
279
+ doctor: `Usage:\n ${name} doctor [--online] [--compat]\n\nChecks the local credential, Codex configuration, and adapter. --online validates account entitlement; --compat sends minimal inference requests and consumes a small amount of Copilot usage.`,
280
+ status: `Usage:\n ${name} status\n\nShows bounded runtime, routing, performance, queue, and cache metrics from a running adapter.`,
281
+ models: `Usage:\n ${name} models [--format table|plain]\n\nPerforms a fresh, read-only Copilot model-directory lookup for the saved account. Interactive terminals use a table by default.`,
302
282
  usage: `Usage:\n ${name} usage [--format table|plain]\n\nSummarizes local token usage metadata without reading prompt or completion content. Interactive terminals use a table by default.`,
283
+ animation: `Usage:\n ${name} animation\n\nInteractively selects the terminal activity animation used the next time the adapter starts.`,
303
284
  "auto-review-model": `Usage:\n ${name} auto-review-model\n\nInteractively selects an enabled Responses model for Codex Auto-review.`,
304
285
  update: `Usage:\n ${name} update [npm|github]\n\nUpdates the global package from the configured npm registry or GitHub main. With no source, an interactive terminal prompts for one.`,
305
286
  version: `Usage:\n ${name} --version`,
@@ -312,29 +293,26 @@ export function cliHelp(commandName = "ccdx", topic = "") {
312
293
  const normalizedTopic = checkedHelpTopic(topic ? topic.split(" ") : []);
313
294
  if (normalizedTopic) return topicHelp(name, normalizedTopic);
314
295
  return `Usage:
315
- ${name} [start] [--configure-claude-app] [--show-request-id]
296
+ ${name} [start] [--show-request-id]
316
297
  ${name} auth status [--online] [--format table|plain]
317
- ${name} auth login claude [--github-login <login>] [--reauth]
318
- ${name} doctor [--online] [--compat] [--profile codex|claude|all]
298
+ ${name} doctor [--online] [--compat]
319
299
  ${name} status
320
- ${name} models [--profile codex|claude] [--format table|plain]
321
- ${name} pms status [--format table|plain]
322
- ${name} pms setup
323
- ${name} pms restore
300
+ ${name} models [--format table|plain]
324
301
  ${name} usage [--format table|plain]
302
+ ${name} animation
325
303
  ${name} auto-review-model
326
304
  ${name} update [npm|github]
327
305
  ${name} --version
328
306
  ${name} --help
329
307
 
330
308
  Commands:
331
- start Start or reuse the adapter and configure local clients
332
- auth Inspect or configure the Codex and Claude accounts
309
+ start Start or reuse the adapter and configure Codex
310
+ auth Inspect the saved GitHub Copilot account
333
311
  doctor Diagnose local config; optional live and inference checks
334
312
  status Show runtime routing, performance, queue, and cache health
335
313
  models Query a saved account's live Copilot model catalog
336
- pms, pm-studio Inspect, install, or restore the PM Studio patch
337
314
  usage Summarize locally recorded token usage
315
+ animation Select the terminal activity animation
338
316
  auto-review-model Select the Codex Auto-review Responses model
339
317
  update Update the global package from npm or GitHub
340
318
 
@@ -61,29 +61,14 @@ function profileSummary(label, profile = {}) {
61
61
  const token = client.token_cached
62
62
  ? `token cached (${duration(client.token_expires_in_ms)} remaining)`
63
63
  : "token not cached";
64
- return `${label} ${mode}: ${token}, ${count(models.models)} total/${count(models.claude_models)} Claude models (${source})`;
64
+ return `${label} ${mode}: ${token}, ${count(models.models)} models (${source})`;
65
65
  }
66
66
 
67
67
  function routingTarget(value) {
68
68
  if (value === "codex") return "Codex";
69
- if (value === "claude") return "Claude";
70
69
  return "unknown";
71
70
  }
72
71
 
73
- function pmRouteSummary(requests = {}) {
74
- const routes = requests.by_route;
75
- if (!routes || typeof routes !== "object") return null;
76
- const names = ["pm_models", "pm_chat_completions"];
77
- const totals = names.reduce((summary, name) => {
78
- const route = routes[name] || {};
79
- summary.total += finiteNumber(route.total) || 0;
80
- summary.active += finiteNumber(route.active) || 0;
81
- summary.errors += finiteNumber(route.errors) || 0;
82
- return summary;
83
- }, { total: 0, active: 0, errors: 0 });
84
- return `PM relay: ${count(totals.total)} requests, ${count(totals.active)} active, ${count(totals.errors)} errors; models ${count(routes.pm_models?.total)}, chat ${count(routes.pm_chat_completions?.total)}`;
85
- }
86
-
87
72
  function errorCode(error) {
88
73
  const seen = new Set();
89
74
  const pending = [error];
@@ -171,15 +156,13 @@ export function formatAdapterStatus({ baseUrl, data }, { commandName = "ccdx", c
171
156
  if (imageHistory && typeof imageHistory === "object") {
172
157
  lines.push(status("info", `Visual history: ${count(imageHistory.active_recovery_trees)} recovery trees, ${count(imageHistory.adapted_requests)} adapted requests, ${count(imageHistory.historical_images_omitted)} older images omitted, ${count(imageHistory.timeouts_recorded)} timeouts`));
173
158
  }
174
- lines.push(status("info", `Models: ${count(models.models)} total, ${count(models.claude_models)} Claude; Copilot token ${copilot.token_cached ? `cached (${duration(copilot.token_expires_in_ms)} remaining)` : "not cached"}`));
175
- if (data.profiles?.codex || data.profiles?.claude) {
176
- lines.push(status("info", `Profiles: ${profileSummary("Codex", data.profiles?.codex)}; ${profileSummary("Claude", data.profiles?.claude)}`));
159
+ lines.push(status("info", `Models: ${count(models.models)} total; Copilot token ${copilot.token_cached ? `cached (${duration(copilot.token_expires_in_ms)} remaining)` : "not cached"}`));
160
+ if (data.profiles?.codex) {
161
+ lines.push(status("info", `Profile: ${profileSummary("Codex", data.profiles.codex)}`));
177
162
  }
178
- if (data.routing) {
179
- lines.push(status("info", `Routing: /v1/responses -> ${routingTarget(data.routing.responses)}; /v1/messages -> ${routingTarget(data.routing.messages)}`));
163
+ if (data.routing?.responses) {
164
+ lines.push(status("info", `Routing: /v1/responses -> ${routingTarget(data.routing.responses)}`));
180
165
  }
181
- const pmSummary = pmRouteSummary(requests);
182
- if (pmSummary) lines.push(status("info", pmSummary));
183
166
  lines.push(status("info", `Limits: request body ${mebibytes(limits.max_body_bytes)}, decoded body ${mebibytes(limits.max_decoded_body_bytes)}`));
184
167
  return lines.join("\n");
185
168
  }
package/src/config.mjs CHANGED
@@ -80,8 +80,7 @@ function setTomlKey(lines, sectionName, key, value) {
80
80
  }
81
81
 
82
82
  export function computeUpdatedCodexConfig(content, adapterPort = 2026, adapterHost = "127.0.0.1") {
83
- const anthropicBaseUrl = adapterBaseUrl(adapterHost, adapterPort);
84
- const baseUrl = `${anthropicBaseUrl}/v1`;
83
+ const baseUrl = `${adapterBaseUrl(adapterHost, adapterPort)}/v1`;
85
84
  const hadTrailingNewline = content.endsWith("\n");
86
85
  const lines = content.split("\n");
87
86
  if (hadTrailingNewline) lines.pop();
@@ -103,8 +102,6 @@ export function computeUpdatedCodexConfig(content, adapterPort = 2026, adapterHo
103
102
 
104
103
  changed = setTopLevelTomlDefault(lines, "model_context_window", MODEL_CONTEXT_WINDOW) || changed;
105
104
  changed = setTopLevelTomlDefault(lines, "model_auto_compact_token_limit", MODEL_AUTO_COMPACT_TOKEN_LIMIT) || changed;
106
- changed = setTomlKey(lines, "shell_environment_policy.set", "ANTHROPIC_AUTH_TOKEN", "dummy") || changed;
107
- changed = setTomlKey(lines, "shell_environment_policy.set", "ANTHROPIC_BASE_URL", anthropicBaseUrl) || changed;
108
105
  changed = setTomlKey(lines, "shell_environment_policy.set", "OPENAI_BASE_URL", baseUrl) || changed;
109
106
  changed = setTomlKey(lines, "shell_environment_policy.set", "OPENAI_API_KEY", "dummy") || changed;
110
107
 
@@ -112,8 +109,7 @@ export function computeUpdatedCodexConfig(content, adapterPort = 2026, adapterHo
112
109
  }
113
110
 
114
111
  function initialCodexConfig(adapterPort, adapterHost) {
115
- const anthropicBaseUrl = adapterBaseUrl(adapterHost, adapterPort);
116
- const baseUrl = `${anthropicBaseUrl}/v1`;
112
+ const baseUrl = `${adapterBaseUrl(adapterHost, adapterPort)}/v1`;
117
113
  return `openai_base_url = "${baseUrl}"
118
114
  model_context_window = ${MODEL_CONTEXT_WINDOW}
119
115
  model_auto_compact_token_limit = ${MODEL_AUTO_COMPACT_TOKEN_LIMIT}
@@ -122,8 +118,6 @@ model_auto_compact_token_limit = ${MODEL_AUTO_COMPACT_TOKEN_LIMIT}
122
118
  inherit = "core"
123
119
 
124
120
  [shell_environment_policy.set]
125
- ANTHROPIC_AUTH_TOKEN = "dummy"
126
- ANTHROPIC_BASE_URL = "${anthropicBaseUrl}"
127
121
  OPENAI_BASE_URL = "${baseUrl}"
128
122
  OPENAI_API_KEY = "dummy"
129
123
  `;
package/src/copilot.mjs CHANGED
@@ -259,9 +259,9 @@ export async function refreshVSCodeVersion() {
259
259
  return cachedVersion;
260
260
  }
261
261
 
262
- // Stateful Copilot data lives inside per-profile clients. These module-level
262
+ // Stateful Copilot data lives inside client instances. These module-level
263
263
  // wrappers preserve every existing export as the default Codex compatibility
264
- // layer while allowing the adapter to use isolated Codex and Claude clients.
264
+ // layer while retaining isolated client injection for internal callers.
265
265
  export function createCopilotClient(options = {}) {
266
266
  return createCopilotClientRuntime({
267
267
  ...options,