@minniexcode/codex-switch 0.1.0 → 0.1.2
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 +141 -110
- package/README.CN.md +215 -179
- package/README.md +224 -183
- package/dist/app/add-provider.js +16 -23
- package/dist/app/bridge.js +2 -1
- package/dist/app/edit-provider.js +30 -65
- package/dist/app/get-current-profile.js +15 -3
- package/dist/app/get-status.js +11 -8
- package/dist/app/list-config-profiles.js +3 -1
- package/dist/app/list-providers.js +10 -4
- package/dist/app/remove-provider.js +52 -19
- package/dist/app/run-doctor.js +26 -29
- package/dist/app/setup-codex.js +3 -3
- package/dist/app/show-config.js +3 -1
- package/dist/app/switch-provider.js +38 -6
- package/dist/cli/output.js +29 -19
- package/dist/commands/handlers.js +3 -2
- package/dist/commands/help.js +3 -3
- package/dist/commands/registry.js +29 -29
- package/dist/domain/config.js +293 -209
- package/dist/domain/providers.js +8 -0
- package/dist/domain/runtime-state.js +15 -15
- package/dist/domain/setup.js +3 -1
- package/dist/interaction/interactive.js +2 -2
- package/dist/runtime/codex-version.js +7 -0
- package/dist/runtime/copilot-adapter.js +326 -70
- package/dist/runtime/copilot-bridge-worker.js +27 -2
- package/dist/runtime/copilot-bridge.js +192 -10
- package/dist/runtime/copilot-cli.js +7 -0
- package/dist/runtime/copilot-installer.js +59 -1
- package/dist/runtime/copilot-sdk-loader.js +4 -1
- package/dist/storage/config-repo.js +6 -14
- package/docs/Design/codex-switch-v0.1.0-design.md +32 -152
- package/docs/Design/codex-switch-v0.1.1-design.md +22 -0
- package/docs/Design/codex-switch-v0.1.2-design.md +65 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +65 -217
- package/docs/PRD/codex-switch-prd-v0.1.1.md +26 -0
- package/docs/PRD/codex-switch-prd-v0.1.2.md +41 -0
- package/docs/Reference/codex-config-reference.md +41 -0
- package/docs/Reference/codex-config-reference.zh-CN.md +41 -0
- package/docs/Tests/testing.md +1 -1
- package/docs/cli-usage.md +290 -223
- package/docs/codex-switch-command-design.md +2 -2
- package/docs/codex-switch-product-overview.md +18 -13
- package/docs/codex-switch-product-research.md +2 -2
- package/docs/codex-switch-technical-architecture.md +84 -1115
- package/package.json +2 -2
- package/docs/Design/codex-switch-copilot-integration-design.md +0 -517
- package/docs/Design/codex-switch-v0.0.10-design.md +0 -669
- package/docs/Design/codex-switch-v0.0.11-design.md +0 -824
- package/docs/Design/codex-switch-v0.0.12-design.md +0 -343
- package/docs/Design/codex-switch-v0.0.4-design.md +0 -874
- package/docs/Design/codex-switch-v0.0.5-design.md +0 -932
- package/docs/Design/codex-switch-v0.0.6-design.md +0 -708
- package/docs/Design/codex-switch-v0.0.7-design.md +0 -862
- package/docs/Design/codex-switch-v0.0.8-design.md +0 -132
- package/docs/Design/codex-switch-v0.0.9-design.md +0 -182
- package/docs/Design/codex-switch-v0.0.9-to-v0.0.12-roadmap.md +0 -413
- package/docs/PRD/codex-switch-prd-v0.0.10.md +0 -406
- package/docs/PRD/codex-switch-prd-v0.0.11.md +0 -577
- package/docs/PRD/codex-switch-prd-v0.0.12.md +0 -279
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +0 -446
- package/docs/PRD/codex-switch-prd-v0.0.8.md +0 -62
- package/docs/PRD/codex-switch-prd-v0.0.9.md +0 -166
- package/docs/PRD/codex-switch-prd.md +0 -650
- package/docs/Tests/test-report-0.0.5.md +0 -163
- package/docs/Tests/test-report-0.0.7.md +0 -118
- package/docs/Tests/testing-bridge-v0.0.9.md +0 -367
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.editProvider = editProvider;
|
|
4
4
|
const errors_1 = require("../domain/errors");
|
|
5
|
-
const config_1 = require("../domain/config");
|
|
6
5
|
const providers_1 = require("../domain/providers");
|
|
7
6
|
const config_repo_1 = require("../storage/config-repo");
|
|
8
7
|
const fs_utils_1 = require("../storage/fs-utils");
|
|
@@ -24,6 +23,7 @@ function editProvider(args) {
|
|
|
24
23
|
}
|
|
25
24
|
const updatedFields = [];
|
|
26
25
|
const nextProfile = args.profile ?? current.profile;
|
|
26
|
+
const nextModel = args.model === null ? undefined : args.model ?? current.model;
|
|
27
27
|
if (args.profile !== undefined && args.profile !== current.profile) {
|
|
28
28
|
updatedFields.push("profile");
|
|
29
29
|
}
|
|
@@ -33,6 +33,9 @@ function editProvider(args) {
|
|
|
33
33
|
if (args.baseUrl !== undefined && (args.baseUrl ?? undefined) !== current.baseUrl) {
|
|
34
34
|
updatedFields.push("baseUrl");
|
|
35
35
|
}
|
|
36
|
+
if (args.model !== undefined && args.model !== current.model) {
|
|
37
|
+
updatedFields.push("model");
|
|
38
|
+
}
|
|
36
39
|
if (args.note !== undefined && (args.note ?? undefined) !== current.note) {
|
|
37
40
|
updatedFields.push("note");
|
|
38
41
|
}
|
|
@@ -41,87 +44,46 @@ function editProvider(args) {
|
|
|
41
44
|
}
|
|
42
45
|
const oldProfile = current.profile;
|
|
43
46
|
const newProfile = nextProfile;
|
|
44
|
-
const targetSection = document.profiles.find((profile) => profile.name === newProfile) ?? null;
|
|
45
47
|
const targetModelProviderSection = document.modelProviders.find((entry) => entry.name === newProfile) ?? null;
|
|
46
|
-
const targetProfileExists = Boolean(targetSection);
|
|
47
|
-
let upsertProfiles;
|
|
48
48
|
let upsertModelProviders;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
profile: newProfile,
|
|
53
|
-
provider: args.providerName,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
if (!args.baseUrl || args.baseUrl.trim() === "") {
|
|
49
|
+
const resolvedBaseUrl = (args.baseUrl ?? current.baseUrl ?? targetModelProviderSection?.baseUrl ?? "").trim();
|
|
50
|
+
if (!current.runtime) {
|
|
51
|
+
if (!resolvedBaseUrl) {
|
|
57
52
|
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${newProfile}" requires base_url.`, {
|
|
58
53
|
profile: newProfile,
|
|
59
54
|
modelProvider: newProfile,
|
|
60
55
|
missingFields: ["base_url"],
|
|
61
56
|
});
|
|
62
57
|
}
|
|
63
|
-
upsertProfiles = {
|
|
64
|
-
[newProfile]: (0, config_1.validateManagedProfileCreation)(newProfile, {
|
|
65
|
-
model: args.model ?? undefined,
|
|
66
|
-
modelProvider: newProfile,
|
|
67
|
-
}),
|
|
68
|
-
};
|
|
69
58
|
upsertModelProviders = {
|
|
70
|
-
[newProfile]: (0, providers_1.buildDirectModelProviderProjection)(newProfile,
|
|
59
|
+
[newProfile]: (0, providers_1.buildDirectModelProviderProjection)(newProfile, resolvedBaseUrl),
|
|
71
60
|
};
|
|
72
61
|
}
|
|
73
|
-
else {
|
|
74
|
-
(0, config_repo_1.requireManagedProfileRuntime)(document, providers, newProfile);
|
|
75
|
-
}
|
|
76
|
-
if (targetProfileExists &&
|
|
77
|
-
!current.runtime &&
|
|
78
|
-
args.baseUrl !== undefined &&
|
|
79
|
-
args.baseUrl !== null) {
|
|
62
|
+
else if (targetModelProviderSection || args.profile !== undefined) {
|
|
80
63
|
upsertModelProviders = {
|
|
81
64
|
...(upsertModelProviders ?? {}),
|
|
82
|
-
[newProfile]:
|
|
65
|
+
[newProfile]: {
|
|
66
|
+
...(current.runtime
|
|
67
|
+
? {
|
|
68
|
+
baseUrl: current.baseUrl ?? targetModelProviderSection?.baseUrl ?? "",
|
|
69
|
+
name: "copilot",
|
|
70
|
+
requiresOpenAiAuth: true,
|
|
71
|
+
wireApi: "responses",
|
|
72
|
+
}
|
|
73
|
+
: (0, providers_1.buildDirectModelProviderProjection)(newProfile, resolvedBaseUrl)),
|
|
74
|
+
},
|
|
83
75
|
};
|
|
84
76
|
}
|
|
85
77
|
const nextRecord = (0, providers_1.cleanProviderRecord)({
|
|
86
78
|
profile: newProfile,
|
|
87
79
|
apiKey: args.apiKey ?? current.apiKey,
|
|
80
|
+
model: nextModel,
|
|
88
81
|
baseUrl: args.baseUrl === null ? undefined : args.baseUrl ?? current.baseUrl,
|
|
89
82
|
note: args.note === null ? undefined : args.note ?? current.note,
|
|
90
83
|
tags: args.tags ?? current.tags,
|
|
84
|
+
runtime: current.runtime,
|
|
91
85
|
});
|
|
92
|
-
|
|
93
|
-
upsertProfiles = {
|
|
94
|
-
[newProfile]: {
|
|
95
|
-
...(args.model !== undefined && args.model !== null ? { model: args.model } : {}),
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
if (args.model !== undefined && targetSection?.model !== args.model && !updatedFields.includes("model")) {
|
|
99
|
-
updatedFields.push("model");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// Compute profile link ownership after the edit so lifecycle planning can decide whether sections stay, move, or delete.
|
|
103
|
-
const remainingLinksByProfile = new Map();
|
|
104
|
-
for (const [name, provider] of Object.entries(providers.providers)) {
|
|
105
|
-
if (name === args.providerName) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
const list = remainingLinksByProfile.get(provider.profile) ?? [];
|
|
109
|
-
list.push(name);
|
|
110
|
-
remainingLinksByProfile.set(provider.profile, list);
|
|
111
|
-
}
|
|
112
|
-
if (newProfile !== oldProfile) {
|
|
113
|
-
const list = remainingLinksByProfile.get(newProfile) ?? [];
|
|
114
|
-
list.push(args.providerName);
|
|
115
|
-
remainingLinksByProfile.set(newProfile, list);
|
|
116
|
-
}
|
|
117
|
-
const lifecycle = (0, config_1.planProfileLifecycleOutcome)({
|
|
118
|
-
providerName: args.providerName,
|
|
119
|
-
oldProfile,
|
|
120
|
-
newProfile,
|
|
121
|
-
activeProfile: document.activeProfile,
|
|
122
|
-
remainingLinksByProfile,
|
|
123
|
-
switchToProfile: args.switchToProfile ?? null,
|
|
124
|
-
});
|
|
86
|
+
const isActive = document.currentModelProvider === oldProfile;
|
|
125
87
|
return (0, run_mutation_1.runMutation)({
|
|
126
88
|
lockPath: args.lockPath,
|
|
127
89
|
backupsDir: args.backupsDir,
|
|
@@ -133,10 +95,12 @@ function editProvider(args) {
|
|
|
133
95
|
],
|
|
134
96
|
mutate: () => {
|
|
135
97
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
136
|
-
upsertProfiles,
|
|
137
98
|
upsertModelProviders,
|
|
138
|
-
|
|
139
|
-
|
|
99
|
+
setCurrentModel: isActive ? nextModel ?? document.currentModel : undefined,
|
|
100
|
+
setCurrentModelProvider: isActive ? newProfile : undefined,
|
|
101
|
+
deleteLegacyProfile: isActive,
|
|
102
|
+
deleteLegacyProfilesByName: isActive ? [newProfile] : [],
|
|
103
|
+
scrubModelProviderEnvKeys: [newProfile],
|
|
140
104
|
});
|
|
141
105
|
const nextProviders = {
|
|
142
106
|
providers: {
|
|
@@ -149,12 +113,13 @@ function editProvider(args) {
|
|
|
149
113
|
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
150
114
|
return {
|
|
151
115
|
provider: args.providerName,
|
|
116
|
+
modelProvider: newProfile,
|
|
152
117
|
updatedFields,
|
|
153
118
|
createdProfileSections: configPlan.createdProfileSections,
|
|
154
119
|
createdModelProviderSections: configPlan.createdModelProviderSections,
|
|
155
120
|
deletedProfileSections: configPlan.deletedProfileSections,
|
|
156
|
-
keptSharedProfiles:
|
|
157
|
-
switchedActiveProfile:
|
|
121
|
+
keptSharedProfiles: [],
|
|
122
|
+
switchedActiveProfile: isActive && newProfile !== oldProfile,
|
|
158
123
|
adoptedProfiles: [],
|
|
159
124
|
repairedProfiles: [],
|
|
160
125
|
};
|
|
@@ -2,13 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getCurrentProfile = getCurrentProfile;
|
|
4
4
|
const config_repo_1 = require("../storage/config-repo");
|
|
5
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
5
6
|
/**
|
|
6
|
-
* Returns the currently active top-level Codex
|
|
7
|
+
* Returns the currently active top-level Codex route.
|
|
7
8
|
*/
|
|
8
|
-
function getCurrentProfile(configPath) {
|
|
9
|
+
function getCurrentProfile(configPath, providersPath) {
|
|
10
|
+
const document = (0, config_repo_1.readStructuredConfig)(configPath);
|
|
11
|
+
const providers = providersPath ? (0, providers_repo_1.readProvidersFileIfExists)(providersPath) : null;
|
|
12
|
+
const providerCandidates = document.currentModelProvider && providers
|
|
13
|
+
? Object.entries(providers.providers)
|
|
14
|
+
.filter(([, provider]) => provider.profile === document.currentModelProvider)
|
|
15
|
+
.map(([name]) => name)
|
|
16
|
+
.sort()
|
|
17
|
+
: [];
|
|
9
18
|
return {
|
|
10
19
|
data: {
|
|
11
|
-
|
|
20
|
+
model: document.currentModel,
|
|
21
|
+
modelProvider: document.currentModelProvider,
|
|
22
|
+
provider: providerCandidates.length === 1 ? providerCandidates[0] : null,
|
|
23
|
+
profile: document.currentModelProvider,
|
|
12
24
|
},
|
|
13
25
|
};
|
|
14
26
|
}
|
package/dist/app/get-status.js
CHANGED
|
@@ -51,7 +51,8 @@ const runtime_state_repo_1 = require("../storage/runtime-state-repo");
|
|
|
51
51
|
async function getStatus(codexDir, configPath, providersPath, authPath, options) {
|
|
52
52
|
const configExists = fs.existsSync(configPath);
|
|
53
53
|
const providersExists = fs.existsSync(providersPath);
|
|
54
|
-
let
|
|
54
|
+
let currentModelProvider = null;
|
|
55
|
+
let currentModel = null;
|
|
55
56
|
const warnings = [];
|
|
56
57
|
const providers = providersExists ? (0, providers_repo_1.readProvidersFile)(providersPath) : null;
|
|
57
58
|
let configViews = [];
|
|
@@ -59,14 +60,15 @@ async function getStatus(codexDir, configPath, providersPath, authPath, options)
|
|
|
59
60
|
const authState = (0, auth_repo_1.readAuthFileState)(authPath);
|
|
60
61
|
if (configExists) {
|
|
61
62
|
const document = (0, config_repo_1.readStructuredConfig)(configPath);
|
|
62
|
-
|
|
63
|
+
currentModel = document.currentModel;
|
|
64
|
+
currentModelProvider = document.currentModelProvider;
|
|
63
65
|
configViews = (0, config_1.buildManagedProfileViews)(document, providers);
|
|
64
66
|
consistencyIssues = (0, config_1.collectConfigConsistencyIssues)(document, providers);
|
|
65
|
-
if (!
|
|
66
|
-
warnings.push("config.toml exists but has no top-level
|
|
67
|
+
if (!currentModelProvider) {
|
|
68
|
+
warnings.push("config.toml exists but has no top-level model_provider.");
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
const liveState = (0, runtime_state_1.inspectLiveStateDrift)(
|
|
71
|
+
const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentModelProvider, providers);
|
|
70
72
|
const activeProviderCandidates = liveState.mappedProviders;
|
|
71
73
|
const activeProvider = liveState.providerResolvable && providers && liveState.mappedProvider
|
|
72
74
|
? providers.providers[liveState.mappedProvider]
|
|
@@ -111,7 +113,7 @@ async function getStatus(codexDir, configPath, providersPath, authPath, options)
|
|
|
111
113
|
warnings.push("Current config profile is not mapped in providers.json. Backfill would be required before treating live state as managed.");
|
|
112
114
|
}
|
|
113
115
|
if (liveState.reason === "shared-profile") {
|
|
114
|
-
warnings.push(`Current
|
|
116
|
+
warnings.push(`Current model provider "${currentModelProvider}" is shared by multiple providers in providers.json, so the active provider cannot be resolved uniquely.`);
|
|
115
117
|
}
|
|
116
118
|
if (runtimeStateInspection.exists && !runtimeStateInspection.valid) {
|
|
117
119
|
warnings.push(`Copilot bridge runtime state is unreadable: ${runtimeStateInspection.parseError ?? "unknown parse failure"}`);
|
|
@@ -130,8 +132,9 @@ async function getStatus(codexDir, configPath, providersPath, authPath, options)
|
|
|
130
132
|
}),
|
|
131
133
|
configExists,
|
|
132
134
|
providersExists,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
currentModelProvider,
|
|
136
|
+
currentModelProviderMapped: liveState.modelProviderMapped,
|
|
137
|
+
currentModel,
|
|
135
138
|
provider: liveState.mappedProvider,
|
|
136
139
|
activeProviderResolvable: liveState.providerResolvable,
|
|
137
140
|
activeProviderCandidates,
|
|
@@ -22,7 +22,9 @@ function listConfigProfilesView(args) {
|
|
|
22
22
|
}));
|
|
23
23
|
return {
|
|
24
24
|
data: {
|
|
25
|
-
|
|
25
|
+
currentModel: document.currentModel,
|
|
26
|
+
currentModelProvider: document.currentModelProvider,
|
|
27
|
+
legacyProfile: document.legacyProfile,
|
|
26
28
|
profiles,
|
|
27
29
|
count: profiles.length,
|
|
28
30
|
},
|
|
@@ -45,13 +45,18 @@ const providers_repo_1 = require("../storage/providers-repo");
|
|
|
45
45
|
function listProviders(providersPath, configPath) {
|
|
46
46
|
const providers = (0, providers_repo_1.readProvidersFile)(providersPath);
|
|
47
47
|
const names = Object.keys(providers.providers).sort();
|
|
48
|
-
const
|
|
49
|
-
? (0, config_repo_1.readStructuredConfig)(configPath).
|
|
48
|
+
const currentModelProvider = configPath && fs.existsSync(configPath)
|
|
49
|
+
? (0, config_repo_1.readStructuredConfig)(configPath).currentModelProvider
|
|
50
50
|
: null;
|
|
51
|
-
const
|
|
51
|
+
const currentModel = configPath && fs.existsSync(configPath)
|
|
52
|
+
? (0, config_repo_1.readStructuredConfig)(configPath).currentModel
|
|
53
|
+
: null;
|
|
54
|
+
const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentModelProvider, providers);
|
|
52
55
|
const items = names.map((name) => ({
|
|
53
56
|
name,
|
|
54
57
|
profile: providers.providers[name].profile,
|
|
58
|
+
modelProvider: providers.providers[name].profile,
|
|
59
|
+
model: providers.providers[name].model ?? null,
|
|
55
60
|
providerType: (0, providers_1.isCopilotBridgeProvider)(providers.providers[name]) ? "copilot" : "direct",
|
|
56
61
|
isActive: liveState.providerResolvable && liveState.mappedProvider === name,
|
|
57
62
|
note: providers.providers[name].note ?? null,
|
|
@@ -61,7 +66,8 @@ function listProviders(providersPath, configPath) {
|
|
|
61
66
|
data: {
|
|
62
67
|
providers: items,
|
|
63
68
|
count: items.length,
|
|
64
|
-
|
|
69
|
+
currentModel,
|
|
70
|
+
currentModelProvider,
|
|
65
71
|
activeProvider: liveState.mappedProvider,
|
|
66
72
|
activeProviderResolvable: liveState.providerResolvable,
|
|
67
73
|
activeProviderCandidates: liveState.mappedProviders,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removeProvider = removeProvider;
|
|
4
4
|
const errors_1 = require("../domain/errors");
|
|
5
|
-
const config_1 = require("../domain/config");
|
|
6
5
|
const config_repo_1 = require("../storage/config-repo");
|
|
7
6
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
7
|
+
const providers_1 = require("../domain/providers");
|
|
8
8
|
const run_mutation_1 = require("./run-mutation");
|
|
9
9
|
/**
|
|
10
10
|
* Removes a provider from the managed providers registry.
|
|
@@ -17,22 +17,48 @@ function removeProvider(args) {
|
|
|
17
17
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${args.providerName}" was not found.`);
|
|
18
18
|
}
|
|
19
19
|
const nextProviders = { ...providers.providers };
|
|
20
|
-
// Delete against a copied object so the original parsed state stays untouched.
|
|
21
20
|
delete nextProviders[args.providerName];
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const activeModelProvider = document.currentModelProvider;
|
|
22
|
+
const linkedProviders = Object.entries(providers.providers)
|
|
23
|
+
.filter(([, provider]) => provider.profile === activeModelProvider)
|
|
24
|
+
.map(([name]) => name)
|
|
25
|
+
.sort();
|
|
26
|
+
const removingActiveProvider = activeModelProvider === current.profile && linkedProviders.length === 1;
|
|
27
|
+
const switchTargetName = args.switchToProvider ?? null;
|
|
28
|
+
const switchTarget = switchTargetName ? nextProviders[switchTargetName] ?? null : null;
|
|
29
|
+
if (removingActiveProvider && !switchTargetName) {
|
|
30
|
+
throw (0, errors_1.cliError)("PROFILE_IN_USE", `Provider "${args.providerName}" is the active route and requires --switch-to <provider-name>.`, {
|
|
31
|
+
provider: args.providerName,
|
|
32
|
+
activeModelProvider,
|
|
33
|
+
linkedProviders,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (switchTargetName && !switchTarget) {
|
|
37
|
+
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${switchTargetName}" was not found.`, {
|
|
38
|
+
provider: switchTargetName,
|
|
39
|
+
availableProviders: Object.keys(nextProviders).sort(),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const switchTargetModel = switchTarget?.model ?? document.currentModel ?? null;
|
|
43
|
+
if (switchTargetName && !switchTargetModel) {
|
|
44
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Provider "${switchTargetName}" has no model to switch with.`, {
|
|
45
|
+
provider: switchTargetName,
|
|
46
|
+
suggestion: "Run `codexs edit <provider> --model <name>` first.",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const switchTargetProjection = switchTarget
|
|
50
|
+
? (0, providers_1.isCopilotBridgeProvider)(switchTarget)
|
|
51
|
+
? (0, providers_1.buildCopilotModelProviderProjection)(switchTarget.runtime)
|
|
52
|
+
: switchTarget.baseUrl
|
|
53
|
+
? (0, providers_1.buildDirectModelProviderProjection)(switchTarget.profile, switchTarget.baseUrl)
|
|
54
|
+
: null
|
|
55
|
+
: null;
|
|
56
|
+
if (switchTargetName && !switchTargetProjection) {
|
|
57
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Provider "${switchTargetName}" requires base_url before it can become active.`, {
|
|
58
|
+
provider: switchTargetName,
|
|
59
|
+
suggestion: "Run `codexs edit <provider> --base-url <url>` first.",
|
|
60
|
+
});
|
|
27
61
|
}
|
|
28
|
-
const lifecycle = (0, config_1.planProfileLifecycleOutcome)({
|
|
29
|
-
providerName: args.providerName,
|
|
30
|
-
oldProfile: current.profile,
|
|
31
|
-
newProfile: null,
|
|
32
|
-
activeProfile: document.activeProfile,
|
|
33
|
-
remainingLinksByProfile,
|
|
34
|
-
switchToProfile: args.switchToProfile ?? null,
|
|
35
|
-
});
|
|
36
62
|
return (0, run_mutation_1.runMutation)({
|
|
37
63
|
lockPath: args.lockPath,
|
|
38
64
|
backupsDir: args.backupsDir,
|
|
@@ -44,17 +70,24 @@ function removeProvider(args) {
|
|
|
44
70
|
],
|
|
45
71
|
mutate: () => {
|
|
46
72
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
setCurrentModel: switchTarget ? switchTargetModel : undefined,
|
|
74
|
+
setCurrentModelProvider: switchTarget ? switchTarget.profile : undefined,
|
|
75
|
+
upsertModelProviders: switchTarget && switchTargetProjection
|
|
76
|
+
? { [switchTarget.profile]: switchTargetProjection }
|
|
77
|
+
: undefined,
|
|
78
|
+
deleteLegacyProfile: Boolean(switchTarget),
|
|
79
|
+
deleteLegacyProfilesByName: switchTarget ? [switchTarget.profile] : [],
|
|
80
|
+
scrubModelProviderEnvKeys: switchTarget ? [switchTarget.profile] : [],
|
|
49
81
|
});
|
|
50
82
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, { providers: nextProviders });
|
|
51
83
|
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
52
84
|
return {
|
|
53
85
|
provider: args.providerName,
|
|
86
|
+
switchedTo: switchTargetName,
|
|
54
87
|
createdProfileSections: configPlan.createdProfileSections,
|
|
55
88
|
deletedProfileSections: configPlan.deletedProfileSections,
|
|
56
|
-
keptSharedProfiles:
|
|
57
|
-
switchedActiveProfile:
|
|
89
|
+
keptSharedProfiles: [],
|
|
90
|
+
switchedActiveProfile: Boolean(switchTarget),
|
|
58
91
|
adoptedProfiles: [],
|
|
59
92
|
repairedProfiles: [],
|
|
60
93
|
};
|
package/dist/app/run-doctor.js
CHANGED
|
@@ -47,12 +47,13 @@ const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
|
47
47
|
const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
48
48
|
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
49
49
|
const runtime_state_repo_1 = require("../storage/runtime-state-repo");
|
|
50
|
+
const codex_version_1 = require("../runtime/codex-version");
|
|
50
51
|
/**
|
|
51
52
|
* Performs consistency checks across config.toml, providers.json, and the local Codex CLI.
|
|
52
53
|
*/
|
|
53
54
|
async function runDoctor(args) {
|
|
54
55
|
const issues = [];
|
|
55
|
-
let
|
|
56
|
+
let currentModelProvider = null;
|
|
56
57
|
let providers = null;
|
|
57
58
|
let document = null;
|
|
58
59
|
if (!fs.existsSync(args.configPath)) {
|
|
@@ -64,11 +65,11 @@ async function runDoctor(args) {
|
|
|
64
65
|
}
|
|
65
66
|
else {
|
|
66
67
|
document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
67
|
-
|
|
68
|
-
if (!
|
|
68
|
+
currentModelProvider = document.currentModelProvider;
|
|
69
|
+
if (!currentModelProvider) {
|
|
69
70
|
issues.push({
|
|
70
|
-
code: "
|
|
71
|
-
message: "config.toml has no top-level
|
|
71
|
+
code: "MODEL_PROVIDER_MISSING",
|
|
72
|
+
message: "config.toml has no top-level model_provider.",
|
|
72
73
|
file: args.configPath,
|
|
73
74
|
});
|
|
74
75
|
}
|
|
@@ -118,8 +119,8 @@ async function runDoctor(args) {
|
|
|
118
119
|
message: `Copilot bridge runtime state is unreadable: ${runtimeStateInspection.parseError ?? "unknown parse failure"}`,
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
|
-
if (document?.
|
|
122
|
-
const matches = (0, providers_1.findProvidersByProfile)(providers, document.
|
|
122
|
+
if (document?.currentModelProvider && providers) {
|
|
123
|
+
const matches = (0, providers_1.findProvidersByProfile)(providers, document.currentModelProvider);
|
|
123
124
|
if (matches.length === 1) {
|
|
124
125
|
const activeProvider = providers.providers[matches[0]];
|
|
125
126
|
if ((0, providers_1.isCopilotBridgeProvider)(activeProvider)) {
|
|
@@ -163,11 +164,11 @@ async function runDoctor(args) {
|
|
|
163
164
|
...runtimeState,
|
|
164
165
|
});
|
|
165
166
|
}
|
|
166
|
-
else if (!document?.
|
|
167
|
+
else if (!document?.currentModelProvider || runtimeProvider.profile !== document.currentModelProvider) {
|
|
167
168
|
issues.push({
|
|
168
169
|
code: "BRIDGE_STATE_STALE",
|
|
169
|
-
message: "Copilot bridge runtime state exists for a provider that is not the current active
|
|
170
|
-
|
|
170
|
+
message: "Copilot bridge runtime state exists for a provider that is not the current active model_provider.",
|
|
171
|
+
activeModelProvider: document?.currentModelProvider ?? null,
|
|
171
172
|
runtimeProvider: runtimeState.provider,
|
|
172
173
|
runtimeProfile: runtimeProvider.profile,
|
|
173
174
|
...runtimeState,
|
|
@@ -175,8 +176,8 @@ async function runDoctor(args) {
|
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
// Drift inspection still runs when files are missing so status output can explain partial state.
|
|
178
|
-
const drift = (0, runtime_state_1.inspectLiveStateDrift)(
|
|
179
|
-
const codexCheck = (0, codex_probe_1.probeCodexRuntime)();
|
|
179
|
+
const drift = (0, runtime_state_1.inspectLiveStateDrift)(currentModelProvider, providers);
|
|
180
|
+
const codexCheck = (0, codex_probe_1.probeCodexRuntime)(codex_version_1.MIN_SUPPORTED_CODEX_VERSION);
|
|
180
181
|
if (!codexCheck.ok) {
|
|
181
182
|
const message = codexCheck.reason === "missing"
|
|
182
183
|
? "codex CLI is not available on PATH."
|
|
@@ -229,32 +230,28 @@ function mapBridgeDiagnosticCode(cause) {
|
|
|
229
230
|
*/
|
|
230
231
|
function renderConfigIssueMessage(issue) {
|
|
231
232
|
switch (issue.code) {
|
|
232
|
-
case "
|
|
233
|
-
return
|
|
234
|
-
case "UNMANAGED_ACTIVE_PROFILE":
|
|
235
|
-
return `Active profile "${issue.profile}" is not mapped by providers.json.`;
|
|
236
|
-
case "SHARED_PROFILE_REFERENCE":
|
|
237
|
-
return `Profile "${issue.profile}" is shared by multiple providers.`;
|
|
238
|
-
case "ORPHANED_PROFILE_SECTION":
|
|
239
|
-
return `Profile section "${issue.profile}" is not linked to any provider.`;
|
|
233
|
+
case "MODEL_MISSING":
|
|
234
|
+
return "Top-level model is missing from config.toml.";
|
|
240
235
|
case "MODEL_PROVIDER_MISSING":
|
|
241
|
-
return
|
|
242
|
-
case "MODEL_PROVIDER_NAME_MISMATCH":
|
|
243
|
-
return `Profile "${issue.profile}" must use matching model_provider name "${issue.profile}", found "${issue.modelProvider}".`;
|
|
236
|
+
return "Top-level model_provider is missing from config.toml.";
|
|
244
237
|
case "MODEL_PROVIDER_SECTION_MISSING":
|
|
245
|
-
return `Model provider section "${issue.modelProvider}"
|
|
238
|
+
return `Model provider section "${issue.modelProvider}" is missing from config.toml.`;
|
|
246
239
|
case "MODEL_PROVIDER_BASE_URL_MISSING":
|
|
247
|
-
return `Model provider section "${issue.modelProvider}"
|
|
240
|
+
return `Model provider section "${issue.modelProvider}" is missing base_url.`;
|
|
241
|
+
case "LEGACY_PROFILE_SELECTOR":
|
|
242
|
+
return `Legacy top-level profile selector "${issue.profile}" is still present.`;
|
|
243
|
+
case "LEGACY_PROFILE_SECTION":
|
|
244
|
+
return `Legacy profile section "${issue.profile}" is still present.`;
|
|
245
|
+
case "LEGACY_MODEL_PROVIDER_ENV_KEY":
|
|
246
|
+
return `Model provider "${issue.modelProvider}" still contains legacy env_key wiring.`;
|
|
248
247
|
case "PROVIDER_BASE_URL_MISMATCH":
|
|
249
248
|
return issue.providerType === "direct"
|
|
250
|
-
? `Direct provider "${issue.provider}" baseUrl does not match config.toml model provider "${issue.
|
|
249
|
+
? `Direct provider "${issue.provider}" baseUrl does not match config.toml model provider "${issue.modelProvider}" base_url.`
|
|
251
250
|
: String(issue.code ?? "UNKNOWN_ISSUE");
|
|
252
|
-
case "ACTIVE_PROVIDER_UNRESOLVED":
|
|
253
|
-
return `Active profile "${issue.profile}" maps to multiple providers, so the active managed provider cannot be resolved uniquely.`;
|
|
254
251
|
case "AUTH_JSON_INVALID":
|
|
255
252
|
return String(issue.message ?? issue.reason ?? "auth.json is invalid.");
|
|
256
253
|
case "DESTRUCTIVE_REMOVE_BLOCKED":
|
|
257
|
-
return `Provider "${issue.provider}" cannot be removed while "${issue.
|
|
254
|
+
return `Provider "${issue.provider}" cannot be removed while "${issue.activeModelProvider}" remains active.`;
|
|
258
255
|
default:
|
|
259
256
|
return String(issue.code ?? "UNKNOWN_ISSUE");
|
|
260
257
|
}
|
package/dist/app/setup-codex.js
CHANGED
|
@@ -42,9 +42,9 @@ 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 codex_version_1 = require("../runtime/codex-version");
|
|
45
46
|
const run_doctor_1 = require("./run-doctor");
|
|
46
47
|
const run_mutation_1 = require("./run-mutation");
|
|
47
|
-
const MIN_CODEX_VERSION = "0.0.1";
|
|
48
48
|
/**
|
|
49
49
|
* Migrates unmanaged Codex config profiles into a managed providers.json registry.
|
|
50
50
|
*/
|
|
@@ -55,10 +55,10 @@ async function migrateCodex(args) {
|
|
|
55
55
|
cause: available.cause,
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
-
const version = (0, codex_cli_1.checkCodexVersion)(
|
|
58
|
+
const version = (0, codex_cli_1.checkCodexVersion)(codex_version_1.MIN_SUPPORTED_CODEX_VERSION);
|
|
59
59
|
if (!version.ok) {
|
|
60
60
|
throw (0, errors_1.cliError)("CODEX_VERSION_UNSUPPORTED", "codex CLI version is below the supported minimum.", {
|
|
61
|
-
minimumVersion:
|
|
61
|
+
minimumVersion: codex_version_1.MIN_SUPPORTED_CODEX_VERSION,
|
|
62
62
|
currentVersion: version.currentVersion ?? null,
|
|
63
63
|
cause: version.cause,
|
|
64
64
|
});
|
package/dist/app/show-config.js
CHANGED
|
@@ -27,7 +27,9 @@ function showConfig(args) {
|
|
|
27
27
|
}
|
|
28
28
|
return {
|
|
29
29
|
data: {
|
|
30
|
-
|
|
30
|
+
currentModel: document.currentModel,
|
|
31
|
+
currentModelProvider: document.currentModelProvider,
|
|
32
|
+
legacyProfile: document.legacyProfile,
|
|
31
33
|
selectedProfile,
|
|
32
34
|
profiles: profiles.map((profile) => ({
|
|
33
35
|
...profile,
|
|
@@ -2,7 +2,6 @@
|
|
|
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");
|
|
6
5
|
const config_repo_1 = require("../storage/config-repo");
|
|
7
6
|
const auth_repo_1 = require("../storage/auth-repo");
|
|
8
7
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
@@ -10,8 +9,9 @@ const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
|
10
9
|
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
11
10
|
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
12
11
|
const run_mutation_1 = require("./run-mutation");
|
|
12
|
+
const providers_1 = require("../domain/providers");
|
|
13
13
|
/**
|
|
14
|
-
* Switches the active Codex
|
|
14
|
+
* Switches the active Codex route to the target provider.
|
|
15
15
|
*/
|
|
16
16
|
async function switchProvider(args) {
|
|
17
17
|
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
@@ -21,8 +21,17 @@ async function switchProvider(args) {
|
|
|
21
21
|
availableProviders: Object.keys(providers.providers).sort(),
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
const document = (0, config_repo_1.
|
|
24
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
25
|
+
const resolvedModel = provider.model ?? document.currentModel;
|
|
26
|
+
if (!resolvedModel) {
|
|
27
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Provider "${args.providerName}" has no model to switch with.`, {
|
|
28
|
+
provider: args.providerName,
|
|
29
|
+
modelProvider: provider.profile,
|
|
30
|
+
suggestion: "Run `codexs edit <provider> --model <name>` or `codexs add <provider> --model <name>`.",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
25
33
|
if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
|
|
34
|
+
(0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
|
|
26
35
|
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(args.runtimesDir);
|
|
27
36
|
if (!installStatus.installed) {
|
|
28
37
|
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
|
|
@@ -31,7 +40,7 @@ async function switchProvider(args) {
|
|
|
31
40
|
});
|
|
32
41
|
}
|
|
33
42
|
await (0, copilot_adapter_1.readCopilotAuthState)(args.runtimesDir);
|
|
34
|
-
const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir);
|
|
43
|
+
const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir, args.runtimesDir);
|
|
35
44
|
const nextProvider = bridge.portChanged
|
|
36
45
|
? (0, providers_1.cleanProviderRecord)({
|
|
37
46
|
...provider,
|
|
@@ -55,10 +64,14 @@ async function switchProvider(args) {
|
|
|
55
64
|
],
|
|
56
65
|
mutate: () => {
|
|
57
66
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
58
|
-
|
|
67
|
+
setCurrentModel: resolvedModel,
|
|
68
|
+
setCurrentModelProvider: provider.profile,
|
|
59
69
|
upsertModelProviders: {
|
|
60
70
|
[provider.profile]: (0, providers_1.buildCopilotModelProviderProjection)(nextProvider.runtime),
|
|
61
71
|
},
|
|
72
|
+
deleteLegacyProfile: true,
|
|
73
|
+
deleteLegacyProfilesByName: [provider.profile],
|
|
74
|
+
scrubModelProviderEnvKeys: [provider.profile],
|
|
62
75
|
});
|
|
63
76
|
if (bridge.portChanged) {
|
|
64
77
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, {
|
|
@@ -72,6 +85,8 @@ async function switchProvider(args) {
|
|
|
72
85
|
(0, auth_repo_1.writeOpenAiApiKeyAuth)(args.authPath, provider.apiKey);
|
|
73
86
|
return {
|
|
74
87
|
provider: args.providerName,
|
|
88
|
+
model: resolvedModel,
|
|
89
|
+
modelProvider: nextProvider.profile,
|
|
75
90
|
profile: nextProvider.profile,
|
|
76
91
|
portChanged: bridge.portChanged,
|
|
77
92
|
bridgePort: bridge.port,
|
|
@@ -96,13 +111,30 @@ async function switchProvider(args) {
|
|
|
96
111
|
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
97
112
|
],
|
|
98
113
|
mutate: () => {
|
|
114
|
+
const directBaseUrl = provider.baseUrl?.trim() ?? "";
|
|
115
|
+
if (!directBaseUrl) {
|
|
116
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Provider "${args.providerName}" requires base_url before switching.`, {
|
|
117
|
+
provider: args.providerName,
|
|
118
|
+
modelProvider: provider.profile,
|
|
119
|
+
suggestion: "Run `codexs edit <provider> --base-url <url>`.",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
99
122
|
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
100
|
-
|
|
123
|
+
setCurrentModel: resolvedModel,
|
|
124
|
+
setCurrentModelProvider: provider.profile,
|
|
125
|
+
upsertModelProviders: {
|
|
126
|
+
[provider.profile]: (0, providers_1.buildDirectModelProviderProjection)(provider.profile, directBaseUrl),
|
|
127
|
+
},
|
|
128
|
+
deleteLegacyProfile: true,
|
|
129
|
+
deleteLegacyProfilesByName: [provider.profile],
|
|
130
|
+
scrubModelProviderEnvKeys: [provider.profile],
|
|
101
131
|
});
|
|
102
132
|
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
103
133
|
(0, auth_repo_1.writeOpenAiApiKeyAuth)(args.authPath, provider.apiKey);
|
|
104
134
|
return {
|
|
105
135
|
provider: args.providerName,
|
|
136
|
+
model: resolvedModel,
|
|
137
|
+
modelProvider: provider.profile,
|
|
106
138
|
profile: provider.profile,
|
|
107
139
|
};
|
|
108
140
|
},
|