@minniexcode/codex-switch 0.0.7 → 0.0.9
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.md +1 -1
- package/dist/app/add-provider.js +70 -3
- package/dist/app/bridge.js +296 -0
- package/dist/app/get-status.js +39 -1
- package/dist/app/run-doctor.js +68 -1
- package/dist/app/setup-codex.js +2 -2
- package/dist/app/switch-provider.js +75 -1
- package/dist/cli.js +1 -1
- package/dist/commands/handlers.js +58 -2
- package/dist/commands/help.js +1 -0
- package/dist/commands/registry.js +56 -2
- package/dist/domain/providers.js +74 -0
- package/dist/runtime/copilot-adapter.js +173 -0
- package/dist/runtime/copilot-bridge-worker.js +25 -0
- package/dist/runtime/copilot-bridge.js +474 -0
- package/dist/runtime/copilot-installer.js +125 -0
- package/dist/runtime/copilot-sdk-loader.js +59 -0
- package/dist/storage/fs-utils.js +3 -0
- package/dist/storage/runtime-state-repo.js +80 -0
- package/docs/Design/codex-switch-v0.0.8-design.md +132 -0
- package/docs/Design/codex-switch-v0.0.9-design.md +182 -0
- package/docs/Design/codex-switch-v0.0.9-to-v0.0.12-roadmap.md +413 -0
- package/docs/PRD/codex-switch-prd-v0.0.8.md +62 -0
- package/docs/PRD/codex-switch-prd-v0.0.9.md +166 -0
- package/docs/Tests/testing-bridge-v0.0.9.md +367 -0
- package/package.json +1 -1
- /package/docs/{test-report-0.0.5.md → Tests/test-report-0.0.5.md} +0 -0
- /package/docs/{test-report-0.0.7.md → Tests/test-report-0.0.7.md} +0 -0
- /package/docs/{testing.md → Tests/testing.md} +0 -0
package/README.md
CHANGED
package/dist/app/add-provider.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
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.addProvider = addProvider;
|
|
37
|
+
const crypto = __importStar(require("node:crypto"));
|
|
4
38
|
const config_1 = require("../domain/config");
|
|
5
39
|
const providers_1 = require("../domain/providers");
|
|
6
40
|
const errors_1 = require("../domain/errors");
|
|
@@ -8,6 +42,7 @@ const config_repo_1 = require("../storage/config-repo");
|
|
|
8
42
|
const fs_utils_1 = require("../storage/fs-utils");
|
|
9
43
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
10
44
|
const auth_repo_1 = require("../storage/auth-repo");
|
|
45
|
+
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
11
46
|
const run_mutation_1 = require("./run-mutation");
|
|
12
47
|
/**
|
|
13
48
|
* Adds a new provider record to the managed providers registry.
|
|
@@ -18,6 +53,34 @@ function addProvider(args) {
|
|
|
18
53
|
if (providers.providers[args.providerName]) {
|
|
19
54
|
throw (0, errors_1.cliError)("INVALID_IMPORT_FILE", `Provider "${args.providerName}" already exists.`);
|
|
20
55
|
}
|
|
56
|
+
const bridgeHost = args.bridgeHost ?? "127.0.0.1";
|
|
57
|
+
const bridgePort = args.bridgePort ?? 41415;
|
|
58
|
+
const runtime = args.copilot
|
|
59
|
+
? {
|
|
60
|
+
kind: "copilot-sdk-bridge",
|
|
61
|
+
upstream: "github-copilot",
|
|
62
|
+
bridgeHost,
|
|
63
|
+
bridgePort,
|
|
64
|
+
bridgePath: "/v1",
|
|
65
|
+
premiumRequests: true,
|
|
66
|
+
authSource: "official-sdk",
|
|
67
|
+
sdkInstallMode: "lazy",
|
|
68
|
+
}
|
|
69
|
+
: undefined;
|
|
70
|
+
if (args.copilot) {
|
|
71
|
+
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
72
|
+
if (!installStatus.installed) {
|
|
73
|
+
if (!args.installCopilotSdk) {
|
|
74
|
+
throw (0, errors_1.cliError)(args.interactive ? "COPILOT_SDK_MISSING" : "COPILOT_SDK_INSTALL_REQUIRES_TTY", args.interactive
|
|
75
|
+
? "The optional Copilot SDK runtime is not installed. Re-run with --install-copilot-sdk or confirm installation interactively."
|
|
76
|
+
: "The optional Copilot SDK runtime is not installed. Pass --install-copilot-sdk when running non-interactively.", {
|
|
77
|
+
installDir: installStatus.installDir,
|
|
78
|
+
packageName: installStatus.packageName,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
(0, copilot_installer_1.installCopilotSdk)();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
21
84
|
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
22
85
|
const existingProfile = document.profiles.find((profile) => profile.name === args.profile);
|
|
23
86
|
const existingModelProvider = document.modelProviders.find((entry) => entry.name === args.profile);
|
|
@@ -38,7 +101,7 @@ function addProvider(args) {
|
|
|
38
101
|
const upsertModelProviders = !existingModelProvider && args.createProfile
|
|
39
102
|
? {
|
|
40
103
|
[args.profile]: {
|
|
41
|
-
baseUrl: args.baseUrl ?? undefined,
|
|
104
|
+
baseUrl: args.copilot ? (0, providers_1.buildCopilotBridgeBaseUrl)(runtime) : args.baseUrl ?? undefined,
|
|
42
105
|
envKey: (0, config_1.buildManagedProfileEnvKey)(args.profile),
|
|
43
106
|
},
|
|
44
107
|
}
|
|
@@ -47,16 +110,19 @@ function addProvider(args) {
|
|
|
47
110
|
(0, config_repo_1.requireManagedProfileRuntime)(document, providers, args.profile);
|
|
48
111
|
}
|
|
49
112
|
const envKey = existingModelProvider?.envKey ?? (0, config_1.buildManagedProfileEnvKey)(args.profile);
|
|
113
|
+
const apiKey = args.copilot ? args.bridgeApiKey ?? crypto.randomBytes(24).toString("hex") : args.apiKey;
|
|
114
|
+
const baseUrl = args.copilot ? (0, providers_1.buildCopilotBridgeBaseUrl)(runtime) : args.baseUrl ?? undefined;
|
|
50
115
|
const next = {
|
|
51
116
|
providers: {
|
|
52
117
|
...providers.providers,
|
|
53
118
|
[args.providerName]: (0, providers_1.cleanProviderRecord)({
|
|
54
119
|
profile: args.profile,
|
|
55
|
-
apiKey
|
|
120
|
+
apiKey,
|
|
56
121
|
envKey,
|
|
57
|
-
baseUrl
|
|
122
|
+
baseUrl,
|
|
58
123
|
note: args.note ?? undefined,
|
|
59
124
|
tags: args.tags,
|
|
125
|
+
runtime,
|
|
60
126
|
}),
|
|
61
127
|
},
|
|
62
128
|
};
|
|
@@ -87,6 +153,7 @@ function addProvider(args) {
|
|
|
87
153
|
provider: args.providerName,
|
|
88
154
|
profile: args.profile,
|
|
89
155
|
envKey,
|
|
156
|
+
runtimeKind: runtime?.kind ?? null,
|
|
90
157
|
createdProfileSections: configPlan.createdProfileSections,
|
|
91
158
|
createdModelProviderSections: configPlan.createdModelProviderSections,
|
|
92
159
|
deletedProfileSections: configPlan.deletedProfileSections,
|
|
@@ -0,0 +1,296 @@
|
|
|
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
|
+
runtime: args.runtime,
|
|
26
|
+
json: args.json,
|
|
27
|
+
commandName: "start",
|
|
28
|
+
preferRuntimeState: false,
|
|
29
|
+
});
|
|
30
|
+
await requireBridgeRuntimeReadiness();
|
|
31
|
+
const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(target.providerName, target.provider);
|
|
32
|
+
const nextProvider = bridge.portChanged ? rewriteBridgeProviderPort(target.provider, bridge.port) : target.provider;
|
|
33
|
+
if (bridge.portChanged) {
|
|
34
|
+
try {
|
|
35
|
+
persistRecoveredBridgePort({
|
|
36
|
+
providersPath: args.providersPath,
|
|
37
|
+
configPath: args.configPath,
|
|
38
|
+
providers,
|
|
39
|
+
providerName: target.providerName,
|
|
40
|
+
previousProvider: target.provider,
|
|
41
|
+
provider: nextProvider,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (!bridge.reused) {
|
|
46
|
+
(0, copilot_bridge_1.stopCopilotBridge)();
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
data: {
|
|
53
|
+
provider: target.providerName,
|
|
54
|
+
profile: nextProvider.profile,
|
|
55
|
+
baseUrl: (0, providers_1.buildCopilotBridgeBaseUrl)(nextProvider.runtime),
|
|
56
|
+
host: bridge.host,
|
|
57
|
+
port: bridge.port,
|
|
58
|
+
reused: bridge.reused,
|
|
59
|
+
portChanged: bridge.portChanged,
|
|
60
|
+
defaultPort: DEFAULT_BRIDGE_PORT,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Stops the managed Copilot bridge.
|
|
66
|
+
*/
|
|
67
|
+
async function stopBridge(args) {
|
|
68
|
+
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
69
|
+
const state = (0, runtime_state_repo_1.readCopilotBridgeState)();
|
|
70
|
+
if (!state && !args.providerName) {
|
|
71
|
+
return {
|
|
72
|
+
data: {
|
|
73
|
+
provider: null,
|
|
74
|
+
stopped: true,
|
|
75
|
+
hadRuntimeState: false,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (!state && args.providerName) {
|
|
80
|
+
resolveNamedBridgeProvider(providers, args.providerName);
|
|
81
|
+
return {
|
|
82
|
+
data: {
|
|
83
|
+
provider: args.providerName,
|
|
84
|
+
stopped: true,
|
|
85
|
+
hadRuntimeState: false,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const target = await resolveBridgeTarget({
|
|
90
|
+
requestedProviderName: args.providerName ?? null,
|
|
91
|
+
providers,
|
|
92
|
+
configPath: args.configPath,
|
|
93
|
+
runtime: args.runtime,
|
|
94
|
+
json: args.json,
|
|
95
|
+
commandName: "stop",
|
|
96
|
+
preferRuntimeState: true,
|
|
97
|
+
});
|
|
98
|
+
if (args.providerName && state?.provider && state.provider !== args.providerName) {
|
|
99
|
+
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Bridge runtime state belongs to "${state.provider}" not "${args.providerName}".`, {
|
|
100
|
+
stateProvider: state.provider,
|
|
101
|
+
requestedProvider: args.providerName,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
(0, copilot_bridge_1.stopCopilotBridge)();
|
|
105
|
+
return {
|
|
106
|
+
data: {
|
|
107
|
+
provider: target.providerName,
|
|
108
|
+
stopped: true,
|
|
109
|
+
hadRuntimeState: Boolean(state),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Reports the managed Copilot bridge state.
|
|
115
|
+
*/
|
|
116
|
+
async function statusBridge(args) {
|
|
117
|
+
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
118
|
+
const state = (0, runtime_state_repo_1.readCopilotBridgeState)();
|
|
119
|
+
const target = await resolveBridgeTarget({
|
|
120
|
+
requestedProviderName: args.providerName ?? null,
|
|
121
|
+
providers,
|
|
122
|
+
configPath: args.configPath,
|
|
123
|
+
runtime: args.runtime,
|
|
124
|
+
json: args.json,
|
|
125
|
+
commandName: "status",
|
|
126
|
+
preferRuntimeState: true,
|
|
127
|
+
});
|
|
128
|
+
const provider = target.provider;
|
|
129
|
+
const runtimeStatus = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(provider);
|
|
130
|
+
const expectedBaseUrl = (0, providers_1.buildCopilotBridgeBaseUrl)(provider.runtime);
|
|
131
|
+
if (args.providerName && state?.provider && state.provider !== args.providerName) {
|
|
132
|
+
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Bridge runtime state belongs to "${state.provider}" not "${args.providerName}".`, {
|
|
133
|
+
stateProvider: state.provider,
|
|
134
|
+
requestedProvider: args.providerName,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
data: {
|
|
139
|
+
provider: target.providerName,
|
|
140
|
+
profile: provider.profile,
|
|
141
|
+
runtimeState: state,
|
|
142
|
+
expectedBaseUrl,
|
|
143
|
+
matches: Boolean(state && state.provider === target.providerName && state.baseUrl === expectedBaseUrl),
|
|
144
|
+
active: runtimeStatus.ok,
|
|
145
|
+
health: runtimeStatus,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Resolves the Copilot provider target for one bridge command.
|
|
151
|
+
*/
|
|
152
|
+
async function resolveBridgeTarget(args) {
|
|
153
|
+
if (args.requestedProviderName) {
|
|
154
|
+
return resolveNamedBridgeProvider(args.providers, args.requestedProviderName);
|
|
155
|
+
}
|
|
156
|
+
if (args.preferRuntimeState) {
|
|
157
|
+
const runtimeState = (0, runtime_state_repo_1.readCopilotBridgeState)();
|
|
158
|
+
if (runtimeState?.provider && args.providers.providers[runtimeState.provider]) {
|
|
159
|
+
return resolveNamedBridgeProvider(args.providers, runtimeState.provider);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const activeTarget = resolveActiveCopilotBridgeProvider(args.providers, args.configPath);
|
|
163
|
+
if (activeTarget) {
|
|
164
|
+
return activeTarget;
|
|
165
|
+
}
|
|
166
|
+
const copilotTargets = listCopilotBridgeProviders(args.providers);
|
|
167
|
+
if (copilotTargets.length === 1) {
|
|
168
|
+
return copilotTargets[0];
|
|
169
|
+
}
|
|
170
|
+
if ((0, interactive_1.canPrompt)(args.runtime, args.json)) {
|
|
171
|
+
const selected = await promptForCopilotBridgeSelection(args.runtime, copilotTargets, args.commandName);
|
|
172
|
+
return resolveNamedBridgeProvider(args.providers, selected);
|
|
173
|
+
}
|
|
174
|
+
throw (0, errors_1.cliError)("BRIDGE_TARGET_UNRESOLVED", `Unable to resolve a Copilot provider for bridge ${args.commandName}.`, {
|
|
175
|
+
availableProviders: copilotTargets.map((entry) => entry.providerName),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Resolves the active provider when the current top-level profile maps to one Copilot bridge provider.
|
|
180
|
+
*/
|
|
181
|
+
function resolveActiveCopilotBridgeProvider(providers, configPath) {
|
|
182
|
+
try {
|
|
183
|
+
const currentProfile = (0, config_repo_1.readCurrentProfile)(configPath);
|
|
184
|
+
const matches = Object.entries(providers.providers)
|
|
185
|
+
.filter(([, provider]) => provider.profile === currentProfile && (0, providers_1.isCopilotBridgeProvider)(provider))
|
|
186
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
187
|
+
if (matches.length === 1) {
|
|
188
|
+
return {
|
|
189
|
+
providerName: matches[0][0],
|
|
190
|
+
provider: matches[0][1],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolves one named provider and enforces the Copilot bridge runtime kind.
|
|
201
|
+
*/
|
|
202
|
+
function resolveNamedBridgeProvider(providers, providerName) {
|
|
203
|
+
const provider = providers.providers[providerName];
|
|
204
|
+
if (!provider) {
|
|
205
|
+
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${providerName}" was not found.`, {
|
|
206
|
+
provider: providerName,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (!(0, providers_1.isCopilotBridgeProvider)(provider)) {
|
|
210
|
+
throw (0, errors_1.cliError)("BRIDGE_PROVIDER_MISMATCH", `Provider "${providerName}" is not a Copilot bridge provider.`, {
|
|
211
|
+
provider: providerName,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return { providerName, provider };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Lists all configured Copilot bridge providers in stable order.
|
|
218
|
+
*/
|
|
219
|
+
function listCopilotBridgeProviders(providers) {
|
|
220
|
+
return Object.entries(providers.providers)
|
|
221
|
+
.filter(([, provider]) => (0, providers_1.isCopilotBridgeProvider)(provider))
|
|
222
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
223
|
+
.map(([providerName, provider]) => ({ providerName, provider }));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Uses a Copilot-only provider picker instead of the generic provider selector.
|
|
227
|
+
*/
|
|
228
|
+
async function promptForCopilotBridgeSelection(runtime, targets, commandName) {
|
|
229
|
+
if (targets.length === 0) {
|
|
230
|
+
throw (0, errors_1.cliError)("BRIDGE_TARGET_UNRESOLVED", `No Copilot bridge providers are configured for bridge ${commandName}.`);
|
|
231
|
+
}
|
|
232
|
+
return runtime.selectOne(`Choose a Copilot provider to ${commandName}`, targets.map((target) => ({
|
|
233
|
+
value: target.providerName,
|
|
234
|
+
label: target.providerName,
|
|
235
|
+
hint: target.provider.profile,
|
|
236
|
+
})));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Verifies that the local Copilot bridge prerequisites are available before startup.
|
|
240
|
+
*/
|
|
241
|
+
async function requireBridgeRuntimeReadiness() {
|
|
242
|
+
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
243
|
+
if (!installStatus.installed) {
|
|
244
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
|
|
245
|
+
installDir: installStatus.installDir,
|
|
246
|
+
packageName: installStatus.packageName,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
await (0, copilot_adapter_1.readCopilotAuthState)();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Rewrites one Copilot bridge provider record with a recovered runtime port.
|
|
253
|
+
*/
|
|
254
|
+
function rewriteBridgeProviderPort(provider, port) {
|
|
255
|
+
return (0, providers_1.cleanProviderRecord)({
|
|
256
|
+
...provider,
|
|
257
|
+
baseUrl: `http://${provider.runtime.bridgeHost}:${port}${provider.runtime.bridgePath}`,
|
|
258
|
+
runtime: {
|
|
259
|
+
...provider.runtime,
|
|
260
|
+
bridgePort: port,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Persists the recovered bridge port to both providers.json and config.toml.
|
|
266
|
+
*/
|
|
267
|
+
function persistRecoveredBridgePort(args) {
|
|
268
|
+
const previousProviders = {
|
|
269
|
+
providers: {
|
|
270
|
+
...args.providers.providers,
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
const nextProviders = {
|
|
274
|
+
providers: {
|
|
275
|
+
...args.providers.providers,
|
|
276
|
+
[args.providerName]: args.provider,
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, nextProviders);
|
|
280
|
+
try {
|
|
281
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
282
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
283
|
+
upsertModelProviders: {
|
|
284
|
+
[args.provider.profile]: {
|
|
285
|
+
baseUrl: (0, providers_1.buildCopilotBridgeBaseUrl)(args.provider.runtime),
|
|
286
|
+
envKey: (0, config_repo_1.requireRuntimeEnvKey)(document, args.provider.profile),
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, previousProviders);
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
}
|
package/dist/app/get-status.js
CHANGED
|
@@ -41,10 +41,14 @@ const providers_1 = require("../domain/providers");
|
|
|
41
41
|
const config_repo_1 = require("../storage/config-repo");
|
|
42
42
|
const providers_repo_1 = require("../storage/providers-repo");
|
|
43
43
|
const auth_repo_1 = require("../storage/auth-repo");
|
|
44
|
+
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
45
|
+
const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
46
|
+
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
47
|
+
const runtime_state_repo_1 = require("../storage/runtime-state-repo");
|
|
44
48
|
/**
|
|
45
49
|
* Reports the current on-disk runtime state and how it maps back to managed providers.
|
|
46
50
|
*/
|
|
47
|
-
function getStatus(codexDir, configPath, providersPath, authPath) {
|
|
51
|
+
async function getStatus(codexDir, configPath, providersPath, authPath) {
|
|
48
52
|
const configExists = fs.existsSync(configPath);
|
|
49
53
|
const providersExists = fs.existsSync(providersPath);
|
|
50
54
|
let currentProfile = null;
|
|
@@ -64,6 +68,30 @@ function getStatus(codexDir, configPath, providersPath, authPath) {
|
|
|
64
68
|
}
|
|
65
69
|
const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentProfile, providers);
|
|
66
70
|
const activeProviderCandidates = currentProfile && providers ? (0, providers_1.findProvidersByProfile)(providers, currentProfile) : [];
|
|
71
|
+
const activeProvider = activeProviderCandidates.length === 1 && providers ? providers.providers[activeProviderCandidates[0]] : null;
|
|
72
|
+
const copilotInstall = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
73
|
+
const runtimeState = (0, runtime_state_repo_1.readCopilotBridgeState)();
|
|
74
|
+
const runtimeStateProvider = runtimeState && providers ? providers.providers[runtimeState.provider] ?? null : null;
|
|
75
|
+
const bridgeProbeTarget = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
|
|
76
|
+
? activeProvider
|
|
77
|
+
: runtimeStateProvider && (0, providers_1.isCopilotBridgeProvider)(runtimeStateProvider)
|
|
78
|
+
? runtimeStateProvider
|
|
79
|
+
: null;
|
|
80
|
+
const copilotBridge = bridgeProbeTarget ? await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(bridgeProbeTarget) : runtimeState ? {
|
|
81
|
+
ok: false,
|
|
82
|
+
runtime: "copilot-bridge",
|
|
83
|
+
reason: "failed",
|
|
84
|
+
cause: "Copilot bridge runtime state exists but no matching managed Copilot provider is active.",
|
|
85
|
+
details: runtimeState,
|
|
86
|
+
} : null;
|
|
87
|
+
const copilotAuth = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
|
|
88
|
+
? await (0, copilot_adapter_1.readCopilotAuthState)().catch((error) => ({
|
|
89
|
+
ready: false,
|
|
90
|
+
source: "official-sdk",
|
|
91
|
+
mode: "session",
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
}))
|
|
94
|
+
: null;
|
|
67
95
|
if (liveState.canBackfillActiveProvider) {
|
|
68
96
|
// Surface unmanaged live state without mutating anything during a read-only status call.
|
|
69
97
|
warnings.push("Current config profile is not mapped in providers.json. Backfill would be required before treating live state as managed.");
|
|
@@ -80,6 +108,16 @@ function getStatus(codexDir, configPath, providersPath, authPath) {
|
|
|
80
108
|
provider: liveState.mappedProvider,
|
|
81
109
|
activeProviderResolvable: activeProviderCandidates.length === 1,
|
|
82
110
|
activeProviderCandidates,
|
|
111
|
+
runtimeProvider: activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider) ? activeProvider.runtime?.kind ?? null : null,
|
|
112
|
+
copilotSdk: {
|
|
113
|
+
installed: copilotInstall.installed,
|
|
114
|
+
installDir: copilotInstall.installDir,
|
|
115
|
+
packageName: copilotInstall.packageName,
|
|
116
|
+
packageVersion: copilotInstall.packageVersion ?? null,
|
|
117
|
+
},
|
|
118
|
+
copilotAuth,
|
|
119
|
+
copilotBridge,
|
|
120
|
+
copilotRuntimeState: runtimeState,
|
|
83
121
|
liveState,
|
|
84
122
|
auth: authState,
|
|
85
123
|
configProfiles: configViews,
|
package/dist/app/run-doctor.js
CHANGED
|
@@ -43,10 +43,14 @@ const errors_1 = require("../domain/errors");
|
|
|
43
43
|
const codex_probe_1 = require("../runtime/codex-probe");
|
|
44
44
|
const auth_repo_1 = require("../storage/auth-repo");
|
|
45
45
|
const providers_1 = require("../domain/providers");
|
|
46
|
+
const copilot_installer_1 = require("../runtime/copilot-installer");
|
|
47
|
+
const copilot_bridge_1 = require("../runtime/copilot-bridge");
|
|
48
|
+
const copilot_adapter_1 = require("../runtime/copilot-adapter");
|
|
49
|
+
const runtime_state_repo_1 = require("../storage/runtime-state-repo");
|
|
46
50
|
/**
|
|
47
51
|
* Performs consistency checks across config.toml, providers.json, and the local Codex CLI.
|
|
48
52
|
*/
|
|
49
|
-
function runDoctor(args) {
|
|
53
|
+
async function runDoctor(args) {
|
|
50
54
|
const issues = [];
|
|
51
55
|
let currentProfile = null;
|
|
52
56
|
let providers = null;
|
|
@@ -99,6 +103,7 @@ function runDoctor(args) {
|
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
const authState = (0, auth_repo_1.readManagedAuthState)(args.authPath);
|
|
106
|
+
const runtimeState = (0, runtime_state_repo_1.readCopilotBridgeState)();
|
|
102
107
|
if (authState.exists && !authState.valid) {
|
|
103
108
|
issues.push({
|
|
104
109
|
code: "AUTH_JSON_INVALID",
|
|
@@ -134,6 +139,56 @@ function runDoctor(args) {
|
|
|
134
139
|
provider: matches[0],
|
|
135
140
|
});
|
|
136
141
|
}
|
|
142
|
+
if ((0, providers_1.isCopilotBridgeProvider)(activeProvider)) {
|
|
143
|
+
const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
|
|
144
|
+
if (!installStatus.installed) {
|
|
145
|
+
issues.push({
|
|
146
|
+
code: "COPILOT_SDK_MISSING",
|
|
147
|
+
message: "The optional Copilot SDK runtime is not installed.",
|
|
148
|
+
installDir: installStatus.installDir,
|
|
149
|
+
packageName: installStatus.packageName,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
await (0, copilot_adapter_1.readCopilotAuthState)();
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const normalized = (0, errors_1.normalizeError)(error);
|
|
157
|
+
issues.push({
|
|
158
|
+
code: normalized.code,
|
|
159
|
+
message: normalized.message,
|
|
160
|
+
...(normalized.details ?? {}),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const bridge = await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(activeProvider);
|
|
164
|
+
if (!bridge.ok) {
|
|
165
|
+
issues.push({
|
|
166
|
+
code: mapBridgeDiagnosticCode(bridge.cause),
|
|
167
|
+
message: bridge.cause,
|
|
168
|
+
...(bridge.details ?? {}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (runtimeState && providers) {
|
|
175
|
+
const runtimeProvider = providers.providers[runtimeState.provider] ?? null;
|
|
176
|
+
if (!runtimeProvider || !(0, providers_1.isCopilotBridgeProvider)(runtimeProvider)) {
|
|
177
|
+
issues.push({
|
|
178
|
+
code: "BRIDGE_STATE_STALE",
|
|
179
|
+
message: "Copilot bridge runtime state exists but no matching managed Copilot provider is available.",
|
|
180
|
+
...runtimeState,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else if (!document?.activeProfile || runtimeProvider.profile !== document.activeProfile) {
|
|
184
|
+
issues.push({
|
|
185
|
+
code: "BRIDGE_STATE_STALE",
|
|
186
|
+
message: "Copilot bridge runtime state exists for a provider that is not the current active profile.",
|
|
187
|
+
activeProfile: document?.activeProfile ?? null,
|
|
188
|
+
runtimeProvider: runtimeState.provider,
|
|
189
|
+
runtimeProfile: runtimeProvider.profile,
|
|
190
|
+
...runtimeState,
|
|
191
|
+
});
|
|
137
192
|
}
|
|
138
193
|
}
|
|
139
194
|
// Drift inspection still runs when files are missing so status output can explain partial state.
|
|
@@ -167,6 +222,18 @@ function runDoctor(args) {
|
|
|
167
222
|
warnings: issues.length === 0 ? [] : [`doctor found ${issues.length} issue(s)`],
|
|
168
223
|
};
|
|
169
224
|
}
|
|
225
|
+
function mapBridgeDiagnosticCode(cause) {
|
|
226
|
+
if (cause === "Copilot bridge state manifest is missing.") {
|
|
227
|
+
return "BRIDGE_STATE_MISSING";
|
|
228
|
+
}
|
|
229
|
+
if (cause === "Copilot bridge runtime state exists but no active Copilot bridge provider is selected.") {
|
|
230
|
+
return "BRIDGE_STATE_STALE";
|
|
231
|
+
}
|
|
232
|
+
if (cause === "Copilot bridge state base URL does not match the provider runtime configuration.") {
|
|
233
|
+
return "PROVIDER_BASE_URL_MISMATCH";
|
|
234
|
+
}
|
|
235
|
+
return "BRIDGE_HEALTHCHECK_FAILED";
|
|
236
|
+
}
|
|
170
237
|
/**
|
|
171
238
|
* Maps structured config consistency issues onto stable human-readable diagnostic text.
|
|
172
239
|
*/
|
package/dist/app/setup-codex.js
CHANGED
|
@@ -49,7 +49,7 @@ const MIN_CODEX_VERSION = "0.0.1";
|
|
|
49
49
|
/**
|
|
50
50
|
* Migrates unmanaged Codex config profiles into a managed providers.json registry.
|
|
51
51
|
*/
|
|
52
|
-
function migrateCodex(args) {
|
|
52
|
+
async function migrateCodex(args) {
|
|
53
53
|
const available = (0, codex_cli_1.checkCodexAvailable)();
|
|
54
54
|
if (!available.ok) {
|
|
55
55
|
throw (0, errors_1.cliError)("CODEX_NOT_INSTALLED", "codex CLI is not available.", {
|
|
@@ -148,7 +148,7 @@ function migrateCodex(args) {
|
|
|
148
148
|
},
|
|
149
149
|
});
|
|
150
150
|
// Re-run doctor on the final state so migrate returns immediate post-migration diagnostics.
|
|
151
|
-
const doctor = (0, run_doctor_1.runDoctor)({
|
|
151
|
+
const doctor = await (0, run_doctor_1.runDoctor)({
|
|
152
152
|
codexDir: args.codexDir,
|
|
153
153
|
configPath: args.configPath,
|
|
154
154
|
providersPath: args.providersPath,
|