@minniexcode/codex-switch 0.2.0 → 0.2.2

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 (40) hide show
  1. package/README.AI.md +66 -94
  2. package/README.CN.md +84 -139
  3. package/README.md +91 -151
  4. package/dist/app/add-provider.js +6 -83
  5. package/dist/app/edit-provider.js +9 -29
  6. package/dist/app/get-status.js +1 -77
  7. package/dist/app/list-providers.js +0 -2
  8. package/dist/app/remove-provider.js +3 -5
  9. package/dist/app/run-doctor.js +2 -99
  10. package/dist/app/setup-codex.js +0 -2
  11. package/dist/app/switch-provider.js +1 -74
  12. package/dist/cli/output.js +3 -89
  13. package/dist/commands/handlers.js +20 -172
  14. package/dist/commands/help.js +1 -4
  15. package/dist/commands/registry.js +6 -74
  16. package/dist/domain/config.js +1 -3
  17. package/dist/domain/providers.js +4 -92
  18. package/dist/domain/runtime-state.js +0 -88
  19. package/dist/interaction/add-interactive.js +1 -55
  20. package/dist/interaction/interactive.js +1 -3
  21. package/dist/runtime/codex-probe.js +0 -12
  22. package/dist/storage/codex-paths.js +0 -2
  23. package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
  24. package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
  25. package/docs/Tests/testing.md +32 -34
  26. package/docs/cli-usage.md +67 -235
  27. package/docs/codex-switch-command-design.md +1 -1
  28. package/docs/codex-switch-product-overview.md +49 -96
  29. package/docs/codex-switch-technical-architecture.md +37 -52
  30. package/package.json +1 -1
  31. package/dist/app/bridge.js +0 -303
  32. package/dist/runtime/copilot-adapter.js +0 -617
  33. package/dist/runtime/copilot-bridge-worker.js +0 -69
  34. package/dist/runtime/copilot-bridge.js +0 -1351
  35. package/dist/runtime/copilot-cli.js +0 -164
  36. package/dist/runtime/copilot-http-bridge-worker.js +0 -228
  37. package/dist/runtime/copilot-installer.js +0 -231
  38. package/dist/runtime/copilot-sdk-loader.js +0 -62
  39. package/dist/runtime/copilot-token.js +0 -294
  40. package/dist/storage/runtime-state-repo.js +0 -121
@@ -47,11 +47,9 @@ function removeProvider(args) {
47
47
  });
48
48
  }
49
49
  const switchTargetProjection = switchTarget
50
- ? (0, providers_1.isCopilotBridgeProvider)(switchTarget)
51
- ? (0, providers_1.buildCopilotModelProviderProjection)(switchTarget.runtime)
52
- : switchTarget.baseUrl
53
- ? (0, providers_1.buildDirectModelProviderProjection)(switchTarget.profile, switchTarget.baseUrl)
54
- : null
50
+ ? switchTarget.baseUrl
51
+ ? (0, providers_1.buildModelProviderProjection)(switchTarget.profile, switchTarget.baseUrl)
52
+ : null
55
53
  : null;
56
54
  if (switchTargetName && !switchTargetProjection) {
57
55
  throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Provider "${switchTargetName}" requires base_url before it can become active.`, {
@@ -42,10 +42,6 @@ const providers_repo_1 = require("../storage/providers-repo");
42
42
  const errors_1 = require("../domain/errors");
43
43
  const codex_probe_1 = require("../runtime/codex-probe");
44
44
  const auth_repo_1 = require("../storage/auth-repo");
45
- const providers_1 = require("../domain/providers");
46
- const copilot_token_1 = require("../runtime/copilot-token");
47
- const copilot_bridge_1 = require("../runtime/copilot-bridge");
48
- const runtime_state_repo_1 = require("../storage/runtime-state-repo");
49
45
  const codex_version_1 = require("../runtime/codex-version");
50
46
  /**
51
47
  * Performs consistency checks across config.toml, providers.json, and the local Codex CLI.
@@ -84,7 +80,6 @@ async function runDoctor(args) {
84
80
  try {
85
81
  providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
86
82
  if (document) {
87
- // Preserve domain issue codes while translating them into user-facing diagnostic messages.
88
83
  for (const issue of (0, config_1.collectConfigConsistencyIssues)(document, providers)) {
89
84
  issues.push({
90
85
  ...issue,
@@ -103,8 +98,6 @@ async function runDoctor(args) {
103
98
  }
104
99
  }
105
100
  const authState = (0, auth_repo_1.readAuthFileState)(args.authPath);
106
- const runtimeStateInspection = (0, runtime_state_repo_1.inspectCopilotBridgeState)(args.runtimeDir);
107
- const runtimeState = runtimeStateInspection.state;
108
101
  if (authState.exists && !authState.valid) {
109
102
  issues.push({
110
103
  code: "AUTH_JSON_INVALID",
@@ -112,70 +105,6 @@ async function runDoctor(args) {
112
105
  file: args.authPath,
113
106
  });
114
107
  }
115
- if (runtimeStateInspection.exists && !runtimeStateInspection.valid) {
116
- issues.push({
117
- code: "BRIDGE_STATE_STALE",
118
- message: `Copilot bridge runtime state is unreadable: ${runtimeStateInspection.parseError ?? "unknown parse failure"}`,
119
- logPath: runtimeState?.logPath ?? null,
120
- });
121
- }
122
- if (document?.currentModelProvider && providers) {
123
- const matches = (0, providers_1.findProvidersByProfile)(providers, document.currentModelProvider);
124
- if (matches.length === 1) {
125
- const activeProvider = providers.providers[matches[0]];
126
- if ((0, providers_1.isCopilotBridgeProvider)(activeProvider)) {
127
- const githubToken = (0, copilot_token_1.readGithubToken)(args.toolHomeDir);
128
- if (!githubToken) {
129
- issues.push({
130
- code: "COPILOT_AUTH_REQUIRED",
131
- message: "GitHub Copilot authentication is required. Run `codexs login copilot`.",
132
- });
133
- }
134
- else {
135
- try {
136
- await (0, copilot_token_1.exchangeForCopilotToken)(githubToken);
137
- }
138
- catch (error) {
139
- const normalized = (0, errors_1.normalizeError)(error);
140
- issues.push({
141
- code: normalized.code,
142
- message: normalized.message,
143
- ...(normalized.details ?? {}),
144
- });
145
- }
146
- }
147
- const bridge = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(activeProvider, runtimeState, args.runtimeDir);
148
- if (!bridge.ok) {
149
- issues.push({
150
- code: mapBridgeDiagnosticCode(bridge.cause),
151
- message: bridge.cause,
152
- ...(bridge.details ?? {}),
153
- });
154
- }
155
- }
156
- }
157
- }
158
- if (runtimeState && providers) {
159
- const runtimeProvider = providers.providers[runtimeState.provider] ?? null;
160
- if (!runtimeProvider || !(0, providers_1.isCopilotBridgeProvider)(runtimeProvider)) {
161
- issues.push({
162
- code: "BRIDGE_STATE_STALE",
163
- message: "Copilot bridge runtime state exists but no matching managed Copilot provider is available.",
164
- ...runtimeState,
165
- });
166
- }
167
- else if (!document?.currentModelProvider || runtimeProvider.profile !== document.currentModelProvider) {
168
- issues.push({
169
- code: "BRIDGE_STATE_STALE",
170
- message: "Copilot bridge runtime state exists for a provider that is not the current active model_provider.",
171
- activeModelProvider: document?.currentModelProvider ?? null,
172
- runtimeProvider: runtimeState.provider,
173
- runtimeProfile: runtimeProvider.profile,
174
- ...runtimeState,
175
- });
176
- }
177
- }
178
- // Drift inspection still runs when files are missing so status output can explain partial state.
179
108
  const drift = (0, runtime_state_1.inspectLiveStateDrift)(currentModelProvider, providers);
180
109
  const codexCheck = (0, codex_probe_1.probeCodexRuntime)(codex_version_1.MIN_SUPPORTED_CODEX_VERSION);
181
110
  if (!codexCheck.ok) {
@@ -199,36 +128,12 @@ async function runDoctor(args) {
199
128
  healthy: issues.length === 0,
200
129
  issues,
201
130
  codexDir: args.codexDir,
202
- storage: (0, runtime_state_1.getStorageRoles)({
203
- codexDir: args.codexDir,
204
- providersPath: args.providersPath,
205
- configPath: args.configPath,
206
- authPath: args.authPath,
207
- runtimeDir: args.runtimeDir,
208
- runtimesDir: args.runtimesDir,
209
- }),
210
131
  liveState: drift,
211
132
  auth: authState,
212
- copilotRuntimeState: runtimeState,
213
133
  },
214
134
  warnings: issues.length === 0 ? [] : [`doctor found ${issues.length} issue(s)`],
215
135
  };
216
136
  }
217
- function mapBridgeDiagnosticCode(cause) {
218
- if (cause === "Copilot bridge state manifest is missing.") {
219
- return "BRIDGE_STATE_MISSING";
220
- }
221
- if (cause === "Copilot bridge runtime state exists but no active Copilot bridge provider is selected.") {
222
- return "BRIDGE_STATE_STALE";
223
- }
224
- if (cause === "Copilot bridge state base URL does not match the provider runtime configuration.") {
225
- return "PROVIDER_BASE_URL_MISMATCH";
226
- }
227
- return "BRIDGE_HEALTHCHECK_FAILED";
228
- }
229
- /**
230
- * Maps structured config consistency issues onto stable human-readable diagnostic text.
231
- */
232
137
  function renderConfigIssueMessage(issue) {
233
138
  switch (issue.code) {
234
139
  case "MODEL_MISSING":
@@ -246,11 +151,9 @@ function renderConfigIssueMessage(issue) {
246
151
  case "LEGACY_MODEL_PROVIDER_ENV_KEY":
247
152
  return `Model provider "${issue.modelProvider}" still contains legacy env_key wiring.`;
248
153
  case "PROVIDER_BASE_URL_MISMATCH":
249
- return issue.providerType === "direct"
250
- ? `Direct provider "${issue.provider}" baseUrl does not match config.toml model provider "${issue.modelProvider}" base_url.`
251
- : String(issue.code ?? "UNKNOWN_ISSUE");
154
+ return `Provider "${issue.provider}" baseUrl does not match config.toml model provider "${issue.modelProvider}" base_url.`;
252
155
  case "AUTH_JSON_INVALID":
253
- return String(issue.message ?? issue.reason ?? "auth.json is invalid.");
156
+ return String(issue.message ?? "auth.json is invalid.");
254
157
  case "DESTRUCTIVE_REMOVE_BLOCKED":
255
158
  return `Provider "${issue.provider}" cannot be removed while "${issue.activeModelProvider}" remains active.`;
256
159
  default:
@@ -163,8 +163,6 @@ async function migrateCodex(args) {
163
163
  configPath: args.configPath,
164
164
  providersPath: args.providersPath,
165
165
  authPath: args.authPath,
166
- runtimeDir: args.runtimeDir,
167
- runtimesDir: args.runtimesDir,
168
166
  });
169
167
  return {
170
168
  data: {
@@ -5,8 +5,6 @@ const errors_1 = require("../domain/errors");
5
5
  const config_repo_1 = require("../storage/config-repo");
6
6
  const auth_repo_1 = require("../storage/auth-repo");
7
7
  const providers_repo_1 = require("../storage/providers-repo");
8
- const copilot_bridge_1 = require("../runtime/copilot-bridge");
9
- const copilot_token_1 = require("../runtime/copilot-token");
10
8
  const run_mutation_1 = require("./run-mutation");
11
9
  const providers_1 = require("../domain/providers");
12
10
  /**
@@ -31,77 +29,6 @@ async function switchProvider(args) {
31
29
  suggestion: "Run `codexs edit <provider> --model <name>` or `codexs add <provider> --model <name>`.",
32
30
  });
33
31
  }
34
- if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
35
- const githubToken = (0, copilot_token_1.readGithubToken)(args.toolHomeDir);
36
- if (!githubToken) {
37
- throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "GitHub Copilot authentication is required. Run `codexs login copilot` first.");
38
- }
39
- await (0, copilot_token_1.exchangeForCopilotToken)(githubToken);
40
- const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir, args.runtimesDir, args.toolHomeDir);
41
- const nextProvider = bridge.portChanged
42
- ? (0, providers_1.cleanProviderRecord)({
43
- ...provider,
44
- baseUrl: bridge.baseUrl,
45
- runtime: {
46
- ...provider.runtime,
47
- bridgePort: bridge.port,
48
- },
49
- })
50
- : provider;
51
- try {
52
- return (0, run_mutation_1.runMutation)({
53
- lockPath: args.lockPath,
54
- backupsDir: args.backupsDir,
55
- latestBackupPath: args.latestBackupPath,
56
- operation: "switch",
57
- files: [
58
- { absolutePath: args.authPath, relativePath: "auth.json" },
59
- { absolutePath: args.providersPath, relativePath: "providers.json" },
60
- { absolutePath: args.configPath, relativePath: "config.toml" },
61
- ],
62
- mutate: () => {
63
- const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
64
- setCurrentModel: resolvedModel,
65
- setCurrentModelProvider: provider.profile,
66
- upsertModelProviders: {
67
- [provider.profile]: (0, providers_1.buildCopilotModelProviderProjection)(nextProvider.runtime),
68
- },
69
- deleteLegacyProfile: true,
70
- deleteLegacyProfilesByName: [provider.profile],
71
- scrubModelProviderEnvKeys: [provider.profile],
72
- });
73
- if (bridge.portChanged) {
74
- (0, providers_repo_1.writeProvidersFile)(args.providersPath, {
75
- providers: {
76
- ...providers.providers,
77
- [args.providerName]: nextProvider,
78
- },
79
- });
80
- }
81
- (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
82
- (0, auth_repo_1.writeOpenAiApiKeyAuth)(args.authPath, provider.apiKey);
83
- return {
84
- provider: args.providerName,
85
- model: resolvedModel,
86
- modelProvider: nextProvider.profile,
87
- profile: nextProvider.profile,
88
- portChanged: bridge.portChanged,
89
- bridgePort: bridge.port,
90
- bridgeReused: bridge.reused,
91
- bridgeReplaced: bridge.replaced,
92
- bridgeRestartReason: bridge.restartReason ?? null,
93
- bridgeLogPath: bridge.logPath,
94
- };
95
- },
96
- });
97
- }
98
- catch (error) {
99
- if (!bridge.reused) {
100
- (0, copilot_bridge_1.stopCopilotBridge)(args.runtimeDir);
101
- }
102
- throw error;
103
- }
104
- }
105
32
  return (0, run_mutation_1.runMutation)({
106
33
  lockPath: args.lockPath,
107
34
  backupsDir: args.backupsDir,
@@ -125,7 +52,7 @@ async function switchProvider(args) {
125
52
  setCurrentModel: resolvedModel,
126
53
  setCurrentModelProvider: provider.profile,
127
54
  upsertModelProviders: {
128
- [provider.profile]: (0, providers_1.buildDirectModelProviderProjection)(provider.profile, resolvedBaseUrl),
55
+ [provider.profile]: (0, providers_1.buildModelProviderProjection)(provider.profile, resolvedBaseUrl),
129
56
  },
130
57
  deleteLegacyProfile: true,
131
58
  deleteLegacyProfilesByName: [provider.profile],
@@ -107,7 +107,7 @@ function renderHumanSuccess(command, data, warnings) {
107
107
  : "";
108
108
  const note = provider.note ? ` note=${provider.note}` : "";
109
109
  const current = provider.isActive ? " current" : "";
110
- lines.push(`${provider.name} [${String(provider.providerType ?? "direct")}]${current} -> ${provider.modelProvider}${provider.model ? ` model=${provider.model}` : ""}${tags}${note}`);
110
+ lines.push(`${provider.name}${current} -> ${provider.modelProvider}${provider.model ? ` model=${provider.model}` : ""}${tags}${note}`);
111
111
  }
112
112
  }
113
113
  break;
@@ -144,12 +144,6 @@ function renderHumanSuccess(command, data, warnings) {
144
144
  lines.push(` mapped provider: ${renderStatusMappedProvider(data)}`);
145
145
  lines.push(` provider path: ${renderStatusProviderPath(data)}`);
146
146
  lines.push(` runtime health: ${renderStatusHealth(data)}`);
147
- if (data?.copilotBridgeLogPath) {
148
- lines.push(` bridge log: ${String(data.copilotBridgeLogPath)}`);
149
- }
150
- if (data?.copilotBridgeRestartReason) {
151
- lines.push(` bridge restart reason: ${String(data.copilotBridgeRestartReason)}`);
152
- }
153
147
  lines.push(` warnings: ${warnings.length}`);
154
148
  lines.push(` next step: ${renderStatusNextStep(data, warnings)}`);
155
149
  break;
@@ -173,15 +167,6 @@ function renderHumanSuccess(command, data, warnings) {
173
167
  case "switch":
174
168
  lines.push(`Switched to provider ${String(data?.provider ?? "")} using model provider ${String(data?.modelProvider ?? data?.profile ?? "")}.`);
175
169
  lines.push(`Model: ${String(data?.model ?? "")}`);
176
- if (data?.bridgeReplaced) {
177
- lines.push(`Bridge replaced: true`);
178
- }
179
- if (data?.bridgeRestartReason) {
180
- lines.push(`Bridge restart reason: ${String(data?.bridgeRestartReason)}`);
181
- }
182
- if (data?.bridgeLogPath) {
183
- lines.push(`Bridge log: ${String(data?.bridgeLogPath)}`);
184
- }
185
170
  lines.push(`Backup: ${String(data?.backupPath ?? "")}`);
186
171
  break;
187
172
  case "import":
@@ -198,19 +183,7 @@ function renderHumanSuccess(command, data, warnings) {
198
183
  lines.push(`tool home created: ${String(data?.createdToolHomeDir ?? false)}`);
199
184
  lines.push(`tool config created: ${String(data?.createdToolConfigFile ?? false)}`);
200
185
  lines.push(`providers registry created: ${String(data?.createdProvidersFile ?? false)}`);
201
- lines.push("next step: run `codexs add ...` for a direct provider, or `codexs login copilot` before `add --copilot`.");
202
- break;
203
- case "login":
204
- lines.push(`Copilot login ready: ${String(data?.authReady ?? false)}`);
205
- lines.push(`upstream: ${String(data?.upstream ?? "")}`);
206
- lines.push(`sdk installed: ${String(data?.sdkInstalled ?? false)}${data?.sdkInstalledNow ? " (installed now)" : ""}`);
207
- lines.push(`copilot cli source: ${String(data?.cliSource ?? "not-needed")}`);
208
- if (data?.cliCommand) {
209
- lines.push(`copilot cli command: ${String(data?.cliCommand)}`);
210
- }
211
- lines.push(`login launched: ${String(data?.loginLaunched ?? false)}`);
212
- lines.push(`auth ready: ${String(data?.authReady ?? false)}`);
213
- lines.push("next step: run `codexs add <provider> --copilot --profile <model-provider-id>` and then `codexs switch <provider>`.");
186
+ lines.push("next step: run `codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> --base-url <url>`.");
214
187
  break;
215
188
  case "migrate":
216
189
  lines.push(`Migrated providers in ${String(data?.codexDir ?? "")} using ${String(data?.strategy ?? "")}.`);
@@ -242,49 +215,12 @@ function renderHumanSuccess(command, data, warnings) {
242
215
  const issues = data?.issues ?? [];
243
216
  lines.push(healthy ? "Doctor summary: healthy. No action required." : `Doctor summary: ${issues.length} issue(s) need attention.`);
244
217
  lines.push(`target runtime: ${String(data?.codexDir ?? "")}`);
245
- if (data?.copilotRuntimeState && data.copilotRuntimeState.logPath) {
246
- lines.push(`bridge log: ${String(data.copilotRuntimeState.logPath)}`);
247
- }
248
218
  for (const issue of issues) {
249
219
  lines.push(`- ${String(issue.code)}: ${String(issue.message)}`);
250
220
  lines.push(` next step: ${renderDoctorIssueNextStep(issue)}`);
251
221
  }
252
222
  break;
253
223
  }
254
- case "bridge-start":
255
- lines.push(`Bridge ready for provider ${String(data?.provider ?? "")}.`);
256
- lines.push(`Endpoint: ${String(data?.baseUrl ?? "")}`);
257
- lines.push(`Reused: ${String(data?.reused ?? false)}`);
258
- lines.push(`Replaced existing: ${String(data?.replaced ?? false)}`);
259
- if (data?.restartReason) {
260
- lines.push(`Restart reason: ${String(data?.restartReason)}`);
261
- }
262
- if (data?.logPath) {
263
- lines.push(`Bridge log: ${String(data?.logPath)}`);
264
- }
265
- break;
266
- case "bridge-status":
267
- lines.push(`Bridge provider: ${String(data?.provider ?? "")}`);
268
- lines.push(`Active: ${String(data?.active ?? false)}`);
269
- lines.push(`Expected base URL: ${String(data?.expectedBaseUrl ?? "")}`);
270
- lines.push(`Matches runtime state: ${String(data?.matches ?? false)}`);
271
- if (data?.lastRestartReason) {
272
- lines.push(`Last restart reason: ${String(data?.lastRestartReason)}`);
273
- }
274
- if (data?.logPath) {
275
- lines.push(`Bridge log: ${String(data?.logPath)}`);
276
- }
277
- break;
278
- case "bridge-stop":
279
- lines.push(`Bridge stopped for provider ${String(data?.provider ?? "(none)")}.`);
280
- lines.push(`Had runtime state: ${String(data?.hadRuntimeState ?? false)}`);
281
- if (data?.lastRestartReason) {
282
- lines.push(`Last restart reason: ${String(data?.lastRestartReason)}`);
283
- }
284
- if (data?.logPath) {
285
- lines.push(`Bridge log: ${String(data?.logPath)}`);
286
- }
287
- break;
288
224
  case "backups-list": {
289
225
  const backups = data?.backups ?? [];
290
226
  for (const backup of backups) {
@@ -312,14 +248,9 @@ function renderStatusHealth(data) {
312
248
  const configExists = Boolean(data?.configExists);
313
249
  const providersExists = Boolean(data?.providersExists);
314
250
  const auth = data?.auth ?? {};
315
- const bridge = data?.copilotBridge ?? null;
316
251
  const issues = Array.isArray(data?.issues) ? data?.issues : [];
317
252
  const activeProviderResolvable = data?.activeProviderResolvable !== false;
318
253
  const liveState = data?.liveState ?? {};
319
- const copilotSdk = data?.copilotSdk ?? {};
320
- const copilotAuth = data?.copilotAuth ?? null;
321
- const runtimeProvider = typeof data?.runtimeProvider === "string" ? data.runtimeProvider : null;
322
- const activePathUsesCopilot = runtimeProvider === "copilot-sdk-bridge";
323
254
  if (!configExists || !providersExists) {
324
255
  return "incomplete local state";
325
256
  }
@@ -329,15 +260,6 @@ function renderStatusHealth(data) {
329
260
  if (issues.some((issue) => issue.code === "PROVIDER_BASE_URL_MISMATCH")) {
330
261
  return "provider projection drift";
331
262
  }
332
- if (activePathUsesCopilot && copilotSdk.installed === false) {
333
- return "copilot sdk missing";
334
- }
335
- if (activePathUsesCopilot && copilotAuth && copilotAuth.ready === false) {
336
- return "copilot auth required";
337
- }
338
- if (activePathUsesCopilot && bridge && bridge.ok === false) {
339
- return "copilot runtime needs repair";
340
- }
341
263
  if (auth.exists === false) {
342
264
  return "auth projection missing";
343
265
  }
@@ -363,7 +285,7 @@ function renderStatusMappedProvider(data) {
363
285
  * Renders the active workflow path in status output.
364
286
  */
365
287
  function renderStatusProviderPath(data) {
366
- return typeof data?.runtimeProvider === "string" && data.runtimeProvider === "copilot-sdk-bridge" ? "copilot" : "direct";
288
+ return data?.provider ? "managed provider" : "unmanaged route";
367
289
  }
368
290
  /**
369
291
  * Suggests the next operator action for the human-readable status output.
@@ -389,14 +311,6 @@ function renderDoctorIssueNextStep(issue) {
389
311
  return "restore or create config.toml before switching providers";
390
312
  case "PROVIDERS_NOT_FOUND":
391
313
  return "run `codexs init` and then add or migrate providers";
392
- case "COPILOT_SDK_MISSING":
393
- return "run `codexs login copilot` to install the optional Copilot runtime";
394
- case "COPILOT_AUTH_REQUIRED":
395
- return "run `codexs login copilot` to complete upstream authentication";
396
- case "BRIDGE_STATE_STALE":
397
- case "BRIDGE_STATE_MISSING":
398
- case "BRIDGE_HEALTHCHECK_FAILED":
399
- return "reselect the provider with `codexs switch <provider>` or inspect the bridge log/state";
400
314
  case "UNMANAGED_ACTIVE_PROFILE":
401
315
  return "switch to a managed provider or adopt the active route with `codexs migrate`";
402
316
  case "LEGACY_PROFILE_SELECTOR":