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
package/src/cli-auth.mjs CHANGED
@@ -1,32 +1,14 @@
1
1
  import os from "node:os";
2
- import { spawn } from "node:child_process";
2
+ import { validateGithubToken } from "./auth.mjs";
3
3
  import {
4
- discoverGithubToken,
5
- fetchGithubIdentity,
6
- pollGithubDeviceFlow,
7
- requestGithubDeviceCode,
8
- sourceDescription,
9
- validateGithubToken,
10
- } from "./auth.mjs";
11
- import {
12
- AUTH_PROFILE_CLAUDE,
13
4
  AUTH_PROFILE_CODEX,
14
5
  readAuthProfileCredentials,
15
- withAuthProfileLock,
16
- writeClaudeAuthProfile,
17
6
  } from "./auth-profile.mjs";
18
7
  import { buildHeaders, FALLBACK_VSCODE_VERSION, parseApiBase } from "./copilot.mjs";
19
- import {
20
- githubIdentitiesEqual,
21
- githubTokenFingerprint,
22
- normalizeGithubIdentity,
23
- } from "./github-identity.mjs";
24
8
  import {
25
9
  MAX_UPSTREAM_MODEL_CATALOG_BYTES,
26
10
  readBoundedResponseText,
27
11
  } from "./http-transport.mjs";
28
- import { saveModelCache } from "./model-cache.mjs";
29
- import { isClaudeCopilotModel } from "./models.mjs";
30
12
  import { profileRouting } from "./profile-routing.mjs";
31
13
  import { status } from "./status.mjs";
32
14
  import {
@@ -38,20 +20,6 @@ import {
38
20
 
39
21
  const DEFAULT_COPILOT_CATALOG_TIMEOUT_MS = 30 * 1000;
40
22
 
41
- function openAndCopy(userCode, verificationUri) {
42
- if (process.platform !== "darwin") return;
43
- try {
44
- const clipboard = spawn("pbcopy");
45
- clipboard.on("error", () => {});
46
- clipboard.stdin.on("error", () => {});
47
- clipboard.stdin.end(userCode);
48
- } catch {}
49
- try {
50
- const browser = spawn("open", [verificationUri], { detached: true, stdio: "ignore" });
51
- browser.on("error", () => {});
52
- } catch {}
53
- }
54
-
55
23
  function responseDetail(value) {
56
24
  return String(value || "").replace(/\s+/g, " ").trim().slice(0, 240);
57
25
  }
@@ -63,97 +31,6 @@ function abortError(signal) {
63
31
  return error;
64
32
  }
65
33
 
66
- function cancelResponseBody(response) {
67
- try { Promise.resolve(response?.body?.cancel?.()).catch(() => {}); } catch {}
68
- }
69
-
70
- function fetchWithSignal(operation, signal) {
71
- if (signal?.aborted) return Promise.reject(abortError(signal));
72
- return new Promise((resolve, reject) => {
73
- let settled = false;
74
- const finish = (callback, value) => {
75
- if (settled) return false;
76
- settled = true;
77
- signal?.removeEventListener("abort", onAbort);
78
- callback(value);
79
- return true;
80
- };
81
- const onAbort = () => finish(reject, abortError(signal));
82
- signal?.addEventListener("abort", onAbort, { once: true });
83
- Promise.resolve()
84
- .then(operation)
85
- .then((response) => {
86
- if (!finish(resolve, response)) cancelResponseBody(response);
87
- }, (error) => finish(reject, error));
88
- });
89
- }
90
-
91
- function catalogTimeoutError(label, timeoutMs) {
92
- const error = new Error(`${label} model catalog timed out after ${timeoutMs}ms`);
93
- error.code = "CCDX_COPILOT_CATALOG_TIMEOUT";
94
- return error;
95
- }
96
-
97
- async function jsonResponse({ response, data, jsonError }, label) {
98
- if (!response.ok) {
99
- throw new Error(`${label} failed with HTTP ${response.status}`);
100
- }
101
- if (jsonError) throw new Error(`${label} returned invalid JSON`);
102
- return data;
103
- }
104
-
105
- export async function requestDeviceFlowToken({
106
- fetchImpl = fetch,
107
- signal,
108
- log = console.log,
109
- openAndCopyFn = openAndCopy,
110
- deviceCodeTimeoutMs,
111
- sleepImpl,
112
- now = Date.now,
113
- } = {}) {
114
- const codeResult = await requestGithubDeviceCode({
115
- fetchImpl,
116
- signal,
117
- timeoutMs: deviceCodeTimeoutMs,
118
- });
119
- const code = await jsonResponse(codeResult, "Device code request");
120
- if (!code.device_code || !code.user_code || !code.verification_uri) {
121
- throw new Error("Device code response is missing required fields");
122
- }
123
-
124
- log(`\n${status("info", `Open ${code.verification_uri}`)}\n${status("info", `Enter Claude account code: ${code.user_code}`)}\n`);
125
- openAndCopyFn(code.user_code, code.verification_uri);
126
-
127
- const result = await pollGithubDeviceFlow({
128
- deviceCode: code.device_code,
129
- interval: code.interval,
130
- expiresIn: code.expires_in,
131
- fetchImpl,
132
- signal,
133
- sleepImpl,
134
- now,
135
- });
136
- if (result.state === "done") return result.token;
137
- if (result.state === "expired") throw new Error("Claude login failed: device code expired");
138
- throw new Error(`Claude login failed: ${result.error}`);
139
- }
140
-
141
- async function codexIdentity({ home, fetchImpl, signal }) {
142
- const codex = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
143
- if (!codex.valid) throw new Error("The existing Codex GitHub account is not configured; refusing to create an unbound Claude profile");
144
- const tokenFingerprint = githubTokenFingerprint(codex.token);
145
- if (codex.identity) return { identity: codex.identity, tokenFingerprint };
146
- const identity = await fetchGithubIdentity(codex.token, { fetchImpl, signal });
147
- if (!identity.ok) throw new Error("Could not verify the existing Codex GitHub account; its credential was not changed");
148
- return { identity: normalizeGithubIdentity(identity), tokenFingerprint };
149
- }
150
-
151
- function claudeModels(payload) {
152
- const models = Array.isArray(payload) ? payload : payload?.data;
153
- if (!Array.isArray(models)) throw new Error("Claude account model catalog contained no model list");
154
- return models.filter(isClaudeCopilotModel);
155
- }
156
-
157
34
  function catalogModels(payload) {
158
35
  const models = Array.isArray(payload) ? payload : payload?.data;
159
36
  if (!Array.isArray(models)) throw new Error("Copilot model catalog contained no model list");
@@ -164,7 +41,6 @@ async function fetchCopilotCatalog(tokenData, {
164
41
  fetchImpl,
165
42
  signal,
166
43
  vscodeVersion = FALLBACK_VSCODE_VERSION,
167
- label = "Copilot",
168
44
  timeoutMs = DEFAULT_COPILOT_CATALOG_TIMEOUT_MS,
169
45
  } = {}) {
170
46
  const parsedTimeout = Number(timeoutMs);
@@ -175,8 +51,7 @@ async function fetchCopilotCatalog(tokenData, {
175
51
  const onCallerAbort = () => controller.abort(signal?.reason);
176
52
  signal?.addEventListener("abort", onCallerAbort, { once: true });
177
53
  if (signal?.aborted) onCallerAbort();
178
- const timer = setTimeout(() => controller.abort(catalogTimeoutError(label, deadlineMs)), deadlineMs);
179
- let modelResponse;
54
+ const timer = setTimeout(() => controller.abort(new Error(`Copilot model catalog timed out after ${deadlineMs}ms`)), deadlineMs);
180
55
  try {
181
56
  const apiBase = parseApiBase(tokenData);
182
57
  const headers = buildHeaders({
@@ -185,26 +60,26 @@ async function fetchCopilotCatalog(tokenData, {
185
60
  initiator: "user",
186
61
  vision: false,
187
62
  });
188
- modelResponse = await fetchWithSignal(() => fetchImpl(`${apiBase}/models`, {
63
+ const response = await fetchImpl(`${apiBase}/models`, {
189
64
  headers,
190
65
  redirect: "error",
191
66
  signal: controller.signal,
192
- }), controller.signal);
193
- const modelText = await readBoundedResponseText(modelResponse, {
67
+ });
68
+ const modelText = await readBoundedResponseText(response, {
194
69
  maxBytes: MAX_UPSTREAM_MODEL_CATALOG_BYTES,
195
- label: `${label} model catalog`,
70
+ label: "Copilot model catalog",
196
71
  signal: controller.signal,
197
72
  });
198
- if (!modelResponse.ok) {
73
+ if (!response.ok) {
199
74
  const detail = responseDetail(modelText);
200
- throw new Error(`${label} model catalog failed with HTTP ${modelResponse.status}${detail ? `: ${detail}` : ""}`);
75
+ throw new Error(`Copilot model catalog failed with HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
201
76
  }
202
77
 
203
78
  let catalog;
204
79
  try {
205
80
  catalog = JSON.parse(modelText);
206
81
  } catch {
207
- throw new Error(`${label} model catalog returned invalid JSON`);
82
+ throw new Error("Copilot model catalog returned invalid JSON");
208
83
  }
209
84
  catalogModels(catalog);
210
85
  return { catalog, apiBase };
@@ -215,185 +90,26 @@ async function fetchCopilotCatalog(tokenData, {
215
90
  } finally {
216
91
  clearTimeout(timer);
217
92
  signal?.removeEventListener("abort", onCallerAbort);
218
- if (controller.signal.aborted) cancelResponseBody(modelResponse);
219
- }
220
- }
221
-
222
- export async function validateClaudeCandidate(githubToken, {
223
- home = os.homedir(),
224
- expectedLogin = "",
225
- fetchImpl = fetch,
226
- signal,
227
- vscodeVersion = FALLBACK_VSCODE_VERSION,
228
- catalogTimeoutMs,
229
- } = {}) {
230
- const validation = await validateGithubToken(githubToken, { fetchImpl, signal });
231
- if (!validation.ok) {
232
- const httpStatus = validation.status ? ` (HTTP ${validation.status})` : "";
233
- throw new Error(`Claude GitHub/Copilot validation failed: ${validation.reason}${httpStatus}`);
234
- }
235
-
236
- const identity = normalizeGithubIdentity(validation);
237
- if (!identity) throw new Error("Claude GitHub account identity is missing");
238
- const pinnedLogin = String(expectedLogin || "").trim();
239
- if (pinnedLogin && identity.login.toLowerCase() !== pinnedLogin.toLowerCase()) {
240
- throw new Error(`Authorized GitHub account ${identity.login || identity.id} does not match requested Claude account ${pinnedLogin}`);
241
- }
242
-
243
- const existingCodex = await codexIdentity({ home, fetchImpl, signal });
244
- if (githubIdentitiesEqual(identity, existingCodex.identity)) {
245
- throw new Error(`Claude must use a different GitHub account; ${identity.login || identity.id} is already the Codex account`);
246
- }
247
-
248
- const { catalog, apiBase } = await fetchCopilotCatalog(validation.copilotTokenData, {
249
- fetchImpl,
250
- signal,
251
- vscodeVersion,
252
- label: "Claude account",
253
- timeoutMs: catalogTimeoutMs,
254
- });
255
- const models = claudeModels(catalog);
256
- if (!models.length) {
257
- throw new Error(`GitHub account ${identity.login || identity.id} has Copilot access but advertises no enabled Claude models`);
258
93
  }
259
-
260
- return {
261
- identity,
262
- models,
263
- catalog,
264
- apiBase,
265
- codexTokenFingerprint: existingCodex.tokenFingerprint,
266
- };
267
94
  }
268
95
 
269
- export async function authorizeClaudeProfile({
270
- home = os.homedir(),
271
- env = process.env,
272
- reauth = false,
273
- expectedLogin = "",
274
- fetchImpl = fetch,
275
- signal,
276
- log = console.log,
277
- openAndCopyFn = openAndCopy,
278
- deviceCodeTimeoutMs,
279
- catalogTimeoutMs,
280
- sleepImpl,
281
- now = Date.now,
282
- saveModelCacheFn = saveModelCache,
283
- } = {}) {
284
- return withAuthProfileLock(AUTH_PROFILE_CLAUDE, async () => {
285
- const current = readAuthProfileCredentials(AUTH_PROFILE_CLAUDE, { home });
286
- if (current.configured && current.valid && !reauth) {
287
- const pinnedLogin = String(expectedLogin || "").trim();
288
- const currentLogin = String(current.identity?.login || "");
289
- if (pinnedLogin && currentLogin.toLowerCase() !== pinnedLogin.toLowerCase()) {
290
- throw new Error(`Claude is already authenticated as ${currentLogin || current.identity?.id || "another account"}; use --reauth to authorize ${pinnedLogin}`);
291
- }
292
- return { changed: false, profile: AUTH_PROFILE_CLAUDE, identity: current.identity, models: [] };
293
- }
294
- if (current.configured && !reauth) {
295
- throw new Error(`Claude profile is configured but invalid (${current.reason}); run ccdx auth login claude --reauth`);
296
- }
297
-
298
- let githubToken = "";
299
- if (!reauth) {
300
- const codex = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
301
- if (!codex.valid) {
302
- throw new Error("The existing Codex GitHub account is not configured; refusing to create an unbound Claude profile");
303
- }
304
- const existingCodex = await codexIdentity({ home, fetchImpl, signal });
305
- const pinnedLogin = String(expectedLogin || "").trim();
306
- const discovered = await discoverGithubToken({
307
- home,
308
- env,
309
- fetchImpl,
310
- signal,
311
- excludeTokens: [codex.token],
312
- excludeIdentities: [existingCodex.identity],
313
- expectedIdentity: pinnedLogin ? { login: pinnedLogin } : undefined,
314
- strictExpectedIdentity: Boolean(pinnedLogin),
315
- });
316
- if (discovered.ambiguous) {
317
- const accounts = [...new Set(discovered.candidates
318
- .map((candidate) => candidate.login || candidate.id || "unknown"))];
319
- throw new Error(`Multiple reusable GitHub Copilot accounts were found (${accounts.join(", ")}). Use --github-login or CCDX_GITHUB_TOKEN_PATH to select the Claude account.`);
320
- }
321
- if (discovered.ok) {
322
- githubToken = discovered.token;
323
- const login = discovered.validation?.login ? ` for ${discovered.validation.login}` : "";
324
- log(status("info", `Reusing a local Copilot credential from ${sourceDescription(discovered.source)}${login}`));
325
- }
326
- }
327
- if (!githubToken) {
328
- githubToken = await requestDeviceFlowToken({
329
- fetchImpl,
330
- signal,
331
- log,
332
- openAndCopyFn,
333
- deviceCodeTimeoutMs,
334
- sleepImpl,
335
- now,
336
- });
337
- }
338
- const candidate = await validateClaudeCandidate(githubToken, {
339
- home,
340
- expectedLogin,
341
- fetchImpl,
342
- signal,
343
- catalogTimeoutMs,
344
- });
345
- await withAuthProfileLock(AUTH_PROFILE_CODEX, () => {
346
- const currentCodex = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
347
- if (!currentCodex.valid
348
- || githubTokenFingerprint(currentCodex.token) !== candidate.codexTokenFingerprint) {
349
- throw new Error("Codex authentication changed while Claude was being authorized; no Claude credential was saved. Retry the command.");
350
- }
351
- writeClaudeAuthProfile(githubToken, candidate.identity, { home });
352
- }, { home, env });
353
- let modelCacheSaved = false;
354
- try {
355
- modelCacheSaved = saveModelCacheFn(candidate.catalog, {
356
- home,
357
- profile: AUTH_PROFILE_CLAUDE,
358
- credentialFingerprint: githubTokenFingerprint(githubToken),
359
- }) === true;
360
- if (!modelCacheSaved) throw new Error("model catalog was not cacheable");
361
- } catch (error) {
362
- log(status("warn", `Claude authentication succeeded, but its model cache could not be saved (${error.message})`));
363
- }
364
- log(status("ok", `Claude authenticated as ${candidate.identity.login || candidate.identity.id}; existing Codex authentication was not changed`));
365
- return {
366
- changed: true,
367
- profile: AUTH_PROFILE_CLAUDE,
368
- identity: candidate.identity,
369
- models: candidate.models,
370
- catalog: candidate.catalog,
371
- apiBase: candidate.apiBase,
372
- modelCacheSaved,
373
- };
374
- }, { home, env });
375
- }
376
-
377
- function publicProfile(credentials, source) {
96
+ function publicProfile(credentials) {
378
97
  return {
379
98
  configured: credentials.configured,
380
99
  valid: credentials.valid,
381
100
  reason: credentials.reason,
382
101
  login: credentials.identity?.login || "",
383
102
  id: credentials.identity?.id || "",
384
- source,
103
+ source: "legacy",
385
104
  };
386
105
  }
387
106
 
388
- function readStatusCredentials(profile, home) {
107
+ function readStatusCredentials(home) {
389
108
  try {
390
- return readAuthProfileCredentials(profile, { home });
109
+ return readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
391
110
  } catch {
392
- // Treat an unreadable optional profile as configured-but-invalid. This
393
- // keeps status useful without leaking a filesystem path or silently
394
- // borrowing the other account.
395
111
  return {
396
- profile,
112
+ profile: AUTH_PROFILE_CODEX,
397
113
  configured: true,
398
114
  valid: false,
399
115
  reason: "credential_read_failed",
@@ -404,40 +120,29 @@ function readStatusCredentials(profile, home) {
404
120
  }
405
121
 
406
122
  export function authStatus({ home = os.homedir() } = {}) {
407
- const codex = readStatusCredentials(AUTH_PROFILE_CODEX, home);
408
- const claude = readStatusCredentials(AUTH_PROFILE_CLAUDE, home);
123
+ const codex = readStatusCredentials(home);
409
124
  return {
410
- profiles: {
411
- codex: publicProfile(codex, "legacy"),
412
- claude: publicProfile(claude, "isolated"),
413
- },
414
- routing: profileRouting({ claudeConfigured: claude.configured }),
125
+ profiles: { codex: publicProfile(codex) },
126
+ routing: profileRouting(),
415
127
  };
416
128
  }
417
129
 
418
- async function inspectProfileOnline(credentials, {
419
- fetchImpl,
420
- timeoutMs,
421
- inherited = false,
422
- } = {}) {
423
- if (inherited) {
424
- return { checked: false, inherited: true, ok: null, reason: "inherits_codex" };
425
- }
130
+ async function inspectProfileOnline(credentials, { fetchImpl, timeoutMs } = {}) {
426
131
  if (!credentials.valid) {
427
- return { checked: false, inherited: false, ok: false, reason: credentials.reason || "unconfigured" };
132
+ return { checked: false, ok: false, reason: credentials.reason || "unconfigured" };
428
133
  }
429
134
 
430
135
  const controller = new AbortController();
431
- const timer = setTimeout(() => controller.abort(), timeoutMs);
136
+ const timer = setTimeout(() => controller.abort(new Error(`timed out after ${timeoutMs}ms`)), timeoutMs);
432
137
  try {
433
138
  const validation = await validateGithubToken(credentials.token, {
434
139
  fetchImpl,
435
140
  signal: controller.signal,
436
141
  });
142
+ if (controller.signal.aborted) throw abortError(controller.signal);
437
143
  if (!validation.ok) {
438
144
  return {
439
145
  checked: true,
440
- inherited: false,
441
146
  ok: false,
442
147
  reason: validation.reason,
443
148
  httpStatus: validation.status || null,
@@ -446,24 +151,19 @@ async function inspectProfileOnline(credentials, {
446
151
  const { catalog, apiBase } = await fetchCopilotCatalog(validation.copilotTokenData, {
447
152
  fetchImpl,
448
153
  signal: controller.signal,
449
- label: credentials.profile === AUTH_PROFILE_CLAUDE ? "Claude account" : "Codex account",
450
154
  });
451
- const models = catalogModels(catalog);
452
155
  let upstreamHost = "GitHub Copilot";
453
156
  try { upstreamHost = new URL(apiBase).hostname; } catch {}
454
157
  return {
455
158
  checked: true,
456
- inherited: false,
457
159
  ok: true,
458
160
  login: validation.login || credentials.identity?.login || "",
459
- models: models.length,
460
- claudeModels: claudeModels(catalog).length,
161
+ models: catalogModels(catalog).length,
461
162
  upstreamHost,
462
163
  };
463
164
  } catch (error) {
464
165
  return {
465
166
  checked: true,
466
- inherited: false,
467
167
  ok: false,
468
168
  reason: controller.signal.aborted ? `timed out after ${timeoutMs}ms` : error.message,
469
169
  };
@@ -478,21 +178,11 @@ export async function authStatusOnline({
478
178
  timeoutMs = 10000,
479
179
  } = {}) {
480
180
  const local = authStatus({ home });
481
- const codexCredentials = readStatusCredentials(AUTH_PROFILE_CODEX, home);
482
- const claudeCredentials = readStatusCredentials(AUTH_PROFILE_CLAUDE, home);
483
- const [codexOnline, claudeOnline] = await Promise.all([
484
- inspectProfileOnline(codexCredentials, { fetchImpl, timeoutMs }),
485
- inspectProfileOnline(claudeCredentials, {
486
- fetchImpl,
487
- timeoutMs,
488
- inherited: !claudeCredentials.configured,
489
- }),
490
- ]);
181
+ const online = await inspectProfileOnline(readStatusCredentials(home), { fetchImpl, timeoutMs });
491
182
  return {
492
183
  ...local,
493
184
  profiles: {
494
- codex: { ...local.profiles.codex, online: codexOnline },
495
- claude: { ...local.profiles.claude, online: claudeOnline },
185
+ codex: { ...local.profiles.codex, online },
496
186
  },
497
187
  };
498
188
  }
@@ -503,72 +193,36 @@ function accountLabel(profile) {
503
193
  return profile.login || profile.id || "configured";
504
194
  }
505
195
 
506
- function onlineStatusLine(name, profile) {
196
+ function onlineStatusLine(profile) {
507
197
  const online = profile.online;
508
198
  if (!online) return null;
509
- if (online.inherited) return status("info", `${name} online: inherits the Codex profile`);
510
199
  if (!online.ok) {
511
200
  const httpStatus = online.httpStatus ? ` (HTTP ${online.httpStatus})` : "";
512
- return status("warn", `${name} online: ${online.reason || "unavailable"}${httpStatus}`);
201
+ return status("warn", `Codex online: ${online.reason || "unavailable"}${httpStatus}`);
513
202
  }
514
203
  const account = online.login ? `${online.login}; ` : "";
515
- return status("ok", `${name} online: ${account}${online.models} models, ${online.claudeModels} Claude`);
204
+ return status("ok", `Codex online: ${account}${online.models} models`);
516
205
  }
517
206
 
518
207
  function authStatusPlainLines(snapshot, { commandName }) {
519
- const claudeLabel = snapshot.profiles.claude.configured
520
- ? `${accountLabel(snapshot.profiles.claude)} [isolated profile]`
521
- : "inherits Codex [no isolated profile]";
522
- const claudeKind = snapshot.profiles.claude.valid
523
- ? "ok"
524
- : snapshot.profiles.claude.configured ? "warn" : "info";
525
208
  return [
526
209
  `${commandName} auth status`,
527
210
  status(snapshot.profiles.codex.valid ? "ok" : "warn", `Codex: ${accountLabel(snapshot.profiles.codex)} [legacy path]`),
528
- onlineStatusLine("Codex", snapshot.profiles.codex),
529
- status(claudeKind, `Claude: ${claudeLabel}`),
530
- onlineStatusLine("Claude", snapshot.profiles.claude),
531
- status("info", `Routing: responses -> ${snapshot.routing.responses}; messages -> ${snapshot.routing.messages}`),
211
+ onlineStatusLine(snapshot.profiles.codex),
212
+ status("info", `Routing: responses -> ${snapshot.routing.responses}`),
532
213
  ].filter(Boolean);
533
214
  }
534
215
 
535
216
  function authTableOnline(profile) {
536
217
  const online = profile.online;
537
- if (!online) return { state: "[INFO] not checked", models: "—", claudeModels: "—" };
538
- if (online.inherited) return { state: "[INFO] inherits Codex", models: "—", claudeModels: "—" };
218
+ if (!online) return { state: "[INFO] not checked", models: "—" };
539
219
  if (!online.ok) {
540
220
  const httpStatus = online.httpStatus ? ` (HTTP ${online.httpStatus})` : "";
541
- return {
542
- state: `[WARN] ${online.reason || "unavailable"}${httpStatus}`,
543
- models: "—",
544
- claudeModels: "—",
545
- };
221
+ return { state: `[WARN] ${online.reason || "unavailable"}${httpStatus}`, models: "—" };
546
222
  }
547
223
  return {
548
224
  state: `[OK] verified${online.login ? ` as ${online.login}` : ""}`,
549
225
  models: Number.isFinite(online.models) ? String(online.models) : "—",
550
- claudeModels: Number.isFinite(online.claudeModels) ? String(online.claudeModels) : "—",
551
- };
552
- }
553
-
554
- function authTableRow(name, profile, { claude = false } = {}) {
555
- const inherited = claude && !profile.configured;
556
- const online = authTableOnline(profile);
557
- const local = inherited
558
- ? "[INFO] inherits Codex"
559
- : profile.valid
560
- ? "[OK] ready"
561
- : `[WARN] ${accountLabel(profile)}`;
562
- const account = inherited ? "inherits Codex" : accountLabel(profile);
563
- const mode = inherited ? "inherited" : claude ? "isolated" : "legacy path";
564
- return {
565
- profile: name,
566
- account,
567
- mode,
568
- local,
569
- online: online.state,
570
- models: online.models,
571
- claudeModels: online.claudeModels,
572
226
  };
573
227
  }
574
228
 
@@ -583,19 +237,22 @@ export function formatAuthStatus(snapshot = authStatus(), {
583
237
  return plainLines.join("\n");
584
238
  }
585
239
 
586
- const rows = [
587
- authTableRow("Codex", snapshot.profiles.codex),
588
- authTableRow("Claude", snapshot.profiles.claude, { claude: true }),
589
- ];
240
+ const profile = snapshot.profiles.codex;
241
+ const online = authTableOnline(profile);
242
+ const rows = [{
243
+ profile: "Codex",
244
+ account: accountLabel(profile),
245
+ local: profile.valid ? "[OK] ready" : `[WARN] ${accountLabel(profile)}`,
246
+ online: online.state,
247
+ models: online.models,
248
+ }];
590
249
  const table = formatResponsiveCliTable({
591
250
  columns: [
592
251
  { key: "profile", label: "PROFILE" },
593
252
  { key: "account", label: "ACCOUNT" },
594
- { key: "mode", label: "MODE" },
595
253
  { key: "local", label: "LOCAL" },
596
254
  { key: "online", label: "ONLINE" },
597
255
  { key: "models", label: "MODELS", align: "right" },
598
- { key: "claudeModels", label: "CLAUDE", align: "right" },
599
256
  ],
600
257
  compactColumns: [
601
258
  { key: "profile", label: "PROFILE" },
@@ -608,11 +265,7 @@ export function formatAuthStatus(snapshot = authStatus(), {
608
265
  if (format === "auto" && table.overflow) {
609
266
  return plainLines.map((line) => terminalCell(line, { fallback: "" })).join("\n");
610
267
  }
611
- const lines = [
612
- `${commandName} auth status`,
613
- "",
614
- table.output,
615
- ];
268
+ const lines = [`${commandName} auth status`, "", table.output];
616
269
  if (table.compact) {
617
270
  lines.push("", "Details:", ...plainLines.slice(1, -1).map((line) => terminalCell(line, { fallback: "" })));
618
271
  }
@@ -622,20 +275,13 @@ export function formatAuthStatus(snapshot = authStatus(), {
622
275
 
623
276
  export async function runAuthCommand({
624
277
  action = "status",
625
- profile = "",
626
278
  online = false,
627
- reauth = false,
628
- expectedLogin = "",
629
279
  commandName = "ccdx",
630
280
  format = "plain",
631
281
  output = process.stdout,
632
282
  ...options
633
283
  } = {}) {
634
- if (action === "status") {
635
- const snapshot = online ? await authStatusOnline(options) : authStatus(options);
636
- return { action, output: formatAuthStatus(snapshot, { commandName, format, output }), snapshot };
637
- }
638
- if (profile !== AUTH_PROFILE_CLAUDE) throw new Error("Only the isolated Claude profile can be changed by this command");
639
- if (action === "login") return { action, ...(await authorizeClaudeProfile({ ...options, reauth, expectedLogin })) };
640
- throw new Error(`Unsupported auth action: ${action}`);
284
+ if (action !== "status") throw new Error(`Unsupported auth action: ${action}`);
285
+ const snapshot = online ? await authStatusOnline(options) : authStatus(options);
286
+ return { action, output: formatAuthStatus(snapshot, { commandName, format, output }), snapshot };
641
287
  }