@minniexcode/codex-switch 0.0.3 → 0.0.5
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.
- package/README.AI.md +8 -3
- package/README.md +160 -91
- package/dist/app/add-provider.js +32 -1
- package/dist/app/edit-provider.js +137 -0
- package/dist/app/get-status.js +9 -2
- package/dist/app/import-providers.js +47 -2
- package/dist/app/list-backups.js +17 -0
- package/dist/app/list-config-profiles.js +29 -0
- package/dist/app/remove-provider.js +34 -2
- package/dist/app/rollback-backup.js +30 -0
- package/dist/app/run-doctor.js +22 -21
- package/dist/app/setup-codex.js +155 -0
- package/dist/app/show-config.js +34 -0
- package/dist/app/show-provider.js +22 -0
- package/dist/app/switch-provider.js +5 -2
- package/dist/cli/add-interactive.js +25 -31
- package/dist/cli/args.js +19 -5
- package/dist/cli/help.js +109 -14
- package/dist/cli/interactive.js +123 -8
- package/dist/cli/output.js +56 -1
- package/dist/cli/prompt.js +19 -2
- package/dist/cli.js +250 -13
- package/dist/domain/backups.js +103 -0
- package/dist/domain/config.js +471 -39
- package/dist/domain/errors.js +3 -3
- package/dist/domain/providers.js +10 -0
- package/dist/domain/setup.js +30 -0
- package/dist/infra/backup-repo.js +65 -6
- package/dist/infra/codex-cli.js +79 -2
- package/dist/infra/codex-discovery.js +10 -0
- package/dist/infra/codex-paths.js +14 -1
- package/dist/infra/config-repo.js +102 -9
- package/dist/infra/providers-repo.js +29 -0
- package/docs/Design/codex-switch-v0.0.4-design.md +874 -0
- package/docs/Design/codex-switch-v0.0.5-design.md +922 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +308 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +343 -0
- package/docs/{codex-switch-prd.md → PRD/codex-switch-prd.md} +9 -5
- package/docs/cli-usage.md +580 -0
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +1 -1
- package/docs/codex-switch-product-research.md +2 -2
- package/docs/codex-switch-technical-architecture.md +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,64 @@
|
|
|
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.executeCommand = executeCommand;
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
7
41
|
const add_provider_1 = require("./app/add-provider");
|
|
42
|
+
const edit_provider_1 = require("./app/edit-provider");
|
|
8
43
|
const export_providers_1 = require("./app/export-providers");
|
|
9
44
|
const get_current_profile_1 = require("./app/get-current-profile");
|
|
10
45
|
const get_status_1 = require("./app/get-status");
|
|
11
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");
|
|
12
49
|
const list_providers_1 = require("./app/list-providers");
|
|
13
50
|
const remove_provider_1 = require("./app/remove-provider");
|
|
14
|
-
const
|
|
51
|
+
const rollback_backup_1 = require("./app/rollback-backup");
|
|
15
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");
|
|
16
56
|
const switch_provider_1 = require("./app/switch-provider");
|
|
57
|
+
const config_1 = require("./domain/config");
|
|
17
58
|
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");
|
|
18
62
|
const providers_repo_1 = require("./infra/providers-repo");
|
|
19
63
|
const codex_paths_1 = require("./infra/codex-paths");
|
|
20
64
|
const args_1 = require("./cli/args");
|
|
@@ -23,7 +67,7 @@ const help_1 = require("./cli/help");
|
|
|
23
67
|
const interactive_1 = require("./cli/interactive");
|
|
24
68
|
const output_1 = require("./cli/output");
|
|
25
69
|
const prompt_1 = require("./cli/prompt");
|
|
26
|
-
const VERSION = "0.0.
|
|
70
|
+
const VERSION = "0.0.5";
|
|
27
71
|
/**
|
|
28
72
|
* Prints the command help text to stdout.
|
|
29
73
|
*/
|
|
@@ -47,7 +91,7 @@ function main() {
|
|
|
47
91
|
}
|
|
48
92
|
if (parsed.helpRequested) {
|
|
49
93
|
if (parsed.helpTarget && !(0, help_1.isKnownCommandName)(parsed.helpTarget)) {
|
|
50
|
-
(0, output_1.outputFailure)({ command: "help", options: parsed.globalOptions }, (0, errors_1.cliError)("
|
|
94
|
+
(0, output_1.outputFailure)({ command: "help", options: parsed.globalOptions }, (0, errors_1.cliError)("INVALID_ARGUMENT", `Unknown help topic: ${parsed.helpTarget}`, {
|
|
51
95
|
availableCommands: (0, help_1.buildHelpText)(parsed.helpTarget).split("\n").slice(2),
|
|
52
96
|
}));
|
|
53
97
|
return;
|
|
@@ -75,14 +119,40 @@ function main() {
|
|
|
75
119
|
* Dispatches a parsed CLI command into the application layer.
|
|
76
120
|
*/
|
|
77
121
|
async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRuntime)()) {
|
|
78
|
-
|
|
122
|
+
let setupPaths = (0, codex_paths_1.createCodexPaths)(ctx.options.codexDir);
|
|
123
|
+
const paths = setupPaths;
|
|
79
124
|
switch (ctx.command) {
|
|
80
125
|
case "list":
|
|
81
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
|
+
}
|
|
82
141
|
case "current":
|
|
83
142
|
return (0, get_current_profile_1.getCurrentProfile)(paths.configPath);
|
|
84
143
|
case "status":
|
|
85
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
|
+
});
|
|
86
156
|
case "switch": {
|
|
87
157
|
let providerName = parsed.positionals[0] ?? null;
|
|
88
158
|
if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
@@ -105,29 +175,45 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
105
175
|
case "import": {
|
|
106
176
|
const sourceFile = parsed.positionals[0];
|
|
107
177
|
if (!sourceFile) {
|
|
108
|
-
throw (0, errors_1.cliError)("
|
|
178
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing import file path.");
|
|
109
179
|
}
|
|
180
|
+
const merge = (0, args_1.hasFlag)(parsed.commandOptions, "--merge");
|
|
181
|
+
let repairProfiles;
|
|
110
182
|
if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
111
|
-
await (0, interactive_1.confirmImport)(runtime, sourceFile);
|
|
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
|
+
}
|
|
112
195
|
}
|
|
113
196
|
return (0, import_providers_1.importProviders)({
|
|
114
197
|
codexDir: paths.codexDir,
|
|
115
198
|
backupsDir: paths.backupsDir,
|
|
116
199
|
latestBackupPath: paths.latestBackupPath,
|
|
117
200
|
providersPath: paths.providersPath,
|
|
201
|
+
configPath: paths.configPath,
|
|
118
202
|
sourceFile,
|
|
203
|
+
merge,
|
|
204
|
+
repairProfiles,
|
|
119
205
|
});
|
|
120
206
|
}
|
|
121
207
|
case "export": {
|
|
122
208
|
const targetFile = parsed.positionals[0];
|
|
123
209
|
if (!targetFile) {
|
|
124
|
-
throw (0, errors_1.cliError)("
|
|
210
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "Missing export file path.");
|
|
125
211
|
}
|
|
126
212
|
let force = (0, args_1.hasFlag)(parsed.commandOptions, "--force");
|
|
127
213
|
if (!force && (0, interactive_1.canPrompt)(runtime, ctx.options.json) && (0, interactive_1.exportTargetExists)(targetFile)) {
|
|
128
214
|
const confirmed = await (0, interactive_1.confirmExportOverwrite)(runtime, targetFile);
|
|
129
215
|
if (!confirmed) {
|
|
130
|
-
throw (0, errors_1.cliError)("
|
|
216
|
+
throw (0, errors_1.cliError)("PROMPT_CANCELLED", "Export cancelled.");
|
|
131
217
|
}
|
|
132
218
|
force = true;
|
|
133
219
|
}
|
|
@@ -142,8 +228,10 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
142
228
|
let profile = (0, args_1.getSingleOption)(parsed.commandOptions, "--profile");
|
|
143
229
|
let apiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--api-key");
|
|
144
230
|
let baseUrl = (0, args_1.getSingleOption)(parsed.commandOptions, "--base-url", false);
|
|
231
|
+
let model = (0, args_1.getSingleOption)(parsed.commandOptions, "--model", false);
|
|
145
232
|
let note = (0, args_1.getSingleOption)(parsed.commandOptions, "--note", false);
|
|
146
233
|
let tags = parsed.commandOptions.get("--tag") ?? [];
|
|
234
|
+
const createProfile = (0, args_1.hasFlag)(parsed.commandOptions, "--create-profile");
|
|
147
235
|
if (!providerName || !profile || !apiKey) {
|
|
148
236
|
if (ctx.options.json || !runtime.isInteractive()) {
|
|
149
237
|
throw (0, add_interactive_1.createNonInteractiveAddError)();
|
|
@@ -155,7 +243,7 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
155
243
|
baseUrl,
|
|
156
244
|
note,
|
|
157
245
|
tags,
|
|
158
|
-
}, (candidate) => Boolean((0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[candidate])
|
|
246
|
+
}, (candidate) => Boolean((0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[candidate]));
|
|
159
247
|
providerName = prompted.providerName;
|
|
160
248
|
profile = prompted.profile;
|
|
161
249
|
apiKey = prompted.apiKey;
|
|
@@ -168,17 +256,82 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
168
256
|
backupsDir: paths.backupsDir,
|
|
169
257
|
latestBackupPath: paths.latestBackupPath,
|
|
170
258
|
providersPath: paths.providersPath,
|
|
259
|
+
configPath: paths.configPath,
|
|
171
260
|
providerName,
|
|
172
261
|
profile,
|
|
173
262
|
apiKey,
|
|
174
263
|
baseUrl,
|
|
264
|
+
model,
|
|
175
265
|
note,
|
|
176
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,
|
|
177
329
|
});
|
|
178
330
|
}
|
|
179
331
|
case "remove": {
|
|
180
332
|
let providerName = parsed.positionals[0] ?? null;
|
|
181
333
|
const force = (0, args_1.hasFlag)(parsed.commandOptions, "--force");
|
|
334
|
+
const switchToProfile = (0, args_1.getSingleOption)(parsed.commandOptions, "--switch-to", false) ?? undefined;
|
|
182
335
|
if (!providerName && (0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
183
336
|
providerName = await (0, interactive_1.promptForProviderSelection)(runtime, paths.providersPath, "Choose a provider to remove");
|
|
184
337
|
}
|
|
@@ -186,7 +339,7 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
186
339
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for remove command.");
|
|
187
340
|
}
|
|
188
341
|
if (!force && !(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
189
|
-
throw (0, errors_1.cliError)("
|
|
342
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "remove requires --force.");
|
|
190
343
|
}
|
|
191
344
|
if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
192
345
|
await (0, interactive_1.confirmProviderRemoval)(runtime, providerName);
|
|
@@ -196,7 +349,9 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
196
349
|
backupsDir: paths.backupsDir,
|
|
197
350
|
latestBackupPath: paths.latestBackupPath,
|
|
198
351
|
providersPath: paths.providersPath,
|
|
352
|
+
configPath: paths.configPath,
|
|
199
353
|
providerName,
|
|
354
|
+
switchToProfile,
|
|
200
355
|
});
|
|
201
356
|
}
|
|
202
357
|
case "doctor":
|
|
@@ -205,13 +360,95 @@ async function executeCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRu
|
|
|
205
360
|
configPath: paths.configPath,
|
|
206
361
|
providersPath: paths.providersPath,
|
|
207
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);
|
|
208
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
|
+
}
|
|
209
442
|
if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
210
|
-
await (0, interactive_1.confirmRollback)(runtime, paths.latestBackupPath);
|
|
443
|
+
await (0, interactive_1.confirmRollback)(runtime, paths.latestBackupPath, paths.backupsDir, parsed.positionals[0] ?? null);
|
|
211
444
|
}
|
|
212
|
-
return (0,
|
|
445
|
+
return (0, rollback_backup_1.rollbackBackup)({
|
|
446
|
+
latestBackupPath: paths.latestBackupPath,
|
|
447
|
+
backupsDir: paths.backupsDir,
|
|
448
|
+
backupId: parsed.positionals[0] ?? null,
|
|
449
|
+
});
|
|
213
450
|
default:
|
|
214
|
-
throw (0, errors_1.cliError)("
|
|
451
|
+
throw (0, errors_1.cliError)("UNKNOWN_COMMAND", `Unknown command: ${ctx.command}`);
|
|
215
452
|
}
|
|
216
453
|
}
|
|
217
454
|
if (require.main === module) {
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getBackupId = getBackupId;
|
|
37
|
+
exports.validateBackupManifest = validateBackupManifest;
|
|
38
|
+
exports.sortBackupList = sortBackupList;
|
|
39
|
+
exports.toBackupListItem = toBackupListItem;
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
/**
|
|
42
|
+
* Returns the explicit backup identifier derived from the backup directory name.
|
|
43
|
+
*/
|
|
44
|
+
function getBackupId(backupDir) {
|
|
45
|
+
return path.basename(backupDir);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validates the minimal manifest shape needed for listing and restoring backups.
|
|
49
|
+
*/
|
|
50
|
+
function validateBackupManifest(input) {
|
|
51
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
52
|
+
throw new Error("Backup manifest must be an object.");
|
|
53
|
+
}
|
|
54
|
+
const manifest = input;
|
|
55
|
+
if (manifest.version !== 1) {
|
|
56
|
+
throw new Error("Unsupported backup manifest version.");
|
|
57
|
+
}
|
|
58
|
+
if (typeof manifest.createdAt !== "string" || manifest.createdAt.trim() === "") {
|
|
59
|
+
throw new Error("Backup manifest is missing createdAt.");
|
|
60
|
+
}
|
|
61
|
+
if (typeof manifest.reason !== "string" || manifest.reason.trim() === "") {
|
|
62
|
+
throw new Error("Backup manifest is missing reason.");
|
|
63
|
+
}
|
|
64
|
+
if (typeof manifest.rootDir !== "string" || manifest.rootDir.trim() === "") {
|
|
65
|
+
throw new Error("Backup manifest is missing rootDir.");
|
|
66
|
+
}
|
|
67
|
+
if (typeof manifest.backupDir !== "string" || manifest.backupDir.trim() === "") {
|
|
68
|
+
throw new Error("Backup manifest is missing backupDir.");
|
|
69
|
+
}
|
|
70
|
+
if (!Array.isArray(manifest.files)) {
|
|
71
|
+
throw new Error("Backup manifest is missing files.");
|
|
72
|
+
}
|
|
73
|
+
for (const entry of manifest.files) {
|
|
74
|
+
if (!entry || typeof entry !== "object") {
|
|
75
|
+
throw new Error("Backup manifest contains an invalid file entry.");
|
|
76
|
+
}
|
|
77
|
+
if (typeof entry.relativePath !== "string" || typeof entry.existed !== "boolean") {
|
|
78
|
+
throw new Error("Backup manifest contains an invalid file entry.");
|
|
79
|
+
}
|
|
80
|
+
if (entry.backupFileName !== null && typeof entry.backupFileName !== "string") {
|
|
81
|
+
throw new Error("Backup manifest contains an invalid backup file name.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return manifest;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sorts backup list items from newest to oldest based on createdAt.
|
|
88
|
+
*/
|
|
89
|
+
function sortBackupList(items) {
|
|
90
|
+
return [...items].sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Converts a manifest into the stable list payload returned by `backups list`.
|
|
94
|
+
*/
|
|
95
|
+
function toBackupListItem(manifest) {
|
|
96
|
+
return {
|
|
97
|
+
backupId: getBackupId(manifest.backupDir),
|
|
98
|
+
createdAt: manifest.createdAt,
|
|
99
|
+
reason: manifest.reason,
|
|
100
|
+
files: manifest.files.map((file) => file.relativePath),
|
|
101
|
+
backupPath: manifest.backupDir,
|
|
102
|
+
};
|
|
103
|
+
}
|