@minniexcode/codex-switch 0.0.10 → 0.0.11
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 +52 -15
- package/README.CN.md +81 -48
- package/README.md +75 -34
- package/dist/app/add-provider.js +29 -15
- package/dist/app/bridge.js +15 -14
- package/dist/app/edit-provider.js +1 -1
- package/dist/app/get-status.js +13 -6
- package/dist/app/import-providers.js +1 -1
- package/dist/app/init-codex.js +13 -14
- package/dist/app/remove-provider.js +1 -1
- package/dist/app/run-doctor.js +12 -5
- package/dist/app/run-mutation.js +3 -2
- package/dist/app/setup-codex.js +3 -1
- package/dist/app/switch-provider.js +11 -13
- package/dist/cli.js +34 -2
- package/dist/commands/args.js +2 -2
- package/dist/commands/dispatch.js +40 -0
- package/dist/commands/handlers.js +121 -156
- package/dist/commands/help.js +2 -0
- package/dist/commands/registry.js +28 -9
- package/dist/domain/backups.js +4 -4
- package/dist/domain/config.js +110 -5
- package/dist/domain/providers.js +12 -0
- package/dist/domain/runtime-state.js +81 -5
- package/dist/runtime/copilot-adapter.js +12 -12
- package/dist/runtime/copilot-bridge.js +392 -44
- package/dist/runtime/copilot-cli.js +84 -12
- package/dist/runtime/copilot-installer.js +10 -9
- package/dist/runtime/copilot-sdk-loader.js +5 -5
- package/dist/storage/backup-repo.js +4 -4
- package/dist/storage/codex-paths.js +34 -8
- package/dist/storage/lock-repo.js +2 -4
- package/dist/storage/runtime-state-repo.js +14 -13
- package/dist/storage/tool-config-repo.js +111 -0
- package/docs/Design/codex-switch-v0.0.11-design.md +824 -0
- package/docs/PRD/codex-switch-prd-v0.0.11.md +577 -0
- package/docs/cli-usage.md +166 -295
- package/package.json +1 -1
|
@@ -71,6 +71,10 @@ const args_1 = require("./args");
|
|
|
71
71
|
* Executes one command handler selected from the shared command registry.
|
|
72
72
|
*/
|
|
73
73
|
async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.createPromptRuntime)()) {
|
|
74
|
+
const packageVersion = require("../../package.json").version ?? "0.0.0";
|
|
75
|
+
if (!ctx.options.codexDir) {
|
|
76
|
+
throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "No Codex directory could be resolved.");
|
|
77
|
+
}
|
|
74
78
|
let setupPaths = (0, codex_paths_1.createCodexPaths)(ctx.options.codexDir);
|
|
75
79
|
const paths = setupPaths;
|
|
76
80
|
switch (ctx.command) {
|
|
@@ -93,12 +97,17 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
93
97
|
case "current":
|
|
94
98
|
return (0, get_current_profile_1.getCurrentProfile)(paths.configPath);
|
|
95
99
|
case "status":
|
|
96
|
-
return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath, paths.authPath
|
|
100
|
+
return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath, paths.authPath, {
|
|
101
|
+
runtimeDir: paths.runtimeDir,
|
|
102
|
+
runtimesDir: paths.runtimesDir,
|
|
103
|
+
});
|
|
97
104
|
case "bridge-start": {
|
|
98
105
|
const providerName = parsed.positionals[0] ?? null;
|
|
99
106
|
return (0, bridge_1.startBridge)({
|
|
100
107
|
providersPath: paths.providersPath,
|
|
101
108
|
configPath: paths.configPath,
|
|
109
|
+
runtimeDir: paths.runtimeDir,
|
|
110
|
+
runtimesDir: paths.runtimesDir,
|
|
102
111
|
providerName,
|
|
103
112
|
runtime,
|
|
104
113
|
json: ctx.options.json,
|
|
@@ -109,6 +118,8 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
109
118
|
return (0, bridge_1.stopBridge)({
|
|
110
119
|
providersPath: paths.providersPath,
|
|
111
120
|
configPath: paths.configPath,
|
|
121
|
+
runtimeDir: paths.runtimeDir,
|
|
122
|
+
runtimesDir: paths.runtimesDir,
|
|
112
123
|
providerName,
|
|
113
124
|
runtime,
|
|
114
125
|
json: ctx.options.json,
|
|
@@ -119,57 +130,103 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
119
130
|
return (0, bridge_1.statusBridge)({
|
|
120
131
|
providersPath: paths.providersPath,
|
|
121
132
|
configPath: paths.configPath,
|
|
133
|
+
runtimeDir: paths.runtimeDir,
|
|
134
|
+
runtimesDir: paths.runtimesDir,
|
|
122
135
|
providerName,
|
|
123
136
|
runtime,
|
|
124
137
|
json: ctx.options.json,
|
|
125
138
|
});
|
|
126
139
|
}
|
|
127
140
|
case "init": {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
codexDir = await (0, interactive_1.chooseCodexDir)(runtime, candidates);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
codexDir = candidates[0];
|
|
149
|
-
}
|
|
141
|
+
return (0, init_codex_1.initCodex)({
|
|
142
|
+
toolHomeDir: setupPaths.toolHomeDir,
|
|
143
|
+
toolConfigPath: setupPaths.toolConfigPath,
|
|
144
|
+
providersPath: setupPaths.providersPath,
|
|
145
|
+
version: packageVersion,
|
|
146
|
+
defaultCodexDir: ctx.options.codexDirExplicit ? setupPaths.codexDir : null,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
case "login": {
|
|
150
|
+
const upstream = (parsed.positionals[0] ?? "").toLowerCase();
|
|
151
|
+
if (ctx.options.json || !runtime.isInteractive()) {
|
|
152
|
+
throw (0, errors_1.cliError)("COPILOT_LOGIN_REQUIRES_TTY", "login requires an interactive TTY and does not support --json.");
|
|
153
|
+
}
|
|
154
|
+
if (upstream !== "copilot" && upstream !== "github-copilot") {
|
|
155
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", `Unsupported upstream "${parsed.positionals[0] ?? ""}".`, {
|
|
156
|
+
supportedUpstreams: ["copilot", "github-copilot"],
|
|
157
|
+
});
|
|
150
158
|
}
|
|
151
|
-
|
|
152
|
-
let
|
|
153
|
-
if (!
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
159
|
+
const installed = (0, copilot_installer_1.probeCopilotSdkInstall)(paths.runtimesDir);
|
|
160
|
+
let installedNow = false;
|
|
161
|
+
if (!installed.installed) {
|
|
162
|
+
const confirmInstall = await runtime.confirmAction("The Copilot SDK runtime is not installed. Install it now?", {
|
|
163
|
+
defaultValue: true,
|
|
164
|
+
});
|
|
165
|
+
if (!confirmInstall) {
|
|
166
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
|
|
167
|
+
installDir: installed.installDir,
|
|
168
|
+
packageName: installed.packageName,
|
|
157
169
|
});
|
|
158
170
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
runtime.writeLine("Installing Copilot SDK runtime...");
|
|
172
|
+
(0, copilot_installer_1.installCopilotSdk)(paths.runtimesDir);
|
|
173
|
+
installedNow = true;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
await (0, copilot_adapter_1.readCopilotAuthState)(paths.runtimesDir);
|
|
177
|
+
return {
|
|
178
|
+
data: {
|
|
179
|
+
upstream: "github-copilot",
|
|
180
|
+
sdkInstalled: true,
|
|
181
|
+
sdkInstalledNow: installedNow,
|
|
182
|
+
authReady: true,
|
|
183
|
+
loginLaunched: false,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
const normalized = (0, errors_1.normalizeError)(error);
|
|
189
|
+
if (normalized.code !== "COPILOT_AUTH_REQUIRED") {
|
|
190
|
+
throw error;
|
|
164
191
|
}
|
|
165
192
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
const availability = (0, copilot_cli_1.checkCopilotCliAvailable)(paths.runtimesDir);
|
|
194
|
+
if (!availability.ok) {
|
|
195
|
+
throw (0, errors_1.cliError)("COPILOT_CLI_MISSING", "The official Copilot CLI could not be resolved from the installed runtime or PATH.", {
|
|
196
|
+
cause: availability.cause,
|
|
197
|
+
source: availability.source ?? null,
|
|
198
|
+
command: availability.command ?? null,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
(0, copilot_cli_1.runCopilotLogin)({ runtimesDir: paths.runtimesDir });
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw (0, errors_1.cliError)("COPILOT_LOGIN_LAUNCH_FAILED", "Failed to launch `copilot login`.", {
|
|
206
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
await (0, copilot_adapter_1.readCopilotAuthState)(paths.runtimesDir);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const normalized = (0, errors_1.normalizeError)(error);
|
|
214
|
+
if (normalized.code === "COPILOT_AUTH_REQUIRED") {
|
|
215
|
+
throw (0, errors_1.cliError)("COPILOT_LOGIN_RECHECK_FAILED", "Copilot login completed but auth readiness recheck still failed.", {
|
|
216
|
+
...(normalized.details ?? {}),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
data: {
|
|
223
|
+
upstream: "github-copilot",
|
|
224
|
+
sdkInstalled: true,
|
|
225
|
+
sdkInstalledNow: installedNow,
|
|
226
|
+
authReady: true,
|
|
227
|
+
loginLaunched: true,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
173
230
|
}
|
|
174
231
|
case "config-show":
|
|
175
232
|
return (0, show_config_1.showConfig)({
|
|
@@ -191,15 +248,20 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
191
248
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for switch command.");
|
|
192
249
|
}
|
|
193
250
|
if ((0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk")) {
|
|
194
|
-
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--install-copilot-sdk is
|
|
251
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--install-copilot-sdk is no longer supported with switch. Run `codexs login copilot` instead.", {
|
|
252
|
+
suggestion: "Run `codexs login copilot` first, then rerun switch without --install-copilot-sdk.",
|
|
253
|
+
});
|
|
195
254
|
}
|
|
196
255
|
return (0, switch_provider_1.switchProvider)({
|
|
197
256
|
codexDir: paths.codexDir,
|
|
257
|
+
lockPath: paths.lockPath,
|
|
198
258
|
backupsDir: paths.backupsDir,
|
|
199
259
|
latestBackupPath: paths.latestBackupPath,
|
|
200
260
|
configPath: paths.configPath,
|
|
201
261
|
providersPath: paths.providersPath,
|
|
202
262
|
authPath: paths.authPath,
|
|
263
|
+
runtimeDir: paths.runtimeDir,
|
|
264
|
+
runtimesDir: paths.runtimesDir,
|
|
203
265
|
providerName,
|
|
204
266
|
});
|
|
205
267
|
}
|
|
@@ -223,6 +285,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
223
285
|
}
|
|
224
286
|
return (0, import_providers_1.importProviders)({
|
|
225
287
|
codexDir: paths.codexDir,
|
|
288
|
+
lockPath: paths.lockPath,
|
|
226
289
|
backupsDir: paths.backupsDir,
|
|
227
290
|
latestBackupPath: paths.latestBackupPath,
|
|
228
291
|
providersPath: paths.providersPath,
|
|
@@ -263,21 +326,19 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
263
326
|
let bridgeHost = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-host", false);
|
|
264
327
|
const bridgePortValue = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-port", false);
|
|
265
328
|
let bridgeApiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-api-key", false);
|
|
266
|
-
|
|
329
|
+
const installCopilotSdk = (0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk");
|
|
267
330
|
let bridgePort = bridgePortValue ? Number(bridgePortValue) : null;
|
|
268
331
|
if (copilot && apiKey) {
|
|
269
332
|
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--copilot does not allow --api-key. Use --bridge-api-key for the local bridge secret.");
|
|
270
333
|
}
|
|
334
|
+
if (copilot && installCopilotSdk) {
|
|
335
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--install-copilot-sdk is no longer supported with add --copilot. Run `codexs login copilot` instead.", {
|
|
336
|
+
suggestion: "Run `codexs login copilot` first, then rerun add --copilot.",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
271
339
|
if (bridgePortValue && (!Number.isInteger(bridgePort) || bridgePort === null || bridgePort <= 0)) {
|
|
272
340
|
throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--bridge-port must be a positive integer.");
|
|
273
341
|
}
|
|
274
|
-
if (copilot) {
|
|
275
|
-
installCopilotSdk = await ensureCopilotReadyForAdd({
|
|
276
|
-
runtime,
|
|
277
|
-
json: ctx.options.json,
|
|
278
|
-
installCopilotSdk,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
342
|
if (!providerName || !profile || (!apiKey && !copilot)) {
|
|
282
343
|
if (ctx.options.json || !runtime.isInteractive()) {
|
|
283
344
|
throw (0, add_interactive_1.createNonInteractiveAddError)({ copilot });
|
|
@@ -327,6 +388,9 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
327
388
|
}
|
|
328
389
|
return (0, add_provider_1.addProvider)({
|
|
329
390
|
codexDir: paths.codexDir,
|
|
391
|
+
toolHomeDir: paths.toolHomeDir,
|
|
392
|
+
lockPath: paths.lockPath,
|
|
393
|
+
runtimesDir: paths.runtimesDir,
|
|
330
394
|
backupsDir: paths.backupsDir,
|
|
331
395
|
latestBackupPath: paths.latestBackupPath,
|
|
332
396
|
providersPath: paths.providersPath,
|
|
@@ -344,8 +408,6 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
344
408
|
bridgeHost,
|
|
345
409
|
bridgePort,
|
|
346
410
|
bridgeApiKey,
|
|
347
|
-
installCopilotSdk,
|
|
348
|
-
interactive: (0, interactive_1.canPrompt)(runtime, ctx.options.json),
|
|
349
411
|
});
|
|
350
412
|
}
|
|
351
413
|
case "edit": {
|
|
@@ -388,6 +450,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
388
450
|
}
|
|
389
451
|
return (0, edit_provider_1.editProvider)({
|
|
390
452
|
codexDir: paths.codexDir,
|
|
453
|
+
lockPath: paths.lockPath,
|
|
391
454
|
backupsDir: paths.backupsDir,
|
|
392
455
|
latestBackupPath: paths.latestBackupPath,
|
|
393
456
|
providersPath: paths.providersPath,
|
|
@@ -422,6 +485,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
422
485
|
}
|
|
423
486
|
return (0, remove_provider_1.removeProvider)({
|
|
424
487
|
codexDir: paths.codexDir,
|
|
488
|
+
lockPath: paths.lockPath,
|
|
425
489
|
backupsDir: paths.backupsDir,
|
|
426
490
|
latestBackupPath: paths.latestBackupPath,
|
|
427
491
|
providersPath: paths.providersPath,
|
|
@@ -436,6 +500,8 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
436
500
|
configPath: paths.configPath,
|
|
437
501
|
providersPath: paths.providersPath,
|
|
438
502
|
authPath: paths.authPath,
|
|
503
|
+
runtimeDir: paths.runtimeDir,
|
|
504
|
+
runtimesDir: paths.runtimesDir,
|
|
439
505
|
});
|
|
440
506
|
case "migrate": {
|
|
441
507
|
let codexDir = ctx.options.codexDir;
|
|
@@ -531,9 +597,12 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
531
597
|
return (0, setup_codex_1.migrateCodex)({
|
|
532
598
|
codexDirOption: ctx.options.codexDir,
|
|
533
599
|
codexDir: setupPaths.codexDir,
|
|
600
|
+
lockPath: setupPaths.lockPath,
|
|
534
601
|
configPath: setupPaths.configPath,
|
|
535
602
|
providersPath: setupPaths.providersPath,
|
|
536
603
|
authPath: setupPaths.authPath,
|
|
604
|
+
runtimeDir: setupPaths.runtimeDir,
|
|
605
|
+
runtimesDir: setupPaths.runtimesDir,
|
|
537
606
|
backupsDir: setupPaths.backupsDir,
|
|
538
607
|
latestBackupPath: setupPaths.latestBackupPath,
|
|
539
608
|
strategy: strategy ?? "overwrite",
|
|
@@ -563,107 +632,3 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
|
|
|
563
632
|
throw (0, errors_1.cliError)("UNKNOWN_COMMAND", `Unknown command: ${ctx.command}`);
|
|
564
633
|
}
|
|
565
634
|
}
|
|
566
|
-
/**
|
|
567
|
-
* Runs the deterministic Copilot onboarding preflight before any provider persistence.
|
|
568
|
-
*/
|
|
569
|
-
async function ensureCopilotReadyForAdd(args) {
|
|
570
|
-
let installCopilotSdk = args.installCopilotSdk;
|
|
571
|
-
const interactive = (0, interactive_1.canPrompt)(args.runtime, args.json);
|
|
572
|
-
if (interactive) {
|
|
573
|
-
args.runtime.writeLine("Checking Copilot SDK runtime...");
|
|
574
|
-
}
|
|
575
|
-
if (!(0, copilot_installer_1.probeCopilotSdkInstall)().installed) {
|
|
576
|
-
if (!interactive) {
|
|
577
|
-
if (!installCopilotSdk) {
|
|
578
|
-
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
579
|
-
throw (0, errors_1.cliError)("COPILOT_SDK_INSTALL_REQUIRES_TTY", "The optional Copilot SDK runtime is not installed. Pass --install-copilot-sdk when running non-interactively.", {
|
|
580
|
-
installDir: installStatus.installDir,
|
|
581
|
-
packageName: installStatus.packageName,
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
(0, copilot_installer_1.installCopilotSdk)();
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
if (!installCopilotSdk) {
|
|
588
|
-
installCopilotSdk = await args.runtime.confirmAction("The optional Copilot SDK runtime is not installed. Install it now?");
|
|
589
|
-
}
|
|
590
|
-
if (!installCopilotSdk) {
|
|
591
|
-
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
592
|
-
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed. Re-run with --install-copilot-sdk or confirm installation interactively.", {
|
|
593
|
-
installDir: installStatus.installDir,
|
|
594
|
-
packageName: installStatus.packageName,
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
if (interactive) {
|
|
598
|
-
args.runtime.writeLine("Installing Copilot SDK runtime...");
|
|
599
|
-
}
|
|
600
|
-
(0, copilot_installer_1.installCopilotSdk)();
|
|
601
|
-
if (interactive) {
|
|
602
|
-
args.runtime.writeLine("Copilot SDK runtime installed.");
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
if (interactive) {
|
|
607
|
-
args.runtime.writeLine("Checking GitHub Copilot login...");
|
|
608
|
-
}
|
|
609
|
-
try {
|
|
610
|
-
await (0, copilot_adapter_1.readCopilotAuthState)();
|
|
611
|
-
return installCopilotSdk;
|
|
612
|
-
}
|
|
613
|
-
catch (error) {
|
|
614
|
-
const normalized = (0, errors_1.normalizeError)(error);
|
|
615
|
-
if (normalized.code !== "COPILOT_AUTH_REQUIRED") {
|
|
616
|
-
throw error;
|
|
617
|
-
}
|
|
618
|
-
if (!interactive) {
|
|
619
|
-
throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "Copilot authentication is required before the local bridge can be added.", {
|
|
620
|
-
...(normalized.details ?? {}),
|
|
621
|
-
manualLoginCommand: "copilot login",
|
|
622
|
-
suggestion: "Run `copilot login` manually or provide supported Copilot SDK credentials, then rerun add --copilot.",
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
args.runtime.writeLine("GitHub Copilot login is required. Starting official copilot login...");
|
|
626
|
-
let loginLaunchCause;
|
|
627
|
-
try {
|
|
628
|
-
const availability = (0, copilot_cli_1.checkCopilotCliAvailable)();
|
|
629
|
-
if (!availability.ok) {
|
|
630
|
-
throw new Error(availability.cause ?? "copilot CLI is unavailable");
|
|
631
|
-
}
|
|
632
|
-
(0, copilot_cli_1.runCopilotLogin)();
|
|
633
|
-
}
|
|
634
|
-
catch (launchError) {
|
|
635
|
-
loginLaunchCause = launchError instanceof Error ? launchError.message : String(launchError);
|
|
636
|
-
args.runtime.writeLine("Unable to launch the official Copilot login automatically.");
|
|
637
|
-
args.runtime.writeLine("Run this command in the current terminal: copilot login");
|
|
638
|
-
args.runtime.writeLine("GitHub's official device flow should open the browser or show the verification URL and code.");
|
|
639
|
-
}
|
|
640
|
-
args.runtime.writeLine("GitHub Copilot login completed. Rechecking session...");
|
|
641
|
-
const retry = await args.runtime.confirmAction("Recheck GitHub Copilot login now?", {
|
|
642
|
-
defaultValue: true,
|
|
643
|
-
});
|
|
644
|
-
if (!retry) {
|
|
645
|
-
throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "Copilot authentication is required before the local bridge can be added.", {
|
|
646
|
-
...(normalized.details ?? {}),
|
|
647
|
-
manualLoginCommand: "copilot login",
|
|
648
|
-
loginLaunchCause,
|
|
649
|
-
suggestion: "Complete GitHub Copilot login with the official tooling, then rerun add --copilot.",
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
try {
|
|
653
|
-
await (0, copilot_adapter_1.readCopilotAuthState)();
|
|
654
|
-
return installCopilotSdk;
|
|
655
|
-
}
|
|
656
|
-
catch (recheckError) {
|
|
657
|
-
const rechecked = (0, errors_1.normalizeError)(recheckError);
|
|
658
|
-
if (rechecked.code !== "COPILOT_AUTH_REQUIRED") {
|
|
659
|
-
throw recheckError;
|
|
660
|
-
}
|
|
661
|
-
throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "Copilot authentication is required before the local bridge can be added.", {
|
|
662
|
-
...(rechecked.details ?? {}),
|
|
663
|
-
manualLoginCommand: "copilot login",
|
|
664
|
-
loginLaunchCause,
|
|
665
|
-
suggestion: "Complete GitHub Copilot login with the official tooling, then rerun add --copilot.",
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
package/dist/commands/help.js
CHANGED
|
@@ -44,6 +44,7 @@ function buildHelpText(commandName) {
|
|
|
44
44
|
" --version Print the current CLI version.",
|
|
45
45
|
"",
|
|
46
46
|
"Environment:",
|
|
47
|
+
" CODEXS_HOME Override the codex-switch tool home directory.",
|
|
47
48
|
" CODEXS_CODEX_DIR Default Codex directory when --codex-dir is not passed.",
|
|
48
49
|
" NODE_ENV=development defaults to ./dev-codex/local-sandbox when no override is set.",
|
|
49
50
|
"",
|
|
@@ -60,6 +61,7 @@ function buildHelpText(commandName) {
|
|
|
60
61
|
"",
|
|
61
62
|
"Examples:",
|
|
62
63
|
" codexs init",
|
|
64
|
+
" codexs login copilot",
|
|
63
65
|
" codexs migrate",
|
|
64
66
|
" codexs list",
|
|
65
67
|
" codexs switch",
|
|
@@ -88,16 +88,32 @@ exports.COMMANDS = [
|
|
|
88
88
|
tokens: ["init"],
|
|
89
89
|
handler: handlers_1.handleRegisteredCommand,
|
|
90
90
|
group: "write",
|
|
91
|
-
summary: "Initialize
|
|
91
|
+
summary: "Initialize the codex-switch tool home and registry files.",
|
|
92
92
|
usage: ["codexs init [--json] [--codex-dir <path>]"],
|
|
93
93
|
details: [
|
|
94
|
-
"Creates providers.json
|
|
95
|
-
"Does not
|
|
96
|
-
"
|
|
97
|
-
"
|
|
94
|
+
"Creates codex-switch.json and providers.json under the tool home when they do not exist yet.",
|
|
95
|
+
"Does not create or validate config.toml, auth.json, or the target Codex directory.",
|
|
96
|
+
"When --codex-dir is passed explicitly and codex-switch.json does not exist yet, init persists it as defaultCodexDir.",
|
|
97
|
+
"Otherwise init stays scoped to tool-home state and does not persist fallback Codex directory resolution.",
|
|
98
98
|
],
|
|
99
99
|
examples: ["codexs init", "codexs init --json --codex-dir ~/.codex"],
|
|
100
100
|
},
|
|
101
|
+
{
|
|
102
|
+
id: "login",
|
|
103
|
+
tokens: ["login"],
|
|
104
|
+
handler: handlers_1.handleRegisteredCommand,
|
|
105
|
+
group: "write",
|
|
106
|
+
summary: "Complete upstream onboarding for interactive providers such as GitHub Copilot.",
|
|
107
|
+
usage: ["codexs login <upstream>"],
|
|
108
|
+
details: [
|
|
109
|
+
"Currently supports copilot and github-copilot as the same upstream.",
|
|
110
|
+
"Installs the local Copilot SDK under the tool home when needed, then checks login readiness.",
|
|
111
|
+
"When login is not ready, launches the bundled Copilot CLI from the runtime when available, otherwise falls back to PATH, then rechecks before succeeding.",
|
|
112
|
+
"Copilot login is shared across the local Copilot runtime, so logging into a different GitHub account replaces the upstream auth used by all Copilot providers.",
|
|
113
|
+
"Requires an interactive TTY and does not support --json.",
|
|
114
|
+
],
|
|
115
|
+
examples: ["codexs login copilot", "codexs login github-copilot"],
|
|
116
|
+
},
|
|
101
117
|
{
|
|
102
118
|
id: "migrate",
|
|
103
119
|
tokens: ["migrate"],
|
|
@@ -213,13 +229,16 @@ exports.COMMANDS = [
|
|
|
213
229
|
"Confirm API key when prompted interactively because the hidden prompt asks twice before writing.",
|
|
214
230
|
"Interactive tags use preset multi-select only.",
|
|
215
231
|
"Automation and non-TTY environments must pass all required values explicitly.",
|
|
216
|
-
"Creating a missing profile section requires --create-profile together with --model and --base-url.",
|
|
232
|
+
"Creating a missing direct-provider profile section requires --create-profile together with --model and --base-url.",
|
|
233
|
+
"Creating a missing Copilot profile section requires --create-profile together with --model; the local bridge base_url is derived automatically.",
|
|
217
234
|
"Use --copilot to create a GitHub Copilot bridge provider backed by the official SDK.",
|
|
218
|
-
"
|
|
235
|
+
"Copilot providers require SDK install and login readiness to already be satisfied via codexs login copilot.",
|
|
236
|
+
"For Copilot providers, provider apiKey stores only the local bridge secret; upstream GitHub Copilot auth stays shared in the official runtime login.",
|
|
237
|
+
"--install-copilot-sdk is kept only as a rejected compatibility flag that points to codexs login copilot.",
|
|
219
238
|
],
|
|
220
239
|
examples: [
|
|
221
240
|
"codexs add packycode --profile packycode --api-key sk-xxx",
|
|
222
|
-
"codexs add copilot-main --copilot --profile copilot-main
|
|
241
|
+
"codexs add copilot-main --copilot --profile copilot-main",
|
|
223
242
|
"codexs add",
|
|
224
243
|
],
|
|
225
244
|
},
|
|
@@ -234,7 +253,7 @@ exports.COMMANDS = [
|
|
|
234
253
|
"When <provider> is omitted in a TTY, an interactive provider selector is shown.",
|
|
235
254
|
"When <provider> is passed explicitly, switch proceeds directly without extra confirmation.",
|
|
236
255
|
"Direct providers update the active config profile and rewrite auth.json with auth_mode=apikey plus OPENAI_API_KEY.",
|
|
237
|
-
"Copilot bridge providers
|
|
256
|
+
"Copilot bridge providers also rewrite OPENAI_API_KEY to the local bridge secret while managing runtime routing and bridge state.",
|
|
238
257
|
"Copilot bridge providers probe the optional official SDK before switching and fail fast if it is missing.",
|
|
239
258
|
"Backs up config.toml and auth.json and rolls back on failure.",
|
|
240
259
|
],
|
package/dist/domain/backups.js
CHANGED
|
@@ -61,9 +61,6 @@ function validateBackupManifest(input) {
|
|
|
61
61
|
if (typeof manifest.reason !== "string" || manifest.reason.trim() === "") {
|
|
62
62
|
throw new Error("Backup manifest is missing reason.");
|
|
63
63
|
}
|
|
64
|
-
if (typeof manifest.rootDir !== "string" || manifest.rootDir.trim() === "") {
|
|
65
|
-
throw new Error("Backup manifest is missing rootDir.");
|
|
66
|
-
}
|
|
67
64
|
if (typeof manifest.backupDir !== "string" || manifest.backupDir.trim() === "") {
|
|
68
65
|
throw new Error("Backup manifest is missing backupDir.");
|
|
69
66
|
}
|
|
@@ -74,7 +71,10 @@ function validateBackupManifest(input) {
|
|
|
74
71
|
if (!entry || typeof entry !== "object") {
|
|
75
72
|
throw new Error("Backup manifest contains an invalid file entry.");
|
|
76
73
|
}
|
|
77
|
-
if (typeof entry.relativePath !== "string" ||
|
|
74
|
+
if (typeof entry.relativePath !== "string" ||
|
|
75
|
+
typeof entry.restorePath !== "string" ||
|
|
76
|
+
entry.restorePath.trim() === "" ||
|
|
77
|
+
typeof entry.existed !== "boolean") {
|
|
78
78
|
throw new Error("Backup manifest contains an invalid file entry.");
|
|
79
79
|
}
|
|
80
80
|
if (entry.backupFileName !== null && typeof entry.backupFileName !== "string") {
|