@minniexcode/codex-switch 0.0.10 → 0.0.12

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 (51) hide show
  1. package/README.AI.md +68 -73
  2. package/README.CN.md +108 -111
  3. package/README.md +87 -80
  4. package/dist/app/add-provider.js +29 -15
  5. package/dist/app/bridge.js +15 -14
  6. package/dist/app/edit-provider.js +1 -1
  7. package/dist/app/get-status.js +21 -9
  8. package/dist/app/import-providers.js +1 -1
  9. package/dist/app/init-codex.js +13 -14
  10. package/dist/app/list-providers.js +48 -1
  11. package/dist/app/remove-provider.js +1 -1
  12. package/dist/app/run-doctor.js +12 -5
  13. package/dist/app/run-mutation.js +3 -2
  14. package/dist/app/setup-codex.js +3 -1
  15. package/dist/app/switch-provider.js +11 -13
  16. package/dist/cli/output.js +145 -18
  17. package/dist/cli.js +34 -2
  18. package/dist/commands/args.js +2 -2
  19. package/dist/commands/dispatch.js +40 -0
  20. package/dist/commands/handlers.js +130 -161
  21. package/dist/commands/help.js +11 -5
  22. package/dist/commands/registry.js +42 -20
  23. package/dist/domain/backups.js +4 -4
  24. package/dist/domain/config.js +110 -5
  25. package/dist/domain/providers.js +12 -0
  26. package/dist/domain/runtime-state.js +111 -13
  27. package/dist/infra/config-repo.js +16 -206
  28. package/dist/interaction/interactive.js +16 -6
  29. package/dist/runtime/copilot-adapter.js +12 -12
  30. package/dist/runtime/copilot-bridge.js +394 -45
  31. package/dist/runtime/copilot-cli.js +84 -12
  32. package/dist/runtime/copilot-installer.js +10 -9
  33. package/dist/runtime/copilot-sdk-loader.js +5 -5
  34. package/dist/storage/backup-repo.js +4 -4
  35. package/dist/storage/codex-paths.js +34 -8
  36. package/dist/storage/config-repo.js +0 -23
  37. package/dist/storage/lock-repo.js +2 -4
  38. package/dist/storage/runtime-state-repo.js +14 -13
  39. package/dist/storage/tool-config-repo.js +111 -0
  40. package/docs/Design/codex-switch-v0.0.11-design.md +824 -0
  41. package/docs/Design/codex-switch-v0.0.12-design.md +343 -0
  42. package/docs/PRD/codex-switch-prd-v0.0.11.md +577 -0
  43. package/docs/PRD/codex-switch-prd-v0.0.12.md +279 -0
  44. package/docs/PRD/codex-switch-prd-v0.1.0.md +125 -237
  45. package/docs/Tests/testing.md +39 -112
  46. package/docs/cli-usage.md +135 -565
  47. package/docs/codex-switch-command-design.md +3 -0
  48. package/docs/codex-switch-product-overview.md +52 -207
  49. package/docs/codex-switch-technical-architecture.md +3 -0
  50. package/package.json +1 -1
  51. package/dist/app/rollback-latest.js +0 -26
@@ -1,16 +1,59 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.listProviders = listProviders;
37
+ const fs = __importStar(require("node:fs"));
38
+ const providers_1 = require("../domain/providers");
39
+ const runtime_state_1 = require("../domain/runtime-state");
40
+ const config_repo_1 = require("../storage/config-repo");
4
41
  const providers_repo_1 = require("../storage/providers-repo");
5
42
  /**
6
43
  * Returns the sorted list of configured providers for display.
7
44
  */
8
- function listProviders(providersPath) {
45
+ function listProviders(providersPath, configPath) {
9
46
  const providers = (0, providers_repo_1.readProvidersFile)(providersPath);
10
47
  const names = Object.keys(providers.providers).sort();
48
+ const currentProfile = configPath && fs.existsSync(configPath)
49
+ ? (0, config_repo_1.readStructuredConfig)(configPath).activeProfile
50
+ : null;
51
+ const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentProfile, providers);
11
52
  const items = names.map((name) => ({
12
53
  name,
13
54
  profile: providers.providers[name].profile,
55
+ providerType: (0, providers_1.isCopilotBridgeProvider)(providers.providers[name]) ? "copilot" : "direct",
56
+ isActive: liveState.providerResolvable && liveState.mappedProvider === name,
14
57
  note: providers.providers[name].note ?? null,
15
58
  tags: providers.providers[name].tags ?? [],
16
59
  }));
@@ -18,6 +61,10 @@ function listProviders(providersPath) {
18
61
  data: {
19
62
  providers: items,
20
63
  count: items.length,
64
+ currentProfile,
65
+ activeProvider: liveState.mappedProvider,
66
+ activeProviderResolvable: liveState.providerResolvable,
67
+ activeProviderCandidates: liveState.mappedProviders,
21
68
  },
22
69
  };
23
70
  }
@@ -34,7 +34,7 @@ function removeProvider(args) {
34
34
  switchToProfile: args.switchToProfile ?? null,
35
35
  });
36
36
  return (0, run_mutation_1.runMutation)({
37
- codexDir: args.codexDir,
37
+ lockPath: args.lockPath,
38
38
  backupsDir: args.backupsDir,
39
39
  latestBackupPath: args.latestBackupPath,
40
40
  operation: "remove",
@@ -103,7 +103,7 @@ async function runDoctor(args) {
103
103
  }
104
104
  }
105
105
  const authState = (0, auth_repo_1.readAuthFileState)(args.authPath);
106
- const runtimeStateInspection = (0, runtime_state_repo_1.inspectCopilotBridgeState)();
106
+ const runtimeStateInspection = (0, runtime_state_repo_1.inspectCopilotBridgeState)(args.runtimeDir);
107
107
  const runtimeState = runtimeStateInspection.state;
108
108
  if (authState.exists && !authState.valid) {
109
109
  issues.push({
@@ -123,7 +123,7 @@ async function runDoctor(args) {
123
123
  if (matches.length === 1) {
124
124
  const activeProvider = providers.providers[matches[0]];
125
125
  if ((0, providers_1.isCopilotBridgeProvider)(activeProvider)) {
126
- const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
126
+ const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(args.runtimesDir);
127
127
  if (!installStatus.installed) {
128
128
  issues.push({
129
129
  code: "COPILOT_SDK_MISSING",
@@ -133,7 +133,7 @@ async function runDoctor(args) {
133
133
  });
134
134
  }
135
135
  try {
136
- await (0, copilot_adapter_1.readCopilotAuthState)();
136
+ await (0, copilot_adapter_1.readCopilotAuthState)(args.runtimesDir);
137
137
  }
138
138
  catch (error) {
139
139
  const normalized = (0, errors_1.normalizeError)(error);
@@ -143,7 +143,7 @@ async function runDoctor(args) {
143
143
  ...(normalized.details ?? {}),
144
144
  });
145
145
  }
146
- const bridge = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(activeProvider, runtimeState);
146
+ const bridge = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(activeProvider, runtimeState, args.runtimeDir);
147
147
  if (!bridge.ok) {
148
148
  issues.push({
149
149
  code: mapBridgeDiagnosticCode(bridge.cause),
@@ -198,7 +198,14 @@ async function runDoctor(args) {
198
198
  healthy: issues.length === 0,
199
199
  issues,
200
200
  codexDir: args.codexDir,
201
- storage: (0, runtime_state_1.getStorageRoles)(),
201
+ storage: (0, runtime_state_1.getStorageRoles)({
202
+ codexDir: args.codexDir,
203
+ providersPath: args.providersPath,
204
+ configPath: args.configPath,
205
+ authPath: args.authPath,
206
+ runtimeDir: args.runtimeDir,
207
+ runtimesDir: args.runtimesDir,
208
+ }),
202
209
  liveState: drift,
203
210
  auth: authState,
204
211
  },
@@ -8,8 +8,9 @@ const lock_repo_1 = require("../storage/lock-repo");
8
8
  * Runs a write operation under a lock with automatic backup and rollback handling.
9
9
  */
10
10
  function runMutation(args) {
11
- return (0, lock_repo_1.withCodexLock)(args.codexDir, args.operation, () => {
12
- const backup = (0, backup_repo_1.createBackup)(args.codexDir, args.backupsDir, args.operation, args.files);
11
+ const lockPath = args.lockPath ?? require("node:path").join(args.codexDir ?? process.cwd(), ".codex-switch.lock");
12
+ return (0, lock_repo_1.withCodexLock)(lockPath, args.operation, () => {
13
+ const backup = (0, backup_repo_1.createBackup)(args.backupsDir, args.operation, args.files);
13
14
  try {
14
15
  const data = args.mutate({ backup });
15
16
  // Record the successful backup only after the mutation completes.
@@ -130,7 +130,7 @@ async function migrateCodex(args) {
130
130
  };
131
131
  const finalProviders = args.strategy === "merge" ? (0, providers_repo_1.mergeProviders)(currentProviders, nextProviders) : nextProviders;
132
132
  const result = (0, run_mutation_1.runMutation)({
133
- codexDir: args.codexDir,
133
+ lockPath: args.lockPath,
134
134
  backupsDir: args.backupsDir,
135
135
  latestBackupPath: args.latestBackupPath,
136
136
  operation: "migrate",
@@ -163,6 +163,8 @@ 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,
166
168
  });
167
169
  return {
168
170
  data: {
@@ -23,15 +23,15 @@ async function switchProvider(args) {
23
23
  }
24
24
  const document = (0, config_repo_1.ensureProfileExists)(args.configPath, provider.profile, args.providerName);
25
25
  if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
26
- const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
26
+ const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(args.runtimesDir);
27
27
  if (!installStatus.installed) {
28
28
  throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
29
29
  installDir: installStatus.installDir,
30
30
  packageName: installStatus.packageName,
31
31
  });
32
32
  }
33
- await (0, copilot_adapter_1.readCopilotAuthState)();
34
- const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider);
33
+ await (0, copilot_adapter_1.readCopilotAuthState)(args.runtimesDir);
34
+ const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir);
35
35
  const nextProvider = bridge.portChanged
36
36
  ? (0, providers_1.cleanProviderRecord)({
37
37
  ...provider,
@@ -44,24 +44,21 @@ async function switchProvider(args) {
44
44
  : provider;
45
45
  try {
46
46
  return (0, run_mutation_1.runMutation)({
47
- codexDir: args.codexDir,
47
+ lockPath: args.lockPath,
48
48
  backupsDir: args.backupsDir,
49
49
  latestBackupPath: args.latestBackupPath,
50
50
  operation: "switch",
51
51
  files: [
52
+ { absolutePath: args.authPath, relativePath: "auth.json" },
52
53
  { absolutePath: args.providersPath, relativePath: "providers.json" },
53
54
  { absolutePath: args.configPath, relativePath: "config.toml" },
54
55
  ],
55
56
  mutate: () => {
56
57
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
57
58
  setActiveProfile: provider.profile,
58
- upsertModelProviders: bridge.portChanged
59
- ? {
60
- [provider.profile]: {
61
- baseUrl: (0, providers_1.buildCopilotBridgeBaseUrl)(nextProvider.runtime),
62
- },
63
- }
64
- : undefined,
59
+ upsertModelProviders: {
60
+ [provider.profile]: (0, providers_1.buildCopilotModelProviderProjection)(nextProvider.runtime),
61
+ },
65
62
  });
66
63
  if (bridge.portChanged) {
67
64
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, {
@@ -72,6 +69,7 @@ async function switchProvider(args) {
72
69
  });
73
70
  }
74
71
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
72
+ (0, auth_repo_1.writeOpenAiApiKeyAuth)(args.authPath, provider.apiKey);
75
73
  return {
76
74
  provider: args.providerName,
77
75
  profile: nextProvider.profile,
@@ -83,13 +81,13 @@ async function switchProvider(args) {
83
81
  }
84
82
  catch (error) {
85
83
  if (!bridge.reused) {
86
- (0, copilot_bridge_1.stopCopilotBridge)();
84
+ (0, copilot_bridge_1.stopCopilotBridge)(args.runtimeDir);
87
85
  }
88
86
  throw error;
89
87
  }
90
88
  }
91
89
  return (0, run_mutation_1.runMutation)({
92
- codexDir: args.codexDir,
90
+ lockPath: args.lockPath,
93
91
  backupsDir: args.backupsDir,
94
92
  latestBackupPath: args.latestBackupPath,
95
93
  operation: "switch",
@@ -85,12 +85,22 @@ function renderHumanSuccess(command, data, warnings) {
85
85
  lines.push("No providers configured.");
86
86
  }
87
87
  else {
88
+ const currentProfile = typeof data?.currentProfile === "string" ? data.currentProfile : null;
89
+ const activeProviderResolvable = data?.activeProviderResolvable !== false;
90
+ const activeCandidates = Array.isArray(data?.activeProviderCandidates) ? data?.activeProviderCandidates : [];
91
+ if (currentProfile) {
92
+ lines.push(`Current profile: ${currentProfile}`);
93
+ if (!activeProviderResolvable && activeCandidates.length > 1) {
94
+ lines.push(`Current provider: ambiguous (${activeCandidates.join(", ")})`);
95
+ }
96
+ }
88
97
  for (const provider of providers) {
89
98
  const tags = Array.isArray(provider.tags) && provider.tags.length > 0
90
99
  ? ` tags=${provider.tags.join(",")}`
91
100
  : "";
92
101
  const note = provider.note ? ` note=${provider.note}` : "";
93
- lines.push(`${provider.name} -> ${provider.profile}${tags}${note}`);
102
+ const current = provider.isActive ? " current" : "";
103
+ lines.push(`${provider.name} [${String(provider.providerType ?? "direct")}]${current} -> ${provider.profile}${tags}${note}`);
94
104
  }
95
105
  }
96
106
  break;
@@ -115,17 +125,15 @@ function renderHumanSuccess(command, data, warnings) {
115
125
  lines.push(`Current profile: ${String(data?.profile ?? "")}`);
116
126
  break;
117
127
  case "status":
118
- lines.push(`codexDir: ${String(data?.codexDir ?? "")}`);
119
- lines.push(`configExists: ${String(data?.configExists ?? false)}`);
120
- lines.push(`providersExists: ${String(data?.providersExists ?? false)}`);
121
- lines.push(`currentProfile: ${String(data?.currentProfile ?? "")}`);
122
- lines.push(`mappedProvider: ${String(data?.provider ?? "")}`);
123
- lines.push(`activeProviderResolvable: ${String(data?.activeProviderResolvable ?? false)}`);
124
- const auth = data?.auth ?? {};
125
- lines.push(`authExists: ${String(auth.exists ?? false)}`);
126
- lines.push(`authValid: ${String(auth.valid ?? false)}`);
127
- lines.push(`authMode: ${String(auth.authMode ?? "")}`);
128
- lines.push(`issues: ${Array.isArray(data?.issues) ? (data?.issues).length : 0}`);
128
+ lines.push("Status summary:");
129
+ lines.push(` target runtime: ${String(data?.codexDir ?? "")}`);
130
+ lines.push(` tool home: ${String(data?.storage?.toolHome?.root ?? "")}`);
131
+ lines.push(` current profile: ${String(data?.currentProfile ?? "(none)")}`);
132
+ lines.push(` mapped provider: ${renderStatusMappedProvider(data)}`);
133
+ lines.push(` provider path: ${renderStatusProviderPath(data)}`);
134
+ lines.push(` runtime health: ${renderStatusHealth(data)}`);
135
+ lines.push(` warnings: ${warnings.length}`);
136
+ lines.push(` next step: ${renderStatusNextStep(data, warnings)}`);
129
137
  break;
130
138
  case "config-show": {
131
139
  lines.push(`activeProfile: ${String(data?.activeProfile ?? "")}`);
@@ -153,10 +161,26 @@ function renderHumanSuccess(command, data, warnings) {
153
161
  lines.push(`Exported providers to ${String(data?.exportedTo ?? "")}.`);
154
162
  break;
155
163
  case "init":
156
- lines.push(`Initialized Codex directory ${String(data?.codexDir ?? "")}.`);
157
- lines.push(`Created codexDir: ${String(data?.createdCodexDir ?? false)}`);
158
- lines.push(`Created providers.json: ${String(data?.createdProvidersFile ?? false)}`);
159
- lines.push(`providersAlreadyExisted: ${String(data?.providersAlreadyExisted ?? false)}`);
164
+ lines.push("Initialized codex-switch tool home.");
165
+ lines.push(`tool home: ${String(data?.toolHomeDir ?? "")}`);
166
+ lines.push(`tool config: ${String(data?.toolConfigPath ?? "")}`);
167
+ lines.push(`providers registry: ${String(data?.providersPath ?? "")}`);
168
+ lines.push(`tool home created: ${String(data?.createdToolHomeDir ?? false)}`);
169
+ lines.push(`tool config created: ${String(data?.createdToolConfigFile ?? false)}`);
170
+ lines.push(`providers registry created: ${String(data?.createdProvidersFile ?? false)}`);
171
+ lines.push("next step: run `codexs add ...` for a direct provider, or `codexs login copilot` before `add --copilot`.");
172
+ break;
173
+ case "login":
174
+ lines.push(`Copilot login ready: ${String(data?.authReady ?? false)}`);
175
+ lines.push(`upstream: ${String(data?.upstream ?? "")}`);
176
+ lines.push(`sdk installed: ${String(data?.sdkInstalled ?? false)}${data?.sdkInstalledNow ? " (installed now)" : ""}`);
177
+ lines.push(`copilot cli source: ${String(data?.cliSource ?? "not-needed")}`);
178
+ if (data?.cliCommand) {
179
+ lines.push(`copilot cli command: ${String(data?.cliCommand)}`);
180
+ }
181
+ lines.push(`login launched: ${String(data?.loginLaunched ?? false)}`);
182
+ lines.push(`auth ready: ${String(data?.authReady ?? false)}`);
183
+ lines.push("next step: run `codexs add <provider> --copilot --profile <name>` and then `codexs switch <provider>`.");
160
184
  break;
161
185
  case "migrate":
162
186
  lines.push(`Migrated providers in ${String(data?.codexDir ?? "")} using ${String(data?.strategy ?? "")}.`);
@@ -185,10 +209,12 @@ function renderHumanSuccess(command, data, warnings) {
185
209
  break;
186
210
  case "doctor": {
187
211
  const healthy = Boolean(data?.healthy);
188
- lines.push(healthy ? "No issues found." : "Issues found:");
189
212
  const issues = data?.issues ?? [];
213
+ lines.push(healthy ? "Doctor summary: healthy. No action required." : `Doctor summary: ${issues.length} issue(s) need attention.`);
214
+ lines.push(`target runtime: ${String(data?.codexDir ?? "")}`);
190
215
  for (const issue of issues) {
191
- lines.push(`${issue.code}: ${issue.message}`);
216
+ lines.push(`- ${String(issue.code)}: ${String(issue.message)}`);
217
+ lines.push(` next step: ${renderDoctorIssueNextStep(issue)}`);
192
218
  }
193
219
  break;
194
220
  }
@@ -212,6 +238,107 @@ function renderHumanSuccess(command, data, warnings) {
212
238
  }
213
239
  return lines;
214
240
  }
241
+ /**
242
+ * Summarizes runtime health for the human-readable status output.
243
+ */
244
+ function renderStatusHealth(data) {
245
+ const configExists = Boolean(data?.configExists);
246
+ const providersExists = Boolean(data?.providersExists);
247
+ const auth = data?.auth ?? {};
248
+ const bridge = data?.copilotBridge ?? null;
249
+ const issues = Array.isArray(data?.issues) ? data?.issues : [];
250
+ const activeProviderResolvable = data?.activeProviderResolvable !== false;
251
+ const liveState = data?.liveState ?? {};
252
+ const copilotSdk = data?.copilotSdk ?? {};
253
+ const copilotAuth = data?.copilotAuth ?? null;
254
+ const runtimeProvider = typeof data?.runtimeProvider === "string" ? data.runtimeProvider : null;
255
+ const activePathUsesCopilot = runtimeProvider === "copilot-sdk-bridge";
256
+ if (!configExists || !providersExists) {
257
+ return "incomplete local state";
258
+ }
259
+ if (!activeProviderResolvable || liveState.reason === "shared-profile") {
260
+ return "active provider ambiguous";
261
+ }
262
+ if (issues.some((issue) => issue.code === "UNMANAGED_ACTIVE_PROFILE")) {
263
+ return "active profile unmanaged";
264
+ }
265
+ if (issues.some((issue) => issue.code === "ACTIVE_PROVIDER_UNRESOLVED")) {
266
+ return "active provider ambiguous";
267
+ }
268
+ if (activePathUsesCopilot && copilotSdk.installed === false) {
269
+ return "copilot sdk missing";
270
+ }
271
+ if (activePathUsesCopilot && copilotAuth && copilotAuth.ready === false) {
272
+ return "copilot auth required";
273
+ }
274
+ if (activePathUsesCopilot && bridge && bridge.ok === false) {
275
+ return "copilot runtime needs repair";
276
+ }
277
+ if (auth.exists === false) {
278
+ return "auth projection missing";
279
+ }
280
+ if (auth.valid === false) {
281
+ return "auth projection invalid";
282
+ }
283
+ return "ok";
284
+ }
285
+ /**
286
+ * Renders the mapped provider line without claiming a unique winner for shared profiles.
287
+ */
288
+ function renderStatusMappedProvider(data) {
289
+ if (typeof data?.provider === "string" && data.provider.length > 0) {
290
+ return data.provider;
291
+ }
292
+ const candidates = Array.isArray(data?.activeProviderCandidates) ? data?.activeProviderCandidates : [];
293
+ if (candidates.length > 1) {
294
+ return `(ambiguous: ${candidates.join(", ")})`;
295
+ }
296
+ return "(unmanaged or unresolved)";
297
+ }
298
+ /**
299
+ * Renders the active workflow path in status output.
300
+ */
301
+ function renderStatusProviderPath(data) {
302
+ return typeof data?.runtimeProvider === "string" && data.runtimeProvider === "copilot-sdk-bridge" ? "copilot" : "direct";
303
+ }
304
+ /**
305
+ * Suggests the next operator action for the human-readable status output.
306
+ */
307
+ function renderStatusNextStep(data, warnings) {
308
+ if (warnings.length > 0) {
309
+ return "run `codexs doctor` to inspect warnings before the next write command";
310
+ }
311
+ if (!data?.provider) {
312
+ return "run `codexs switch <provider>` after adding or adopting a managed provider";
313
+ }
314
+ return "run `codexs doctor` if you need a deeper diagnostic pass";
315
+ }
316
+ /**
317
+ * Turns structured doctor issue codes into repair-oriented next steps.
318
+ */
319
+ function renderDoctorIssueNextStep(issue) {
320
+ switch (issue.code) {
321
+ case "CONFIG_NOT_FOUND":
322
+ return "restore or create config.toml before switching providers";
323
+ case "PROVIDERS_NOT_FOUND":
324
+ return "run `codexs init` and then add or migrate providers";
325
+ case "COPILOT_SDK_MISSING":
326
+ return "run `codexs login copilot` to install the optional Copilot runtime";
327
+ case "COPILOT_AUTH_REQUIRED":
328
+ return "run `codexs login copilot` to complete upstream authentication";
329
+ case "BRIDGE_STATE_STALE":
330
+ case "BRIDGE_STATE_MISSING":
331
+ case "BRIDGE_HEALTHCHECK_FAILED":
332
+ return "reselect the provider with `codexs switch <provider>` or inspect bridge state";
333
+ case "UNMANAGED_ACTIVE_PROFILE":
334
+ return "switch to a managed provider or adopt the active profile with `codexs migrate`";
335
+ case "ACTIVE_PROVIDER_UNRESOLVED":
336
+ case "SHARED_PROFILE_REFERENCE":
337
+ return "make provider-to-profile mappings unique before relying on current-provider detection";
338
+ default:
339
+ return "inspect the issue details and rerun `codexs doctor` after fixing the state";
340
+ }
341
+ }
215
342
  /**
216
343
  * Writes one rendered line to either stdout or stderr.
217
344
  */
package/dist/cli.js CHANGED
@@ -1,10 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
37
  exports.printHelp = printHelp;
5
38
  exports.printVersion = printVersion;
6
39
  exports.main = main;
7
- const dispatch_1 = require("./commands/dispatch");
8
40
  const args_1 = require("./commands/args");
9
41
  const help_1 = require("./commands/help");
10
42
  const errors_1 = require("./domain/errors");
@@ -49,7 +81,7 @@ function main() {
49
81
  command: parsed.command,
50
82
  options: parsed.globalOptions,
51
83
  };
52
- (0, dispatch_1.executeCommand)(ctx, parsed)
84
+ Promise.resolve().then(() => __importStar(require("./commands/dispatch"))).then(({ executeCommand }) => executeCommand(ctx, parsed))
53
85
  .then((result) => {
54
86
  (0, output_1.outputSuccess)(ctx, result);
55
87
  })
@@ -11,7 +11,7 @@ const registry_1 = require("./registry");
11
11
  */
12
12
  function parseArgs(argv) {
13
13
  let json = false;
14
- let codexDir = (0, codex_paths_1.resolveCodexDir)();
14
+ let codexDir = null;
15
15
  let codexDirExplicit = false;
16
16
  const remaining = [];
17
17
  for (let index = 0; index < argv.length; index += 1) {
@@ -105,7 +105,7 @@ function defaultParsed(command, overrides) {
105
105
  positionals: [],
106
106
  globalOptions: {
107
107
  json: overrides?.json ?? false,
108
- codexDir: overrides?.codexDir ?? (0, codex_paths_1.resolveCodexDir)(),
108
+ codexDir: overrides?.codexDir ?? null,
109
109
  codexDirExplicit: false,
110
110
  },
111
111
  commandOptions: new Map(),
@@ -1,8 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.executeCommand = executeCommand;
37
+ const path = __importStar(require("node:path"));
4
38
  const errors_1 = require("../domain/errors");
5
39
  const prompt_1 = require("../interaction/prompt");
40
+ const codex_paths_1 = require("../storage/codex-paths");
41
+ const tool_config_repo_1 = require("../storage/tool-config-repo");
6
42
  const registry_1 = require("./registry");
7
43
  /**
8
44
  * Resolves the shared command definition and executes its registered handler.
@@ -12,5 +48,9 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
12
48
  if (!definition) {
13
49
  throw (0, errors_1.cliError)("UNKNOWN_COMMAND", `Unknown command: ${ctx.command}`);
14
50
  }
51
+ const toolHomeDir = (0, codex_paths_1.resolveCodexSwitchHome)();
52
+ const toolConfigPath = path.join(toolHomeDir, "codex-switch.json");
53
+ const toolConfig = (0, tool_config_repo_1.readToolConfigIfExists)(toolConfigPath);
54
+ ctx.options.codexDir = (0, codex_paths_1.resolveCodexDir)(ctx.options.codexDir ?? undefined, toolConfig);
15
55
  return definition.handler(ctx, parsed, runtime);
16
56
  }