@minniexcode/codex-switch 0.0.9 → 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 -13
- package/README.CN.md +94 -39
- package/README.md +75 -33
- package/dist/app/add-provider.js +29 -26
- package/dist/app/bridge.js +15 -15
- package/dist/app/edit-provider.js +2 -18
- package/dist/app/get-status.js +35 -13
- package/dist/app/import-providers.js +1 -1
- package/dist/app/init-codex.js +13 -14
- package/dist/app/list-providers.js +0 -1
- package/dist/app/remove-provider.js +1 -1
- package/dist/app/run-doctor.js +21 -39
- package/dist/app/run-mutation.js +3 -2
- package/dist/app/setup-codex.js +30 -18
- package/dist/app/show-config.js +1 -5
- package/dist/app/switch-provider.js +16 -33
- package/dist/cli/output.js +4 -6
- package/dist/cli.js +35 -3
- package/dist/commands/args.js +2 -2
- package/dist/commands/dispatch.js +40 -0
- package/dist/commands/handlers.js +202 -84
- package/dist/commands/help.js +2 -0
- package/dist/commands/registry.js +33 -12
- package/dist/domain/backups.js +4 -4
- package/dist/domain/config.js +102 -61
- package/dist/domain/providers.js +12 -5
- package/dist/domain/runtime-state.js +81 -4
- package/dist/domain/setup.js +58 -3
- package/dist/interaction/add-interactive.js +55 -1
- package/dist/interaction/interactive.js +1 -5
- package/dist/runtime/copilot-adapter.js +56 -13
- package/dist/runtime/copilot-bridge.js +392 -44
- package/dist/runtime/copilot-cli.js +142 -0
- package/dist/runtime/copilot-installer.js +59 -11
- package/dist/runtime/copilot-sdk-loader.js +5 -5
- package/dist/storage/auth-repo.js +28 -77
- package/dist/storage/backup-repo.js +4 -4
- package/dist/storage/codex-paths.js +34 -8
- package/dist/storage/config-repo.js +1 -36
- package/dist/storage/lock-repo.js +2 -4
- package/dist/storage/runtime-state-repo.js +43 -10
- package/dist/storage/tool-config-repo.js +111 -0
- package/docs/Design/codex-switch-copilot-integration-design.md +517 -0
- package/docs/Design/codex-switch-v0.0.10-design.md +669 -0
- package/docs/Design/codex-switch-v0.0.11-design.md +824 -0
- package/docs/PRD/codex-switch-prd-v0.0.10.md +406 -0
- package/docs/PRD/codex-switch-prd-v0.0.11.md +577 -0
- package/docs/cli-usage.md +166 -271
- package/docs/codex-switch-product-overview.md +2 -2
- package/docs/codex-switch-technical-architecture.md +6 -5
- package/package.json +1 -1
package/dist/domain/config.js
CHANGED
|
@@ -41,7 +41,6 @@ exports.parseStructuredConfig = parseStructuredConfig;
|
|
|
41
41
|
exports.buildManagedProfileViews = buildManagedProfileViews;
|
|
42
42
|
exports.collectConfigConsistencyIssues = collectConfigConsistencyIssues;
|
|
43
43
|
exports.validateManagedProfileCreation = validateManagedProfileCreation;
|
|
44
|
-
exports.buildManagedProfileEnvKey = buildManagedProfileEnvKey;
|
|
45
44
|
exports.planProfileLifecycleOutcome = planProfileLifecycleOutcome;
|
|
46
45
|
exports.planConfigMutation = planConfigMutation;
|
|
47
46
|
exports.applyPatchOperations = applyPatchOperations;
|
|
@@ -118,10 +117,15 @@ function parseStructuredConfig(configContent) {
|
|
|
118
117
|
name: modelProviderHeaderMatch[1],
|
|
119
118
|
sectionStart: line.start,
|
|
120
119
|
sectionEnd: configContent.length,
|
|
120
|
+
managedFieldInsertIndex: configContent.length,
|
|
121
121
|
baseUrlValueRange: null,
|
|
122
122
|
baseUrl: null,
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
nameValueRange: null,
|
|
124
|
+
providerName: null,
|
|
125
|
+
requiresOpenAiAuthValueRange: null,
|
|
126
|
+
requiresOpenAiAuth: null,
|
|
127
|
+
wireApiValueRange: null,
|
|
128
|
+
wireApi: null,
|
|
125
129
|
};
|
|
126
130
|
modelProviders.push(currentModelProvider);
|
|
127
131
|
inRoot = false;
|
|
@@ -176,12 +180,28 @@ function parseStructuredConfig(configContent) {
|
|
|
176
180
|
end: line.start + baseUrlMatch.valueEnd,
|
|
177
181
|
};
|
|
178
182
|
}
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
currentModelProvider.
|
|
182
|
-
currentModelProvider.
|
|
183
|
-
start: line.start +
|
|
184
|
-
end: line.start +
|
|
183
|
+
const nameMatch = matchKeyValueLine(line.content, "name");
|
|
184
|
+
if (nameMatch) {
|
|
185
|
+
currentModelProvider.providerName = nameMatch.value;
|
|
186
|
+
currentModelProvider.nameValueRange = {
|
|
187
|
+
start: line.start + nameMatch.valueStart,
|
|
188
|
+
end: line.start + nameMatch.valueEnd,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const requiresOpenAiAuthMatch = matchBooleanKeyValueLine(line.content, "requires_openai_auth");
|
|
192
|
+
if (requiresOpenAiAuthMatch) {
|
|
193
|
+
currentModelProvider.requiresOpenAiAuth = requiresOpenAiAuthMatch.value;
|
|
194
|
+
currentModelProvider.requiresOpenAiAuthValueRange = {
|
|
195
|
+
start: line.start + requiresOpenAiAuthMatch.valueStart,
|
|
196
|
+
end: line.start + requiresOpenAiAuthMatch.valueEnd,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const wireApiMatch = matchKeyValueLine(line.content, "wire_api");
|
|
200
|
+
if (wireApiMatch) {
|
|
201
|
+
currentModelProvider.wireApi = wireApiMatch.value;
|
|
202
|
+
currentModelProvider.wireApiValueRange = {
|
|
203
|
+
start: line.start + wireApiMatch.valueStart,
|
|
204
|
+
end: line.start + wireApiMatch.valueEnd,
|
|
185
205
|
};
|
|
186
206
|
}
|
|
187
207
|
}
|
|
@@ -195,7 +215,10 @@ function parseStructuredConfig(configContent) {
|
|
|
195
215
|
...profile,
|
|
196
216
|
managedFieldInsertIndex: findManagedFieldInsertIndex(configContent, profile.sectionStart, profile.sectionEnd),
|
|
197
217
|
})),
|
|
198
|
-
modelProviders
|
|
218
|
+
modelProviders: modelProviders.map((provider) => ({
|
|
219
|
+
...provider,
|
|
220
|
+
managedFieldInsertIndex: findManagedFieldInsertIndex(configContent, provider.sectionStart, provider.sectionEnd),
|
|
221
|
+
})),
|
|
199
222
|
};
|
|
200
223
|
}
|
|
201
224
|
/**
|
|
@@ -218,7 +241,6 @@ function buildManagedProfileViews(document, providers) {
|
|
|
218
241
|
model: section.model,
|
|
219
242
|
modelProvider: section.modelProvider,
|
|
220
243
|
baseUrl: modelProviderSection?.baseUrl ?? null,
|
|
221
|
-
envKey: modelProviderSection?.envKey ?? null,
|
|
222
244
|
managedFields: collectManagedFields(section.model, section.modelProvider),
|
|
223
245
|
source: linkInfo.managed ? "managed" : "unmanaged",
|
|
224
246
|
});
|
|
@@ -235,7 +257,6 @@ function buildManagedProfileViews(document, providers) {
|
|
|
235
257
|
model: null,
|
|
236
258
|
modelProvider: null,
|
|
237
259
|
baseUrl: null,
|
|
238
|
-
envKey: null,
|
|
239
260
|
managedFields: [],
|
|
240
261
|
source: "orphaned-reference",
|
|
241
262
|
});
|
|
@@ -298,28 +319,6 @@ function collectConfigConsistencyIssues(document, providers) {
|
|
|
298
319
|
modelProvider: view.modelProvider,
|
|
299
320
|
});
|
|
300
321
|
}
|
|
301
|
-
else if (!modelProviderSection.envKey) {
|
|
302
|
-
issues.push({
|
|
303
|
-
code: "MODEL_PROVIDER_ENV_KEY_MISSING",
|
|
304
|
-
profile: view.name,
|
|
305
|
-
modelProvider: view.modelProvider,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
for (const providerName of view.linkedProviders) {
|
|
310
|
-
const provider = providers?.providers[providerName];
|
|
311
|
-
if (!provider) {
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
if (provider.envKey !== view.envKey) {
|
|
315
|
-
issues.push({
|
|
316
|
-
code: "PROVIDER_ENV_KEY_MISMATCH",
|
|
317
|
-
provider: providerName,
|
|
318
|
-
profile: view.name,
|
|
319
|
-
providerEnvKey: provider.envKey,
|
|
320
|
-
runtimeEnvKey: view.envKey,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
322
|
}
|
|
324
323
|
}
|
|
325
324
|
}
|
|
@@ -366,17 +365,6 @@ function validateManagedProfileCreation(profile, fields) {
|
|
|
366
365
|
modelProvider,
|
|
367
366
|
};
|
|
368
367
|
}
|
|
369
|
-
/**
|
|
370
|
-
* Normalizes a profile name into the default env_key used for generated runtime sections.
|
|
371
|
-
*/
|
|
372
|
-
function buildManagedProfileEnvKey(profile) {
|
|
373
|
-
const normalized = profile
|
|
374
|
-
.trim()
|
|
375
|
-
.replace(/[^A-Za-z0-9]+/g, "_")
|
|
376
|
-
.replace(/^_+|_+$/g, "")
|
|
377
|
-
.toUpperCase();
|
|
378
|
-
return `${normalized || "PROVIDER"}_API_KEY`;
|
|
379
|
-
}
|
|
380
368
|
/**
|
|
381
369
|
* Computes keep/delete/switch outcomes when a provider leaves or changes profiles.
|
|
382
370
|
*/
|
|
@@ -491,26 +479,29 @@ function planConfigMutation(document, args) {
|
|
|
491
479
|
const section = modelProviderSectionMap.get(profileName);
|
|
492
480
|
if (!section) {
|
|
493
481
|
const baseUrl = fields.baseUrl?.trim() ?? "";
|
|
494
|
-
const
|
|
495
|
-
if (!baseUrl
|
|
496
|
-
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${profileName}" requires
|
|
482
|
+
const providerName = fields.name?.trim() ?? "";
|
|
483
|
+
if (!baseUrl) {
|
|
484
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${profileName}" requires base_url.`, {
|
|
497
485
|
profile: profileName,
|
|
498
486
|
modelProvider: profileName,
|
|
499
487
|
missingFields: [
|
|
500
488
|
!baseUrl ? "base_url" : null,
|
|
501
|
-
!envKey ? "env_key" : null,
|
|
502
489
|
].filter((value) => Boolean(value)),
|
|
503
490
|
});
|
|
504
491
|
}
|
|
505
492
|
const prefix = document.rawText.length > 0 && !document.rawText.endsWith(document.lineEnding)
|
|
506
493
|
? document.lineEnding
|
|
507
494
|
: "";
|
|
495
|
+
const requiresOpenAiAuth = fields.requiresOpenAiAuth;
|
|
496
|
+
const wireApi = fields.wireApi?.trim() ?? "";
|
|
508
497
|
operations.push({
|
|
509
498
|
kind: "insert-at",
|
|
510
499
|
index: document.rawText.length,
|
|
511
500
|
text: `${prefix}[model_providers.${profileName}]${document.lineEnding}` +
|
|
512
501
|
`base_url = ${JSON.stringify(baseUrl)}${document.lineEnding}` +
|
|
513
|
-
`
|
|
502
|
+
(providerName ? `name = ${JSON.stringify(providerName)}${document.lineEnding}` : "") +
|
|
503
|
+
(requiresOpenAiAuth !== undefined ? `requires_openai_auth = ${String(requiresOpenAiAuth)}${document.lineEnding}` : "") +
|
|
504
|
+
(wireApi ? `wire_api = ${JSON.stringify(wireApi)}${document.lineEnding}` : ""),
|
|
514
505
|
});
|
|
515
506
|
createdModelProviderSections.push(profileName);
|
|
516
507
|
continue;
|
|
@@ -594,12 +585,14 @@ function planSectionFieldMutation(document, section, fields, operations) {
|
|
|
594
585
|
return updated;
|
|
595
586
|
}
|
|
596
587
|
/**
|
|
597
|
-
* Plans
|
|
588
|
+
* Plans managed field updates for one model_providers section.
|
|
598
589
|
*/
|
|
599
590
|
function planModelProviderFieldMutation(section, fields, operations) {
|
|
600
591
|
let updated = false;
|
|
601
592
|
const baseUrlText = fields.baseUrl !== undefined ? JSON.stringify(fields.baseUrl) : null;
|
|
602
|
-
const
|
|
593
|
+
const nameText = fields.name !== undefined ? JSON.stringify(fields.name) : null;
|
|
594
|
+
const requiresOpenAiAuthText = fields.requiresOpenAiAuth !== undefined ? String(fields.requiresOpenAiAuth) : null;
|
|
595
|
+
const wireApiText = fields.wireApi !== undefined ? JSON.stringify(fields.wireApi) : null;
|
|
603
596
|
const inserts = [];
|
|
604
597
|
if (baseUrlText !== null && section.baseUrlValueRange) {
|
|
605
598
|
if (section.baseUrl !== fields.baseUrl) {
|
|
@@ -614,28 +607,59 @@ function planModelProviderFieldMutation(section, fields, operations) {
|
|
|
614
607
|
}
|
|
615
608
|
else if (baseUrlText !== null) {
|
|
616
609
|
inserts.push(`base_url = ${baseUrlText}`);
|
|
610
|
+
updated = true;
|
|
617
611
|
}
|
|
618
|
-
if (
|
|
619
|
-
if (section.
|
|
612
|
+
if (nameText !== null && section.nameValueRange) {
|
|
613
|
+
if (section.providerName !== fields.name) {
|
|
620
614
|
operations.push({
|
|
621
615
|
kind: "replace-range",
|
|
622
|
-
start: section.
|
|
623
|
-
end: section.
|
|
624
|
-
text:
|
|
616
|
+
start: section.nameValueRange.start,
|
|
617
|
+
end: section.nameValueRange.end,
|
|
618
|
+
text: nameText,
|
|
625
619
|
});
|
|
626
620
|
updated = true;
|
|
627
621
|
}
|
|
628
622
|
}
|
|
629
|
-
else if (
|
|
630
|
-
inserts.push(`
|
|
623
|
+
else if (nameText !== null) {
|
|
624
|
+
inserts.push(`name = ${nameText}`);
|
|
625
|
+
updated = true;
|
|
626
|
+
}
|
|
627
|
+
if (requiresOpenAiAuthText !== null && section.requiresOpenAiAuthValueRange) {
|
|
628
|
+
if (section.requiresOpenAiAuth !== fields.requiresOpenAiAuth) {
|
|
629
|
+
operations.push({
|
|
630
|
+
kind: "replace-range",
|
|
631
|
+
start: section.requiresOpenAiAuthValueRange.start,
|
|
632
|
+
end: section.requiresOpenAiAuthValueRange.end,
|
|
633
|
+
text: requiresOpenAiAuthText,
|
|
634
|
+
});
|
|
635
|
+
updated = true;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
else if (requiresOpenAiAuthText !== null) {
|
|
639
|
+
inserts.push(`requires_openai_auth = ${requiresOpenAiAuthText}`);
|
|
640
|
+
updated = true;
|
|
641
|
+
}
|
|
642
|
+
if (wireApiText !== null && section.wireApiValueRange) {
|
|
643
|
+
if (section.wireApi !== fields.wireApi) {
|
|
644
|
+
operations.push({
|
|
645
|
+
kind: "replace-range",
|
|
646
|
+
start: section.wireApiValueRange.start,
|
|
647
|
+
end: section.wireApiValueRange.end,
|
|
648
|
+
text: wireApiText,
|
|
649
|
+
});
|
|
650
|
+
updated = true;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else if (wireApiText !== null) {
|
|
654
|
+
inserts.push(`wire_api = ${wireApiText}`);
|
|
655
|
+
updated = true;
|
|
631
656
|
}
|
|
632
657
|
if (inserts.length > 0) {
|
|
633
658
|
operations.push({
|
|
634
659
|
kind: "insert-at",
|
|
635
|
-
index: section.
|
|
660
|
+
index: section.managedFieldInsertIndex,
|
|
636
661
|
text: `${inserts.join("\n")}\n`,
|
|
637
662
|
});
|
|
638
|
-
updated = true;
|
|
639
663
|
}
|
|
640
664
|
return updated;
|
|
641
665
|
}
|
|
@@ -680,6 +704,23 @@ function matchKeyValueLine(line, key) {
|
|
|
680
704
|
valueEnd,
|
|
681
705
|
};
|
|
682
706
|
}
|
|
707
|
+
function matchBooleanKeyValueLine(line, key) {
|
|
708
|
+
const match = line.match(new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(true|false)\\s*(#.*)?$`));
|
|
709
|
+
if (!match || match.index === undefined) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const value = match[1] === "true";
|
|
713
|
+
const valueStart = line.indexOf(match[1], match.index);
|
|
714
|
+
if (valueStart === -1) {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
const valueEnd = valueStart + match[1].length;
|
|
718
|
+
return {
|
|
719
|
+
value,
|
|
720
|
+
valueStart,
|
|
721
|
+
valueEnd,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
683
724
|
function findManagedFieldInsertIndex(rawText, sectionStart, sectionEnd) {
|
|
684
725
|
const sectionText = rawText.slice(sectionStart, sectionEnd);
|
|
685
726
|
const lines = splitWithOffsets(sectionText);
|
package/dist/domain/providers.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.maskSecret = maskSecret;
|
|
|
9
9
|
exports.isRuntimeBackedProvider = isRuntimeBackedProvider;
|
|
10
10
|
exports.isCopilotBridgeProvider = isCopilotBridgeProvider;
|
|
11
11
|
exports.buildCopilotBridgeBaseUrl = buildCopilotBridgeBaseUrl;
|
|
12
|
+
exports.buildCopilotModelProviderProjection = buildCopilotModelProviderProjection;
|
|
12
13
|
/**
|
|
13
14
|
* Validates and normalizes unknown JSON into the providers.json domain model.
|
|
14
15
|
*/
|
|
@@ -32,9 +33,6 @@ function validateProvidersShape(input) {
|
|
|
32
33
|
if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") {
|
|
33
34
|
throw new Error(`Provider "${name}" is missing a valid apiKey.`);
|
|
34
35
|
}
|
|
35
|
-
if (typeof provider.envKey !== "string" || provider.envKey.trim() === "") {
|
|
36
|
-
throw new Error(`Provider "${name}" is missing a valid envKey.`);
|
|
37
|
-
}
|
|
38
36
|
if (provider.baseUrl !== undefined && typeof provider.baseUrl !== "string") {
|
|
39
37
|
throw new Error(`Provider "${name}" has an invalid baseUrl.`);
|
|
40
38
|
}
|
|
@@ -56,7 +54,6 @@ function validateProvidersShape(input) {
|
|
|
56
54
|
providers[name] = cleanProviderRecord({
|
|
57
55
|
profile: provider.profile,
|
|
58
56
|
apiKey: provider.apiKey,
|
|
59
|
-
envKey: provider.envKey,
|
|
60
57
|
baseUrl: provider.baseUrl,
|
|
61
58
|
note: provider.note,
|
|
62
59
|
tags: provider.tags,
|
|
@@ -72,7 +69,6 @@ function cleanProviderRecord(record) {
|
|
|
72
69
|
const next = {
|
|
73
70
|
profile: record.profile.trim(),
|
|
74
71
|
apiKey: record.apiKey.trim(),
|
|
75
|
-
envKey: record.envKey.trim(),
|
|
76
72
|
};
|
|
77
73
|
if (record.baseUrl && record.baseUrl.trim() !== "") {
|
|
78
74
|
next.baseUrl = record.baseUrl.trim();
|
|
@@ -155,6 +151,17 @@ function isCopilotBridgeProvider(provider) {
|
|
|
155
151
|
function buildCopilotBridgeBaseUrl(runtime) {
|
|
156
152
|
return `http://${runtime.bridgeHost}:${runtime.bridgePort}${runtime.bridgePath}`;
|
|
157
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Builds the Codex-facing custom model_provider projection for the managed Copilot bridge.
|
|
156
|
+
*/
|
|
157
|
+
function buildCopilotModelProviderProjection(runtime) {
|
|
158
|
+
return {
|
|
159
|
+
baseUrl: buildCopilotBridgeBaseUrl(runtime),
|
|
160
|
+
name: "copilot",
|
|
161
|
+
requiresOpenAiAuth: true,
|
|
162
|
+
wireApi: "responses",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
158
165
|
/**
|
|
159
166
|
* Validates one runtime-backed provider block.
|
|
160
167
|
*/
|
|
@@ -1,15 +1,92 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.getStorageRoles = getStorageRoles;
|
|
4
37
|
exports.inspectLiveStateDrift = inspectLiveStateDrift;
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
5
39
|
/**
|
|
6
40
|
* Returns the stable storage contract used by the CLI.
|
|
7
41
|
*/
|
|
8
|
-
function getStorageRoles() {
|
|
42
|
+
function getStorageRoles(args) {
|
|
43
|
+
const toolHomeDir = path.dirname(path.resolve(args.providersPath));
|
|
44
|
+
const backupsDir = path.join(toolHomeDir, "backups");
|
|
45
|
+
const runtimeDir = args.runtimeDir ? path.resolve(args.runtimeDir) : path.join(toolHomeDir, "runtime");
|
|
46
|
+
const runtimesDir = args.runtimesDir ? path.resolve(args.runtimesDir) : path.join(toolHomeDir, "runtimes");
|
|
9
47
|
return {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
48
|
+
toolHome: {
|
|
49
|
+
root: toolHomeDir,
|
|
50
|
+
toolConfig: path.join(toolHomeDir, "codex-switch.json"),
|
|
51
|
+
providers: path.resolve(args.providersPath),
|
|
52
|
+
backupsDir,
|
|
53
|
+
latestBackup: path.join(backupsDir, "latest.json"),
|
|
54
|
+
runtimeStateDir: runtimeDir,
|
|
55
|
+
runtimeInstallDir: runtimesDir,
|
|
56
|
+
},
|
|
57
|
+
targetRuntime: {
|
|
58
|
+
root: path.resolve(args.codexDir),
|
|
59
|
+
config: path.resolve(args.configPath),
|
|
60
|
+
auth: path.resolve(args.authPath),
|
|
61
|
+
},
|
|
62
|
+
managementSSOT: {
|
|
63
|
+
scope: "toolHome",
|
|
64
|
+
path: path.resolve(args.providersPath),
|
|
65
|
+
},
|
|
66
|
+
runtimeMirrors: [
|
|
67
|
+
{
|
|
68
|
+
scope: "targetRuntime",
|
|
69
|
+
path: path.resolve(args.configPath),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
authStateFile: {
|
|
73
|
+
scope: "targetRuntime",
|
|
74
|
+
path: path.resolve(args.authPath),
|
|
75
|
+
},
|
|
76
|
+
rollbackState: {
|
|
77
|
+
scope: "toolHome",
|
|
78
|
+
path: path.join(backupsDir, "latest.json"),
|
|
79
|
+
},
|
|
80
|
+
runtimeState: {
|
|
81
|
+
scope: "toolHome",
|
|
82
|
+
path: runtimeDir,
|
|
83
|
+
managedBackup: false,
|
|
84
|
+
},
|
|
85
|
+
runtimeInstall: {
|
|
86
|
+
scope: "toolHome",
|
|
87
|
+
path: runtimesDir,
|
|
88
|
+
managedBackup: false,
|
|
89
|
+
},
|
|
13
90
|
};
|
|
14
91
|
}
|
|
15
92
|
/**
|
package/dist/domain/setup.js
CHANGED
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildSetupDrafts = buildSetupDrafts;
|
|
4
4
|
exports.findIncompleteSetupProfiles = findIncompleteSetupProfiles;
|
|
5
|
+
exports.collectMigrateAdoptability = collectMigrateAdoptability;
|
|
6
|
+
const config_1 = require("./config");
|
|
5
7
|
const providers_1 = require("./providers");
|
|
6
8
|
/**
|
|
7
9
|
* Creates initial provider drafts from config profile names.
|
|
8
10
|
*/
|
|
9
|
-
function buildSetupDrafts(profiles, detailsByProfile) {
|
|
11
|
+
function buildSetupDrafts(profiles, detailsByProfile, runtimeByProfile) {
|
|
10
12
|
return profiles.map((profile) => {
|
|
11
13
|
const detail = detailsByProfile[profile] ?? {};
|
|
14
|
+
const runtime = runtimeByProfile[profile];
|
|
12
15
|
const providerName = (detail.providerName ?? profile).trim();
|
|
13
16
|
return {
|
|
14
17
|
providerName,
|
|
15
18
|
record: (0, providers_1.cleanProviderRecord)({
|
|
16
19
|
profile,
|
|
17
20
|
apiKey: detail.apiKey ?? "",
|
|
18
|
-
|
|
19
|
-
baseUrl: detail.baseUrl,
|
|
21
|
+
baseUrl: detail.baseUrl ?? runtime?.baseUrl,
|
|
20
22
|
note: detail.note,
|
|
21
23
|
tags: detail.tags,
|
|
22
24
|
}),
|
|
@@ -29,3 +31,56 @@ function buildSetupDrafts(profiles, detailsByProfile) {
|
|
|
29
31
|
function findIncompleteSetupProfiles(drafts) {
|
|
30
32
|
return drafts.filter((draft) => draft.record.apiKey.trim() === "").map((draft) => draft.record.profile);
|
|
31
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Collects the unmanaged profiles that can be safely adopted by migrate.
|
|
36
|
+
*/
|
|
37
|
+
function collectMigrateAdoptability(document, providers) {
|
|
38
|
+
const views = (0, config_1.buildManagedProfileViews)(document, providers)
|
|
39
|
+
.filter((view) => view.source !== "orphaned-reference")
|
|
40
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
41
|
+
const modelProvidersByName = new Map(document.modelProviders.map((provider) => [provider.name, provider]));
|
|
42
|
+
const availableProfiles = views.map((view) => view.name);
|
|
43
|
+
const adoptableProfileDetails = [];
|
|
44
|
+
const blockingReasonsByProfile = {};
|
|
45
|
+
for (const view of views) {
|
|
46
|
+
const reasons = [];
|
|
47
|
+
if (!view.model) {
|
|
48
|
+
reasons.push("model is missing.");
|
|
49
|
+
}
|
|
50
|
+
if (!view.modelProvider) {
|
|
51
|
+
reasons.push("model_provider is missing.");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (view.modelProvider !== view.name) {
|
|
55
|
+
reasons.push(`model_provider must match the profile name "${view.name}".`);
|
|
56
|
+
}
|
|
57
|
+
const modelProviderSection = modelProvidersByName.get(view.modelProvider);
|
|
58
|
+
if (!modelProviderSection) {
|
|
59
|
+
reasons.push(`model_providers.${view.modelProvider} section is missing.`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (!modelProviderSection.baseUrl) {
|
|
63
|
+
reasons.push(`model_providers.${view.modelProvider}.base_url is missing.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (view.source !== "unmanaged") {
|
|
68
|
+
reasons.push("profile is already managed by providers.json.");
|
|
69
|
+
}
|
|
70
|
+
if (reasons.length === 0) {
|
|
71
|
+
adoptableProfileDetails.push({
|
|
72
|
+
name: view.name,
|
|
73
|
+
model: view.model,
|
|
74
|
+
baseUrl: view.baseUrl,
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
blockingReasonsByProfile[view.name] = reasons;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
availableProfiles,
|
|
82
|
+
adoptableProfiles: adoptableProfileDetails.map((profile) => profile.name),
|
|
83
|
+
blockingReasonsByProfile,
|
|
84
|
+
adoptableProfileDetails,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.COMMON_TAG_CHOICES = void 0;
|
|
4
4
|
exports.collectAddInput = collectAddInput;
|
|
5
|
+
exports.collectCopilotAddInput = collectCopilotAddInput;
|
|
5
6
|
exports.createNonInteractiveAddError = createNonInteractiveAddError;
|
|
6
7
|
exports.promptTags = promptTags;
|
|
7
8
|
const errors_1 = require("../domain/errors");
|
|
@@ -37,10 +38,51 @@ async function collectAddInput(runtime, defaults, providerExists, profileExists)
|
|
|
37
38
|
tags,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Collects Copilot add command inputs interactively when required values are missing.
|
|
43
|
+
*/
|
|
44
|
+
async function collectCopilotAddInput(runtime, defaults, providerExists, profileExists, options) {
|
|
45
|
+
runtime.writeLine("Interactive add mode");
|
|
46
|
+
runtime.writeLine("Provide the missing Copilot provider fields. Press Enter to keep optional values empty.");
|
|
47
|
+
const providerName = defaults.providerName
|
|
48
|
+
? normalizeRequiredValue(defaults.providerName)
|
|
49
|
+
: await promptProviderName(runtime, providerExists);
|
|
50
|
+
const profile = defaults.profile ? normalizeRequiredValue(defaults.profile) : await promptRequiredValue(runtime, "Profile");
|
|
51
|
+
const createProfile = !profileExists(profile);
|
|
52
|
+
const model = createProfile
|
|
53
|
+
? defaults.model
|
|
54
|
+
? normalizeRequiredValue(defaults.model)
|
|
55
|
+
: await promptRequiredValue(runtime, `Model for new profile "${profile}"`)
|
|
56
|
+
: defaults.model ?? null;
|
|
57
|
+
const note = defaults.note ?? normalizeOptionalValue(await runtime.inputText("Note (optional)"));
|
|
58
|
+
const tags = defaults.tags.length > 0 ? defaults.tags : await promptTags(runtime);
|
|
59
|
+
const bridgeHost = options?.bridgeHost ?? normalizeOptionalValue(await runtime.inputText("Bridge host (optional)", { defaultValue: "127.0.0.1" }));
|
|
60
|
+
const bridgePortText = options?.bridgePort !== undefined && options.bridgePort !== null
|
|
61
|
+
? String(options.bridgePort)
|
|
62
|
+
: await runtime.inputText("Bridge port (optional)", { defaultValue: "41415" });
|
|
63
|
+
const bridgePort = normalizeBridgePort(runtime, bridgePortText);
|
|
64
|
+
const bridgeApiKey = options?.bridgeApiKey ?? normalizeOptionalValue(await runtime.inputSecret("Bridge API key (optional)"));
|
|
65
|
+
return {
|
|
66
|
+
providerName,
|
|
67
|
+
profile,
|
|
68
|
+
createProfile,
|
|
69
|
+
model,
|
|
70
|
+
note,
|
|
71
|
+
tags,
|
|
72
|
+
bridgeApiKey,
|
|
73
|
+
bridgeHost,
|
|
74
|
+
bridgePort,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
40
77
|
/**
|
|
41
78
|
* Throws a consistent error when interactive add is unavailable.
|
|
42
79
|
*/
|
|
43
|
-
function createNonInteractiveAddError() {
|
|
80
|
+
function createNonInteractiveAddError(options) {
|
|
81
|
+
if (options?.copilot) {
|
|
82
|
+
return (0, errors_1.cliError)("INVALID_ARGUMENT", "add --copilot requires <provider> and --profile when running without an interactive TTY.", {
|
|
83
|
+
suggestion: "Run in a terminal TTY or pass <provider>, --profile, and any optional Copilot bridge flags explicitly.",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
44
86
|
return (0, errors_1.cliError)("INVALID_ARGUMENT", "add requires <provider>, --profile, and --api-key when running without an interactive TTY.", {
|
|
45
87
|
suggestion: "Run in a terminal TTY or pass all required values explicitly.",
|
|
46
88
|
});
|
|
@@ -90,6 +132,18 @@ function normalizeOptionalValue(value) {
|
|
|
90
132
|
const normalized = value.trim();
|
|
91
133
|
return normalized === "" ? null : normalized;
|
|
92
134
|
}
|
|
135
|
+
function normalizeBridgePort(runtime, value) {
|
|
136
|
+
const normalized = value.trim();
|
|
137
|
+
if (normalized === "") {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const parsed = Number(normalized);
|
|
141
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
142
|
+
return parsed;
|
|
143
|
+
}
|
|
144
|
+
runtime.writeLine("Bridge port must be a positive integer. Falling back to the default port.");
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
93
147
|
async function promptTags(runtime, defaults = []) {
|
|
94
148
|
const defaultPresetTags = defaults.filter(isCommonTag);
|
|
95
149
|
return runtime.selectMany("Select tags (optional)", exports.COMMON_TAG_CHOICES.map((tag) => ({ value: tag, label: tag })), { defaultValues: defaultPresetTags });
|
|
@@ -222,7 +222,7 @@ async function chooseSetupProfiles(runtime, profiles) {
|
|
|
222
222
|
return runtime.selectMany("Choose unmanaged config profiles to adopt into providers.json.", profiles.map((profile) => ({
|
|
223
223
|
value: profile.name,
|
|
224
224
|
label: profile.name,
|
|
225
|
-
hint: `${profile.model} | ${profile.baseUrl}
|
|
225
|
+
hint: `${profile.model} | ${profile.baseUrl}`,
|
|
226
226
|
})));
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
@@ -235,9 +235,6 @@ async function collectSetupProviderDetails(runtime, profiles, defaultsByProfile
|
|
|
235
235
|
const providerName = (await runtime.inputText(`Provider name for profile "${profile}"`, {
|
|
236
236
|
defaultValue: defaults.providerName ?? profile,
|
|
237
237
|
})).trim();
|
|
238
|
-
if (defaults.envKey) {
|
|
239
|
-
runtime.writeLine(`Runtime env key for "${profile}": ${defaults.envKey}`);
|
|
240
|
-
}
|
|
241
238
|
const apiKey = await promptRequiredSecret(runtime, `API key for profile "${profile}"`, defaults.apiKey?.trim() || undefined);
|
|
242
239
|
const baseUrl = (await runtime.inputText(`Base URL note for profile "${profile}" (optional)`, {
|
|
243
240
|
defaultValue: defaults.baseUrl ?? "",
|
|
@@ -249,7 +246,6 @@ async function collectSetupProviderDetails(runtime, profiles, defaultsByProfile
|
|
|
249
246
|
result[profile] = {
|
|
250
247
|
providerName: providerName || defaults.providerName || profile,
|
|
251
248
|
apiKey,
|
|
252
|
-
envKey: defaults.envKey,
|
|
253
249
|
baseUrl: baseUrl || defaults.baseUrl || undefined,
|
|
254
250
|
note: note || defaults.note || undefined,
|
|
255
251
|
// Empty selections are omitted so downstream setup validation can distinguish unset from explicit data.
|