@minniexcode/codex-switch 0.0.4 → 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.
- package/README.md +35 -97
- package/dist/app/add-provider.js +40 -3
- package/dist/app/edit-provider.js +76 -3
- package/dist/app/export-providers.js +2 -2
- package/dist/app/get-current-profile.js +1 -1
- package/dist/app/get-status.js +10 -3
- package/dist/app/import-providers.js +47 -3
- package/dist/app/list-backups.js +1 -1
- package/dist/app/list-config-profiles.js +30 -0
- package/dist/app/list-providers.js +1 -1
- package/dist/app/remove-provider.js +35 -3
- package/dist/app/rollback-backup.js +1 -1
- package/dist/app/rollback-latest.js +1 -1
- package/dist/app/run-doctor.js +44 -26
- package/dist/app/run-mutation.js +2 -2
- package/dist/app/setup-codex.js +37 -20
- package/dist/app/show-config.js +34 -0
- package/dist/app/show-provider.js +1 -1
- package/dist/app/switch-provider.js +8 -5
- package/dist/cli/add-interactive.js +7 -106
- package/dist/cli/args.js +5 -126
- package/dist/cli/help.js +5 -276
- package/dist/cli/interactive.js +16 -171
- package/dist/cli/output.js +23 -1
- package/dist/cli/prompt.js +3 -108
- package/dist/cli.js +10 -315
- package/dist/commands/args.js +132 -0
- package/dist/commands/dispatch.js +16 -0
- package/dist/commands/handlers.js +391 -0
- package/dist/commands/help.js +119 -0
- package/dist/commands/registry.js +291 -0
- package/dist/commands/types.js +2 -0
- package/dist/domain/config.js +548 -39
- package/dist/infra/backup-repo.js +8 -208
- package/dist/infra/codex-cli.js +8 -113
- package/dist/infra/codex-discovery.js +3 -41
- package/dist/infra/codex-paths.js +5 -69
- package/dist/infra/config-repo.js +161 -9
- package/dist/infra/fs-utils.js +7 -95
- package/dist/infra/lock-repo.js +3 -97
- package/dist/infra/providers-repo.js +7 -96
- package/dist/interaction/add-interactive.js +108 -0
- package/dist/interaction/interactive.js +216 -0
- package/dist/interaction/prompt.js +110 -0
- package/dist/runtime/codex-cli.js +130 -0
- package/dist/runtime/codex-probe.js +50 -0
- package/dist/runtime/types.js +2 -0
- package/dist/storage/backup-repo.js +210 -0
- package/dist/storage/codex-paths.js +71 -0
- package/dist/storage/config-repo.js +208 -0
- package/dist/storage/fs-utils.js +97 -0
- package/dist/storage/lock-repo.js +99 -0
- package/dist/storage/providers-repo.js +98 -0
- package/docs/Design/codex-switch-v0.0.5-design.md +932 -0
- package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +340 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +215 -291
- package/docs/PRD/codex-switch-prd.md +1 -1
- package/docs/cli-usage.md +2 -1
- package/docs/codex-switch-technical-architecture.md +73 -4
- package/docs/test-report-0.0.5.md +163 -0
- package/docs/testing.md +131 -0
- package/package.json +1 -1
- /package/docs/{codex-switch-v0.0.4-design.md → Design/codex-switch-v0.0.4-design.md} +0 -0
|
@@ -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("../
|
|
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("../
|
|
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
|
*/
|
package/dist/app/run-doctor.js
CHANGED
|
@@ -37,17 +37,18 @@ 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
|
|
41
|
-
const providers_repo_1 = require("../
|
|
40
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
41
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
42
42
|
const errors_1 = require("../domain/errors");
|
|
43
|
+
const codex_probe_1 = require("../runtime/codex-probe");
|
|
43
44
|
/**
|
|
44
45
|
* Performs consistency checks across config.toml, providers.json, and the local Codex CLI.
|
|
45
46
|
*/
|
|
46
47
|
function runDoctor(args) {
|
|
47
48
|
const issues = [];
|
|
48
|
-
let configProfiles = new Set();
|
|
49
49
|
let currentProfile = null;
|
|
50
50
|
let providers = null;
|
|
51
|
+
let document = null;
|
|
51
52
|
if (!fs.existsSync(args.configPath)) {
|
|
52
53
|
issues.push({
|
|
53
54
|
code: "CONFIG_NOT_FOUND",
|
|
@@ -56,9 +57,8 @@ function runDoctor(args) {
|
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
59
|
else {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
currentProfile = (0, config_1.parseTopLevelProfile)(configContent);
|
|
60
|
+
document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
61
|
+
currentProfile = document.activeProfile;
|
|
62
62
|
if (!currentProfile) {
|
|
63
63
|
issues.push({
|
|
64
64
|
code: "PROFILE_NOT_FOUND",
|
|
@@ -77,14 +77,11 @@ function runDoctor(args) {
|
|
|
77
77
|
else {
|
|
78
78
|
try {
|
|
79
79
|
providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (!configProfiles.has(provider.profile)) {
|
|
80
|
+
if (document) {
|
|
81
|
+
for (const issue of (0, config_1.collectConfigConsistencyIssues)(document, providers)) {
|
|
83
82
|
issues.push({
|
|
84
|
-
|
|
85
|
-
message:
|
|
86
|
-
provider: name,
|
|
87
|
-
profile: provider.profile,
|
|
83
|
+
...issue,
|
|
84
|
+
message: renderConfigIssueMessage(issue),
|
|
88
85
|
});
|
|
89
86
|
}
|
|
90
87
|
}
|
|
@@ -99,21 +96,20 @@ function runDoctor(args) {
|
|
|
99
96
|
}
|
|
100
97
|
}
|
|
101
98
|
const drift = (0, runtime_state_1.inspectLiveStateDrift)(currentProfile, providers);
|
|
102
|
-
|
|
103
|
-
// Distinguish unmanaged live state from hard parse/configuration errors.
|
|
104
|
-
issues.push({
|
|
105
|
-
code: "LIVE_STATE_DRIFT",
|
|
106
|
-
message: `Active profile "${drift.currentProfile}" is present in config.toml but not mapped by providers.json.`,
|
|
107
|
-
currentProfile: drift.currentProfile,
|
|
108
|
-
suggestedAction: "backfill-active-provider",
|
|
109
|
-
storage: (0, runtime_state_1.getStorageRoles)(),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
const codexCheck = (0, codex_cli_1.checkCodexAvailable)();
|
|
99
|
+
const codexCheck = (0, codex_probe_1.probeCodexRuntime)();
|
|
113
100
|
if (!codexCheck.ok) {
|
|
101
|
+
const message = codexCheck.reason === "missing"
|
|
102
|
+
? "codex CLI is not available on PATH."
|
|
103
|
+
: codexCheck.reason === "unsupported"
|
|
104
|
+
? "codex CLI version is below the supported minimum."
|
|
105
|
+
: "codex CLI probe failed.";
|
|
114
106
|
issues.push({
|
|
115
|
-
code: "
|
|
116
|
-
|
|
107
|
+
code: codexCheck.reason === "unsupported"
|
|
108
|
+
? "CODEX_VERSION_UNSUPPORTED"
|
|
109
|
+
: codexCheck.reason === "missing"
|
|
110
|
+
? "CODEX_NOT_INSTALLED"
|
|
111
|
+
: "CODEX_LOGIN_FAILED",
|
|
112
|
+
message,
|
|
117
113
|
cause: codexCheck.cause,
|
|
118
114
|
});
|
|
119
115
|
}
|
|
@@ -128,3 +124,25 @@ function runDoctor(args) {
|
|
|
128
124
|
warnings: issues.length === 0 ? [] : [`doctor found ${issues.length} issue(s)`],
|
|
129
125
|
};
|
|
130
126
|
}
|
|
127
|
+
function renderConfigIssueMessage(issue) {
|
|
128
|
+
switch (issue.code) {
|
|
129
|
+
case "ORPHANED_PROFILE_REFERENCE":
|
|
130
|
+
return `Profile "${issue.profile}" is referenced by providers but missing from config.toml.`;
|
|
131
|
+
case "UNMANAGED_ACTIVE_PROFILE":
|
|
132
|
+
return `Active profile "${issue.profile}" is not mapped by providers.json.`;
|
|
133
|
+
case "SHARED_PROFILE_REFERENCE":
|
|
134
|
+
return `Profile "${issue.profile}" is shared by multiple providers.`;
|
|
135
|
+
case "ORPHANED_PROFILE_SECTION":
|
|
136
|
+
return `Profile section "${issue.profile}" is not linked to any provider.`;
|
|
137
|
+
case "MODEL_PROVIDER_MISSING":
|
|
138
|
+
return `Profile "${issue.profile}" is missing model_provider.`;
|
|
139
|
+
case "MODEL_PROVIDER_NAME_MISMATCH":
|
|
140
|
+
return `Profile "${issue.profile}" must use matching model_provider name "${issue.profile}", found "${issue.modelProvider}".`;
|
|
141
|
+
case "MODEL_PROVIDER_SECTION_MISSING":
|
|
142
|
+
return `Model provider section "${issue.modelProvider}" for profile "${issue.profile}" is missing from config.toml.`;
|
|
143
|
+
case "MODEL_PROVIDER_BASE_URL_MISSING":
|
|
144
|
+
return `Model provider section "${issue.modelProvider}" for profile "${issue.profile}" is missing base_url.`;
|
|
145
|
+
case "DESTRUCTIVE_REMOVE_BLOCKED":
|
|
146
|
+
return `Provider "${issue.provider}" cannot be removed while "${issue.activeProfile}" remains active.`;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/dist/app/run-mutation.js
CHANGED
|
@@ -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("../
|
|
6
|
-
const lock_repo_1 = require("../
|
|
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
|
*/
|
package/dist/app/setup-codex.js
CHANGED
|
@@ -37,11 +37,11 @@ exports.setupCodex = setupCodex;
|
|
|
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
|
-
const
|
|
41
|
-
const codex_cli_1 = require("../
|
|
42
|
-
const config_repo_1 = require("../
|
|
43
|
-
const fs_utils_1 = require("../
|
|
44
|
-
const providers_repo_1 = require("../
|
|
40
|
+
const config_1 = require("../domain/config");
|
|
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
45
|
const run_doctor_1 = require("./run-doctor");
|
|
46
46
|
const run_mutation_1 = require("./run-mutation");
|
|
47
47
|
const MIN_CODEX_VERSION = "0.0.1";
|
|
@@ -63,29 +63,35 @@ function setupCodex(args) {
|
|
|
63
63
|
cause: version.cause,
|
|
64
64
|
});
|
|
65
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
66
|
if (!fs.existsSync(args.codexDir)) {
|
|
78
67
|
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "The requested Codex directory does not exist.", {
|
|
79
68
|
codexDir: args.codexDir,
|
|
80
69
|
});
|
|
81
70
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
71
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
72
|
+
const profileViews = (0, config_1.buildManagedProfileViews)(document, null);
|
|
73
|
+
const adoptableProfiles = profileViews
|
|
74
|
+
.filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl)
|
|
75
|
+
.map((view) => view.name)
|
|
76
|
+
.sort();
|
|
77
|
+
if (profileViews.length === 0) {
|
|
84
78
|
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", "No profiles were found in config.toml.", {
|
|
85
79
|
file: args.configPath,
|
|
86
80
|
});
|
|
87
81
|
}
|
|
88
|
-
const
|
|
82
|
+
const invalidAdoptProfiles = args.adoptProfiles.filter((profile) => !adoptableProfiles.includes(profile));
|
|
83
|
+
if (invalidAdoptProfiles.length > 0) {
|
|
84
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup only adopts unmanaged profiles that already contain model, model_provider, and a matching model_providers base_url.", {
|
|
85
|
+
invalidProfiles: invalidAdoptProfiles.sort(),
|
|
86
|
+
adoptableProfiles,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (args.adoptProfiles.length === 0) {
|
|
90
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup requires at least one explicit profile to adopt.", {
|
|
91
|
+
adoptableProfiles,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const drafts = (0, setup_1.buildSetupDrafts)(args.adoptProfiles, args.providerDetailsByProfile);
|
|
89
95
|
const incompleteProfiles = (0, setup_1.findIncompleteSetupProfiles)(drafts);
|
|
90
96
|
if (incompleteProfiles.length > 0) {
|
|
91
97
|
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "setup requires complete provider data for every selected profile.", {
|
|
@@ -112,14 +118,25 @@ function setupCodex(args) {
|
|
|
112
118
|
backupsDir: args.backupsDir,
|
|
113
119
|
latestBackupPath: args.latestBackupPath,
|
|
114
120
|
operation: "setup",
|
|
115
|
-
files: [
|
|
121
|
+
files: [
|
|
122
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
123
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
124
|
+
],
|
|
116
125
|
mutate: () => {
|
|
126
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {});
|
|
117
127
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, finalProviders);
|
|
128
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
118
129
|
return {
|
|
119
130
|
codexDir: args.codexDir,
|
|
120
131
|
strategy: args.strategy,
|
|
121
132
|
providersInitialized: Object.keys(nextProviders.providers).length,
|
|
122
133
|
providerNames: Object.keys(finalProviders.providers).sort(),
|
|
134
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
135
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
136
|
+
keptSharedProfiles: [],
|
|
137
|
+
switchedActiveProfile: false,
|
|
138
|
+
adoptedProfiles: [...args.adoptProfiles].sort(),
|
|
139
|
+
repairedProfiles: [],
|
|
123
140
|
};
|
|
124
141
|
},
|
|
125
142
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.showConfig = showConfig;
|
|
4
|
+
const config_1 = require("../domain/config");
|
|
5
|
+
const errors_1 = require("../domain/errors");
|
|
6
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
7
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
8
|
+
/**
|
|
9
|
+
* Returns the structured config view, optionally filtered to one profile.
|
|
10
|
+
*/
|
|
11
|
+
function showConfig(args) {
|
|
12
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
13
|
+
const providers = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
14
|
+
const views = (0, config_1.buildManagedProfileViews)(document, providers);
|
|
15
|
+
let selectedProfile = null;
|
|
16
|
+
let profiles = views;
|
|
17
|
+
if (args.profileName) {
|
|
18
|
+
const found = views.find((view) => view.name === args.profileName);
|
|
19
|
+
if (!found) {
|
|
20
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${args.profileName}" was not found.`, {
|
|
21
|
+
profile: args.profileName,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
selectedProfile = args.profileName;
|
|
25
|
+
profiles = [found];
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
data: {
|
|
29
|
+
activeProfile: document.activeProfile,
|
|
30
|
+
selectedProfile,
|
|
31
|
+
profiles,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -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("../
|
|
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,10 +2,10 @@
|
|
|
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("../
|
|
6
|
-
const
|
|
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");
|
|
8
7
|
const run_mutation_1 = require("./run-mutation");
|
|
8
|
+
const codex_cli_1 = require("../runtime/codex-cli");
|
|
9
9
|
/**
|
|
10
10
|
* Switches the active Codex profile and optionally refreshes the CLI login session.
|
|
11
11
|
*/
|
|
@@ -17,7 +17,7 @@ function switchProvider(args) {
|
|
|
17
17
|
availableProviders: Object.keys(providers.providers).sort(),
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
const
|
|
20
|
+
const document = (0, config_repo_1.ensureProfileExists)(args.configPath, provider.profile, args.providerName);
|
|
21
21
|
return (0, run_mutation_1.runMutation)({
|
|
22
22
|
codexDir: args.codexDir,
|
|
23
23
|
backupsDir: args.backupsDir,
|
|
@@ -28,8 +28,11 @@ function switchProvider(args) {
|
|
|
28
28
|
{ absolutePath: args.authPath, relativePath: "auth.json" },
|
|
29
29
|
],
|
|
30
30
|
mutate: () => {
|
|
31
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
32
|
+
setActiveProfile: provider.profile,
|
|
33
|
+
});
|
|
31
34
|
// Update the runtime profile first so any subsequent login is associated with the new target.
|
|
32
|
-
(0, config_repo_1.
|
|
35
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
33
36
|
if (!args.noLogin) {
|
|
34
37
|
(0, codex_cli_1.runCodexLogin)(provider.apiKey, args.codexDir);
|
|
35
38
|
}
|
|
@@ -1,108 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.COMMON_TAG_CHOICES = void 0;
|
|
4
|
-
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
|
|
9
|
-
exports
|
|
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
|
-
}
|
|
3
|
+
exports.promptTags = exports.parseTags = exports.createNonInteractiveAddError = exports.COMMON_TAG_CHOICES = exports.collectAddInput = void 0;
|
|
4
|
+
var add_interactive_1 = require("../interaction/add-interactive");
|
|
5
|
+
Object.defineProperty(exports, "collectAddInput", { enumerable: true, get: function () { return add_interactive_1.collectAddInput; } });
|
|
6
|
+
Object.defineProperty(exports, "COMMON_TAG_CHOICES", { enumerable: true, get: function () { return add_interactive_1.COMMON_TAG_CHOICES; } });
|
|
7
|
+
Object.defineProperty(exports, "createNonInteractiveAddError", { enumerable: true, get: function () { return add_interactive_1.createNonInteractiveAddError; } });
|
|
8
|
+
Object.defineProperty(exports, "parseTags", { enumerable: true, get: function () { return add_interactive_1.parseTags; } });
|
|
9
|
+
Object.defineProperty(exports, "promptTags", { enumerable: true, get: function () { return add_interactive_1.promptTags; } });
|
package/dist/cli/args.js
CHANGED
|
@@ -1,128 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseArgs =
|
|
4
|
-
|
|
5
|
-
exports
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Parses argv into command positionals, global flags, and command-scoped options.
|
|
10
|
-
*/
|
|
11
|
-
function parseArgs(argv) {
|
|
12
|
-
let json = false;
|
|
13
|
-
let codexDir = (0, codex_paths_1.resolveCodexDir)();
|
|
14
|
-
const remaining = [];
|
|
15
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
16
|
-
const value = argv[index];
|
|
17
|
-
if (value === "--json") {
|
|
18
|
-
json = true;
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (value === "--codex-dir") {
|
|
22
|
-
const next = argv[index + 1];
|
|
23
|
-
if (!next) {
|
|
24
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--codex-dir requires a path value.");
|
|
25
|
-
}
|
|
26
|
-
codexDir = (0, codex_paths_1.resolveCodexDir)(next);
|
|
27
|
-
index += 1;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
remaining.push(value);
|
|
31
|
-
}
|
|
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;
|
|
57
|
-
const positionals = [];
|
|
58
|
-
const commandOptions = new Map();
|
|
59
|
-
let helpRequested = false;
|
|
60
|
-
const startIndex = command === "backups-list" ? 2 : 1;
|
|
61
|
-
for (let index = startIndex; index < remaining.length; index += 1) {
|
|
62
|
-
const value = remaining[index];
|
|
63
|
-
if (value === "--help" || value === "-h") {
|
|
64
|
-
helpRequested = true;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (value.startsWith("--")) {
|
|
68
|
-
const optionName = value;
|
|
69
|
-
const next = remaining[index + 1];
|
|
70
|
-
if (!next || next.startsWith("--")) {
|
|
71
|
-
// Boolean flags are stored as "true" so later access uses one uniform map shape.
|
|
72
|
-
commandOptions.set(optionName, ["true"]);
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const existing = commandOptions.get(optionName) ?? [];
|
|
76
|
-
existing.push(next);
|
|
77
|
-
commandOptions.set(optionName, existing);
|
|
78
|
-
index += 1;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
positionals.push(value);
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
command,
|
|
85
|
-
positionals,
|
|
86
|
-
globalOptions: {
|
|
87
|
-
json,
|
|
88
|
-
codexDir,
|
|
89
|
-
},
|
|
90
|
-
commandOptions,
|
|
91
|
-
helpRequested,
|
|
92
|
-
helpTarget: helpRequested ? (command === "backups-list" ? "backups" : command) : null,
|
|
93
|
-
versionRequested: false,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Creates a parsed result for built-in synthetic commands such as help/version.
|
|
98
|
-
*/
|
|
99
|
-
function defaultParsed(command, overrides) {
|
|
100
|
-
return {
|
|
101
|
-
command,
|
|
102
|
-
positionals: [],
|
|
103
|
-
globalOptions: {
|
|
104
|
-
json: overrides?.json ?? false,
|
|
105
|
-
codexDir: overrides?.codexDir ?? (0, codex_paths_1.resolveCodexDir)(),
|
|
106
|
-
},
|
|
107
|
-
commandOptions: new Map(),
|
|
108
|
-
helpRequested: overrides?.helpRequested ?? false,
|
|
109
|
-
helpTarget: overrides?.helpTarget ?? null,
|
|
110
|
-
versionRequested: overrides?.versionRequested ?? false,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Checks whether a boolean-style option was supplied.
|
|
115
|
-
*/
|
|
116
|
-
function hasFlag(options, name) {
|
|
117
|
-
return options.has(name);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Returns the last supplied value for a single-valued command option.
|
|
121
|
-
*/
|
|
122
|
-
function getSingleOption(options, name, required = true) {
|
|
123
|
-
const values = options.get(name) ?? [];
|
|
124
|
-
if (values.length === 0) {
|
|
125
|
-
return required ? null : null;
|
|
126
|
-
}
|
|
127
|
-
return values[values.length - 1];
|
|
128
|
-
}
|
|
3
|
+
exports.parseArgs = exports.hasFlag = exports.getSingleOption = void 0;
|
|
4
|
+
var args_1 = require("../commands/args");
|
|
5
|
+
Object.defineProperty(exports, "getSingleOption", { enumerable: true, get: function () { return args_1.getSingleOption; } });
|
|
6
|
+
Object.defineProperty(exports, "hasFlag", { enumerable: true, get: function () { return args_1.hasFlag; } });
|
|
7
|
+
Object.defineProperty(exports, "parseArgs", { enumerable: true, get: function () { return args_1.parseArgs; } });
|