@minniexcode/codex-switch 0.2.0 → 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.
Files changed (40) hide show
  1. package/README.AI.md +66 -94
  2. package/README.CN.md +84 -139
  3. package/README.md +91 -151
  4. package/dist/app/add-provider.js +6 -83
  5. package/dist/app/edit-provider.js +9 -29
  6. package/dist/app/get-status.js +1 -77
  7. package/dist/app/list-providers.js +0 -2
  8. package/dist/app/remove-provider.js +3 -5
  9. package/dist/app/run-doctor.js +2 -99
  10. package/dist/app/setup-codex.js +0 -2
  11. package/dist/app/switch-provider.js +1 -74
  12. package/dist/cli/output.js +3 -89
  13. package/dist/commands/handlers.js +20 -172
  14. package/dist/commands/help.js +1 -4
  15. package/dist/commands/registry.js +6 -74
  16. package/dist/domain/config.js +1 -3
  17. package/dist/domain/providers.js +4 -92
  18. package/dist/domain/runtime-state.js +0 -88
  19. package/dist/interaction/add-interactive.js +1 -55
  20. package/dist/interaction/interactive.js +1 -3
  21. package/dist/runtime/codex-probe.js +0 -12
  22. package/dist/storage/codex-paths.js +0 -2
  23. package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
  24. package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
  25. package/docs/Tests/testing.md +32 -34
  26. package/docs/cli-usage.md +67 -235
  27. package/docs/codex-switch-command-design.md +1 -1
  28. package/docs/codex-switch-product-overview.md +49 -96
  29. package/docs/codex-switch-technical-architecture.md +37 -52
  30. package/package.json +1 -1
  31. package/dist/app/bridge.js +0 -303
  32. package/dist/runtime/copilot-adapter.js +0 -617
  33. package/dist/runtime/copilot-bridge-worker.js +0 -69
  34. package/dist/runtime/copilot-bridge.js +0 -1351
  35. package/dist/runtime/copilot-cli.js +0 -164
  36. package/dist/runtime/copilot-http-bridge-worker.js +0 -228
  37. package/dist/runtime/copilot-installer.js +0 -231
  38. package/dist/runtime/copilot-sdk-loader.js +0 -62
  39. package/dist/runtime/copilot-token.js +0 -294
  40. package/dist/storage/runtime-state-repo.js +0 -121
package/README.md CHANGED
@@ -1,162 +1,112 @@
1
- # @minniexcode/codex-switch
1
+ # codex-switch
2
2
 
3
- `@minniexcode/codex-switch` is a local-first CLI for managing and switching Codex provider and model-provider routing safely.
3
+ `@minniexcode/codex-switch` is a local-first provider/model-provider management CLI for Codex.
4
4
 
5
- It keeps `codex-switch` tool state separate from the target Codex runtime, so provider management, backup flow, and runtime projection stay explicit instead of relying on manual file edits.
5
+ It keeps `codex-switch` tool state separate from the target Codex directory, so managed providers, backups, and Codex `model_provider` projection are handled through explicit commands instead of manual file edits.
6
6
 
7
- Chinese version: [README.CN.md](./README.CN.md)
7
+ Current package version: `0.2.1`
8
8
 
9
- ## Version
10
-
11
- Current package version: `0.1.5`
12
-
13
- This is the current repository development line. `0.1.5` is a Copilot Bridge process-visibility patch, focused on streaming commentary/reasoning signals, defensive SDK-event normalization, and safer redaction for unknown runtime events while keeping the provider surface unchanged.
9
+ `0.2.1` is the current repository development line. It is a provider-management-only consolidation release: direct OpenAI-compatible provider records are managed locally and projected into Codex config/auth files. This version does not include the previous account-login, local bridge, or background runtime experiments.
14
10
 
15
11
  ## Install
16
12
 
17
13
  ```bash
18
14
  npm install -g @minniexcode/codex-switch
19
- ```
20
-
21
- Run without a global install:
22
-
23
- ```bash
24
- npx @minniexcode/codex-switch --help
25
- ```
26
-
27
- Built CLI entrypoint:
28
-
29
- ```bash
30
15
  codexs --help
31
16
  ```
32
17
 
33
- ## Primary Workflows
34
-
35
- Direct provider workflow:
18
+ For local development:
36
19
 
37
20
  ```bash
38
- codexs init
39
- codexs add my-provider --profile my-provider --model gpt-5.5 --base-url https://gateway.example.com/v1 --api-key sk-xxx
40
- codexs switch my-provider
41
- codexs status
42
- codexs doctor
21
+ npm install
22
+ npm run build
23
+ node dist/cli.js --help
43
24
  ```
44
25
 
45
- GitHub Copilot workflow:
26
+ Node.js `>=18` is required.
27
+
28
+ ## Primary Workflow
46
29
 
47
30
  ```bash
48
31
  codexs init
49
- codexs login copilot
50
- codexs add copilot-main --copilot --profile copilot-main --model gpt-4.1
51
- codexs switch copilot-main
32
+ codexs add packycode --profile packycode --model gpt-5 --api-key sk-xxx --base-url https://api.example/v1
33
+ codexs switch packycode
52
34
  codexs status
53
35
  codexs doctor
54
36
  ```
55
37
 
56
- Notes:
38
+ What the workflow does:
57
39
 
58
- - `init` prepares the `codex-switch` tool home and managed state.
59
- - `login copilot` handles upstream Copilot onboarding and auth readiness.
60
- - `add --copilot` does not perform login for you; it assumes Copilot login is already ready.
61
- - For non-interactive use, pass `--profile` explicitly. In TTY mode, `add` and `edit` can prompt for missing required fields.
62
- - Copilot support is an experimental local bridge. The managed installer defaults to `@github/copilot-sdk@1.0.2`, Copilot runtime paths require Node.js `>=20`, and runtime checks separately reject older or prerelease SDK installs while validating API shape when the client or session is used.
63
- - `switch` projects the selected provider into the target Codex runtime as top-level `model` plus `model_provider`.
64
- - `status` is the main read command after switching.
65
- - `doctor` is the main repair-oriented diagnostic command.
40
+ - `init` creates the `codex-switch` tool home files.
41
+ - `add` stores a managed provider in `providers.json` and creates or updates the matching `[model_providers.<id>]` projection in `config.toml`.
42
+ - `switch` writes top-level `model` and `model_provider` in the target Codex config and projects `OPENAI_API_KEY` into `auth.json`.
43
+ - `status` summarizes current mapping, auth projection, and drift.
44
+ - `doctor` reports issue-first diagnostics.
66
45
 
67
- ## Runtime Routing Model
46
+ `--profile` is a CLI alias for the managed Codex `model_provider` id. It is not the legacy Codex top-level `profile` selector.
68
47
 
69
- For Codex `0.134.0+`, the active runtime route is selected through top-level `model` and `model_provider` in `config.toml`.
70
-
71
- `codex-switch` treats that route as the runtime contract:
72
-
73
- - top-level `model` selects the active model id
74
- - top-level `model_provider` selects the active provider route
75
- - managed `[model_providers.<id>]` entries are the projected runtime provider definitions
76
- - `--profile` is only an alias for the managed `model_provider` id, not the primary runtime selector
77
-
78
- Direct-provider projection writes:
79
-
80
- - top-level `model`
81
- - top-level `model_provider`
82
- - `[model_providers.<id>]`
83
- - `auth.json` with `OPENAI_API_KEY`
84
-
85
- Managed direct-provider projection does not keep `env_key` or `env_key_instructions` in the generated runtime config. `switch`, `add`, and `edit` clean old legacy projection fields before writing the active route.
86
-
87
- For managed OpenAI-compatible routes, the projected provider entry keeps the fixed runtime shape:
88
-
89
- ```toml
90
- model = "gpt-5.5"
91
- model_provider = "my-provider"
92
-
93
- [model_providers.my-provider]
94
- name = "my-provider"
95
- base_url = "https://gateway.example.com/v1"
96
- wire_api = "responses"
97
- requires_openai_auth = true
98
- ```
99
-
100
- Managed Copilot projection additionally writes:
101
-
102
- ```toml
103
- stream_idle_timeout_ms = 300000
104
- ```
48
+ ## Commands
105
49
 
106
- ## Advanced Adopt Workflow
50
+ Current `0.2.1` command surface:
107
51
 
108
- Use `migrate` only when you already have Codex runtime state that should be adopted into managed `providers.json` state:
109
-
110
- ```bash
111
- codexs init
112
- codexs migrate
113
- ```
114
-
115
- `migrate` is an advanced adopt helper. It is not the default first step for a fresh install.
116
-
117
- ## Command Surface
118
-
119
- ```bash
52
+ ```text
120
53
  codexs init
121
- codexs login copilot
122
54
  codexs migrate
123
55
  codexs list
124
56
  codexs show <provider>
125
57
  codexs current
126
58
  codexs status
127
- codexs config show [profile]
59
+ codexs config show
128
60
  codexs config list-profiles
129
61
  codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
130
- codexs add <provider> --copilot --profile <model-provider-id> --model <model>
131
- codexs edit <provider>
62
+ codexs edit <provider> [options]
132
63
  codexs switch <provider>
133
- codexs remove <provider> [--force] [--switch-to <provider>]
64
+ codexs remove <provider> --force
134
65
  codexs import <file>
135
- codexs export <file> [--force]
136
- codexs bridge start [provider]
137
- codexs bridge status [provider]
138
- codexs bridge stop [provider]
66
+ codexs export <file>
139
67
  codexs backups list
140
68
  codexs rollback [backup-id]
141
69
  codexs doctor
70
+ codexs setup
71
+ ```
72
+
73
+ `setup` is deprecated and exists only as a pointer to `init` for fresh state or `migrate` for advanced adoption of existing Codex config.
74
+
75
+ All commands accept `--json` for the standard JSON envelope where supported by the parser, and `--codex-dir <path>` to target a specific Codex directory.
76
+
77
+ ## Runtime Projection
78
+
79
+ For Codex `0.134.0+`, the active route is the top-level `model` and `model_provider` in `config.toml`.
80
+
81
+ Managed OpenAI-compatible provider projection uses this shape:
82
+
83
+ ```toml
84
+ model = "gpt-5"
85
+ model_provider = "packycode"
86
+
87
+ [model_providers.packycode]
88
+ name = "packycode"
89
+ base_url = "https://api.example/v1"
90
+ wire_api = "responses"
91
+ requires_openai_auth = true
142
92
  ```
143
93
 
144
- `setup` still exists only as a deprecated compatibility entry that points callers to `init` or `migrate`.
94
+ `codex-switch` intentionally does not write legacy `[profiles.*]` sections for new managed providers, and it removes legacy `env_key`/`env_key_instructions` fields from managed model-provider projections when it writes them.
95
+
96
+ Authentication is projected into the target Codex `auth.json` as API-key mode with `OPENAI_API_KEY`. Do not commit real keys or private provider exports.
145
97
 
146
- ## Runtime Model
98
+ ## Managed State
147
99
 
148
100
  Tool home:
149
101
 
150
102
  ```text
151
- ~/.config/codex-switch/
103
+ ~/.codex-switch/
152
104
  codex-switch.json
153
105
  providers.json
154
106
  backups/
155
- runtime/
156
- runtimes/
157
107
  ```
158
108
 
159
- Target Codex runtime:
109
+ Target Codex directory:
160
110
 
161
111
  ```text
162
112
  ~/.codex/
@@ -164,66 +114,56 @@ Target Codex runtime:
164
114
  auth.json
165
115
  ```
166
116
 
167
- Key points:
117
+ Environment variables:
168
118
 
169
- - `providers.json` is the managed provider registry and lives under the tool home.
170
- - `codex-switch.json` stores tool-level metadata such as `defaultCodexDir`.
171
- - `config.toml` remains the active runtime routing file in the target Codex directory.
172
- - `auth.json` remains the active auth projection file in the target Codex directory.
173
- - Direct providers rewrite `OPENAI_API_KEY` into the active runtime projection.
174
- - Copilot providers keep upstream GitHub authentication in the official Copilot runtime while `codex-switch` manages local bridge state and routing.
119
+ - `CODEXS_HOME` overrides the `codex-switch` tool home.
120
+ - `CODEXS_CODEX_DIR` provides the default target Codex directory when `--codex-dir` is not passed.
121
+ - In development, `NODE_ENV=development` defaults to `./dev-codex/local-sandbox` when no override is set.
175
122
 
176
- Path controls:
123
+ ## Migration And Adoption
177
124
 
178
- - `--codex-dir <path>` targets a specific Codex runtime directory.
179
- - `CODEXS_CODEX_DIR` provides the default target runtime when `--codex-dir` is not passed.
180
- - `CODEXS_HOME` overrides the tool home location.
125
+ Use `migrate` only when you already have Codex runtime config that should be adopted into managed `providers.json` state. It is not the default fresh-install command.
181
126
 
182
- ## Automation Notes
127
+ ```bash
128
+ codexs migrate
129
+ codexs migrate --overwrite --codex-dir ~/.codex
130
+ ```
183
131
 
184
- This CLI supports both human TTY usage and non-interactive automation.
132
+ The development-version policy applies to `0.2.1`: old local experimental state is not automatically migrated. Clean up or re-add providers manually when moving from an older local experiment.
185
133
 
186
- Global flags:
134
+ ## Current Non-Goals
187
135
 
188
- ```bash
189
- --json
190
- --codex-dir <path>
191
- --help
192
- --version
193
- ```
136
+ `0.2.1` does not implement or reserve runtime code paths for:
194
137
 
195
- Operational limits:
138
+ - GitHub Copilot SDK integration.
139
+ - GitHub device-flow login.
140
+ - `login copilot`.
141
+ - `add --copilot`.
142
+ - HTTP proxy bridge or local bridge worker commands.
143
+ - Background runtime services, bridge logs, or bridge runtime state.
144
+ - Built-in third-party router packaging.
145
+ - Account systems or cloud sync.
146
+ - Automatic migration of old Copilot or bridge state.
196
147
 
197
- - `login copilot` requires a real TTY and does not support `--json`.
198
- - `migrate` remains interactive when provider adoption requires human input.
199
- - Automation should pass explicit arguments and prefer `--json` for stable parsing.
148
+ A future release may integrate a third-party router-like capability, but `0.2.1` makes no workflow, schema, or runtime guarantee for that.
200
149
 
201
- ## Local Development
150
+ ## Development
202
151
 
203
152
  ```bash
204
153
  npm run build
205
- npm test
206
154
  npx tsc --noEmit
155
+ npm test
207
156
  node dist/cli.js --help
157
+ node dist/cli.js --version
208
158
  npm pack --dry-run
209
159
  ```
210
160
 
211
- ## Documentation
212
-
213
- - [Chinese README](./README.CN.md)
214
- - [AI README](./README.AI.md)
215
- - [Detailed CLI Usage](./docs/cli-usage.md)
216
- - [Testing Guide](./docs/Tests/testing.md)
217
- - [Product Overview](./docs/codex-switch-product-overview.md)
218
- - [PRD 0.1.0](./docs/PRD/codex-switch-prd-v0.1.0.md)
219
- - [PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
220
- - [PRD 0.1.2](./docs/PRD/codex-switch-prd-v0.1.2.md)
221
- - [PRD 0.1.3](./docs/PRD/codex-switch-prd-v0.1.3.md)
222
- - [PRD 0.1.5](./docs/PRD/codex-switch-prd-v0.1.5.md)
223
- - [Design 0.1.2](./docs/Design/codex-switch-v0.1.2-design.md)
224
- - [Design 0.1.3](./docs/Design/codex-switch-v0.1.3-design.md)
225
- - [Design 0.1.5](./docs/Design/codex-switch-v0.1.5-design.md)
226
-
227
- ## License
228
-
229
- MIT
161
+ ## Fact Sources
162
+
163
+ Current fact sources:
164
+
165
+ - [PRD 0.2.1](./docs/PRD/codex-switch-prd-v0.2.1.md)
166
+ - [Design 0.2.1](./docs/Design/codex-switch-v0.2.1-design.md)
167
+ - [CLI usage](./docs/cli-usage.md)
168
+
169
+ Historical documents remain under `docs/PRD/` and `docs/Design/` for context only.
@@ -1,46 +1,11 @@
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
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.addProvider = addProvider;
37
- const crypto = __importStar(require("node:crypto"));
38
4
  const providers_1 = require("../domain/providers");
39
5
  const errors_1 = require("../domain/errors");
40
6
  const config_repo_1 = require("../storage/config-repo");
41
7
  const fs_utils_1 = require("../storage/fs-utils");
42
8
  const providers_repo_1 = require("../storage/providers-repo");
43
- const copilot_token_1 = require("../runtime/copilot-token");
44
9
  const run_mutation_1 = require("./run-mutation");
45
10
  /**
46
11
  * Adds a new provider record to the managed providers registry.
@@ -51,39 +16,6 @@ async function addProvider(args) {
51
16
  if (providers.providers[args.providerName]) {
52
17
  throw (0, errors_1.cliError)("INVALID_IMPORT_FILE", `Provider "${args.providerName}" already exists.`);
53
18
  }
54
- const bridgeHost = args.bridgeHost ?? "127.0.0.1";
55
- const bridgePort = args.bridgePort ?? 41415;
56
- const runtime = args.copilot
57
- ? {
58
- kind: "copilot-http-proxy",
59
- upstream: "github-copilot",
60
- bridgeHost,
61
- bridgePort,
62
- bridgePath: "/v1",
63
- premiumRequests: true,
64
- authSource: "github-pat",
65
- }
66
- : undefined;
67
- if (args.copilot) {
68
- const githubToken = (0, copilot_token_1.readGithubToken)(args.toolHomeDir);
69
- if (!githubToken) {
70
- throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "GitHub Copilot authentication is required. Run `codexs login copilot` first.", {
71
- suggestion: "Run `codexs login copilot` to complete GitHub Copilot login.",
72
- });
73
- }
74
- try {
75
- await (0, copilot_token_1.exchangeForCopilotToken)(githubToken);
76
- }
77
- catch (error) {
78
- const normalized = (0, errors_1.normalizeError)(error);
79
- if (normalized.code === "COPILOT_AUTH_REQUIRED" || normalized.code === "COPILOT_TOKEN_EXCHANGE_FAILED") {
80
- throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "GitHub token is invalid or expired. Run `codexs login copilot` to re-authenticate.", {
81
- suggestion: "Run `codexs login copilot` to complete GitHub Copilot login.",
82
- });
83
- }
84
- throw error;
85
- }
86
- }
87
19
  const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
88
20
  const existingModelProvider = document.modelProviders.find((entry) => entry.name === args.profile);
89
21
  const inheritedModel = document.currentModel ?? undefined;
@@ -97,33 +29,26 @@ async function addProvider(args) {
97
29
  });
98
30
  }
99
31
  const directBaseUrl = args.baseUrl;
100
- if (!args.copilot && (!directBaseUrl || directBaseUrl.trim() === "") && !existingModelProvider) {
32
+ if ((!directBaseUrl || directBaseUrl.trim() === "") && !existingModelProvider) {
101
33
  throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${args.profile}" requires base_url.`, {
102
34
  profile: args.profile,
103
35
  modelProvider: args.profile,
104
36
  missingFields: ["base_url"],
105
37
  });
106
38
  }
107
- const upsertModelProviders = args.copilot
108
- ? {
109
- [args.profile]: (0, providers_1.buildCopilotModelProviderProjection)(runtime),
110
- }
111
- : {
112
- [args.profile]: (0, providers_1.buildDirectModelProviderProjection)(args.profile, (directBaseUrl ?? existingModelProvider?.baseUrl ?? "").trim()),
113
- };
114
- const apiKey = args.copilot ? args.bridgeApiKey ?? crypto.randomBytes(24).toString("hex") : args.apiKey;
115
- const baseUrl = args.copilot ? (0, providers_1.buildCopilotBridgeBaseUrl)(runtime) : args.baseUrl ?? undefined;
39
+ const upsertModelProviders = {
40
+ [args.profile]: (0, providers_1.buildModelProviderProjection)(args.profile, (directBaseUrl ?? existingModelProvider?.baseUrl ?? "").trim()),
41
+ };
116
42
  const next = {
117
43
  providers: {
118
44
  ...providers.providers,
119
45
  [args.providerName]: (0, providers_1.cleanProviderRecord)({
120
46
  profile: args.profile,
121
- apiKey,
47
+ apiKey: args.apiKey,
122
48
  model: providerModel,
123
- baseUrl,
49
+ baseUrl: args.baseUrl ?? undefined,
124
50
  note: args.note ?? undefined,
125
51
  tags: args.tags,
126
- runtime,
127
52
  }),
128
53
  },
129
54
  };
@@ -141,7 +66,6 @@ async function addProvider(args) {
141
66
  upsertModelProviders,
142
67
  scrubModelProviderEnvKeys: [args.profile],
143
68
  });
144
- // Persist only the normalized provider payload so later reads are deterministic.
145
69
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
146
70
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
147
71
  return {
@@ -149,7 +73,6 @@ async function addProvider(args) {
149
73
  model: providerModel,
150
74
  modelProvider: args.profile,
151
75
  profile: args.profile,
152
- runtimeKind: runtime?.kind ?? null,
153
76
  createdProfileSections: configPlan.createdProfileSections,
154
77
  createdModelProviderSections: configPlan.createdModelProviderSections,
155
78
  deletedProfileSections: configPlan.deletedProfileSections,
@@ -45,35 +45,17 @@ function editProvider(args) {
45
45
  const oldProfile = current.profile;
46
46
  const newProfile = nextProfile;
47
47
  const targetModelProviderSection = document.modelProviders.find((entry) => entry.name === newProfile) ?? null;
48
- let upsertModelProviders;
49
48
  const resolvedBaseUrl = (args.baseUrl ?? current.baseUrl ?? targetModelProviderSection?.baseUrl ?? "").trim();
50
- if (!current.runtime) {
51
- if (!resolvedBaseUrl) {
52
- throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${newProfile}" requires base_url.`, {
53
- profile: newProfile,
54
- modelProvider: newProfile,
55
- missingFields: ["base_url"],
56
- });
57
- }
58
- upsertModelProviders = {
59
- [newProfile]: (0, providers_1.buildDirectModelProviderProjection)(newProfile, resolvedBaseUrl),
60
- };
61
- }
62
- else if (targetModelProviderSection || args.profile !== undefined) {
63
- upsertModelProviders = {
64
- ...(upsertModelProviders ?? {}),
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
- },
75
- };
49
+ if (!resolvedBaseUrl) {
50
+ throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${newProfile}" requires base_url.`, {
51
+ profile: newProfile,
52
+ modelProvider: newProfile,
53
+ missingFields: ["base_url"],
54
+ });
76
55
  }
56
+ const upsertModelProviders = {
57
+ [newProfile]: (0, providers_1.buildModelProviderProjection)(newProfile, resolvedBaseUrl),
58
+ };
77
59
  const nextRecord = (0, providers_1.cleanProviderRecord)({
78
60
  profile: newProfile,
79
61
  apiKey: args.apiKey ?? current.apiKey,
@@ -81,7 +63,6 @@ function editProvider(args) {
81
63
  baseUrl: args.baseUrl === null ? undefined : args.baseUrl ?? current.baseUrl,
82
64
  note: args.note === null ? undefined : args.note ?? current.note,
83
65
  tags: args.tags ?? current.tags,
84
- runtime: current.runtime,
85
66
  });
86
67
  const isActive = document.currentModelProvider === oldProfile;
87
68
  return (0, run_mutation_1.runMutation)({
@@ -108,7 +89,6 @@ function editProvider(args) {
108
89
  [args.providerName]: nextRecord,
109
90
  },
110
91
  };
111
- // Write providers first so the registry and config move together inside the managed backup boundary.
112
92
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, nextProviders);
113
93
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
114
94
  return {
@@ -37,17 +37,13 @@ exports.getStatus = getStatus;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const config_1 = require("../domain/config");
39
39
  const runtime_state_1 = require("../domain/runtime-state");
40
- const providers_1 = require("../domain/providers");
41
40
  const config_repo_1 = require("../storage/config-repo");
42
41
  const providers_repo_1 = require("../storage/providers-repo");
43
42
  const auth_repo_1 = require("../storage/auth-repo");
44
- const copilot_token_1 = require("../runtime/copilot-token");
45
- const copilot_bridge_1 = require("../runtime/copilot-bridge");
46
- const runtime_state_repo_1 = require("../storage/runtime-state-repo");
47
43
  /**
48
44
  * Reports the current on-disk runtime state and how it maps back to managed providers.
49
45
  */
50
- async function getStatus(codexDir, configPath, providersPath, authPath, options) {
46
+ async function getStatus(codexDir, configPath, providersPath, authPath) {
51
47
  const configExists = fs.existsSync(configPath);
52
48
  const providersExists = fs.existsSync(providersPath);
53
49
  let currentModelProvider = null;
@@ -69,78 +65,16 @@ async function getStatus(codexDir, configPath, providersPath, authPath, options)
69
65
  }
70
66
  const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentModelProvider, providers);
71
67
  const activeProviderCandidates = liveState.mappedProviders;
72
- const activeProvider = liveState.providerResolvable && providers && liveState.mappedProvider
73
- ? providers.providers[liveState.mappedProvider]
74
- : null;
75
- const copilotInstall = { installed: Boolean((0, copilot_token_1.readGithubToken)(options?.toolHomeDir)), source: "github-pat" };
76
- const runtimeStateInspection = (0, runtime_state_repo_1.inspectCopilotBridgeState)(options?.runtimeDir);
77
- const runtimeState = runtimeStateInspection.state;
78
- const runtimeStateProvider = runtimeState && providers ? providers.providers[runtimeState.provider] ?? null : null;
79
- const bridgeProbeTarget = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
80
- ? activeProvider
81
- : runtimeStateProvider && (0, providers_1.isCopilotBridgeProvider)(runtimeStateProvider)
82
- ? runtimeStateProvider
83
- : null;
84
- const copilotBridge = !runtimeStateInspection.valid && runtimeStateInspection.exists
85
- ? {
86
- ok: false,
87
- runtime: "copilot-bridge",
88
- reason: "failed",
89
- cause: runtimeStateInspection.parseError ?? "Failed to parse Copilot bridge runtime state.",
90
- details: {
91
- logPath: runtimeState?.logPath ?? null,
92
- },
93
- }
94
- : bridgeProbeTarget
95
- ? await (0, copilot_bridge_1.probeCopilotBridgeRuntime)(bridgeProbeTarget, runtimeState, options?.runtimeDir)
96
- : runtimeState
97
- ? {
98
- ok: false,
99
- runtime: "copilot-bridge",
100
- reason: "failed",
101
- cause: "Copilot bridge runtime state exists but no matching managed Copilot provider is active.",
102
- details: {
103
- ...runtimeState,
104
- logPath: runtimeState.logPath ?? null,
105
- },
106
- }
107
- : null;
108
- const copilotAuth = activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider)
109
- ? await (async () => {
110
- const token = (0, copilot_token_1.readGithubToken)(options?.toolHomeDir);
111
- if (!token)
112
- return { ready: false, source: "github-pat", mode: "token", error: "No GitHub token found" };
113
- try {
114
- await (0, copilot_token_1.exchangeForCopilotToken)(token);
115
- return { ready: true, source: "github-pat", mode: "token" };
116
- }
117
- catch (error) {
118
- return { ready: false, source: "github-pat", mode: "token", error: error instanceof Error ? error.message : String(error) };
119
- }
120
- })()
121
- : null;
122
68
  if (liveState.canBackfillActiveProvider) {
123
- // Surface unmanaged live state without mutating anything during a read-only status call.
124
69
  warnings.push("Current config profile is not mapped in providers.json. Backfill would be required before treating live state as managed.");
125
70
  }
126
71
  if (liveState.reason === "shared-profile") {
127
72
  warnings.push(`Current model provider "${currentModelProvider}" is shared by multiple providers in providers.json, so the active provider cannot be resolved uniquely.`);
128
73
  }
129
- if (runtimeStateInspection.exists && !runtimeStateInspection.valid) {
130
- warnings.push(`Copilot bridge runtime state is unreadable: ${runtimeStateInspection.parseError ?? "unknown parse failure"}`);
131
- }
132
74
  return {
133
75
  warnings,
134
76
  data: {
135
77
  codexDir,
136
- storage: (0, runtime_state_1.getStorageRoles)({
137
- codexDir,
138
- providersPath,
139
- configPath,
140
- authPath,
141
- runtimeDir: options?.runtimeDir,
142
- runtimesDir: options?.runtimesDir,
143
- }),
144
78
  configExists,
145
79
  providersExists,
146
80
  currentModelProvider,
@@ -149,16 +83,6 @@ async function getStatus(codexDir, configPath, providersPath, authPath, options)
149
83
  provider: liveState.mappedProvider,
150
84
  activeProviderResolvable: liveState.providerResolvable,
151
85
  activeProviderCandidates,
152
- runtimeProvider: activeProvider && (0, providers_1.isCopilotBridgeProvider)(activeProvider) ? activeProvider.runtime?.kind ?? null : null,
153
- copilotSdk: {
154
- installed: copilotInstall.installed,
155
- source: copilotInstall.source,
156
- },
157
- copilotAuth,
158
- copilotBridge,
159
- copilotRuntimeState: runtimeState,
160
- copilotBridgeLogPath: runtimeState?.logPath ?? null,
161
- copilotBridgeRestartReason: runtimeState?.lastRestartReason ?? null,
162
86
  liveState,
163
87
  auth: authState,
164
88
  configProfiles: configViews,
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.listProviders = listProviders;
37
37
  const fs = __importStar(require("node:fs"));
38
- const providers_1 = require("../domain/providers");
39
38
  const runtime_state_1 = require("../domain/runtime-state");
40
39
  const config_repo_1 = require("../storage/config-repo");
41
40
  const providers_repo_1 = require("../storage/providers-repo");
@@ -57,7 +56,6 @@ function listProviders(providersPath, configPath) {
57
56
  profile: providers.providers[name].profile,
58
57
  modelProvider: providers.providers[name].profile,
59
58
  model: providers.providers[name].model ?? null,
60
- providerType: (0, providers_1.isCopilotBridgeProvider)(providers.providers[name]) ? "copilot" : "direct",
61
59
  isActive: liveState.providerResolvable && liveState.mappedProvider === name,
62
60
  note: providers.providers[name].note ?? null,
63
61
  tags: providers.providers[name].tags ?? [],