@minniexcode/codex-switch 0.0.5 → 0.0.6

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 (62) hide show
  1. package/README.md +35 -97
  2. package/dist/app/add-provider.js +10 -4
  3. package/dist/app/edit-provider.js +9 -9
  4. package/dist/app/export-providers.js +2 -2
  5. package/dist/app/get-current-profile.js +1 -1
  6. package/dist/app/get-status.js +2 -2
  7. package/dist/app/import-providers.js +15 -7
  8. package/dist/app/list-backups.js +1 -1
  9. package/dist/app/list-config-profiles.js +3 -2
  10. package/dist/app/list-providers.js +1 -1
  11. package/dist/app/remove-provider.js +2 -2
  12. package/dist/app/rollback-backup.js +1 -1
  13. package/dist/app/rollback-latest.js +1 -1
  14. package/dist/app/run-doctor.js +23 -6
  15. package/dist/app/run-mutation.js +2 -2
  16. package/dist/app/setup-codex.js +6 -6
  17. package/dist/app/show-config.js +2 -2
  18. package/dist/app/show-provider.js +1 -1
  19. package/dist/app/switch-provider.js +3 -3
  20. package/dist/cli/add-interactive.js +7 -106
  21. package/dist/cli/args.js +5 -137
  22. package/dist/cli/help.js +5 -313
  23. package/dist/cli/interactive.js +16 -227
  24. package/dist/cli/output.js +2 -2
  25. package/dist/cli/prompt.js +3 -108
  26. package/dist/cli.js +10 -404
  27. package/dist/commands/args.js +132 -0
  28. package/dist/commands/dispatch.js +16 -0
  29. package/dist/commands/handlers.js +391 -0
  30. package/dist/commands/help.js +119 -0
  31. package/dist/commands/registry.js +291 -0
  32. package/dist/commands/types.js +2 -0
  33. package/dist/domain/config.js +100 -23
  34. package/dist/infra/backup-repo.js +8 -208
  35. package/dist/infra/codex-cli.js +8 -128
  36. package/dist/infra/codex-paths.js +5 -69
  37. package/dist/infra/config-repo.js +59 -0
  38. package/dist/infra/fs-utils.js +7 -95
  39. package/dist/infra/lock-repo.js +3 -97
  40. package/dist/infra/providers-repo.js +7 -96
  41. package/dist/interaction/add-interactive.js +108 -0
  42. package/dist/interaction/interactive.js +216 -0
  43. package/dist/interaction/prompt.js +110 -0
  44. package/dist/runtime/codex-cli.js +130 -0
  45. package/dist/runtime/codex-probe.js +50 -0
  46. package/dist/runtime/types.js +2 -0
  47. package/dist/storage/backup-repo.js +210 -0
  48. package/dist/storage/codex-paths.js +71 -0
  49. package/dist/storage/config-repo.js +208 -0
  50. package/dist/storage/fs-utils.js +97 -0
  51. package/dist/storage/lock-repo.js +99 -0
  52. package/dist/storage/providers-repo.js +98 -0
  53. package/docs/Design/codex-switch-v0.0.5-design.md +32 -22
  54. package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
  55. package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +125 -93
  56. package/docs/PRD/codex-switch-prd-v0.1.0.md +200 -226
  57. package/docs/PRD/codex-switch-prd.md +1 -1
  58. package/docs/cli-usage.md +2 -1
  59. package/docs/codex-switch-technical-architecture.md +73 -4
  60. package/docs/test-report-0.0.5.md +163 -0
  61. package/docs/testing.md +131 -0
  62. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,73 +1,15 @@
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
- })();
36
3
  Object.defineProperty(exports, "__esModule", { value: true });
37
4
  exports.printHelp = printHelp;
38
5
  exports.printVersion = printVersion;
39
- exports.executeCommand = executeCommand;
40
- const fs = __importStar(require("node:fs"));
41
- const add_provider_1 = require("./app/add-provider");
42
- const edit_provider_1 = require("./app/edit-provider");
43
- const export_providers_1 = require("./app/export-providers");
44
- const get_current_profile_1 = require("./app/get-current-profile");
45
- const get_status_1 = require("./app/get-status");
46
- const import_providers_1 = require("./app/import-providers");
47
- const list_config_profiles_1 = require("./app/list-config-profiles");
48
- const list_backups_1 = require("./app/list-backups");
49
- const list_providers_1 = require("./app/list-providers");
50
- const remove_provider_1 = require("./app/remove-provider");
51
- const rollback_backup_1 = require("./app/rollback-backup");
52
- const run_doctor_1 = require("./app/run-doctor");
53
- const setup_codex_1 = require("./app/setup-codex");
54
- const show_config_1 = require("./app/show-config");
55
- const show_provider_1 = require("./app/show-provider");
56
- const switch_provider_1 = require("./app/switch-provider");
57
- const config_1 = require("./domain/config");
6
+ exports.main = main;
7
+ const dispatch_1 = require("./commands/dispatch");
8
+ const args_1 = require("./commands/args");
9
+ const help_1 = require("./commands/help");
58
10
  const errors_1 = require("./domain/errors");
59
- const providers_1 = require("./domain/providers");
60
- const config_repo_1 = require("./infra/config-repo");
61
- const config_repo_2 = require("./infra/config-repo");
62
- const providers_repo_1 = require("./infra/providers-repo");
63
- const codex_paths_1 = require("./infra/codex-paths");
64
- const args_1 = require("./cli/args");
65
- const add_interactive_1 = require("./cli/add-interactive");
66
- const help_1 = require("./cli/help");
67
- const interactive_1 = require("./cli/interactive");
68
11
  const output_1 = require("./cli/output");
69
- const prompt_1 = require("./cli/prompt");
70
- const VERSION = "0.0.5";
12
+ const VERSION = "0.0.6";
71
13
  /**
72
14
  * Prints the command help text to stdout.
73
15
  */
@@ -90,10 +32,10 @@ function main() {
90
32
  process.exit(0);
91
33
  }
92
34
  if (parsed.helpRequested) {
93
- if (parsed.helpTarget && !(0, help_1.isKnownCommandName)(parsed.helpTarget)) {
94
- (0, output_1.outputFailure)({ command: "help", options: parsed.globalOptions }, (0, errors_1.cliError)("INVALID_ARGUMENT", `Unknown help topic: ${parsed.helpTarget}`, {
95
- availableCommands: (0, help_1.buildHelpText)(parsed.helpTarget).split("\n").slice(2),
96
- }));
35
+ if (parsed.helpTarget && !(0, help_1.isKnownCommandNameForHelp)(parsed.helpTarget)) {
36
+ (0, output_1.outputFailure)({ command: "help", options: parsed.globalOptions }, (0, errors_1.normalizeError)((0, errors_1.cliError)("INVALID_ARGUMENT", `Unknown help topic: ${parsed.helpTarget}`, {
37
+ availableCommands: (0, help_1.getKnownCommandNames)(),
38
+ })));
97
39
  return;
98
40
  }
99
41
  printHelp(parsed.helpTarget);
@@ -107,7 +49,7 @@ function main() {
107
49
  command: parsed.command,
108
50
  options: parsed.globalOptions,
109
51
  };
110
- executeCommand(ctx, parsed)
52
+ (0, dispatch_1.executeCommand)(ctx, parsed)
111
53
  .then((result) => {
112
54
  (0, output_1.outputSuccess)(ctx, result);
113
55
  })
@@ -115,342 +57,6 @@ function main() {
115
57
  (0, output_1.outputFailure)(ctx, (0, errors_1.normalizeError)(error));
116
58
  });
117
59
  }
118
- /**
119
- * Dispatches a parsed CLI command into the application layer.
120
- */
121
- async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRuntime)()) {
122
- let setupPaths = (0, codex_paths_1.createCodexPaths)(ctx.options.codexDir);
123
- const paths = setupPaths;
124
- switch (ctx.command) {
125
- case "list":
126
- return (0, list_providers_1.listProviders)(paths.providersPath);
127
- case "show": {
128
- let providerName = parsed.positionals[0] ?? null;
129
- if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
130
- providerName = await (0, interactive_1.promptForProviderSelection)(runtime, paths.providersPath, "Choose a provider to show");
131
- }
132
- if (!providerName) {
133
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing provider name for show command.");
134
- }
135
- return (0, show_provider_1.showProvider)({
136
- providersPath: paths.providersPath,
137
- providerName,
138
- includeSecret: ctx.options.json,
139
- });
140
- }
141
- case "current":
142
- return (0, get_current_profile_1.getCurrentProfile)(paths.configPath);
143
- case "status":
144
- return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath);
145
- case "config-show":
146
- return (0, show_config_1.showConfig)({
147
- configPath: paths.configPath,
148
- providersPath: paths.providersPath,
149
- profileName: parsed.positionals[0] ?? null,
150
- });
151
- case "config-list-profiles":
152
- return (0, list_config_profiles_1.listConfigProfilesView)({
153
- configPath: paths.configPath,
154
- providersPath: paths.providersPath,
155
- });
156
- case "switch": {
157
- let providerName = parsed.positionals[0] ?? null;
158
- if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
159
- providerName = await (0, interactive_1.promptForProviderSelection)(runtime, paths.providersPath, "Choose a provider to switch to");
160
- }
161
- if (!providerName) {
162
- throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for switch command.");
163
- }
164
- return (0, switch_provider_1.switchProvider)({
165
- codexDir: paths.codexDir,
166
- backupsDir: paths.backupsDir,
167
- latestBackupPath: paths.latestBackupPath,
168
- configPath: paths.configPath,
169
- providersPath: paths.providersPath,
170
- authPath: paths.authPath,
171
- providerName,
172
- noLogin: (0, args_1.hasFlag)(parsed.commandOptions, "--no-login"),
173
- });
174
- }
175
- case "import": {
176
- const sourceFile = parsed.positionals[0];
177
- if (!sourceFile) {
178
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing import file path.");
179
- }
180
- const merge = (0, args_1.hasFlag)(parsed.commandOptions, "--merge");
181
- let repairProfiles;
182
- if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
183
- await (0, interactive_1.confirmImport)(runtime, sourceFile, merge);
184
- const document = (0, config_repo_2.readStructuredConfig)(paths.configPath);
185
- const imported = (0, providers_1.validateProvidersShape)(JSON.parse(fs.readFileSync(sourceFile, "utf8")));
186
- const current = (0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath);
187
- const next = merge ? (0, providers_repo_1.mergeProviders)(current, imported) : imported;
188
- const missingProfiles = (0, config_1.buildManagedProfileViews)(document, next)
189
- .filter((view) => view.source === "orphaned-reference")
190
- .map((view) => view.name)
191
- .sort();
192
- if (missingProfiles.length > 0) {
193
- repairProfiles = await (0, interactive_1.collectImportRepairDetails)(runtime, missingProfiles);
194
- }
195
- }
196
- return (0, import_providers_1.importProviders)({
197
- codexDir: paths.codexDir,
198
- backupsDir: paths.backupsDir,
199
- latestBackupPath: paths.latestBackupPath,
200
- providersPath: paths.providersPath,
201
- configPath: paths.configPath,
202
- sourceFile,
203
- merge,
204
- repairProfiles,
205
- });
206
- }
207
- case "export": {
208
- const targetFile = parsed.positionals[0];
209
- if (!targetFile) {
210
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing export file path.");
211
- }
212
- let force = (0, args_1.hasFlag)(parsed.commandOptions, "--force");
213
- if (!force && (0, interactive_1.canPrompt)(runtime, ctx.options.json) && (0, interactive_1.exportTargetExists)(targetFile)) {
214
- const confirmed = await (0, interactive_1.confirmExportOverwrite)(runtime, targetFile);
215
- if (!confirmed) {
216
- throw (0, errors_1.cliError)("PROMPT_CANCELLED", "Export cancelled.");
217
- }
218
- force = true;
219
- }
220
- return (0, export_providers_1.exportProviders)({
221
- providersPath: paths.providersPath,
222
- targetFile,
223
- force,
224
- });
225
- }
226
- case "add": {
227
- let providerName = parsed.positionals[0] ?? null;
228
- let profile = (0, args_1.getSingleOption)(parsed.commandOptions, "--profile");
229
- let apiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--api-key");
230
- let baseUrl = (0, args_1.getSingleOption)(parsed.commandOptions, "--base-url", false);
231
- let model = (0, args_1.getSingleOption)(parsed.commandOptions, "--model", false);
232
- let note = (0, args_1.getSingleOption)(parsed.commandOptions, "--note", false);
233
- let tags = parsed.commandOptions.get("--tag") ?? [];
234
- const createProfile = (0, args_1.hasFlag)(parsed.commandOptions, "--create-profile");
235
- if (!providerName || !profile || !apiKey) {
236
- if (ctx.options.json || !runtime.isInteractive()) {
237
- throw (0, add_interactive_1.createNonInteractiveAddError)();
238
- }
239
- const prompted = await (0, add_interactive_1.collectAddInput)(runtime, {
240
- providerName,
241
- profile,
242
- apiKey,
243
- baseUrl,
244
- note,
245
- tags,
246
- }, (candidate) => Boolean((0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[candidate]));
247
- providerName = prompted.providerName;
248
- profile = prompted.profile;
249
- apiKey = prompted.apiKey;
250
- baseUrl = prompted.baseUrl ?? null;
251
- note = prompted.note ?? null;
252
- tags = prompted.tags;
253
- }
254
- return (0, add_provider_1.addProvider)({
255
- codexDir: paths.codexDir,
256
- backupsDir: paths.backupsDir,
257
- latestBackupPath: paths.latestBackupPath,
258
- providersPath: paths.providersPath,
259
- configPath: paths.configPath,
260
- providerName,
261
- profile,
262
- apiKey,
263
- baseUrl,
264
- model,
265
- note,
266
- tags,
267
- createProfile,
268
- });
269
- }
270
- case "edit": {
271
- let providerName = parsed.positionals[0] ?? null;
272
- if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
273
- providerName = await (0, interactive_1.promptForProviderSelection)(runtime, paths.providersPath, "Choose a provider to edit");
274
- }
275
- if (!providerName) {
276
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing provider name for edit command.");
277
- }
278
- let profile = (0, args_1.getSingleOption)(parsed.commandOptions, "--profile", false) ?? undefined;
279
- let apiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--api-key", false) ?? undefined;
280
- let baseUrl = (0, args_1.getSingleOption)(parsed.commandOptions, "--base-url", false) ?? undefined;
281
- let model = (0, args_1.getSingleOption)(parsed.commandOptions, "--model", false) ?? undefined;
282
- let note = (0, args_1.getSingleOption)(parsed.commandOptions, "--note", false) ?? undefined;
283
- let tags = parsed.commandOptions.has("--tag")
284
- ? parsed.commandOptions.get("--tag") ?? []
285
- : undefined;
286
- const createProfile = (0, args_1.hasFlag)(parsed.commandOptions, "--create-profile");
287
- const switchToProfile = (0, args_1.getSingleOption)(parsed.commandOptions, "--switch-to", false) ?? undefined;
288
- if (profile === undefined &&
289
- apiKey === undefined &&
290
- baseUrl === undefined &&
291
- model === undefined &&
292
- note === undefined &&
293
- tags === undefined &&
294
- (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
295
- const provider = (0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[providerName];
296
- if (!provider) {
297
- throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${providerName}" was not found.`);
298
- }
299
- const prompted = await (0, interactive_1.collectEditInput)(runtime, provider);
300
- profile = prompted.profile;
301
- apiKey = prompted.apiKey;
302
- baseUrl = prompted.baseUrl;
303
- note = prompted.note;
304
- tags = prompted.tags;
305
- }
306
- if (profile === undefined &&
307
- apiKey === undefined &&
308
- baseUrl === undefined &&
309
- model === undefined &&
310
- note === undefined &&
311
- tags === undefined) {
312
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "edit requires at least one field to update.");
313
- }
314
- return (0, edit_provider_1.editProvider)({
315
- codexDir: paths.codexDir,
316
- backupsDir: paths.backupsDir,
317
- latestBackupPath: paths.latestBackupPath,
318
- providersPath: paths.providersPath,
319
- configPath: paths.configPath,
320
- providerName,
321
- profile,
322
- apiKey,
323
- baseUrl,
324
- model,
325
- note,
326
- tags,
327
- createProfile,
328
- switchToProfile,
329
- });
330
- }
331
- case "remove": {
332
- let providerName = parsed.positionals[0] ?? null;
333
- const force = (0, args_1.hasFlag)(parsed.commandOptions, "--force");
334
- const switchToProfile = (0, args_1.getSingleOption)(parsed.commandOptions, "--switch-to", false) ?? undefined;
335
- if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
336
- providerName = await (0, interactive_1.promptForProviderSelection)(runtime, paths.providersPath, "Choose a provider to remove");
337
- }
338
- if (!providerName) {
339
- throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for remove command.");
340
- }
341
- if (!force && !(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
342
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "remove requires --force.");
343
- }
344
- if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
345
- await (0, interactive_1.confirmProviderRemoval)(runtime, providerName);
346
- }
347
- return (0, remove_provider_1.removeProvider)({
348
- codexDir: paths.codexDir,
349
- backupsDir: paths.backupsDir,
350
- latestBackupPath: paths.latestBackupPath,
351
- providersPath: paths.providersPath,
352
- configPath: paths.configPath,
353
- providerName,
354
- switchToProfile,
355
- });
356
- }
357
- case "doctor":
358
- return (0, run_doctor_1.runDoctor)({
359
- codexDir: paths.codexDir,
360
- configPath: paths.configPath,
361
- providersPath: paths.providersPath,
362
- });
363
- case "setup": {
364
- let codexDir = ctx.options.codexDir;
365
- const candidates = (0, config_repo_1.findCodexDirCandidates)(ctx.options.codexDirExplicit ? ctx.options.codexDir : null);
366
- if (!ctx.options.codexDirExplicit) {
367
- if (candidates.length > 1) {
368
- if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
369
- throw (0, errors_1.cliError)("CODEX_DIR_AMBIGUOUS", "Multiple Codex directories were found.", {
370
- candidates,
371
- });
372
- }
373
- codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
374
- }
375
- else if (candidates.length === 0) {
376
- if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
377
- throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "No Codex directory could be found.");
378
- }
379
- codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
380
- }
381
- else {
382
- codexDir = candidates[0];
383
- }
384
- }
385
- setupPaths = (0, codex_paths_1.createCodexPaths)(codexDir);
386
- const overwrite = (0, args_1.hasFlag)(parsed.commandOptions, "--overwrite");
387
- const merge = (0, args_1.hasFlag)(parsed.commandOptions, "--merge");
388
- if (overwrite && merge) {
389
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup does not allow both --merge and --overwrite.");
390
- }
391
- let strategy = overwrite ? "overwrite" : merge ? "merge" : null;
392
- const providersExists = fs.existsSync(setupPaths.providersPath);
393
- if (providersExists && strategy === null) {
394
- if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
395
- throw (0, errors_1.cliError)("PROVIDERS_ALREADY_EXISTS", "providers.json already exists. Pass --merge or --overwrite.", {
396
- file: setupPaths.providersPath,
397
- });
398
- }
399
- const selected = await (0, interactive_1.chooseSetupStrategy)(runtime);
400
- if (selected === "cancel") {
401
- throw (0, errors_1.cliError)("PROMPT_CANCELLED", "Setup cancelled.");
402
- }
403
- strategy = selected;
404
- }
405
- const document = (0, config_repo_2.readStructuredConfig)(setupPaths.configPath);
406
- const adoptableProfiles = (0, config_1.buildManagedProfileViews)(document, null)
407
- .filter((view) => view.source === "unmanaged" && view.model && view.baseUrl)
408
- .map((view) => ({
409
- name: view.name,
410
- model: view.model,
411
- baseUrl: view.baseUrl,
412
- }))
413
- .sort((left, right) => left.name.localeCompare(right.name));
414
- const selectedProfiles = Array.from((0, config_repo_1.listConfigProfiles)(setupPaths.configPath)).sort();
415
- let adoptProfiles = [];
416
- let providerDetailsByProfile = {};
417
- if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
418
- adoptProfiles = await (0, interactive_1.chooseSetupProfiles)(runtime, adoptableProfiles);
419
- providerDetailsByProfile = await (0, interactive_1.collectSetupProviderDetails)(runtime, adoptProfiles);
420
- }
421
- else {
422
- adoptProfiles = selectedProfiles.filter((profile) => Object.prototype.hasOwnProperty.call(providerDetailsByProfile, profile));
423
- }
424
- return (0, setup_codex_1.setupCodex)({
425
- codexDirOption: ctx.options.codexDir,
426
- codexDir: setupPaths.codexDir,
427
- configPath: setupPaths.configPath,
428
- providersPath: setupPaths.providersPath,
429
- backupsDir: setupPaths.backupsDir,
430
- latestBackupPath: setupPaths.latestBackupPath,
431
- strategy: strategy ?? "overwrite",
432
- adoptProfiles,
433
- providerDetailsByProfile,
434
- });
435
- }
436
- case "backups-list":
437
- return (0, list_backups_1.listBackupEntries)(paths.backupsDir);
438
- case "rollback":
439
- if (parsed.positionals.length > 1) {
440
- throw (0, errors_1.cliError)("INVALID_ARGUMENT", "rollback accepts at most one backup id.");
441
- }
442
- if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
443
- await (0, interactive_1.confirmRollback)(runtime, paths.latestBackupPath, paths.backupsDir, parsed.positionals[0] ?? null);
444
- }
445
- return (0, rollback_backup_1.rollbackBackup)({
446
- latestBackupPath: paths.latestBackupPath,
447
- backupsDir: paths.backupsDir,
448
- backupId: parsed.positionals[0] ?? null,
449
- });
450
- default:
451
- throw (0, errors_1.cliError)("UNKNOWN_COMMAND", `Unknown command: ${ctx.command}`);
452
- }
453
- }
454
60
  if (require.main === module) {
455
61
  main();
456
62
  }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseArgs = parseArgs;
4
+ exports.hasFlag = hasFlag;
5
+ exports.getSingleOption = getSingleOption;
6
+ const errors_1 = require("../domain/errors");
7
+ const codex_paths_1 = require("../storage/codex-paths");
8
+ const registry_1 = require("./registry");
9
+ /**
10
+ * Parses argv into command positionals, global flags, and command-scoped options.
11
+ */
12
+ function parseArgs(argv) {
13
+ let json = false;
14
+ let codexDir = (0, codex_paths_1.resolveCodexDir)();
15
+ let codexDirExplicit = false;
16
+ const remaining = [];
17
+ for (let index = 0; index < argv.length; index += 1) {
18
+ const value = argv[index];
19
+ if (value === "--json") {
20
+ json = true;
21
+ continue;
22
+ }
23
+ if (value === "--codex-dir") {
24
+ const next = argv[index + 1];
25
+ if (!next) {
26
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--codex-dir requires a path value.");
27
+ }
28
+ codexDir = (0, codex_paths_1.resolveCodexDir)(next);
29
+ codexDirExplicit = true;
30
+ index += 1;
31
+ continue;
32
+ }
33
+ remaining.push(value);
34
+ }
35
+ if (remaining[0] === "help") {
36
+ return {
37
+ command: null,
38
+ positionals: [],
39
+ globalOptions: {
40
+ json,
41
+ codexDir,
42
+ codexDirExplicit,
43
+ },
44
+ commandOptions: new Map(),
45
+ helpRequested: true,
46
+ helpTarget: remaining.slice(1).join(" ") || null,
47
+ versionRequested: false,
48
+ };
49
+ }
50
+ const versionRequested = remaining.includes("--version") || remaining.includes("-v");
51
+ if (versionRequested) {
52
+ return defaultParsed(null, {
53
+ json,
54
+ codexDir,
55
+ versionRequested: true,
56
+ });
57
+ }
58
+ const { definition, consumedTokens } = (0, registry_1.resolveCommandFromArgv)(remaining);
59
+ const command = definition?.id ?? null;
60
+ const positionals = [];
61
+ const commandOptions = new Map();
62
+ let helpRequested = false;
63
+ const startIndex = consumedTokens > 0 ? consumedTokens : Math.min(remaining.length, 1);
64
+ for (let index = startIndex; index < remaining.length; index += 1) {
65
+ const value = remaining[index];
66
+ if (value === "--help" || value === "-h") {
67
+ helpRequested = true;
68
+ continue;
69
+ }
70
+ if (value.startsWith("--")) {
71
+ const optionName = value;
72
+ const next = remaining[index + 1];
73
+ if (!next || next.startsWith("--")) {
74
+ commandOptions.set(optionName, ["true"]);
75
+ continue;
76
+ }
77
+ const existing = commandOptions.get(optionName) ?? [];
78
+ existing.push(next);
79
+ commandOptions.set(optionName, existing);
80
+ index += 1;
81
+ continue;
82
+ }
83
+ positionals.push(value);
84
+ }
85
+ return {
86
+ command,
87
+ positionals,
88
+ globalOptions: {
89
+ json,
90
+ codexDir,
91
+ codexDirExplicit,
92
+ },
93
+ commandOptions,
94
+ helpRequested,
95
+ helpTarget: helpRequested && definition ? definition.tokens.join(" ") : null,
96
+ versionRequested: false,
97
+ };
98
+ }
99
+ /**
100
+ * Creates a parsed result for built-in synthetic commands such as help/version.
101
+ */
102
+ function defaultParsed(command, overrides) {
103
+ return {
104
+ command,
105
+ positionals: [],
106
+ globalOptions: {
107
+ json: overrides?.json ?? false,
108
+ codexDir: overrides?.codexDir ?? (0, codex_paths_1.resolveCodexDir)(),
109
+ codexDirExplicit: false,
110
+ },
111
+ commandOptions: new Map(),
112
+ helpRequested: overrides?.helpRequested ?? false,
113
+ helpTarget: overrides?.helpTarget ?? null,
114
+ versionRequested: overrides?.versionRequested ?? false,
115
+ };
116
+ }
117
+ /**
118
+ * Checks whether a boolean-style option was supplied.
119
+ */
120
+ function hasFlag(options, name) {
121
+ return options.has(name);
122
+ }
123
+ /**
124
+ * Returns the last supplied value for a single-valued command option.
125
+ */
126
+ function getSingleOption(options, name, required = true) {
127
+ const values = options.get(name) ?? [];
128
+ if (values.length === 0) {
129
+ return required ? null : null;
130
+ }
131
+ return values[values.length - 1];
132
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeCommand = executeCommand;
4
+ const errors_1 = require("../domain/errors");
5
+ const prompt_1 = require("../interaction/prompt");
6
+ const registry_1 = require("./registry");
7
+ /**
8
+ * Resolves the shared command definition and executes its registered handler.
9
+ */
10
+ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRuntime)()) {
11
+ const definition = (0, registry_1.findCommandDefinition)(ctx.command);
12
+ if (!definition) {
13
+ throw (0, errors_1.cliError)("UNKNOWN_COMMAND", `Unknown command: ${ctx.command}`);
14
+ }
15
+ return definition.handler(ctx, parsed, runtime);
16
+ }