@minniexcode/codex-switch 0.0.2 → 0.0.4
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 +110 -0
- package/README.CN.md +160 -0
- package/README.md +159 -121
- package/dist/app/edit-provider.js +64 -0
- package/dist/app/import-providers.js +10 -1
- package/dist/app/list-backups.js +17 -0
- package/dist/app/rollback-backup.js +30 -0
- package/dist/app/setup-codex.js +138 -0
- package/dist/app/show-provider.js +22 -0
- package/dist/cli/add-interactive.js +108 -0
- package/dist/cli/args.js +42 -12
- package/dist/cli/help.js +278 -0
- package/dist/cli/interactive.js +173 -0
- package/dist/cli/output.js +34 -1
- package/dist/cli/prompt.js +110 -0
- package/dist/cli.js +229 -52
- package/dist/domain/backups.js +103 -0
- 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 +62 -0
- package/dist/infra/codex-discovery.js +48 -0
- package/dist/infra/codex-paths.js +14 -1
- package/dist/infra/providers-repo.js +29 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +393 -0
- package/docs/{codex-switch-prd.md → PRD/codex-switch-prd.md} +31 -8
- package/docs/cli-usage.md +580 -0
- package/docs/codex-switch-command-design.md +30 -5
- 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 +79 -38
- package/docs/codex-switch-v0.0.4-design.md +874 -0
- package/package.json +4 -1
|
@@ -65,9 +65,18 @@ function importProviders(args) {
|
|
|
65
65
|
operation: "import",
|
|
66
66
|
files: [{ absolutePath: args.providersPath, relativePath: "providers.json" }],
|
|
67
67
|
mutate: () => {
|
|
68
|
-
(0, providers_repo_1.
|
|
68
|
+
const current = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
69
|
+
const next = args.merge ? (0, providers_repo_1.mergeProviders)(current, imported) : imported;
|
|
70
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
|
|
71
|
+
const replacedProviders = args.merge
|
|
72
|
+
? Object.keys(imported.providers).filter((name) => current.providers[name]).sort()
|
|
73
|
+
: [];
|
|
69
74
|
return {
|
|
75
|
+
mode: args.merge ? "merge" : "replace",
|
|
70
76
|
importedProviders: Object.keys(imported.providers).sort(),
|
|
77
|
+
importedCount: Object.keys(imported.providers).length,
|
|
78
|
+
mergedCount: Object.keys(next.providers).length,
|
|
79
|
+
replacedProviders,
|
|
71
80
|
};
|
|
72
81
|
},
|
|
73
82
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listBackupEntries = listBackupEntries;
|
|
4
|
+
const backup_repo_1 = require("../infra/backup-repo");
|
|
5
|
+
/**
|
|
6
|
+
* Lists backup manifests available under the managed Codex backups directory.
|
|
7
|
+
*/
|
|
8
|
+
function listBackupEntries(backupsDir) {
|
|
9
|
+
const result = (0, backup_repo_1.listBackups)(backupsDir);
|
|
10
|
+
return {
|
|
11
|
+
data: {
|
|
12
|
+
backups: result.backups,
|
|
13
|
+
count: result.backups.length,
|
|
14
|
+
},
|
|
15
|
+
warnings: result.warnings,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rollbackBackup = rollbackBackup;
|
|
4
|
+
const errors_1 = require("../domain/errors");
|
|
5
|
+
const backup_repo_1 = require("../infra/backup-repo");
|
|
6
|
+
/**
|
|
7
|
+
* Restores either the latest backup or a specific historical backup by id.
|
|
8
|
+
*/
|
|
9
|
+
function rollbackBackup(args) {
|
|
10
|
+
const manifest = args.backupId
|
|
11
|
+
? (0, backup_repo_1.loadManifestById)(args.backupsDir, args.backupId)
|
|
12
|
+
: (0, backup_repo_1.loadLatestManifest)(args.latestBackupPath);
|
|
13
|
+
try {
|
|
14
|
+
(0, backup_repo_1.restoreManifest)(manifest);
|
|
15
|
+
return {
|
|
16
|
+
data: {
|
|
17
|
+
restoredFiles: manifest.files.map((file) => file.relativePath),
|
|
18
|
+
backupId: args.backupId ?? null,
|
|
19
|
+
backupPath: manifest.backupDir,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw (0, errors_1.cliError)("ROLLBACK_FAILED", "Rollback failed.", {
|
|
25
|
+
cause: (0, errors_1.normalizeError)(error).message,
|
|
26
|
+
backupPath: manifest.backupDir,
|
|
27
|
+
backupId: args.backupId ?? null,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
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.setupCodex = setupCodex;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const setup_1 = require("../domain/setup");
|
|
39
|
+
const errors_1 = require("../domain/errors");
|
|
40
|
+
const codex_discovery_1 = require("../infra/codex-discovery");
|
|
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");
|
|
45
|
+
const run_doctor_1 = require("./run-doctor");
|
|
46
|
+
const run_mutation_1 = require("./run-mutation");
|
|
47
|
+
const MIN_CODEX_VERSION = "0.0.1";
|
|
48
|
+
/**
|
|
49
|
+
* Bootstraps a managed providers.json from the existing Codex directory.
|
|
50
|
+
*/
|
|
51
|
+
function setupCodex(args) {
|
|
52
|
+
const available = (0, codex_cli_1.checkCodexAvailable)();
|
|
53
|
+
if (!available.ok) {
|
|
54
|
+
throw (0, errors_1.cliError)("CODEX_NOT_INSTALLED", "codex CLI is not available.", {
|
|
55
|
+
cause: available.cause,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const version = (0, codex_cli_1.checkCodexVersion)(MIN_CODEX_VERSION);
|
|
59
|
+
if (!version.ok) {
|
|
60
|
+
throw (0, errors_1.cliError)("CODEX_VERSION_UNSUPPORTED", "codex CLI version is below the supported minimum.", {
|
|
61
|
+
minimumVersion: MIN_CODEX_VERSION,
|
|
62
|
+
currentVersion: version.currentVersion ?? null,
|
|
63
|
+
cause: version.cause,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const candidates = (0, codex_discovery_1.findCodexDirCandidates)(args.codexDirOption);
|
|
67
|
+
if (candidates.length === 0) {
|
|
68
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "No Codex directory could be found.", {
|
|
69
|
+
codexDir: args.codexDir,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (candidates.length > 1) {
|
|
73
|
+
throw (0, errors_1.cliError)("CODEX_DIR_AMBIGUOUS", "Multiple Codex directories were found.", {
|
|
74
|
+
candidates,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (!fs.existsSync(args.codexDir)) {
|
|
78
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "The requested Codex directory does not exist.", {
|
|
79
|
+
codexDir: args.codexDir,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const profiles = Array.from((0, config_repo_1.listConfigProfiles)(args.configPath)).sort();
|
|
83
|
+
if (profiles.length === 0) {
|
|
84
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", "No profiles were found in config.toml.", {
|
|
85
|
+
file: args.configPath,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const drafts = (0, setup_1.buildSetupDrafts)(profiles, args.providerDetailsByProfile);
|
|
89
|
+
const incompleteProfiles = (0, setup_1.findIncompleteSetupProfiles)(drafts);
|
|
90
|
+
if (incompleteProfiles.length > 0) {
|
|
91
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup requires complete provider data for every selected profile.", {
|
|
92
|
+
incompleteProfiles,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
(0, fs_utils_1.ensureDir)(args.codexDir);
|
|
96
|
+
const currentProviders = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
97
|
+
const providersExists = fs.existsSync(args.providersPath);
|
|
98
|
+
if (providersExists && args.strategy !== "merge" && args.strategy !== "overwrite") {
|
|
99
|
+
throw (0, errors_1.cliError)("PROVIDERS_ALREADY_EXISTS", "providers.json already exists.", {
|
|
100
|
+
file: args.providersPath,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const nextProviders = {
|
|
104
|
+
providers: drafts.reduce((accumulator, draft) => {
|
|
105
|
+
accumulator[draft.providerName] = draft.record;
|
|
106
|
+
return accumulator;
|
|
107
|
+
}, {}),
|
|
108
|
+
};
|
|
109
|
+
const finalProviders = args.strategy === "merge" ? (0, providers_repo_1.mergeProviders)(currentProviders, nextProviders) : nextProviders;
|
|
110
|
+
const result = (0, run_mutation_1.runMutation)({
|
|
111
|
+
codexDir: args.codexDir,
|
|
112
|
+
backupsDir: args.backupsDir,
|
|
113
|
+
latestBackupPath: args.latestBackupPath,
|
|
114
|
+
operation: "setup",
|
|
115
|
+
files: [{ absolutePath: args.providersPath, relativePath: "providers.json" }],
|
|
116
|
+
mutate: () => {
|
|
117
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, finalProviders);
|
|
118
|
+
return {
|
|
119
|
+
codexDir: args.codexDir,
|
|
120
|
+
strategy: args.strategy,
|
|
121
|
+
providersInitialized: Object.keys(nextProviders.providers).length,
|
|
122
|
+
providerNames: Object.keys(finalProviders.providers).sort(),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
const doctor = (0, run_doctor_1.runDoctor)({
|
|
127
|
+
codexDir: args.codexDir,
|
|
128
|
+
configPath: args.configPath,
|
|
129
|
+
providersPath: args.providersPath,
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
data: {
|
|
133
|
+
...result.data,
|
|
134
|
+
doctor: doctor.data,
|
|
135
|
+
},
|
|
136
|
+
warnings: doctor.warnings,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.showProvider = showProvider;
|
|
4
|
+
const providers_1 = require("../domain/providers");
|
|
5
|
+
const providers_repo_1 = require("../infra/providers-repo");
|
|
6
|
+
/**
|
|
7
|
+
* Returns a single provider record, with text-mode callers able to use a masked preview.
|
|
8
|
+
*/
|
|
9
|
+
function showProvider(args) {
|
|
10
|
+
const provider = (0, providers_repo_1.readProviderRecord)(args.providersPath, args.providerName);
|
|
11
|
+
return {
|
|
12
|
+
data: {
|
|
13
|
+
providerName: args.providerName,
|
|
14
|
+
provider: args.includeSecret
|
|
15
|
+
? provider
|
|
16
|
+
: {
|
|
17
|
+
...provider,
|
|
18
|
+
apiKey: (0, providers_1.maskSecret)(provider.apiKey),
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
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"];
|
|
10
|
+
/**
|
|
11
|
+
* Collects add command inputs interactively when required values are missing.
|
|
12
|
+
*/
|
|
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
|
+
}
|
package/dist/cli/args.js
CHANGED
|
@@ -9,12 +9,6 @@ const codex_paths_1 = require("../infra/codex-paths");
|
|
|
9
9
|
* Parses argv into command positionals, global flags, and command-scoped options.
|
|
10
10
|
*/
|
|
11
11
|
function parseArgs(argv) {
|
|
12
|
-
if (argv.includes("--help") || argv.includes("-h")) {
|
|
13
|
-
return defaultParsed("help");
|
|
14
|
-
}
|
|
15
|
-
if (argv.includes("--version") || argv.includes("-v")) {
|
|
16
|
-
return defaultParsed("version");
|
|
17
|
-
}
|
|
18
12
|
let json = false;
|
|
19
13
|
let codexDir = (0, codex_paths_1.resolveCodexDir)();
|
|
20
14
|
const remaining = [];
|
|
@@ -27,7 +21,7 @@ function parseArgs(argv) {
|
|
|
27
21
|
if (value === "--codex-dir") {
|
|
28
22
|
const next = argv[index + 1];
|
|
29
23
|
if (!next) {
|
|
30
|
-
throw (0, errors_1.cliError)("
|
|
24
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--codex-dir requires a path value.");
|
|
31
25
|
}
|
|
32
26
|
codexDir = (0, codex_paths_1.resolveCodexDir)(next);
|
|
33
27
|
index += 1;
|
|
@@ -35,11 +29,41 @@ function parseArgs(argv) {
|
|
|
35
29
|
}
|
|
36
30
|
remaining.push(value);
|
|
37
31
|
}
|
|
38
|
-
|
|
32
|
+
if (remaining[0] === "help") {
|
|
33
|
+
const helpTarget = remaining[1] === "backups" && remaining[2] === "list" ? "backups" : remaining[1] ?? null;
|
|
34
|
+
return {
|
|
35
|
+
command: null,
|
|
36
|
+
positionals: [],
|
|
37
|
+
globalOptions: {
|
|
38
|
+
json,
|
|
39
|
+
codexDir,
|
|
40
|
+
},
|
|
41
|
+
commandOptions: new Map(),
|
|
42
|
+
helpRequested: true,
|
|
43
|
+
helpTarget,
|
|
44
|
+
versionRequested: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const versionRequested = remaining.includes("--version") || remaining.includes("-v");
|
|
48
|
+
if (versionRequested) {
|
|
49
|
+
return defaultParsed(null, {
|
|
50
|
+
json,
|
|
51
|
+
codexDir,
|
|
52
|
+
versionRequested: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const commandToken = remaining[0] ?? null;
|
|
56
|
+
const command = commandToken === "backups" && remaining[1] === "list" ? "backups-list" : commandToken;
|
|
39
57
|
const positionals = [];
|
|
40
58
|
const commandOptions = new Map();
|
|
41
|
-
|
|
59
|
+
let helpRequested = false;
|
|
60
|
+
const startIndex = command === "backups-list" ? 2 : 1;
|
|
61
|
+
for (let index = startIndex; index < remaining.length; index += 1) {
|
|
42
62
|
const value = remaining[index];
|
|
63
|
+
if (value === "--help" || value === "-h") {
|
|
64
|
+
helpRequested = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
43
67
|
if (value.startsWith("--")) {
|
|
44
68
|
const optionName = value;
|
|
45
69
|
const next = remaining[index + 1];
|
|
@@ -64,20 +88,26 @@ function parseArgs(argv) {
|
|
|
64
88
|
codexDir,
|
|
65
89
|
},
|
|
66
90
|
commandOptions,
|
|
91
|
+
helpRequested,
|
|
92
|
+
helpTarget: helpRequested ? (command === "backups-list" ? "backups" : command) : null,
|
|
93
|
+
versionRequested: false,
|
|
67
94
|
};
|
|
68
95
|
}
|
|
69
96
|
/**
|
|
70
97
|
* Creates a parsed result for built-in synthetic commands such as help/version.
|
|
71
98
|
*/
|
|
72
|
-
function defaultParsed(command) {
|
|
99
|
+
function defaultParsed(command, overrides) {
|
|
73
100
|
return {
|
|
74
101
|
command,
|
|
75
102
|
positionals: [],
|
|
76
103
|
globalOptions: {
|
|
77
|
-
json: false,
|
|
78
|
-
codexDir: (0, codex_paths_1.resolveCodexDir)(),
|
|
104
|
+
json: overrides?.json ?? false,
|
|
105
|
+
codexDir: overrides?.codexDir ?? (0, codex_paths_1.resolveCodexDir)(),
|
|
79
106
|
},
|
|
80
107
|
commandOptions: new Map(),
|
|
108
|
+
helpRequested: overrides?.helpRequested ?? false,
|
|
109
|
+
helpTarget: overrides?.helpTarget ?? null,
|
|
110
|
+
versionRequested: overrides?.versionRequested ?? false,
|
|
81
111
|
};
|
|
82
112
|
}
|
|
83
113
|
/**
|