@minniexcode/codex-switch 0.0.8 → 0.0.10

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.
Files changed (44) hide show
  1. package/README.AI.md +5 -3
  2. package/README.CN.md +25 -3
  3. package/README.md +3 -2
  4. package/dist/app/add-provider.js +1 -12
  5. package/dist/app/bridge.js +295 -0
  6. package/dist/app/edit-provider.js +1 -17
  7. package/dist/app/get-status.js +32 -2
  8. package/dist/app/list-providers.js +0 -1
  9. package/dist/app/run-doctor.js +45 -38
  10. package/dist/app/setup-codex.js +27 -17
  11. package/dist/app/show-config.js +1 -5
  12. package/dist/app/switch-provider.js +33 -20
  13. package/dist/cli/output.js +4 -6
  14. package/dist/cli.js +1 -1
  15. package/dist/commands/handlers.js +223 -39
  16. package/dist/commands/help.js +1 -0
  17. package/dist/commands/registry.js +48 -4
  18. package/dist/domain/config.js +4 -68
  19. package/dist/domain/providers.js +0 -5
  20. package/dist/domain/runtime-state.js +2 -1
  21. package/dist/domain/setup.js +58 -3
  22. package/dist/interaction/add-interactive.js +55 -1
  23. package/dist/interaction/interactive.js +1 -5
  24. package/dist/runtime/copilot-adapter.js +44 -1
  25. package/dist/runtime/copilot-bridge-worker.js +1 -1
  26. package/dist/runtime/copilot-bridge.js +60 -19
  27. package/dist/runtime/copilot-cli.js +70 -0
  28. package/dist/runtime/copilot-installer.js +49 -2
  29. package/dist/storage/auth-repo.js +28 -77
  30. package/dist/storage/config-repo.js +1 -36
  31. package/dist/storage/runtime-state-repo.js +32 -0
  32. package/docs/Design/codex-switch-copilot-integration-design.md +517 -0
  33. package/docs/Design/codex-switch-v0.0.10-design.md +669 -0
  34. package/docs/Design/codex-switch-v0.0.9-design.md +182 -0
  35. package/docs/PRD/codex-switch-prd-v0.0.10.md +406 -0
  36. package/docs/PRD/codex-switch-prd-v0.0.9.md +166 -0
  37. package/docs/Tests/testing-bridge-v0.0.9.md +367 -0
  38. package/docs/cli-usage.md +38 -14
  39. package/docs/codex-switch-product-overview.md +2 -2
  40. package/docs/codex-switch-technical-architecture.md +6 -5
  41. package/package.json +1 -1
  42. /package/docs/{test-report-0.0.5.md → Tests/test-report-0.0.5.md} +0 -0
  43. /package/docs/{test-report-0.0.7.md → Tests/test-report-0.0.7.md} +0 -0
  44. /package/docs/{testing.md → Tests/testing.md} +0 -0
package/README.AI.md CHANGED
@@ -54,7 +54,8 @@ Shared flags:
54
54
  Operational model:
55
55
 
56
56
  - `providers.json` is the management-state source of truth
57
- - `config.toml` and `auth.json` are runtime mirrors
57
+ - `config.toml` is the managed runtime-routing file
58
+ - `auth.json` is the active auth projection file for direct providers and is also inspected by status/doctor
58
59
  - `backups/latest.json` tracks the latest rollback state
59
60
  - mutating commands should back up first and run under a lightweight file lock
60
61
 
@@ -93,12 +94,13 @@ codexs status --json
93
94
  Current package version in this repository:
94
95
 
95
96
  ```text
96
- 0.0.7
97
+ 0.0.10
97
98
  ```
98
99
 
99
100
  Recent version summary:
100
101
 
101
- - `0.0.7`: command-surface refactor, env_key/auth-mirror model corrections, and the `setup` split into `init` plus `migrate`
102
+ - `0.0.10`: `init` / `migrate` command split finalized, `setup` deprecated, and the managed provider model reduced to static profile plus `base_url` configuration
103
+ - `0.0.7`: command-surface refactor, env_key/auth-mirror model corrections, and the initial `setup` split toward `init` plus `migrate`
102
104
  - `0.0.4`: setup/show/edit/backups list/specific rollback/import merge and clearer CLI semantics
103
105
  - `0.0.3`: interactive TTY flows and improved help
104
106
  - `0.0.2`: mutation orchestration, backups, rollback, locks, drift detection improvements
package/README.CN.md CHANGED
@@ -17,30 +17,43 @@
17
17
 
18
18
  ## 现在可以做什么
19
19
 
20
- 当前 MVP 命令如下:
20
+ 当前命令面如下:
21
21
 
22
22
  ```bash
23
+ codexs init
24
+ codexs migrate
23
25
  codexs list
26
+ codexs show <provider>
24
27
  codexs current
25
- codexs switch <provider>
26
28
  codexs status
29
+ codexs edit <provider>
30
+ codexs switch <provider>
27
31
  codexs import <file>
28
32
  codexs export <file>
29
33
  codexs add <provider>
30
34
  codexs remove <provider>
35
+ codexs backups list
31
36
  codexs doctor
32
37
  codexs rollback
38
+ codexs setup
33
39
  ```
34
40
 
35
41
  对应能力包括:
36
42
 
43
+ - 初始化空的受管 `providers.json`
44
+ - 从已有 `config.toml` adopt 可管理的 runtime profile
37
45
  - 查看本地已管理的 provider
46
+ - 查看单个 provider 详情
38
47
  - 查看当前激活的 profile
48
+ - 查看本地运行态摘要
49
+ - 编辑已有 provider
39
50
  - 安全切换 provider
40
51
  - 导入和导出 provider 映射
41
52
  - 新增和删除 provider
53
+ - 查看备份列表
42
54
  - 检查配置漂移和常见本地问题
43
55
  - 在变更前自动备份,并在失败时回滚
56
+ - 保留 `setup` 作为弃用入口,并引导到 `init` / `migrate`
44
57
 
45
58
  ## 简单用法
46
59
 
@@ -65,6 +78,8 @@ codexs --help
65
78
  典型使用方式:
66
79
 
67
80
  ```bash
81
+ codexs init
82
+ codexs migrate
68
83
  codexs list
69
84
  codexs current
70
85
  codexs add my-provider --profile my-provider --api-key sk-xxx
@@ -111,7 +126,8 @@ codexs status --json
111
126
  存储模型:
112
127
 
113
128
  - `providers.json` 是管理态的单一事实来源
114
- - `config.toml` 和 `auth.json` 是运行态文件
129
+ - `config.toml` 是受管的运行时路由文件
130
+ - `auth.json` 是独立的 Codex 认证状态文件,`status` / `doctor` 只读检查它
115
131
  - `backups/latest.json` 记录最近一次可回滚窗口
116
132
 
117
133
  注意:`providers.json` 可能包含 API key,应视为本地敏感文件。
@@ -128,6 +144,12 @@ codexs status --json
128
144
 
129
145
  ## 最近 3 个版本更新
130
146
 
147
+ ### 0.0.10
148
+
149
+ - 正式拆分 `setup`:新增 `init` 和 `migrate`,`setup` 变为弃用命令
150
+ - 增加 `show`、`edit`、`backups list` 等对当前命令面的整理
151
+ - 清理 provider/runtime 管理语义,CLI 只负责静态 profile 与 `base_url` 层配置
152
+
131
153
  ### 0.0.3
132
154
 
133
155
  - 为 `add`、`switch`、`remove`、`import`、`export`、`rollback` 增加了交互式 TTY 流程
package/README.md CHANGED
@@ -18,7 +18,7 @@ What it does:
18
18
  - Run diagnostics and detect local drift
19
19
  - List backups and roll back to a previous managed state
20
20
 
21
- Current version: `0.0.8`
21
+ Current version: `0.0.10`
22
22
 
23
23
  ## Install
24
24
 
@@ -117,7 +117,8 @@ Managed files:
117
117
  Notes:
118
118
 
119
119
  - `providers.json` is the managed provider registry
120
- - `config.toml` and `auth.json` represent runtime state
120
+ - `config.toml` is the managed runtime-routing file
121
+ - `auth.json` is the active auth projection file; direct-provider switches rewrite `OPENAI_API_KEY`, while `status` and `doctor` inspect its state
121
122
  - mutating commands back up before writing
122
123
  - rollback is available after failed or undesired changes
123
124
 
@@ -41,7 +41,6 @@ const errors_1 = require("../domain/errors");
41
41
  const config_repo_1 = require("../storage/config-repo");
42
42
  const fs_utils_1 = require("../storage/fs-utils");
43
43
  const providers_repo_1 = require("../storage/providers-repo");
44
- const auth_repo_1 = require("../storage/auth-repo");
45
44
  const copilot_installer_1 = require("../runtime/copilot-installer");
46
45
  const run_mutation_1 = require("./run-mutation");
47
46
  /**
@@ -54,7 +53,7 @@ function addProvider(args) {
54
53
  throw (0, errors_1.cliError)("INVALID_IMPORT_FILE", `Provider "${args.providerName}" already exists.`);
55
54
  }
56
55
  const bridgeHost = args.bridgeHost ?? "127.0.0.1";
57
- const bridgePort = args.bridgePort ?? 4141;
56
+ const bridgePort = args.bridgePort ?? 41415;
58
57
  const runtime = args.copilot
59
58
  ? {
60
59
  kind: "copilot-sdk-bridge",
@@ -102,14 +101,12 @@ function addProvider(args) {
102
101
  ? {
103
102
  [args.profile]: {
104
103
  baseUrl: args.copilot ? (0, providers_1.buildCopilotBridgeBaseUrl)(runtime) : args.baseUrl ?? undefined,
105
- envKey: (0, config_1.buildManagedProfileEnvKey)(args.profile),
106
104
  },
107
105
  }
108
106
  : undefined;
109
107
  if (existingProfile) {
110
108
  (0, config_repo_1.requireManagedProfileRuntime)(document, providers, args.profile);
111
109
  }
112
- const envKey = existingModelProvider?.envKey ?? (0, config_1.buildManagedProfileEnvKey)(args.profile);
113
110
  const apiKey = args.copilot ? args.bridgeApiKey ?? crypto.randomBytes(24).toString("hex") : args.apiKey;
114
111
  const baseUrl = args.copilot ? (0, providers_1.buildCopilotBridgeBaseUrl)(runtime) : args.baseUrl ?? undefined;
115
112
  const next = {
@@ -118,7 +115,6 @@ function addProvider(args) {
118
115
  [args.providerName]: (0, providers_1.cleanProviderRecord)({
119
116
  profile: args.profile,
120
117
  apiKey,
121
- envKey,
122
118
  baseUrl,
123
119
  note: args.note ?? undefined,
124
120
  tags: args.tags,
@@ -134,7 +130,6 @@ function addProvider(args) {
134
130
  files: [
135
131
  { absolutePath: args.providersPath, relativePath: "providers.json" },
136
132
  { absolutePath: args.configPath, relativePath: "config.toml" },
137
- { absolutePath: args.authPath, relativePath: "auth.json" },
138
133
  ],
139
134
  mutate: () => {
140
135
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
@@ -144,15 +139,9 @@ function addProvider(args) {
144
139
  // Persist only the normalized provider payload so later reads are deterministic.
145
140
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
146
141
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
147
- if (document.activeProfile === args.profile) {
148
- const activeProviderName = (0, config_repo_1.resolveActiveProviderName)(document, next);
149
- const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
150
- (0, auth_repo_1.writeAuthFile)(args.authPath, next.providers[activeProviderName], existingAuth ?? undefined);
151
- }
152
142
  return {
153
143
  provider: args.providerName,
154
144
  profile: args.profile,
155
- envKey,
156
145
  runtimeKind: runtime?.kind ?? null,
157
146
  createdProfileSections: configPlan.createdProfileSections,
158
147
  createdModelProviderSections: configPlan.createdModelProviderSections,
@@ -0,0 +1,295 @@
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
+ },
287
+ },
288
+ });
289
+ (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
290
+ }
291
+ catch (error) {
292
+ (0, providers_repo_1.writeProvidersFile)(args.providersPath, previousProviders);
293
+ throw error;
294
+ }
295
+ }
@@ -7,7 +7,6 @@ const providers_1 = require("../domain/providers");
7
7
  const config_repo_1 = require("../storage/config-repo");
8
8
  const fs_utils_1 = require("../storage/fs-utils");
9
9
  const providers_repo_1 = require("../storage/providers-repo");
10
- const auth_repo_1 = require("../storage/auth-repo");
11
10
  const run_mutation_1 = require("./run-mutation");
12
11
  /**
13
12
  * Updates selected fields on a single managed provider.
@@ -63,23 +62,15 @@ function editProvider(args) {
63
62
  upsertModelProviders = {
64
63
  [newProfile]: {
65
64
  baseUrl: args.baseUrl ?? undefined,
66
- envKey: (0, config_1.buildManagedProfileEnvKey)(newProfile),
67
65
  },
68
66
  };
69
67
  }
70
68
  else {
71
69
  (0, config_repo_1.requireManagedProfileRuntime)(document, providers, newProfile);
72
70
  }
73
- const nextEnvKey = args.profile !== undefined && args.profile !== current.profile
74
- ? targetModelProviderSection?.envKey ?? (0, config_1.buildManagedProfileEnvKey)(newProfile)
75
- : current.envKey;
76
- if (nextEnvKey !== current.envKey) {
77
- updatedFields.push("envKey");
78
- }
79
71
  const nextRecord = (0, providers_1.cleanProviderRecord)({
80
72
  profile: newProfile,
81
73
  apiKey: args.apiKey ?? current.apiKey,
82
- envKey: nextEnvKey,
83
74
  baseUrl: args.baseUrl === null ? undefined : args.baseUrl ?? current.baseUrl,
84
75
  note: args.note === null ? undefined : args.note ?? current.note,
85
76
  tags: args.tags ?? current.tags,
@@ -90,7 +81,7 @@ function editProvider(args) {
90
81
  ...(args.model !== undefined && args.model !== null ? { model: args.model } : {}),
91
82
  },
92
83
  };
93
- if (args.model !== undefined && (targetSection?.model !== args.model) && !updatedFields.includes("model")) {
84
+ if (args.model !== undefined && targetSection?.model !== args.model && !updatedFields.includes("model")) {
94
85
  updatedFields.push("model");
95
86
  }
96
87
  }
@@ -125,7 +116,6 @@ function editProvider(args) {
125
116
  files: [
126
117
  { absolutePath: args.providersPath, relativePath: "providers.json" },
127
118
  { absolutePath: args.configPath, relativePath: "config.toml" },
128
- { absolutePath: args.authPath, relativePath: "auth.json" },
129
119
  ],
130
120
  mutate: () => {
131
121
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
@@ -143,12 +133,6 @@ function editProvider(args) {
143
133
  // Write providers first so the registry and config move together inside the managed backup boundary.
144
134
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, nextProviders);
145
135
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
146
- const updatedDocument = (0, config_repo_1.readStructuredConfig)(args.configPath);
147
- if (updatedDocument.activeProfile) {
148
- const activeProviderName = (0, config_repo_1.resolveActiveProviderName)(updatedDocument, nextProviders);
149
- const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
150
- (0, auth_repo_1.writeAuthFile)(args.authPath, nextProviders.providers[activeProviderName], existingAuth ?? undefined);
151
- }
152
136
  return {
153
137
  provider: args.providerName,
154
138
  updatedFields,
@@ -44,6 +44,7 @@ const auth_repo_1 = require("../storage/auth-repo");
44
44
  const copilot_installer_1 = require("../runtime/copilot-installer");
45
45
  const copilot_bridge_1 = require("../runtime/copilot-bridge");
46
46
  const copilot_adapter_1 = require("../runtime/copilot-adapter");
47
+ const runtime_state_repo_1 = require("../storage/runtime-state-repo");
47
48
  /**
48
49
  * Reports the current on-disk runtime state and how it maps back to managed providers.
49
50
  */
@@ -55,7 +56,7 @@ async function getStatus(codexDir, configPath, providersPath, authPath) {
55
56
  const providers = providersExists ? (0, providers_repo_1.readProvidersFile)(providersPath) : null;
56
57
  let configViews = [];
57
58
  let consistencyIssues = [];
58
- const authState = (0, auth_repo_1.readManagedAuthState)(authPath);
59
+ const authState = (0, auth_repo_1.readAuthFileState)(authPath);
59
60
  if (configExists) {
60
61
  const document = (0, config_repo_1.readStructuredConfig)(configPath);
61
62
  currentProfile = document.activeProfile;
@@ -69,7 +70,32 @@ async function getStatus(codexDir, configPath, providersPath, authPath) {
69
70
  const activeProviderCandidates = currentProfile && providers ? (0, providers_1.findProvidersByProfile)(providers, currentProfile) : [];
70
71
  const activeProvider = activeProviderCandidates.length === 1 && providers ? providers.providers[activeProviderCandidates[0]] : null;
71
72
  const copilotInstall = (0, copilot_installer_1.probeCopilotSdkInstall)();
72
- const copilotBridge = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider) ? await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(activeProvider) : null;
73
+ const runtimeStateInspection = (0, runtime_state_repo_1.inspectCopilotBridgeState)();
74
+ const runtimeState = runtimeStateInspection.state;
75
+ const runtimeStateProvider = runtimeState && providers ? providers.providers[runtimeState.provider] ?? null : null;
76
+ const bridgeProbeTarget = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
77
+ ? activeProvider
78
+ : runtimeStateProvider && (0, providers_1.isCopilotBridgeProvider)(runtimeStateProvider)
79
+ ? runtimeStateProvider
80
+ : null;
81
+ const copilotBridge = !runtimeStateInspection.valid && runtimeStateInspection.exists
82
+ ? {
83
+ ok: false,
84
+ runtime: "copilot-bridge",
85
+ reason: "failed",
86
+ cause: runtimeStateInspection.parseError ?? "Failed to parse Copilot bridge runtime state.",
87
+ }
88
+ : bridgeProbeTarget
89
+ ? await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(bridgeProbeTarget, runtimeState)
90
+ : runtimeState
91
+ ? {
92
+ ok: false,
93
+ runtime: "copilot-bridge",
94
+ reason: "failed",
95
+ cause: "Copilot bridge runtime state exists but no matching managed Copilot provider is active.",
96
+ details: runtimeState,
97
+ }
98
+ : null;
73
99
  const copilotAuth = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
74
100
  ? await (0, copilot_adapter_1.readCopilotAuthState)().catch((error) => ({
75
101
  ready: false,
@@ -82,6 +108,9 @@ async function getStatus(codexDir, configPath, providersPath, authPath) {
82
108
  // Surface unmanaged live state without mutating anything during a read-only status call.
83
109
  warnings.push("Current config profile is not mapped in providers.json. Backfill would be required before treating live state as managed.");
84
110
  }
111
+ if (runtimeStateInspection.exists && !runtimeStateInspection.valid) {
112
+ warnings.push(`Copilot bridge runtime state is unreadable: ${runtimeStateInspection.parseError ?? "unknown parse failure"}`);
113
+ }
85
114
  return {
86
115
  warnings,
87
116
  data: {
@@ -103,6 +132,7 @@ async function getStatus(codexDir, configPath, providersPath, authPath) {
103
132
  },
104
133
  copilotAuth,
105
134
  copilotBridge,
135
+ copilotRuntimeState: runtimeState,
106
136
  liveState,
107
137
  auth: authState,
108
138
  configProfiles: configViews,
@@ -11,7 +11,6 @@ function listProviders(providersPath) {
11
11
  const items = names.map((name) => ({
12
12
  name,
13
13
  profile: providers.providers[name].profile,
14
- envKey: providers.providers[name].envKey,
15
14
  note: providers.providers[name].note ?? null,
16
15
  tags: providers.providers[name].tags ?? [],
17
16
  }));