@minniexcode/codex-switch 0.0.6 → 0.0.8
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 +5 -2
- package/README.md +12 -6
- package/dist/app/add-provider.js +90 -5
- package/dist/app/edit-provider.js +39 -11
- package/dist/app/get-status.js +31 -1
- package/dist/app/init-codex.js +68 -0
- package/dist/app/list-providers.js +1 -0
- package/dist/app/run-doctor.js +96 -1
- package/dist/app/setup-codex.js +18 -9
- package/dist/app/show-config.js +9 -1
- package/dist/app/switch-provider.js +61 -8
- package/dist/cli/add-interactive.js +4 -2
- package/dist/cli/args.js +3 -0
- package/dist/cli/help.js +3 -0
- package/dist/cli/interactive.js +3 -0
- package/dist/cli/output.js +20 -5
- package/dist/cli/prompt.js +3 -0
- package/dist/cli.js +1 -1
- package/dist/commands/handlers.js +107 -13
- package/dist/commands/help.js +2 -1
- package/dist/commands/registry.js +87 -15
- package/dist/domain/config.js +137 -0
- package/dist/domain/providers.js +90 -2
- package/dist/domain/setup.js +1 -0
- package/dist/infra/backup-repo.js +3 -0
- package/dist/infra/codex-cli.js +3 -0
- package/dist/infra/codex-paths.js +3 -0
- package/dist/infra/fs-utils.js +3 -0
- package/dist/infra/lock-repo.js +3 -0
- package/dist/infra/providers-repo.js +3 -0
- package/dist/interaction/add-interactive.js +9 -18
- package/dist/interaction/interactive.js +84 -11
- package/dist/runtime/codex-probe.js +7 -0
- package/dist/runtime/copilot-adapter.js +173 -0
- package/dist/runtime/copilot-bridge-worker.js +25 -0
- package/dist/runtime/copilot-bridge.js +433 -0
- package/dist/runtime/copilot-installer.js +125 -0
- package/dist/runtime/copilot-sdk-loader.js +59 -0
- package/dist/storage/auth-repo.js +160 -0
- package/dist/storage/config-repo.js +58 -0
- package/dist/storage/fs-utils.js +3 -0
- package/dist/storage/runtime-state-repo.js +80 -0
- package/docs/Design/codex-switch-v0.0.7-design.md +862 -0
- package/docs/Design/codex-switch-v0.0.8-design.md +132 -0
- package/docs/Design/codex-switch-v0.0.9-to-v0.0.12-roadmap.md +413 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +131 -25
- package/docs/PRD/codex-switch-prd-v0.0.8.md +62 -0
- package/docs/Reference/codex-config-reference.md +604 -0
- package/docs/Reference/codex-config-reference.zh-CN.md +633 -0
- package/docs/cli-usage.md +77 -29
- package/docs/test-report-0.0.7.md +118 -0
- package/docs/testing.md +67 -47
- package/package.json +1 -1
package/dist/app/setup-codex.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.migrateCodex = migrateCodex;
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const setup_1 = require("../domain/setup");
|
|
39
39
|
const errors_1 = require("../domain/errors");
|
|
@@ -42,13 +42,14 @@ const codex_cli_1 = require("../runtime/codex-cli");
|
|
|
42
42
|
const config_repo_1 = require("../storage/config-repo");
|
|
43
43
|
const fs_utils_1 = require("../storage/fs-utils");
|
|
44
44
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
45
|
+
const auth_repo_1 = require("../storage/auth-repo");
|
|
45
46
|
const run_doctor_1 = require("./run-doctor");
|
|
46
47
|
const run_mutation_1 = require("./run-mutation");
|
|
47
48
|
const MIN_CODEX_VERSION = "0.0.1";
|
|
48
49
|
/**
|
|
49
|
-
*
|
|
50
|
+
* Migrates unmanaged Codex config profiles into a managed providers.json registry.
|
|
50
51
|
*/
|
|
51
|
-
function
|
|
52
|
+
async function migrateCodex(args) {
|
|
52
53
|
const available = (0, codex_cli_1.checkCodexAvailable)();
|
|
53
54
|
if (!available.ok) {
|
|
54
55
|
throw (0, errors_1.cliError)("CODEX_NOT_INSTALLED", "codex CLI is not available.", {
|
|
@@ -70,8 +71,9 @@ function setupCodex(args) {
|
|
|
70
71
|
}
|
|
71
72
|
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
72
73
|
const profileViews = (0, config_1.buildManagedProfileViews)(document, null);
|
|
74
|
+
// Migrate can only adopt unmanaged profiles that already contain enough runtime data to become managed.
|
|
73
75
|
const adoptableProfiles = profileViews
|
|
74
|
-
.filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl)
|
|
76
|
+
.filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl && view.envKey)
|
|
75
77
|
.map((view) => view.name)
|
|
76
78
|
.sort();
|
|
77
79
|
if (profileViews.length === 0) {
|
|
@@ -81,20 +83,20 @@ function setupCodex(args) {
|
|
|
81
83
|
}
|
|
82
84
|
const invalidAdoptProfiles = args.adoptProfiles.filter((profile) => !adoptableProfiles.includes(profile));
|
|
83
85
|
if (invalidAdoptProfiles.length > 0) {
|
|
84
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "
|
|
86
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate only adopts unmanaged profiles that already contain model, model_provider, and matching model_providers base_url/env_key.", {
|
|
85
87
|
invalidProfiles: invalidAdoptProfiles.sort(),
|
|
86
88
|
adoptableProfiles,
|
|
87
89
|
});
|
|
88
90
|
}
|
|
89
91
|
if (args.adoptProfiles.length === 0) {
|
|
90
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "
|
|
92
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate requires at least one explicit profile to adopt.", {
|
|
91
93
|
adoptableProfiles,
|
|
92
94
|
});
|
|
93
95
|
}
|
|
94
96
|
const drafts = (0, setup_1.buildSetupDrafts)(args.adoptProfiles, args.providerDetailsByProfile);
|
|
95
97
|
const incompleteProfiles = (0, setup_1.findIncompleteSetupProfiles)(drafts);
|
|
96
98
|
if (incompleteProfiles.length > 0) {
|
|
97
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "
|
|
99
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate requires complete provider data for every selected profile.", {
|
|
98
100
|
incompleteProfiles,
|
|
99
101
|
});
|
|
100
102
|
}
|
|
@@ -117,15 +119,20 @@ function setupCodex(args) {
|
|
|
117
119
|
codexDir: args.codexDir,
|
|
118
120
|
backupsDir: args.backupsDir,
|
|
119
121
|
latestBackupPath: args.latestBackupPath,
|
|
120
|
-
operation: "
|
|
122
|
+
operation: "migrate",
|
|
121
123
|
files: [
|
|
122
124
|
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
123
125
|
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
126
|
+
{ absolutePath: args.authPath, relativePath: "auth.json" },
|
|
124
127
|
],
|
|
125
128
|
mutate: () => {
|
|
129
|
+
// migrate currently preserves config structure and only asserts that the file remains writable inside the mutation flow.
|
|
126
130
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {});
|
|
127
131
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, finalProviders);
|
|
128
132
|
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
133
|
+
const activeProviderName = (0, config_repo_1.resolveActiveProviderName)(document, finalProviders);
|
|
134
|
+
const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
|
|
135
|
+
(0, auth_repo_1.writeAuthFile)(args.authPath, finalProviders.providers[activeProviderName], existingAuth ?? undefined);
|
|
129
136
|
return {
|
|
130
137
|
codexDir: args.codexDir,
|
|
131
138
|
strategy: args.strategy,
|
|
@@ -140,10 +147,12 @@ function setupCodex(args) {
|
|
|
140
147
|
};
|
|
141
148
|
},
|
|
142
149
|
});
|
|
143
|
-
|
|
150
|
+
// Re-run doctor on the final state so migrate returns immediate post-migration diagnostics.
|
|
151
|
+
const doctor = await (0, run_doctor_1.runDoctor)({
|
|
144
152
|
codexDir: args.codexDir,
|
|
145
153
|
configPath: args.configPath,
|
|
146
154
|
providersPath: args.providersPath,
|
|
155
|
+
authPath: args.authPath,
|
|
147
156
|
});
|
|
148
157
|
return {
|
|
149
158
|
data: {
|
package/dist/app/show-config.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.showConfig = showConfig;
|
|
4
4
|
const config_1 = require("../domain/config");
|
|
5
5
|
const errors_1 = require("../domain/errors");
|
|
6
|
+
const providers_1 = require("../domain/providers");
|
|
6
7
|
const config_repo_1 = require("../storage/config-repo");
|
|
7
8
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
8
9
|
/**
|
|
@@ -28,7 +29,14 @@ function showConfig(args) {
|
|
|
28
29
|
data: {
|
|
29
30
|
activeProfile: document.activeProfile,
|
|
30
31
|
selectedProfile,
|
|
31
|
-
profiles
|
|
32
|
+
profiles: profiles.map((profile) => ({
|
|
33
|
+
...profile,
|
|
34
|
+
managedProviderEnvKeys: (0, providers_1.findProvidersByProfile)(providers, profile.name).map((providerName) => ({
|
|
35
|
+
providerName,
|
|
36
|
+
envKey: providers.providers[providerName].envKey,
|
|
37
|
+
matchesRuntime: providers.providers[providerName].envKey === profile.envKey,
|
|
38
|
+
})),
|
|
39
|
+
})),
|
|
32
40
|
},
|
|
33
41
|
};
|
|
34
42
|
}
|
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.switchProvider = switchProvider;
|
|
4
4
|
const errors_1 = require("../domain/errors");
|
|
5
|
+
const providers_1 = require("../domain/providers");
|
|
5
6
|
const config_repo_1 = require("../storage/config-repo");
|
|
6
7
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
8
|
+
const auth_repo_1 = require("../storage/auth-repo");
|
|
9
|
+
const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
10
|
+
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
11
|
+
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
7
12
|
const run_mutation_1 = require("./run-mutation");
|
|
8
|
-
const codex_cli_1 = require("../runtime/codex-cli");
|
|
9
13
|
/**
|
|
10
|
-
* Switches the active Codex profile and
|
|
14
|
+
* Switches the active Codex profile and rewrites auth.json for the target provider.
|
|
11
15
|
*/
|
|
12
|
-
function switchProvider(args) {
|
|
16
|
+
async function switchProvider(args) {
|
|
13
17
|
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
14
18
|
const provider = providers.providers[args.providerName];
|
|
15
19
|
if (!provider) {
|
|
@@ -18,6 +22,57 @@ function switchProvider(args) {
|
|
|
18
22
|
});
|
|
19
23
|
}
|
|
20
24
|
const document = (0, config_repo_1.ensureProfileExists)(args.configPath, provider.profile, args.providerName);
|
|
25
|
+
const envKey = (0, config_repo_1.requireRuntimeEnvKey)(document, provider.profile);
|
|
26
|
+
if (provider.envKey !== envKey) {
|
|
27
|
+
throw (0, errors_1.cliError)("PROVIDER_ENV_KEY_MISMATCH", `Provider "${args.providerName}" envKey does not match runtime env_key.`, {
|
|
28
|
+
provider: args.providerName,
|
|
29
|
+
profile: provider.profile,
|
|
30
|
+
providerEnvKey: provider.envKey,
|
|
31
|
+
runtimeEnvKey: envKey,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
|
|
35
|
+
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
36
|
+
if (!installStatus.installed) {
|
|
37
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
|
|
38
|
+
installDir: installStatus.installDir,
|
|
39
|
+
packageName: installStatus.packageName,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
await (0, copilot_adapter_1.readCopilotAuthState)();
|
|
43
|
+
const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider);
|
|
44
|
+
try {
|
|
45
|
+
return (0, run_mutation_1.runMutation)({
|
|
46
|
+
codexDir: args.codexDir,
|
|
47
|
+
backupsDir: args.backupsDir,
|
|
48
|
+
latestBackupPath: args.latestBackupPath,
|
|
49
|
+
operation: "switch",
|
|
50
|
+
files: [
|
|
51
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
52
|
+
{ absolutePath: args.authPath, relativePath: "auth.json" },
|
|
53
|
+
],
|
|
54
|
+
mutate: () => {
|
|
55
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
56
|
+
setActiveProfile: provider.profile,
|
|
57
|
+
});
|
|
58
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
59
|
+
const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
|
|
60
|
+
(0, auth_repo_1.writeAuthFile)(args.authPath, provider, existingAuth ?? undefined);
|
|
61
|
+
return {
|
|
62
|
+
provider: args.providerName,
|
|
63
|
+
profile: provider.profile,
|
|
64
|
+
envKey: provider.envKey,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (!bridge.reused) {
|
|
71
|
+
(0, copilot_bridge_1.stopCopilotBridge)();
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
21
76
|
return (0, run_mutation_1.runMutation)({
|
|
22
77
|
codexDir: args.codexDir,
|
|
23
78
|
backupsDir: args.backupsDir,
|
|
@@ -31,15 +86,13 @@ function switchProvider(args) {
|
|
|
31
86
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
32
87
|
setActiveProfile: provider.profile,
|
|
33
88
|
});
|
|
34
|
-
// Update the runtime profile first so any subsequent login is associated with the new target.
|
|
35
89
|
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
90
|
+
const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
|
|
91
|
+
(0, auth_repo_1.writeAuthFile)(args.authPath, provider, existingAuth ?? undefined);
|
|
39
92
|
return {
|
|
40
93
|
provider: args.providerName,
|
|
41
94
|
profile: provider.profile,
|
|
42
|
-
|
|
95
|
+
envKey: provider.envKey,
|
|
43
96
|
};
|
|
44
97
|
},
|
|
45
98
|
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.promptTags = exports.
|
|
3
|
+
exports.promptTags = exports.createNonInteractiveAddError = exports.COMMON_TAG_CHOICES = exports.collectAddInput = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Compatibility facade that re-exports interactive add helpers for older imports.
|
|
6
|
+
*/
|
|
4
7
|
var add_interactive_1 = require("../interaction/add-interactive");
|
|
5
8
|
Object.defineProperty(exports, "collectAddInput", { enumerable: true, get: function () { return add_interactive_1.collectAddInput; } });
|
|
6
9
|
Object.defineProperty(exports, "COMMON_TAG_CHOICES", { enumerable: true, get: function () { return add_interactive_1.COMMON_TAG_CHOICES; } });
|
|
7
10
|
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
11
|
Object.defineProperty(exports, "promptTags", { enumerable: true, get: function () { return add_interactive_1.promptTags; } });
|
package/dist/cli/args.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseArgs = exports.hasFlag = exports.getSingleOption = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Compatibility facade that re-exports shared CLI argument parsing helpers.
|
|
6
|
+
*/
|
|
4
7
|
var args_1 = require("../commands/args");
|
|
5
8
|
Object.defineProperty(exports, "getSingleOption", { enumerable: true, get: function () { return args_1.getSingleOption; } });
|
|
6
9
|
Object.defineProperty(exports, "hasFlag", { enumerable: true, get: function () { return args_1.hasFlag; } });
|
package/dist/cli/help.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isKnownCommandName = exports.getKnownCommandNames = exports.buildHelpText = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Compatibility facade that re-exports CLI help helpers from the commands layer.
|
|
6
|
+
*/
|
|
4
7
|
var help_1 = require("../commands/help");
|
|
5
8
|
Object.defineProperty(exports, "buildHelpText", { enumerable: true, get: function () { return help_1.buildHelpText; } });
|
|
6
9
|
Object.defineProperty(exports, "getKnownCommandNames", { enumerable: true, get: function () { return help_1.getKnownCommandNames; } });
|
package/dist/cli/interactive.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.promptForProviderSelection = exports.getRollbackSummaryById = exports.getRollbackSummary = exports.exportTargetExists = exports.confirmRollback = exports.confirmProviderRemoval = exports.confirmImport = exports.confirmExportOverwrite = exports.collectSetupProviderDetails = exports.collectEditInput = exports.chooseSetupStrategy = exports.chooseSetupProfiles = exports.chooseCodexDir = exports.canPrompt = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Compatibility facade that re-exports interactive command helpers.
|
|
6
|
+
*/
|
|
4
7
|
var interactive_1 = require("../interaction/interactive");
|
|
5
8
|
Object.defineProperty(exports, "canPrompt", { enumerable: true, get: function () { return interactive_1.canPrompt; } });
|
|
6
9
|
Object.defineProperty(exports, "chooseCodexDir", { enumerable: true, get: function () { return interactive_1.chooseCodexDir; } });
|
package/dist/cli/output.js
CHANGED
|
@@ -90,7 +90,8 @@ function renderHumanSuccess(command, data, warnings) {
|
|
|
90
90
|
? ` tags=${provider.tags.join(",")}`
|
|
91
91
|
: "";
|
|
92
92
|
const note = provider.note ? ` note=${provider.note}` : "";
|
|
93
|
-
|
|
93
|
+
const envKey = provider.envKey ? ` envKey=${provider.envKey}` : "";
|
|
94
|
+
lines.push(`${provider.name} -> ${provider.profile}${envKey}${tags}${note}`);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
break;
|
|
@@ -100,6 +101,7 @@ function renderHumanSuccess(command, data, warnings) {
|
|
|
100
101
|
lines.push(`Provider: ${String(data?.providerName ?? "")}`);
|
|
101
102
|
lines.push(`profile: ${String(provider.profile ?? "")}`);
|
|
102
103
|
lines.push(`apiKey: ${String(provider.apiKey ?? "")}`);
|
|
104
|
+
lines.push(`envKey: ${String(provider.envKey ?? "")}`);
|
|
103
105
|
if (provider.baseUrl) {
|
|
104
106
|
lines.push(`baseUrl: ${String(provider.baseUrl)}`);
|
|
105
107
|
}
|
|
@@ -120,13 +122,17 @@ function renderHumanSuccess(command, data, warnings) {
|
|
|
120
122
|
lines.push(`providersExists: ${String(data?.providersExists ?? false)}`);
|
|
121
123
|
lines.push(`currentProfile: ${String(data?.currentProfile ?? "")}`);
|
|
122
124
|
lines.push(`mappedProvider: ${String(data?.provider ?? "")}`);
|
|
125
|
+
lines.push(`activeProviderResolvable: ${String(data?.activeProviderResolvable ?? false)}`);
|
|
126
|
+
const auth = data?.auth ?? {};
|
|
127
|
+
lines.push(`authExists: ${String(auth.exists ?? false)}`);
|
|
128
|
+
lines.push(`authManagedKeys: ${Array.isArray(auth.managedSecretKeys) ? auth.managedSecretKeys.join(",") : ""}`);
|
|
123
129
|
lines.push(`issues: ${Array.isArray(data?.issues) ? (data?.issues).length : 0}`);
|
|
124
130
|
break;
|
|
125
131
|
case "config-show": {
|
|
126
132
|
lines.push(`activeProfile: ${String(data?.activeProfile ?? "")}`);
|
|
127
133
|
const profiles = data?.profiles ?? [];
|
|
128
134
|
for (const profile of profiles) {
|
|
129
|
-
lines.push(`${String(profile.name)} managed=${String(profile.managed)} active=${String(profile.isActive)} source=${String(profile.source)} model=${String(profile.model ?? "")} modelProvider=${String(profile.modelProvider ?? "")} baseUrl=${String(profile.baseUrl ?? "")}`);
|
|
135
|
+
lines.push(`${String(profile.name)} managed=${String(profile.managed)} active=${String(profile.isActive)} source=${String(profile.source)} model=${String(profile.model ?? "")} modelProvider=${String(profile.modelProvider ?? "")} baseUrl=${String(profile.baseUrl ?? "")} envKey=${String(profile.envKey ?? "")}`);
|
|
130
136
|
}
|
|
131
137
|
break;
|
|
132
138
|
}
|
|
@@ -139,8 +145,8 @@ function renderHumanSuccess(command, data, warnings) {
|
|
|
139
145
|
}
|
|
140
146
|
case "switch":
|
|
141
147
|
lines.push(`Switched to provider ${String(data?.provider ?? "")} using profile ${String(data?.profile ?? "")}.`);
|
|
148
|
+
lines.push(`envKey: ${String(data?.envKey ?? "")}`);
|
|
142
149
|
lines.push(`Backup: ${String(data?.backupPath ?? "")}`);
|
|
143
|
-
lines.push(`Login performed: ${String(data?.loginPerformed ?? false)}`);
|
|
144
150
|
break;
|
|
145
151
|
case "import":
|
|
146
152
|
lines.push(`Imported providers from file using mode ${String(data?.mode ?? "replace")}. Backup: ${String(data?.backupPath ?? "")}`);
|
|
@@ -148,12 +154,21 @@ function renderHumanSuccess(command, data, warnings) {
|
|
|
148
154
|
case "export":
|
|
149
155
|
lines.push(`Exported providers to ${String(data?.exportedTo ?? "")}.`);
|
|
150
156
|
break;
|
|
151
|
-
case "
|
|
152
|
-
lines.push(`Initialized
|
|
157
|
+
case "init":
|
|
158
|
+
lines.push(`Initialized Codex directory ${String(data?.codexDir ?? "")}.`);
|
|
159
|
+
lines.push(`Created codexDir: ${String(data?.createdCodexDir ?? false)}`);
|
|
160
|
+
lines.push(`Created providers.json: ${String(data?.createdProvidersFile ?? false)}`);
|
|
161
|
+
lines.push(`providersAlreadyExisted: ${String(data?.providersAlreadyExisted ?? false)}`);
|
|
162
|
+
break;
|
|
163
|
+
case "migrate":
|
|
164
|
+
lines.push(`Migrated providers in ${String(data?.codexDir ?? "")} using ${String(data?.strategy ?? "")}.`);
|
|
153
165
|
lines.push(`Providers initialized: ${String(data?.providersInitialized ?? 0)}`);
|
|
154
166
|
lines.push(`Doctor healthy: ${String(data?.doctor?.healthy ?? false)}`);
|
|
155
167
|
lines.push(`Backup: ${String(data?.backupPath ?? "")}`);
|
|
156
168
|
break;
|
|
169
|
+
case "setup":
|
|
170
|
+
lines.push("setup is deprecated. Use `codexs init` or `codexs migrate`.");
|
|
171
|
+
break;
|
|
157
172
|
case "edit":
|
|
158
173
|
lines.push(`Updated provider ${String(data?.provider ?? "")}. Backup: ${String(data?.backupPath ?? "")}`);
|
|
159
174
|
lines.push(`Updated fields: ${Array.isArray(data?.updatedFields) ? (data?.updatedFields).join(", ") : ""}`);
|
package/dist/cli/prompt.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createPromptRuntime = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Compatibility facade that re-exports the CLI prompt runtime types and factory.
|
|
6
|
+
*/
|
|
4
7
|
var prompt_1 = require("../interaction/prompt");
|
|
5
8
|
Object.defineProperty(exports, "createPromptRuntime", { enumerable: true, get: function () { return prompt_1.createPromptRuntime; } });
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ const args_1 = require("./commands/args");
|
|
|
9
9
|
const help_1 = require("./commands/help");
|
|
10
10
|
const errors_1 = require("./domain/errors");
|
|
11
11
|
const output_1 = require("./cli/output");
|
|
12
|
-
const VERSION = "0.0.
|
|
12
|
+
const VERSION = "0.0.8";
|
|
13
13
|
/**
|
|
14
14
|
* Prints the command help text to stdout.
|
|
15
15
|
*/
|
|
@@ -40,6 +40,7 @@ const edit_provider_1 = require("../app/edit-provider");
|
|
|
40
40
|
const export_providers_1 = require("../app/export-providers");
|
|
41
41
|
const get_current_profile_1 = require("../app/get-current-profile");
|
|
42
42
|
const get_status_1 = require("../app/get-status");
|
|
43
|
+
const init_codex_1 = require("../app/init-codex");
|
|
43
44
|
const import_providers_1 = require("../app/import-providers");
|
|
44
45
|
const list_config_profiles_1 = require("../app/list-config-profiles");
|
|
45
46
|
const list_backups_1 = require("../app/list-backups");
|
|
@@ -57,6 +58,7 @@ const providers_1 = require("../domain/providers");
|
|
|
57
58
|
const add_interactive_1 = require("../interaction/add-interactive");
|
|
58
59
|
const interactive_1 = require("../interaction/interactive");
|
|
59
60
|
const prompt_1 = require("../interaction/prompt");
|
|
61
|
+
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
60
62
|
const config_repo_1 = require("../storage/config-repo");
|
|
61
63
|
const codex_paths_1 = require("../storage/codex-paths");
|
|
62
64
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
@@ -87,7 +89,54 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
87
89
|
case "current":
|
|
88
90
|
return (0, get_current_profile_1.getCurrentProfile)(paths.configPath);
|
|
89
91
|
case "status":
|
|
90
|
-
return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath);
|
|
92
|
+
return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath, paths.authPath);
|
|
93
|
+
case "init": {
|
|
94
|
+
let codexDir = ctx.options.codexDir;
|
|
95
|
+
const candidates = (0, config_repo_1.findCodexDirCandidates)(ctx.options.codexDirExplicit ? ctx.options.codexDir : null);
|
|
96
|
+
if (!ctx.options.codexDirExplicit) {
|
|
97
|
+
if (candidates.length > 1) {
|
|
98
|
+
if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
99
|
+
throw (0, errors_1.cliError)("CODEX_DIR_AMBIGUOUS", "Multiple Codex directories were found.", {
|
|
100
|
+
candidates,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
|
|
104
|
+
}
|
|
105
|
+
else if (candidates.length === 0) {
|
|
106
|
+
if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
107
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "No Codex directory could be found.", {
|
|
108
|
+
codexDir: ctx.options.codexDir,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
codexDir = candidates[0];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
setupPaths = (0, codex_paths_1.createCodexPaths)(codexDir);
|
|
118
|
+
let createCodexDir = false;
|
|
119
|
+
if (!fs.existsSync(setupPaths.codexDir)) {
|
|
120
|
+
if (!(0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
121
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "The requested Codex directory does not exist.", {
|
|
122
|
+
codexDir: setupPaths.codexDir,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
createCodexDir = await (0, interactive_1.confirmCreateCodexDir)(runtime, setupPaths.codexDir);
|
|
126
|
+
if (!createCodexDir) {
|
|
127
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "The requested Codex directory does not exist.", {
|
|
128
|
+
codexDir: setupPaths.codexDir,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return (0, init_codex_1.initCodex)({
|
|
133
|
+
codexDir: setupPaths.codexDir,
|
|
134
|
+
providersPath: setupPaths.providersPath,
|
|
135
|
+
configPath: setupPaths.configPath,
|
|
136
|
+
authPath: setupPaths.authPath,
|
|
137
|
+
createCodexDir,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
91
140
|
case "config-show":
|
|
92
141
|
return (0, show_config_1.showConfig)({
|
|
93
142
|
configPath: paths.configPath,
|
|
@@ -107,6 +156,9 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
107
156
|
if (!providerName) {
|
|
108
157
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for switch command.");
|
|
109
158
|
}
|
|
159
|
+
if ((0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk")) {
|
|
160
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--install-copilot-sdk is only supported with add --copilot.");
|
|
161
|
+
}
|
|
110
162
|
return (0, switch_provider_1.switchProvider)({
|
|
111
163
|
codexDir: paths.codexDir,
|
|
112
164
|
backupsDir: paths.backupsDir,
|
|
@@ -115,7 +167,6 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
115
167
|
providersPath: paths.providersPath,
|
|
116
168
|
authPath: paths.authPath,
|
|
117
169
|
providerName,
|
|
118
|
-
noLogin: (0, args_1.hasFlag)(parsed.commandOptions, "--no-login"),
|
|
119
170
|
});
|
|
120
171
|
}
|
|
121
172
|
case "import": {
|
|
@@ -130,6 +181,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
130
181
|
const imported = (0, providers_1.validateProvidersShape)(JSON.parse(fs.readFileSync(sourceFile, "utf8")));
|
|
131
182
|
const current = (0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath);
|
|
132
183
|
const next = merge ? (0, providers_repo_1.mergeProviders)(current, imported) : imported;
|
|
184
|
+
// Precompute orphaned references during confirmation so the interactive path fails before mutation.
|
|
133
185
|
(0, config_1.buildManagedProfileViews)(document, next)
|
|
134
186
|
.filter((view) => view.source === "orphaned-reference")
|
|
135
187
|
.map((view) => view.name)
|
|
@@ -172,8 +224,23 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
172
224
|
let model = (0, args_1.getSingleOption)(parsed.commandOptions, "--model", false);
|
|
173
225
|
let note = (0, args_1.getSingleOption)(parsed.commandOptions, "--note", false);
|
|
174
226
|
let tags = parsed.commandOptions.get("--tag") ?? [];
|
|
175
|
-
|
|
176
|
-
|
|
227
|
+
let createProfile = (0, args_1.hasFlag)(parsed.commandOptions, "--create-profile");
|
|
228
|
+
const copilot = (0, args_1.hasFlag)(parsed.commandOptions, "--copilot");
|
|
229
|
+
const bridgeHost = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-host", false);
|
|
230
|
+
const bridgePortValue = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-port", false);
|
|
231
|
+
const bridgeApiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-api-key", false);
|
|
232
|
+
let installCopilotSdk = (0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk");
|
|
233
|
+
const bridgePort = bridgePortValue ? Number(bridgePortValue) : null;
|
|
234
|
+
if (copilot && apiKey) {
|
|
235
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--copilot does not allow --api-key. Use --bridge-api-key for the local bridge secret.");
|
|
236
|
+
}
|
|
237
|
+
if (bridgePortValue && (!Number.isInteger(bridgePort) || bridgePort === null || bridgePort <= 0)) {
|
|
238
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--bridge-port must be a positive integer.");
|
|
239
|
+
}
|
|
240
|
+
if (copilot && !installCopilotSdk && (0, interactive_1.canPrompt)(runtime, ctx.options.json) && !(0, copilot_installer_1.probeCopilotSdkInstall)().installed) {
|
|
241
|
+
installCopilotSdk = await runtime.confirmAction("The optional Copilot SDK runtime is not installed. Install it now?");
|
|
242
|
+
}
|
|
243
|
+
if (!providerName || !profile || (!apiKey && !copilot)) {
|
|
177
244
|
if (ctx.options.json || !runtime.isInteractive()) {
|
|
178
245
|
throw (0, add_interactive_1.createNonInteractiveAddError)();
|
|
179
246
|
}
|
|
@@ -184,13 +251,15 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
184
251
|
baseUrl,
|
|
185
252
|
note,
|
|
186
253
|
tags,
|
|
187
|
-
}, (candidate) => Boolean((0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[candidate]));
|
|
254
|
+
}, (candidate) => Boolean((0, providers_repo_1.readProvidersFileIfExists)(paths.providersPath).providers[candidate]), (candidate) => Boolean((0, config_repo_1.readStructuredConfig)(paths.configPath).profiles.find((profileView) => profileView.name === candidate)));
|
|
188
255
|
providerName = prompted.providerName;
|
|
189
256
|
profile = prompted.profile;
|
|
190
257
|
apiKey = prompted.apiKey;
|
|
258
|
+
model = prompted.model ?? null;
|
|
191
259
|
baseUrl = prompted.baseUrl ?? null;
|
|
192
260
|
note = prompted.note ?? null;
|
|
193
261
|
tags = prompted.tags;
|
|
262
|
+
createProfile = createProfile || prompted.createProfile;
|
|
194
263
|
}
|
|
195
264
|
return (0, add_provider_1.addProvider)({
|
|
196
265
|
codexDir: paths.codexDir,
|
|
@@ -198,14 +267,21 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
198
267
|
latestBackupPath: paths.latestBackupPath,
|
|
199
268
|
providersPath: paths.providersPath,
|
|
200
269
|
configPath: paths.configPath,
|
|
270
|
+
authPath: paths.authPath,
|
|
201
271
|
providerName,
|
|
202
272
|
profile,
|
|
203
|
-
apiKey,
|
|
273
|
+
apiKey: apiKey ?? "",
|
|
204
274
|
baseUrl,
|
|
205
275
|
model,
|
|
206
276
|
note,
|
|
207
277
|
tags,
|
|
208
278
|
createProfile,
|
|
279
|
+
copilot,
|
|
280
|
+
bridgeHost,
|
|
281
|
+
bridgePort,
|
|
282
|
+
bridgeApiKey,
|
|
283
|
+
installCopilotSdk,
|
|
284
|
+
interactive: (0, interactive_1.canPrompt)(runtime, ctx.options.json),
|
|
209
285
|
});
|
|
210
286
|
}
|
|
211
287
|
case "edit": {
|
|
@@ -235,6 +311,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
235
311
|
if (!provider) {
|
|
236
312
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${providerName}" was not found.`);
|
|
237
313
|
}
|
|
314
|
+
// Prompted edit starts from the stored record so blank answers can safely preserve current values.
|
|
238
315
|
const prompted = await (0, interactive_1.collectEditInput)(runtime, provider);
|
|
239
316
|
profile = prompted.profile;
|
|
240
317
|
apiKey = prompted.apiKey;
|
|
@@ -251,6 +328,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
251
328
|
latestBackupPath: paths.latestBackupPath,
|
|
252
329
|
providersPath: paths.providersPath,
|
|
253
330
|
configPath: paths.configPath,
|
|
331
|
+
authPath: paths.authPath,
|
|
254
332
|
providerName,
|
|
255
333
|
profile,
|
|
256
334
|
apiKey,
|
|
@@ -293,8 +371,9 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
293
371
|
codexDir: paths.codexDir,
|
|
294
372
|
configPath: paths.configPath,
|
|
295
373
|
providersPath: paths.providersPath,
|
|
374
|
+
authPath: paths.authPath,
|
|
296
375
|
});
|
|
297
|
-
case "
|
|
376
|
+
case "migrate": {
|
|
298
377
|
let codexDir = ctx.options.codexDir;
|
|
299
378
|
const candidates = (0, config_repo_1.findCodexDirCandidates)(ctx.options.codexDirExplicit ? ctx.options.codexDir : null);
|
|
300
379
|
if (!ctx.options.codexDirExplicit) {
|
|
@@ -304,6 +383,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
304
383
|
candidates,
|
|
305
384
|
});
|
|
306
385
|
}
|
|
386
|
+
// Ambiguous auto-discovery must be resolved before path-dependent flags are interpreted.
|
|
307
387
|
codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
|
|
308
388
|
}
|
|
309
389
|
else if (candidates.length === 0) {
|
|
@@ -320,7 +400,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
320
400
|
const overwrite = (0, args_1.hasFlag)(parsed.commandOptions, "--overwrite");
|
|
321
401
|
const merge = (0, args_1.hasFlag)(parsed.commandOptions, "--merge");
|
|
322
402
|
if (overwrite && merge) {
|
|
323
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "
|
|
403
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate does not allow both --merge and --overwrite.");
|
|
324
404
|
}
|
|
325
405
|
let strategy = overwrite ? "overwrite" : merge ? "merge" : null;
|
|
326
406
|
const providersExists = fs.existsSync(setupPaths.providersPath);
|
|
@@ -338,11 +418,12 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
338
418
|
}
|
|
339
419
|
const document = (0, config_repo_1.readStructuredConfig)(setupPaths.configPath);
|
|
340
420
|
const adoptableProfiles = (0, config_1.buildManagedProfileViews)(document, null)
|
|
341
|
-
.filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl)
|
|
421
|
+
.filter((view) => view.source === "unmanaged" && view.model && view.modelProvider === view.name && view.baseUrl && view.envKey)
|
|
342
422
|
.map((view) => ({
|
|
343
423
|
name: view.name,
|
|
344
424
|
model: view.model,
|
|
345
425
|
baseUrl: view.baseUrl,
|
|
426
|
+
envKey: view.envKey,
|
|
346
427
|
}))
|
|
347
428
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
348
429
|
const selectedProfiles = Array.from((0, config_repo_1.listConfigProfiles)(setupPaths.configPath)).sort();
|
|
@@ -350,20 +431,29 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
350
431
|
let providerDetailsByProfile = {};
|
|
351
432
|
if ((0, interactive_1.canPrompt)(runtime, ctx.options.json)) {
|
|
352
433
|
adoptProfiles = await (0, interactive_1.chooseSetupProfiles)(runtime, adoptableProfiles);
|
|
353
|
-
|
|
434
|
+
// Defaults are derived from config.toml so interactive setup only asks for missing provider metadata.
|
|
435
|
+
providerDetailsByProfile = await (0, interactive_1.collectSetupProviderDetails)(runtime, adoptProfiles, adoptableProfiles.reduce((accumulator, profile) => {
|
|
436
|
+
accumulator[profile.name] = {
|
|
437
|
+
providerName: profile.name,
|
|
438
|
+
envKey: profile.envKey,
|
|
439
|
+
baseUrl: profile.baseUrl,
|
|
440
|
+
};
|
|
441
|
+
return accumulator;
|
|
442
|
+
}, {}));
|
|
354
443
|
}
|
|
355
444
|
else {
|
|
356
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "
|
|
445
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "migrate currently requires an interactive TTY to choose adoptable profiles and collect provider details.", {
|
|
357
446
|
adoptableProfiles,
|
|
358
447
|
availableProfiles: selectedProfiles,
|
|
359
|
-
suggestion: "Run `codexs
|
|
448
|
+
suggestion: "Run `codexs migrate` in an interactive terminal. Non-interactive migrate flags for profile selection and provider secrets are not available in this release.",
|
|
360
449
|
});
|
|
361
450
|
}
|
|
362
|
-
return (0, setup_codex_1.
|
|
451
|
+
return (0, setup_codex_1.migrateCodex)({
|
|
363
452
|
codexDirOption: ctx.options.codexDir,
|
|
364
453
|
codexDir: setupPaths.codexDir,
|
|
365
454
|
configPath: setupPaths.configPath,
|
|
366
455
|
providersPath: setupPaths.providersPath,
|
|
456
|
+
authPath: setupPaths.authPath,
|
|
367
457
|
backupsDir: setupPaths.backupsDir,
|
|
368
458
|
latestBackupPath: setupPaths.latestBackupPath,
|
|
369
459
|
strategy: strategy ?? "overwrite",
|
|
@@ -371,6 +461,10 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
371
461
|
providerDetailsByProfile,
|
|
372
462
|
});
|
|
373
463
|
}
|
|
464
|
+
case "setup":
|
|
465
|
+
throw (0, errors_1.cliError)("COMMAND_DEPRECATED", "setup has been split into init and migrate.", {
|
|
466
|
+
replacements: ["init", "migrate"],
|
|
467
|
+
});
|
|
374
468
|
case "backups-list":
|
|
375
469
|
return (0, list_backups_1.listBackupEntries)(paths.backupsDir);
|
|
376
470
|
case "rollback":
|
package/dist/commands/help.js
CHANGED
|
@@ -59,7 +59,8 @@ function buildHelpText(commandName) {
|
|
|
59
59
|
" rollback restores files from a managed backup.",
|
|
60
60
|
"",
|
|
61
61
|
"Examples:",
|
|
62
|
-
" codexs
|
|
62
|
+
" codexs init",
|
|
63
|
+
" codexs migrate",
|
|
63
64
|
" codexs list",
|
|
64
65
|
" codexs switch",
|
|
65
66
|
" codexs add packycode --profile packycode --api-key sk-xxx",
|