@minniexcode/codex-switch 0.1.5 → 0.2.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 +66 -94
- package/README.CN.md +84 -139
- package/README.md +91 -151
- package/dist/app/add-provider.js +6 -89
- package/dist/app/edit-provider.js +9 -29
- package/dist/app/get-status.js +1 -74
- package/dist/app/list-providers.js +0 -2
- package/dist/app/remove-provider.js +3 -5
- package/dist/app/run-doctor.js +2 -100
- package/dist/app/setup-codex.js +0 -2
- package/dist/app/switch-provider.js +1 -79
- package/dist/cli/output.js +3 -89
- package/dist/commands/handlers.js +20 -209
- package/dist/commands/help.js +1 -4
- package/dist/commands/registry.js +6 -74
- package/dist/domain/config.js +1 -3
- package/dist/domain/providers.js +4 -92
- package/dist/domain/runtime-state.js +0 -88
- package/dist/interaction/add-interactive.js +1 -55
- package/dist/interaction/interactive.js +1 -3
- package/dist/runtime/codex-probe.js +0 -12
- package/dist/storage/codex-paths.js +0 -2
- package/docs/Design/codex-switch-v0.2.0-design.md +56 -0
- package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
- package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
- package/docs/Tests/testing.md +32 -34
- package/docs/cli-usage.md +67 -235
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +49 -96
- package/docs/codex-switch-technical-architecture.md +37 -52
- package/package.json +1 -1
- package/dist/app/bridge.js +0 -308
- package/dist/runtime/copilot-adapter.js +0 -612
- package/dist/runtime/copilot-bridge-worker.js +0 -69
- package/dist/runtime/copilot-bridge.js +0 -1318
- package/dist/runtime/copilot-cli.js +0 -164
- package/dist/runtime/copilot-installer.js +0 -231
- package/dist/runtime/copilot-sdk-loader.js +0 -62
- package/dist/storage/runtime-state-repo.js +0 -121
package/dist/app/bridge.js
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startBridge = startBridge;
|
|
4
|
-
exports.stopBridge = stopBridge;
|
|
5
|
-
exports.statusBridge = statusBridge;
|
|
6
|
-
const errors_1 = require("../domain/errors");
|
|
7
|
-
const providers_1 = require("../domain/providers");
|
|
8
|
-
const config_repo_1 = require("../storage/config-repo");
|
|
9
|
-
const providers_repo_1 = require("../storage/providers-repo");
|
|
10
|
-
const interactive_1 = require("../interaction/interactive");
|
|
11
|
-
const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
12
|
-
const runtime_state_repo_1 = require("../storage/runtime-state-repo");
|
|
13
|
-
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
14
|
-
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
15
|
-
const DEFAULT_BRIDGE_PORT = 41415;
|
|
16
|
-
/**
|
|
17
|
-
* Starts or reuses the managed Copilot bridge for one provider.
|
|
18
|
-
*/
|
|
19
|
-
async function startBridge(args) {
|
|
20
|
-
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
21
|
-
const target = await resolveBridgeTarget({
|
|
22
|
-
requestedProviderName: args.providerName ?? null,
|
|
23
|
-
providers,
|
|
24
|
-
configPath: args.configPath,
|
|
25
|
-
runtimeDir: args.runtimeDir,
|
|
26
|
-
runtime: args.runtime,
|
|
27
|
-
json: args.json,
|
|
28
|
-
commandName: "start",
|
|
29
|
-
preferRuntimeState: false,
|
|
30
|
-
});
|
|
31
|
-
await requireBridgeRuntimeReadiness(args.runtimesDir);
|
|
32
|
-
const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(target.providerName, target.provider, args.runtimeDir, args.runtimesDir);
|
|
33
|
-
const nextProvider = bridge.portChanged ? rewriteBridgeProviderPort(target.provider, bridge.port) : target.provider;
|
|
34
|
-
if (bridge.portChanged) {
|
|
35
|
-
try {
|
|
36
|
-
persistRecoveredBridgePort({
|
|
37
|
-
providersPath: args.providersPath,
|
|
38
|
-
configPath: args.configPath,
|
|
39
|
-
providers,
|
|
40
|
-
providerName: target.providerName,
|
|
41
|
-
previousProvider: target.provider,
|
|
42
|
-
provider: nextProvider,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
if (!bridge.reused) {
|
|
47
|
-
(0, copilot_bridge_1.stopCopilotBridge)(args.runtimeDir);
|
|
48
|
-
}
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
data: {
|
|
54
|
-
provider: target.providerName,
|
|
55
|
-
profile: nextProvider.profile,
|
|
56
|
-
baseUrl: (0, providers_1.buildCopilotBridgeBaseUrl)(nextProvider.runtime),
|
|
57
|
-
host: bridge.host,
|
|
58
|
-
port: bridge.port,
|
|
59
|
-
reused: bridge.reused,
|
|
60
|
-
portChanged: bridge.portChanged,
|
|
61
|
-
replaced: bridge.replaced,
|
|
62
|
-
restartReason: bridge.restartReason ?? null,
|
|
63
|
-
logPath: bridge.logPath,
|
|
64
|
-
defaultPort: DEFAULT_BRIDGE_PORT,
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Stops the managed Copilot bridge.
|
|
70
|
-
*/
|
|
71
|
-
async function stopBridge(args) {
|
|
72
|
-
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
73
|
-
const state = (0, runtime_state_repo_1.readCopilotBridgeState)(args.runtimeDir);
|
|
74
|
-
if (!state && !args.providerName) {
|
|
75
|
-
return {
|
|
76
|
-
data: {
|
|
77
|
-
provider: null,
|
|
78
|
-
stopped: true,
|
|
79
|
-
hadRuntimeState: false,
|
|
80
|
-
logPath: null,
|
|
81
|
-
lastRestartReason: null,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
if (!state && args.providerName) {
|
|
86
|
-
resolveNamedBridgeProvider(providers, args.providerName);
|
|
87
|
-
return {
|
|
88
|
-
data: {
|
|
89
|
-
provider: args.providerName,
|
|
90
|
-
stopped: true,
|
|
91
|
-
hadRuntimeState: false,
|
|
92
|
-
logPath: null,
|
|
93
|
-
lastRestartReason: null,
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
const target = await resolveBridgeTarget({
|
|
98
|
-
requestedProviderName: args.providerName ?? null,
|
|
99
|
-
providers,
|
|
100
|
-
configPath: args.configPath,
|
|
101
|
-
runtimeDir: args.runtimeDir,
|
|
102
|
-
runtime: args.runtime,
|
|
103
|
-
json: args.json,
|
|
104
|
-
commandName: "stop",
|
|
105
|
-
preferRuntimeState: true,
|
|
106
|
-
});
|
|
107
|
-
if (args.providerName && state?.provider && state.provider !== args.providerName) {
|
|
108
|
-
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Bridge runtime state belongs to "${state.provider}" not "${args.providerName}".`, {
|
|
109
|
-
stateProvider: state.provider,
|
|
110
|
-
requestedProvider: args.providerName,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
(0, copilot_bridge_1.stopCopilotBridge)(args.runtimeDir);
|
|
114
|
-
return {
|
|
115
|
-
data: {
|
|
116
|
-
provider: target.providerName,
|
|
117
|
-
stopped: true,
|
|
118
|
-
hadRuntimeState: Boolean(state),
|
|
119
|
-
logPath: state?.logPath ?? null,
|
|
120
|
-
lastRestartReason: state?.lastRestartReason ?? null,
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Reports the managed Copilot bridge state.
|
|
126
|
-
*/
|
|
127
|
-
async function statusBridge(args) {
|
|
128
|
-
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
129
|
-
const state = (0, runtime_state_repo_1.readCopilotBridgeState)(args.runtimeDir);
|
|
130
|
-
const target = await resolveBridgeTarget({
|
|
131
|
-
requestedProviderName: args.providerName ?? null,
|
|
132
|
-
providers,
|
|
133
|
-
configPath: args.configPath,
|
|
134
|
-
runtimeDir: args.runtimeDir,
|
|
135
|
-
runtime: args.runtime,
|
|
136
|
-
json: args.json,
|
|
137
|
-
commandName: "status",
|
|
138
|
-
preferRuntimeState: true,
|
|
139
|
-
});
|
|
140
|
-
const provider = target.provider;
|
|
141
|
-
const runtimeStatus = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(provider, state, args.runtimeDir);
|
|
142
|
-
const expectedBaseUrl = (0, providers_1.buildCopilotBridgeBaseUrl)(provider.runtime);
|
|
143
|
-
if (args.providerName && state?.provider && state.provider !== args.providerName) {
|
|
144
|
-
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Bridge runtime state belongs to "${state.provider}" not "${args.providerName}".`, {
|
|
145
|
-
stateProvider: state.provider,
|
|
146
|
-
requestedProvider: args.providerName,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
return {
|
|
150
|
-
data: {
|
|
151
|
-
provider: target.providerName,
|
|
152
|
-
profile: provider.profile,
|
|
153
|
-
runtimeState: state,
|
|
154
|
-
expectedBaseUrl,
|
|
155
|
-
matches: Boolean(state && state.provider === target.providerName && state.baseUrl === expectedBaseUrl),
|
|
156
|
-
active: runtimeStatus.ok,
|
|
157
|
-
health: runtimeStatus,
|
|
158
|
-
logPath: state?.logPath ?? null,
|
|
159
|
-
lastRestartReason: state?.lastRestartReason ?? null,
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Resolves the Copilot provider target for one bridge command.
|
|
165
|
-
*/
|
|
166
|
-
async function resolveBridgeTarget(args) {
|
|
167
|
-
if (args.requestedProviderName) {
|
|
168
|
-
return resolveNamedBridgeProvider(args.providers, args.requestedProviderName);
|
|
169
|
-
}
|
|
170
|
-
if (args.preferRuntimeState) {
|
|
171
|
-
const runtimeState = (0, runtime_state_repo_1.readCopilotBridgeState)(args.runtimeDir);
|
|
172
|
-
if (runtimeState?.provider && args.providers.providers[runtimeState.provider]) {
|
|
173
|
-
return resolveNamedBridgeProvider(args.providers, runtimeState.provider);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const activeTarget = resolveActiveCopilotBridgeProvider(args.providers, args.configPath);
|
|
177
|
-
if (activeTarget) {
|
|
178
|
-
return activeTarget;
|
|
179
|
-
}
|
|
180
|
-
const copilotTargets = listCopilotBridgeProviders(args.providers);
|
|
181
|
-
if (copilotTargets.length === 1) {
|
|
182
|
-
return copilotTargets[0];
|
|
183
|
-
}
|
|
184
|
-
if ((0, interactive_1.canPrompt)(args.runtime, args.json)) {
|
|
185
|
-
const selected = await promptForCopilotBridgeSelection(args.runtime, copilotTargets, args.commandName);
|
|
186
|
-
return resolveNamedBridgeProvider(args.providers, selected);
|
|
187
|
-
}
|
|
188
|
-
throw (0, errors_1.cliError)("BRIDGE_TARGET_UNRESOLVED", `Unable to resolve a Copilot provider for bridge ${args.commandName}.`, {
|
|
189
|
-
availableProviders: copilotTargets.map((entry) => entry.providerName),
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Resolves the active provider when the current top-level profile maps to one Copilot bridge provider.
|
|
194
|
-
*/
|
|
195
|
-
function resolveActiveCopilotBridgeProvider(providers, configPath) {
|
|
196
|
-
try {
|
|
197
|
-
const currentProfile = (0, config_repo_1.readCurrentProfile)(configPath);
|
|
198
|
-
const matches = Object.entries(providers.providers)
|
|
199
|
-
.filter(([, provider]) => provider.profile === currentProfile && (0, providers_1.isCopilotBridgeProvider)(provider))
|
|
200
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
201
|
-
if (matches.length === 1) {
|
|
202
|
-
return {
|
|
203
|
-
providerName: matches[0][0],
|
|
204
|
-
provider: matches[0][1],
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Resolves one named provider and enforces the Copilot bridge runtime kind.
|
|
215
|
-
*/
|
|
216
|
-
function resolveNamedBridgeProvider(providers, providerName) {
|
|
217
|
-
const provider = providers.providers[providerName];
|
|
218
|
-
if (!provider) {
|
|
219
|
-
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${providerName}" was not found.`, {
|
|
220
|
-
provider: providerName,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
if (!(0, providers_1.isCopilotBridgeProvider)(provider)) {
|
|
224
|
-
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Provider "${providerName}" is not a Copilot bridge provider.`, {
|
|
225
|
-
provider: providerName,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
return { providerName, provider };
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Lists all configured Copilot bridge providers in stable order.
|
|
232
|
-
*/
|
|
233
|
-
function listCopilotBridgeProviders(providers) {
|
|
234
|
-
return Object.entries(providers.providers)
|
|
235
|
-
.filter(([, provider]) => (0, providers_1.isCopilotBridgeProvider)(provider))
|
|
236
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
237
|
-
.map(([providerName, provider]) => ({ providerName, provider }));
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Uses a Copilot-only provider picker instead of the generic provider selector.
|
|
241
|
-
*/
|
|
242
|
-
async function promptForCopilotBridgeSelection(runtime, targets, commandName) {
|
|
243
|
-
if (targets.length === 0) {
|
|
244
|
-
throw (0, errors_1.cliError)("BRIDGE_TARGET_UNRESOLVED", `No Copilot bridge providers are configured for bridge ${commandName}.`);
|
|
245
|
-
}
|
|
246
|
-
return runtime.selectOne(`Choose a Copilot provider to ${commandName}`, targets.map((target) => ({
|
|
247
|
-
value: target.providerName,
|
|
248
|
-
label: target.providerName,
|
|
249
|
-
hint: target.provider.profile,
|
|
250
|
-
})));
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Verifies that the local Copilot bridge prerequisites are available before startup.
|
|
254
|
-
*/
|
|
255
|
-
async function requireBridgeRuntimeReadiness(runtimesDir) {
|
|
256
|
-
(0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
|
|
257
|
-
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(runtimesDir);
|
|
258
|
-
if (!installStatus.installed) {
|
|
259
|
-
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
|
|
260
|
-
installDir: installStatus.installDir,
|
|
261
|
-
packageName: installStatus.packageName,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
await (0, copilot_adapter_1.readCopilotAuthState)(runtimesDir);
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Rewrites one Copilot bridge provider record with a recovered runtime port.
|
|
268
|
-
*/
|
|
269
|
-
function rewriteBridgeProviderPort(provider, port) {
|
|
270
|
-
return (0, providers_1.cleanProviderRecord)({
|
|
271
|
-
...provider,
|
|
272
|
-
baseUrl: `http://${provider.runtime.bridgeHost}:${port}${provider.runtime.bridgePath}`,
|
|
273
|
-
runtime: {
|
|
274
|
-
...provider.runtime,
|
|
275
|
-
bridgePort: port,
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Persists the recovered bridge port to both providers.json and config.toml.
|
|
281
|
-
*/
|
|
282
|
-
function persistRecoveredBridgePort(args) {
|
|
283
|
-
const previousProviders = {
|
|
284
|
-
providers: {
|
|
285
|
-
...args.providers.providers,
|
|
286
|
-
},
|
|
287
|
-
};
|
|
288
|
-
const nextProviders = {
|
|
289
|
-
providers: {
|
|
290
|
-
...args.providers.providers,
|
|
291
|
-
[args.providerName]: args.provider,
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
(0, providers_repo_1.writeProvidersFile)(args.providersPath, nextProviders);
|
|
295
|
-
try {
|
|
296
|
-
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
297
|
-
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
298
|
-
upsertModelProviders: {
|
|
299
|
-
[args.provider.profile]: (0, providers_1.buildCopilotModelProviderProjection)(args.provider.runtime),
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
(0, providers_repo_1.writeProvidersFile)(args.providersPath, previousProviders);
|
|
306
|
-
throw error;
|
|
307
|
-
}
|
|
308
|
-
}
|