@minniexcode/codex-switch 0.0.5 → 0.0.7

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 (71) hide show
  1. package/README.AI.md +5 -2
  2. package/README.md +44 -100
  3. package/dist/app/add-provider.js +28 -4
  4. package/dist/app/edit-provider.js +47 -19
  5. package/dist/app/export-providers.js +2 -2
  6. package/dist/app/get-current-profile.js +1 -1
  7. package/dist/app/get-status.js +10 -3
  8. package/dist/app/import-providers.js +15 -7
  9. package/dist/app/init-codex.js +68 -0
  10. package/dist/app/list-backups.js +1 -1
  11. package/dist/app/list-config-profiles.js +3 -2
  12. package/dist/app/list-providers.js +2 -1
  13. package/dist/app/remove-provider.js +2 -2
  14. package/dist/app/rollback-backup.js +1 -1
  15. package/dist/app/rollback-latest.js +1 -1
  16. package/dist/app/run-doctor.js +83 -6
  17. package/dist/app/run-mutation.js +2 -2
  18. package/dist/app/setup-codex.js +21 -12
  19. package/dist/app/show-config.js +11 -3
  20. package/dist/app/show-provider.js +1 -1
  21. package/dist/app/switch-provider.js +16 -9
  22. package/dist/cli/add-interactive.js +7 -104
  23. package/dist/cli/args.js +6 -135
  24. package/dist/cli/help.js +8 -313
  25. package/dist/cli/interactive.js +17 -225
  26. package/dist/cli/output.js +21 -6
  27. package/dist/cli/prompt.js +4 -106
  28. package/dist/cli.js +10 -404
  29. package/dist/commands/args.js +132 -0
  30. package/dist/commands/dispatch.js +16 -0
  31. package/dist/commands/handlers.js +460 -0
  32. package/dist/commands/help.js +120 -0
  33. package/dist/commands/registry.js +351 -0
  34. package/dist/commands/types.js +2 -0
  35. package/dist/domain/config.js +235 -21
  36. package/dist/domain/providers.js +16 -2
  37. package/dist/domain/setup.js +1 -0
  38. package/dist/infra/backup-repo.js +9 -206
  39. package/dist/infra/codex-cli.js +9 -126
  40. package/dist/infra/codex-paths.js +6 -67
  41. package/dist/infra/config-repo.js +59 -0
  42. package/dist/infra/fs-utils.js +8 -93
  43. package/dist/infra/lock-repo.js +4 -95
  44. package/dist/infra/providers-repo.js +8 -94
  45. package/dist/interaction/add-interactive.js +99 -0
  46. package/dist/interaction/interactive.js +289 -0
  47. package/dist/interaction/prompt.js +110 -0
  48. package/dist/runtime/codex-cli.js +130 -0
  49. package/dist/runtime/codex-probe.js +57 -0
  50. package/dist/runtime/types.js +2 -0
  51. package/dist/storage/auth-repo.js +160 -0
  52. package/dist/storage/backup-repo.js +210 -0
  53. package/dist/storage/codex-paths.js +71 -0
  54. package/dist/storage/config-repo.js +266 -0
  55. package/dist/storage/fs-utils.js +97 -0
  56. package/dist/storage/lock-repo.js +99 -0
  57. package/dist/storage/providers-repo.js +98 -0
  58. package/docs/Design/codex-switch-v0.0.5-design.md +32 -22
  59. package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
  60. package/docs/Design/codex-switch-v0.0.7-design.md +862 -0
  61. package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +227 -89
  62. package/docs/PRD/codex-switch-prd-v0.1.0.md +200 -226
  63. package/docs/PRD/codex-switch-prd.md +1 -1
  64. package/docs/Reference/codex-config-reference.md +604 -0
  65. package/docs/Reference/codex-config-reference.zh-CN.md +633 -0
  66. package/docs/cli-usage.md +78 -29
  67. package/docs/codex-switch-technical-architecture.md +73 -4
  68. package/docs/test-report-0.0.5.md +163 -0
  69. package/docs/test-report-0.0.7.md +118 -0
  70. package/docs/testing.md +151 -0
  71. package/package.json +1 -1
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.listConfigProfilesView = listConfigProfilesView;
4
4
  const config_1 = require("../domain/config");
5
- const config_repo_1 = require("../infra/config-repo");
6
- const providers_repo_1 = require("../infra/providers-repo");
5
+ const config_repo_1 = require("../storage/config-repo");
6
+ const providers_repo_1 = require("../storage/providers-repo");
7
7
  /**
8
8
  * Returns the lightweight config profile listing.
9
9
  */
@@ -16,6 +16,7 @@ function listConfigProfilesView(args) {
16
16
  isActive: profile.isActive,
17
17
  linkedProviders: profile.linkedProviders,
18
18
  model: profile.model,
19
+ modelProvider: profile.modelProvider,
19
20
  baseUrl: profile.baseUrl,
20
21
  source: profile.source,
21
22
  }));
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.listProviders = listProviders;
4
- const providers_repo_1 = require("../infra/providers-repo");
4
+ const providers_repo_1 = require("../storage/providers-repo");
5
5
  /**
6
6
  * Returns the sorted list of configured providers for display.
7
7
  */
@@ -11,6 +11,7 @@ function listProviders(providersPath) {
11
11
  const items = names.map((name) => ({
12
12
  name,
13
13
  profile: providers.providers[name].profile,
14
+ envKey: providers.providers[name].envKey,
14
15
  note: providers.providers[name].note ?? null,
15
16
  tags: providers.providers[name].tags ?? [],
16
17
  }));
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.removeProvider = removeProvider;
4
4
  const errors_1 = require("../domain/errors");
5
5
  const config_1 = require("../domain/config");
6
- const config_repo_1 = require("../infra/config-repo");
7
- const providers_repo_1 = require("../infra/providers-repo");
6
+ const config_repo_1 = require("../storage/config-repo");
7
+ const providers_repo_1 = require("../storage/providers-repo");
8
8
  const run_mutation_1 = require("./run-mutation");
9
9
  /**
10
10
  * Removes a provider from the managed providers registry.
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.rollbackBackup = rollbackBackup;
4
4
  const errors_1 = require("../domain/errors");
5
- const backup_repo_1 = require("../infra/backup-repo");
5
+ const backup_repo_1 = require("../storage/backup-repo");
6
6
  /**
7
7
  * Restores either the latest backup or a specific historical backup by id.
8
8
  */
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.rollbackLatest = rollbackLatest;
4
4
  const errors_1 = require("../domain/errors");
5
- const backup_repo_1 = require("../infra/backup-repo");
5
+ const backup_repo_1 = require("../storage/backup-repo");
6
6
  /**
7
7
  * Restores the most recent mutation backup recorded by codex-switch.
8
8
  */
@@ -37,10 +37,12 @@ exports.runDoctor = runDoctor;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const config_1 = require("../domain/config");
39
39
  const runtime_state_1 = require("../domain/runtime-state");
40
- const codex_cli_1 = require("../infra/codex-cli");
41
- const config_repo_1 = require("../infra/config-repo");
42
- const providers_repo_1 = require("../infra/providers-repo");
40
+ const config_repo_1 = require("../storage/config-repo");
41
+ const providers_repo_1 = require("../storage/providers-repo");
43
42
  const errors_1 = require("../domain/errors");
43
+ const codex_probe_1 = require("../runtime/codex-probe");
44
+ const auth_repo_1 = require("../storage/auth-repo");
45
+ const providers_1 = require("../domain/providers");
44
46
  /**
45
47
  * Performs consistency checks across config.toml, providers.json, and the local Codex CLI.
46
48
  */
@@ -78,6 +80,7 @@ function runDoctor(args) {
78
80
  try {
79
81
  providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
80
82
  if (document) {
83
+ // Preserve domain issue codes while translating them into user-facing diagnostic messages.
81
84
  for (const issue of (0, config_1.collectConfigConsistencyIssues)(document, providers)) {
82
85
  issues.push({
83
86
  ...issue,
@@ -95,12 +98,60 @@ function runDoctor(args) {
95
98
  });
96
99
  }
97
100
  }
101
+ const authState = (0, auth_repo_1.readManagedAuthState)(args.authPath);
102
+ if (authState.exists && !authState.valid) {
103
+ issues.push({
104
+ code: "AUTH_JSON_INVALID",
105
+ message: authState.parseError ?? "auth.json is invalid.",
106
+ file: args.authPath,
107
+ });
108
+ }
109
+ if (document?.activeProfile && providers) {
110
+ const matches = (0, providers_1.findProvidersByProfile)(providers, document.activeProfile);
111
+ if (matches.length === 1) {
112
+ const activeProvider = providers.providers[matches[0]];
113
+ const payload = authState.payload ?? {};
114
+ const actualKeys = authState.managedSecretKeys;
115
+ if (authState.authMode !== null && authState.authMode !== "apikey") {
116
+ issues.push({
117
+ code: "AUTH_JSON_INVALID",
118
+ message: `auth.json auth_mode must be "apikey", found "${authState.authMode}".`,
119
+ });
120
+ }
121
+ if (!actualKeys.includes(activeProvider.envKey) || actualKeys.length !== 1) {
122
+ issues.push({
123
+ code: "AUTH_JSON_ENV_KEY_MISMATCH",
124
+ message: `auth.json managed env key does not match active provider "${matches[0]}".`,
125
+ provider: matches[0],
126
+ expectedEnvKey: activeProvider.envKey,
127
+ actualEnvKeys: actualKeys,
128
+ });
129
+ }
130
+ if (payload[activeProvider.envKey] !== activeProvider.apiKey) {
131
+ issues.push({
132
+ code: "AUTH_JSON_APIKEY_MISMATCH",
133
+ message: `auth.json secret value does not match active provider "${matches[0]}".`,
134
+ provider: matches[0],
135
+ });
136
+ }
137
+ }
138
+ }
139
+ // Drift inspection still runs when files are missing so status output can explain partial state.
98
140
  const drift = (0, runtime_state_1.inspectLiveStateDrift)(currentProfile, providers);
99
- const codexCheck = (0, codex_cli_1.checkCodexAvailable)();
141
+ const codexCheck = (0, codex_probe_1.probeCodexRuntime)();
100
142
  if (!codexCheck.ok) {
143
+ const message = codexCheck.reason === "missing"
144
+ ? "codex CLI is not available on PATH."
145
+ : codexCheck.reason === "unsupported"
146
+ ? "codex CLI version is below the supported minimum."
147
+ : "codex CLI probe failed.";
101
148
  issues.push({
102
- code: "CODEX_LOGIN_FAILED",
103
- message: "codex CLI is not available.",
149
+ code: codexCheck.reason === "unsupported"
150
+ ? "CODEX_VERSION_UNSUPPORTED"
151
+ : codexCheck.reason === "missing"
152
+ ? "CODEX_NOT_INSTALLED"
153
+ : "CODEX_LOGIN_FAILED",
154
+ message,
104
155
  cause: codexCheck.cause,
105
156
  });
106
157
  }
@@ -111,10 +162,14 @@ function runDoctor(args) {
111
162
  codexDir: args.codexDir,
112
163
  storage: (0, runtime_state_1.getStorageRoles)(),
113
164
  liveState: drift,
165
+ auth: authState,
114
166
  },
115
167
  warnings: issues.length === 0 ? [] : [`doctor found ${issues.length} issue(s)`],
116
168
  };
117
169
  }
170
+ /**
171
+ * Maps structured config consistency issues onto stable human-readable diagnostic text.
172
+ */
118
173
  function renderConfigIssueMessage(issue) {
119
174
  switch (issue.code) {
120
175
  case "ORPHANED_PROFILE_REFERENCE":
@@ -125,7 +180,29 @@ function renderConfigIssueMessage(issue) {
125
180
  return `Profile "${issue.profile}" is shared by multiple providers.`;
126
181
  case "ORPHANED_PROFILE_SECTION":
127
182
  return `Profile section "${issue.profile}" is not linked to any provider.`;
183
+ case "MODEL_PROVIDER_MISSING":
184
+ return `Profile "${issue.profile}" is missing model_provider.`;
185
+ case "MODEL_PROVIDER_NAME_MISMATCH":
186
+ return `Profile "${issue.profile}" must use matching model_provider name "${issue.profile}", found "${issue.modelProvider}".`;
187
+ case "MODEL_PROVIDER_SECTION_MISSING":
188
+ return `Model provider section "${issue.modelProvider}" for profile "${issue.profile}" is missing from config.toml.`;
189
+ case "MODEL_PROVIDER_BASE_URL_MISSING":
190
+ return `Model provider section "${issue.modelProvider}" for profile "${issue.profile}" is missing base_url.`;
191
+ case "MODEL_PROVIDER_ENV_KEY_MISSING":
192
+ return `Model provider section "${issue.modelProvider}" for profile "${issue.profile}" is missing env_key.`;
193
+ case "PROVIDER_ENV_KEY_MISMATCH":
194
+ return `Provider "${issue.provider}" envKey does not match runtime env_key for profile "${issue.profile}".`;
195
+ case "ACTIVE_PROVIDER_UNRESOLVED":
196
+ return `Active profile "${issue.profile}" maps to multiple providers and cannot determine the current auth mirror owner.`;
197
+ case "AUTH_JSON_INVALID":
198
+ return String(issue.message ?? issue.reason ?? "auth.json is invalid.");
199
+ case "AUTH_JSON_ENV_KEY_MISMATCH":
200
+ return `auth.json managed env key does not match provider "${String(issue.provider ?? "")}".`;
201
+ case "AUTH_JSON_APIKEY_MISMATCH":
202
+ return `auth.json secret does not match provider "${String(issue.provider ?? "")}".`;
128
203
  case "DESTRUCTIVE_REMOVE_BLOCKED":
129
204
  return `Provider "${issue.provider}" cannot be removed while "${issue.activeProfile}" remains active.`;
205
+ default:
206
+ return String(issue.code ?? "UNKNOWN_ISSUE");
130
207
  }
131
208
  }
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runMutation = runMutation;
4
4
  const errors_1 = require("../domain/errors");
5
- const backup_repo_1 = require("../infra/backup-repo");
6
- const lock_repo_1 = require("../infra/lock-repo");
5
+ const backup_repo_1 = require("../storage/backup-repo");
6
+ const lock_repo_1 = require("../storage/lock-repo");
7
7
  /**
8
8
  * Runs a write operation under a lock with automatic backup and rollback handling.
9
9
  */
@@ -33,22 +33,23 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.setupCodex = setupCodex;
36
+ exports.migrateCodex = migrateCodex;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const setup_1 = require("../domain/setup");
39
39
  const errors_1 = require("../domain/errors");
40
40
  const config_1 = require("../domain/config");
41
- const codex_cli_1 = require("../infra/codex-cli");
42
- const config_repo_1 = require("../infra/config-repo");
43
- const fs_utils_1 = require("../infra/fs-utils");
44
- const providers_repo_1 = require("../infra/providers-repo");
41
+ const codex_cli_1 = require("../runtime/codex-cli");
42
+ const config_repo_1 = require("../storage/config-repo");
43
+ const fs_utils_1 = require("../storage/fs-utils");
44
+ const providers_repo_1 = require("../storage/providers-repo");
45
+ const auth_repo_1 = require("../storage/auth-repo");
45
46
  const run_doctor_1 = require("./run-doctor");
46
47
  const run_mutation_1 = require("./run-mutation");
47
48
  const MIN_CODEX_VERSION = "0.0.1";
48
49
  /**
49
- * Bootstraps a managed providers.json from the existing Codex directory.
50
+ * Migrates unmanaged Codex config profiles into a managed providers.json registry.
50
51
  */
51
- function setupCodex(args) {
52
+ function migrateCodex(args) {
52
53
  const available = (0, codex_cli_1.checkCodexAvailable)();
53
54
  if (!available.ok) {
54
55
  throw (0, errors_1.cliError)("CODEX_NOT_INSTALLED", "codex CLI is not available.", {
@@ -70,8 +71,9 @@ function setupCodex(args) {
70
71
  }
71
72
  const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
72
73
  const profileViews = (0, config_1.buildManagedProfileViews)(document, null);
74
+ // Migrate can only adopt unmanaged profiles that already contain enough runtime data to become managed.
73
75
  const adoptableProfiles = profileViews
74
- .filter((view) => view.source === "unmanaged" && view.model && view.baseUrl)
76
+ .filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl && view.envKey)
75
77
  .map((view) => view.name)
76
78
  .sort();
77
79
  if (profileViews.length === 0) {
@@ -81,20 +83,20 @@ function setupCodex(args) {
81
83
  }
82
84
  const invalidAdoptProfiles = args.adoptProfiles.filter((profile) => !adoptableProfiles.includes(profile));
83
85
  if (invalidAdoptProfiles.length > 0) {
84
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup only adopts unmanaged profiles that already contain model and base_url.", {
86
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate only adopts unmanaged profiles that already contain model, model_provider, and matching model_providers base_url/env_key.", {
85
87
  invalidProfiles: invalidAdoptProfiles.sort(),
86
88
  adoptableProfiles,
87
89
  });
88
90
  }
89
91
  if (args.adoptProfiles.length === 0) {
90
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup requires at least one explicit profile to adopt.", {
92
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate requires at least one explicit profile to adopt.", {
91
93
  adoptableProfiles,
92
94
  });
93
95
  }
94
96
  const drafts = (0, setup_1.buildSetupDrafts)(args.adoptProfiles, args.providerDetailsByProfile);
95
97
  const incompleteProfiles = (0, setup_1.findIncompleteSetupProfiles)(drafts);
96
98
  if (incompleteProfiles.length > 0) {
97
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup requires complete provider data for every selected profile.", {
99
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate requires complete provider data for every selected profile.", {
98
100
  incompleteProfiles,
99
101
  });
100
102
  }
@@ -117,15 +119,20 @@ function setupCodex(args) {
117
119
  codexDir: args.codexDir,
118
120
  backupsDir: args.backupsDir,
119
121
  latestBackupPath: args.latestBackupPath,
120
- operation: "setup",
122
+ operation: "migrate",
121
123
  files: [
122
124
  { absolutePath: args.providersPath, relativePath: "providers.json" },
123
125
  { absolutePath: args.configPath, relativePath: "config.toml" },
126
+ { absolutePath: args.authPath, relativePath: "auth.json" },
124
127
  ],
125
128
  mutate: () => {
129
+ // migrate currently preserves config structure and only asserts that the file remains writable inside the mutation flow.
126
130
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {});
127
131
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, finalProviders);
128
132
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
133
+ const activeProviderName = (0, config_repo_1.resolveActiveProviderName)(document, finalProviders);
134
+ const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
135
+ (0, auth_repo_1.writeAuthFile)(args.authPath, finalProviders.providers[activeProviderName], existingAuth ?? undefined);
129
136
  return {
130
137
  codexDir: args.codexDir,
131
138
  strategy: args.strategy,
@@ -140,10 +147,12 @@ function setupCodex(args) {
140
147
  };
141
148
  },
142
149
  });
150
+ // Re-run doctor on the final state so migrate returns immediate post-migration diagnostics.
143
151
  const doctor = (0, run_doctor_1.runDoctor)({
144
152
  codexDir: args.codexDir,
145
153
  configPath: args.configPath,
146
154
  providersPath: args.providersPath,
155
+ authPath: args.authPath,
147
156
  });
148
157
  return {
149
158
  data: {
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.showConfig = showConfig;
4
4
  const config_1 = require("../domain/config");
5
5
  const errors_1 = require("../domain/errors");
6
- const config_repo_1 = require("../infra/config-repo");
7
- const providers_repo_1 = require("../infra/providers-repo");
6
+ const providers_1 = require("../domain/providers");
7
+ const config_repo_1 = require("../storage/config-repo");
8
+ const providers_repo_1 = require("../storage/providers-repo");
8
9
  /**
9
10
  * Returns the structured config view, optionally filtered to one profile.
10
11
  */
@@ -28,7 +29,14 @@ function showConfig(args) {
28
29
  data: {
29
30
  activeProfile: document.activeProfile,
30
31
  selectedProfile,
31
- profiles,
32
+ profiles: profiles.map((profile) => ({
33
+ ...profile,
34
+ managedProviderEnvKeys: (0, providers_1.findProvidersByProfile)(providers, profile.name).map((providerName) => ({
35
+ providerName,
36
+ envKey: providers.providers[providerName].envKey,
37
+ matchesRuntime: providers.providers[providerName].envKey === profile.envKey,
38
+ })),
39
+ })),
32
40
  },
33
41
  };
34
42
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.showProvider = showProvider;
4
4
  const providers_1 = require("../domain/providers");
5
- const providers_repo_1 = require("../infra/providers-repo");
5
+ const providers_repo_1 = require("../storage/providers-repo");
6
6
  /**
7
7
  * Returns a single provider record, with text-mode callers able to use a masked preview.
8
8
  */
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.switchProvider = switchProvider;
4
4
  const errors_1 = require("../domain/errors");
5
- const config_repo_1 = require("../infra/config-repo");
6
- const codex_cli_1 = require("../infra/codex-cli");
7
- const providers_repo_1 = require("../infra/providers-repo");
5
+ const config_repo_1 = require("../storage/config-repo");
6
+ const providers_repo_1 = require("../storage/providers-repo");
7
+ const auth_repo_1 = require("../storage/auth-repo");
8
8
  const run_mutation_1 = require("./run-mutation");
9
9
  /**
10
- * Switches the active Codex profile and optionally refreshes the CLI login session.
10
+ * Switches the active Codex profile and rewrites auth.json for the target provider.
11
11
  */
12
12
  function switchProvider(args) {
13
13
  const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
@@ -18,6 +18,15 @@ function switchProvider(args) {
18
18
  });
19
19
  }
20
20
  const document = (0, config_repo_1.ensureProfileExists)(args.configPath, provider.profile, args.providerName);
21
+ const envKey = (0, config_repo_1.requireRuntimeEnvKey)(document, provider.profile);
22
+ if (provider.envKey !== envKey) {
23
+ throw (0, errors_1.cliError)("PROVIDER_ENV_KEY_MISMATCH", `Provider "${args.providerName}" envKey does not match runtime env_key.`, {
24
+ provider: args.providerName,
25
+ profile: provider.profile,
26
+ providerEnvKey: provider.envKey,
27
+ runtimeEnvKey: envKey,
28
+ });
29
+ }
21
30
  return (0, run_mutation_1.runMutation)({
22
31
  codexDir: args.codexDir,
23
32
  backupsDir: args.backupsDir,
@@ -31,15 +40,13 @@ function switchProvider(args) {
31
40
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
32
41
  setActiveProfile: provider.profile,
33
42
  });
34
- // Update the runtime profile first so any subsequent login is associated with the new target.
35
43
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
36
- if (!args.noLogin) {
37
- (0, codex_cli_1.runCodexLogin)(provider.apiKey, args.codexDir);
38
- }
44
+ const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
45
+ (0, auth_repo_1.writeAuthFile)(args.authPath, provider, existingAuth ?? undefined);
39
46
  return {
40
47
  provider: args.providerName,
41
48
  profile: provider.profile,
42
- loginPerformed: !args.noLogin,
49
+ envKey: provider.envKey,
43
50
  };
44
51
  },
45
52
  });
@@ -1,108 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.COMMON_TAG_CHOICES = void 0;
4
- exports.collectAddInput = collectAddInput;
5
- exports.createNonInteractiveAddError = createNonInteractiveAddError;
6
- exports.promptTags = promptTags;
7
- exports.parseTags = parseTags;
8
- const errors_1 = require("../domain/errors");
9
- exports.COMMON_TAG_CHOICES = ["free", "paid", "daily", "backup"];
3
+ exports.promptTags = exports.createNonInteractiveAddError = exports.COMMON_TAG_CHOICES = exports.collectAddInput = void 0;
10
4
  /**
11
- * Collects add command inputs interactively when required values are missing.
5
+ * Compatibility facade that re-exports interactive add helpers for older imports.
12
6
  */
13
- async function collectAddInput(runtime, defaults, providerExists) {
14
- runtime.writeLine("Interactive add mode");
15
- runtime.writeLine("Provide the missing required fields. Press Enter to skip optional fields.");
16
- const providerName = defaults.providerName
17
- ? normalizeRequiredValue(defaults.providerName)
18
- : await promptProviderName(runtime, providerExists);
19
- const profile = defaults.profile ? normalizeRequiredValue(defaults.profile) : await promptRequiredValue(runtime, "Profile");
20
- const apiKey = defaults.apiKey
21
- ? normalizeRequiredValue(defaults.apiKey)
22
- : await promptConfirmedSecret(runtime, "API key", "Confirm API key");
23
- const baseUrl = defaults.baseUrl ?? normalizeOptionalValue(await runtime.inputText("Base URL (optional)"));
24
- const note = defaults.note ?? normalizeOptionalValue(await runtime.inputText("Note (optional)"));
25
- const tags = defaults.tags.length > 0 ? defaults.tags : await promptTags(runtime);
26
- return {
27
- providerName,
28
- profile,
29
- apiKey,
30
- baseUrl,
31
- note,
32
- tags,
33
- };
34
- }
35
- /**
36
- * Throws a consistent error when interactive add is unavailable.
37
- */
38
- function createNonInteractiveAddError() {
39
- return (0, errors_1.cliError)("INVALID_ARGUMENT", "add requires <provider>, --profile, and --api-key when running without an interactive TTY.", {
40
- suggestion: "Run in a terminal TTY or pass all required values explicitly.",
41
- });
42
- }
43
- async function promptProviderName(runtime, providerExists) {
44
- while (true) {
45
- const providerName = await promptRequiredValue(runtime, "Provider name");
46
- if (providerExists(providerName)) {
47
- runtime.writeLine(`Provider "${providerName}" already exists. Choose a different name.`);
48
- continue;
49
- }
50
- return providerName;
51
- }
52
- }
53
- async function promptRequiredValue(runtime, label) {
54
- while (true) {
55
- const value = normalizeRequiredValue(await runtime.inputText(label));
56
- if (value.length > 0) {
57
- return value;
58
- }
59
- runtime.writeLine(`${label} is required.`);
60
- }
61
- }
62
- async function promptConfirmedSecret(runtime, label, confirmationLabel) {
63
- while (true) {
64
- const first = normalizeRequiredValue(await runtime.inputSecret(label));
65
- if (first.length === 0) {
66
- runtime.writeLine(`${label} is required.`);
67
- continue;
68
- }
69
- const second = normalizeRequiredValue(await runtime.inputSecret(confirmationLabel));
70
- if (second.length === 0) {
71
- runtime.writeLine(`${confirmationLabel} is required.`);
72
- continue;
73
- }
74
- if (first !== second) {
75
- runtime.writeLine("API key entries did not match. Try again.");
76
- continue;
77
- }
78
- return first;
79
- }
80
- }
81
- function normalizeRequiredValue(value) {
82
- return value.trim();
83
- }
84
- function normalizeOptionalValue(value) {
85
- const normalized = value.trim();
86
- return normalized === "" ? null : normalized;
87
- }
88
- async function promptTags(runtime, defaults = []) {
89
- const defaultPresetTags = defaults.filter(isCommonTag);
90
- const defaultCustomTags = defaults.filter((tag) => !isCommonTag(tag));
91
- const presetTags = await runtime.selectMany("Select tags (optional)", exports.COMMON_TAG_CHOICES.map((tag) => ({ value: tag, label: tag })), { defaultValues: defaultPresetTags });
92
- const customTags = parseTags(await runtime.inputText("Custom tags (optional, comma-separated)", {
93
- defaultValue: defaultCustomTags.join(", "),
94
- }));
95
- return dedupeTags([...presetTags, ...customTags]);
96
- }
97
- function parseTags(value) {
98
- return dedupeTags(value
99
- .split(",")
100
- .map((tag) => tag.trim())
101
- .filter((tag) => tag.length > 0));
102
- }
103
- function isCommonTag(tag) {
104
- return exports.COMMON_TAG_CHOICES.includes(tag);
105
- }
106
- function dedupeTags(tags) {
107
- return Array.from(new Set(tags));
108
- }
7
+ var add_interactive_1 = require("../interaction/add-interactive");
8
+ Object.defineProperty(exports, "collectAddInput", { enumerable: true, get: function () { return add_interactive_1.collectAddInput; } });
9
+ Object.defineProperty(exports, "COMMON_TAG_CHOICES", { enumerable: true, get: function () { return add_interactive_1.COMMON_TAG_CHOICES; } });
10
+ Object.defineProperty(exports, "createNonInteractiveAddError", { enumerable: true, get: function () { return add_interactive_1.createNonInteractiveAddError; } });
11
+ Object.defineProperty(exports, "promptTags", { enumerable: true, get: function () { return add_interactive_1.promptTags; } });