@minniexcode/codex-switch 0.0.5 → 0.0.7

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 (71) hide show
  1. package/README.AI.md +5 -2
  2. package/README.md +44 -100
  3. package/dist/app/add-provider.js +28 -4
  4. package/dist/app/edit-provider.js +47 -19
  5. package/dist/app/export-providers.js +2 -2
  6. package/dist/app/get-current-profile.js +1 -1
  7. package/dist/app/get-status.js +10 -3
  8. package/dist/app/import-providers.js +15 -7
  9. package/dist/app/init-codex.js +68 -0
  10. package/dist/app/list-backups.js +1 -1
  11. package/dist/app/list-config-profiles.js +3 -2
  12. package/dist/app/list-providers.js +2 -1
  13. package/dist/app/remove-provider.js +2 -2
  14. package/dist/app/rollback-backup.js +1 -1
  15. package/dist/app/rollback-latest.js +1 -1
  16. package/dist/app/run-doctor.js +83 -6
  17. package/dist/app/run-mutation.js +2 -2
  18. package/dist/app/setup-codex.js +21 -12
  19. package/dist/app/show-config.js +11 -3
  20. package/dist/app/show-provider.js +1 -1
  21. package/dist/app/switch-provider.js +16 -9
  22. package/dist/cli/add-interactive.js +7 -104
  23. package/dist/cli/args.js +6 -135
  24. package/dist/cli/help.js +8 -313
  25. package/dist/cli/interactive.js +17 -225
  26. package/dist/cli/output.js +21 -6
  27. package/dist/cli/prompt.js +4 -106
  28. package/dist/cli.js +10 -404
  29. package/dist/commands/args.js +132 -0
  30. package/dist/commands/dispatch.js +16 -0
  31. package/dist/commands/handlers.js +460 -0
  32. package/dist/commands/help.js +120 -0
  33. package/dist/commands/registry.js +351 -0
  34. package/dist/commands/types.js +2 -0
  35. package/dist/domain/config.js +235 -21
  36. package/dist/domain/providers.js +16 -2
  37. package/dist/domain/setup.js +1 -0
  38. package/dist/infra/backup-repo.js +9 -206
  39. package/dist/infra/codex-cli.js +9 -126
  40. package/dist/infra/codex-paths.js +6 -67
  41. package/dist/infra/config-repo.js +59 -0
  42. package/dist/infra/fs-utils.js +8 -93
  43. package/dist/infra/lock-repo.js +4 -95
  44. package/dist/infra/providers-repo.js +8 -94
  45. package/dist/interaction/add-interactive.js +99 -0
  46. package/dist/interaction/interactive.js +289 -0
  47. package/dist/interaction/prompt.js +110 -0
  48. package/dist/runtime/codex-cli.js +130 -0
  49. package/dist/runtime/codex-probe.js +57 -0
  50. package/dist/runtime/types.js +2 -0
  51. package/dist/storage/auth-repo.js +160 -0
  52. package/dist/storage/backup-repo.js +210 -0
  53. package/dist/storage/codex-paths.js +71 -0
  54. package/dist/storage/config-repo.js +266 -0
  55. package/dist/storage/fs-utils.js +97 -0
  56. package/dist/storage/lock-repo.js +99 -0
  57. package/dist/storage/providers-repo.js +98 -0
  58. package/docs/Design/codex-switch-v0.0.5-design.md +32 -22
  59. package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
  60. package/docs/Design/codex-switch-v0.0.7-design.md +862 -0
  61. package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +227 -89
  62. package/docs/PRD/codex-switch-prd-v0.1.0.md +200 -226
  63. package/docs/PRD/codex-switch-prd.md +1 -1
  64. package/docs/Reference/codex-config-reference.md +604 -0
  65. package/docs/Reference/codex-config-reference.zh-CN.md +633 -0
  66. package/docs/cli-usage.md +78 -29
  67. package/docs/codex-switch-technical-architecture.md +73 -4
  68. package/docs/test-report-0.0.5.md +163 -0
  69. package/docs/test-report-0.0.7.md +118 -0
  70. package/docs/testing.md +151 -0
  71. package/package.json +1 -1
package/README.AI.md CHANGED
@@ -17,7 +17,8 @@ Primary goals:
17
17
  ## Main Command Surface
18
18
 
19
19
  ```bash
20
- codexs setup
20
+ codexs init
21
+ codexs migrate
21
22
  codexs list
22
23
  codexs show <provider>
23
24
  codexs current
@@ -92,11 +93,12 @@ codexs status --json
92
93
  Current package version in this repository:
93
94
 
94
95
  ```text
95
- 0.0.4
96
+ 0.0.7
96
97
  ```
97
98
 
98
99
  Recent version summary:
99
100
 
101
+ - `0.0.7`: command-surface refactor, env_key/auth-mirror model corrections, and the `setup` split into `init` plus `migrate`
100
102
  - `0.0.4`: setup/show/edit/backups list/specific rollback/import merge and clearer CLI semantics
101
103
  - `0.0.3`: interactive TTY flows and improved help
102
104
  - `0.0.2`: mutation orchestration, backups, rollback, locks, drift detection improvements
@@ -107,4 +109,5 @@ Recent version summary:
107
109
  - Prefer `--json` when invoking commands programmatically
108
110
  - Treat `providers.json` as sensitive because it may contain API keys
109
111
  - Do not assume silent write-back from runtime files into `providers.json`
112
+ - Prefer `init` for repeatable machine setup and `migrate` for human-led adopt flows
110
113
  - Use `docs/` for deeper product and architecture context
package/README.md CHANGED
@@ -1,38 +1,26 @@
1
- # @minniexcode/codex-switch
1
+ ## @minniexcode/codex-switch
2
2
 
3
3
  `codex-switch` is a local-first CLI for managing and switching Codex provider/profile configuration safely.
4
4
 
5
- `codex-switch` 是一个本地优先的 CLI,用来安全地管理和切换 Codex 的 provider/profile 配置。
6
-
7
5
  It is designed for users who work with multiple Codex providers, API keys, or profiles and want a repeatable, backup-first workflow instead of manually editing files under `~/.codex/`.
8
6
 
9
- 它面向同时维护多个 Codex provider、API key 或 profile 的用户,目标是用可重复、先备份再写入的方式替代手动修改 `~/.codex/` 下的文件。
7
+ 中文版: [README.CN.md](./README.CN.md)
10
8
 
11
- ## Overview | 简介
9
+ ## Overview
12
10
 
13
11
  What it does:
14
12
 
15
- - Initialize `providers.json` from an existing Codex directory
13
+ - Initialize an empty managed `providers.json`
14
+ - Migrate unmanaged runtime profiles from an existing Codex directory
16
15
  - List, show, add, edit, and remove provider records
17
16
  - Switch the active provider/profile safely
18
17
  - Import and export provider definitions
19
18
  - Run diagnostics and detect local drift
20
- - List backups and rollback to a previous managed state
21
-
22
- 它可以完成的事情:
23
-
24
- - 从现有 Codex 目录初始化 `providers.json`
25
- - 查看、新增、编辑、删除 provider 记录
26
- - 安全切换当前激活的 provider/profile
27
- - 导入和导出 provider 配置
28
- - 运行诊断并识别本地配置漂移
29
- - 查看备份并回滚到之前的受管状态
30
-
31
- Current version: `0.0.4`
19
+ - List backups and roll back to a previous managed state
32
20
 
33
- 当前版本:`0.0.4`
21
+ Current version: `0.0.7`
34
22
 
35
- ## Install | 安装
23
+ ## Install
36
24
 
37
25
  Install globally:
38
26
 
@@ -46,42 +34,19 @@ Or run directly:
46
34
  npx @minniexcode/codex-switch --help
47
35
  ```
48
36
 
49
- 全局安装:
50
-
51
- ```bash
52
- npm install -g @minniexcode/codex-switch
53
- ```
54
-
55
- 或者直接运行:
56
-
57
- ```bash
58
- npx @minniexcode/codex-switch --help
59
- ```
60
-
61
37
  CLI entry:
62
38
 
63
39
  ```bash
64
40
  codexs --help
65
41
  ```
66
42
 
67
- 命令入口:
68
-
69
- ```bash
70
- codexs --help
71
- ```
72
-
73
- ## Quick Start | 快速开始
43
+ ## Quick Start
74
44
 
75
45
  Take over an existing Codex directory:
76
46
 
77
47
  ```bash
78
- codexs setup
79
- ```
80
-
81
- 接管当前已有的 Codex 目录:
82
-
83
- ```bash
84
- codexs setup
48
+ codexs init
49
+ codexs migrate
85
50
  ```
86
51
 
87
52
  Inspect managed providers:
@@ -91,13 +56,6 @@ codexs list
91
56
  codexs show my-provider
92
57
  ```
93
58
 
94
- 查看已管理的 provider:
95
-
96
- ```bash
97
- codexs list
98
- codexs show my-provider
99
- ```
100
-
101
59
  Add and switch:
102
60
 
103
61
  ```bash
@@ -105,13 +63,6 @@ codexs add my-provider --profile my-provider --api-key sk-xxx
105
63
  codexs switch my-provider
106
64
  ```
107
65
 
108
- 新增并切换:
109
-
110
- ```bash
111
- codexs add my-provider --profile my-provider --api-key sk-xxx
112
- codexs switch my-provider
113
- ```
114
-
115
66
  Check runtime state:
116
67
 
117
68
  ```bash
@@ -120,18 +71,11 @@ codexs status
120
71
  codexs doctor
121
72
  ```
122
73
 
123
- 检查当前运行状态:
74
+ ## Common Commands
124
75
 
125
76
  ```bash
126
- codexs current
127
- codexs status
128
- codexs doctor
129
- ```
130
-
131
- ## Common Commands | 常用命令
132
-
133
- ```bash
134
- codexs setup
77
+ codexs init
78
+ codexs migrate
135
79
  codexs list
136
80
  codexs show <provider>
137
81
  codexs current
@@ -151,22 +95,15 @@ Command help:
151
95
 
152
96
  ```bash
153
97
  codexs help switch
98
+ codexs help init
99
+ codexs help migrate
154
100
  codexs help setup
155
101
  ```
156
102
 
157
- 命令帮助:
158
-
159
- ```bash
160
- codexs help switch
161
- codexs help setup
162
- ```
163
-
164
- ## How It Works | 工作方式
103
+ ## How It Works
165
104
 
166
105
  By default, `codex-switch` operates on `~/.codex/`, and you can override the target with `--codex-dir`.
167
106
 
168
- `codex-switch` 默认围绕 `~/.codex/` 工作,也可以通过 `--codex-dir` 指向其他目录。
169
-
170
107
  Managed files:
171
108
 
172
109
  ```text
@@ -177,23 +114,20 @@ Managed files:
177
114
  backups/
178
115
  ```
179
116
 
180
- 说明:
117
+ Notes:
181
118
 
182
119
  - `providers.json` is the managed provider registry
183
120
  - `config.toml` and `auth.json` represent runtime state
184
121
  - mutating commands back up before writing
185
122
  - rollback is available after failed or undesired changes
186
123
 
187
- - `providers.json` 是受管理的 provider 注册表
188
- - `config.toml` 和 `auth.json` 代表当前运行态
189
- - 所有写操作都会先备份再写入
190
- - 变更失败或结果不符合预期时可以回滚
191
-
192
- ## Automation | 自动化
124
+ ## Automation
193
125
 
194
126
  This CLI supports both human TTY use and non-interactive automation.
195
127
 
196
- 这个 CLI 同时支持人工交互和非交互自动化。
128
+ Current exceptions:
129
+ - `init` is automation-friendly and idempotent, but still returns a structured error in non-interactive or `--json` mode when the resolved target directory does not exist.
130
+ - `migrate` remains intentionally TTY-only for adopt initialization. It requires interactive profile selection and provider detail collection, and non-interactive/`--json` runs fail fast with a structured error.
197
131
 
198
132
  Recommended global flags:
199
133
 
@@ -204,28 +138,38 @@ Recommended global flags:
204
138
  --version
205
139
  ```
206
140
 
207
- 建议:
141
+ Recommendations:
208
142
 
209
143
  - use `--json` for stable machine-readable output
210
144
  - pass all required arguments explicitly in scripts or CI
211
145
  - use `--codex-dir <path>` for sandbox or test environments
212
146
 
213
- - 在脚本或 CI 中使用 `--json` 获取稳定输出
214
- - 在非交互环境中显式传入所有必需参数
215
- - 在测试环境中优先配合 `--codex-dir <path>` 使用
147
+ ## Testing
216
148
 
217
- ## Documentation | 文档
149
+ Build and test locally:
218
150
 
219
- - [Detailed CLI Usage](./docs/cli-usage.md)
220
- - [详细 CLI 使用文档](./docs/cli-usage.md)
221
- - [Changelog](./CHANGELOG.md)
222
- - [更新日志](./CHANGELOG.md)
151
+ ```bash
152
+ npm run build
153
+ npm test
154
+ ```
155
+
156
+ The repository includes a development fixture under `dev-codex/local-sandbox` plus dedicated test docs:
157
+
158
+ - [Testing Guide](./docs/testing.md)
159
+ - [Test Report for 0.0.5](./docs/test-report-0.0.5.md)
160
+
161
+ ## Documentation
162
+
163
+ - [Chinese README](./README.CN.md)
223
164
  - [AI README](./README.AI.md)
224
- - [中文 README(历史版)](./README.CN.md)
165
+ - [Detailed CLI Usage](./docs/cli-usage.md)
166
+ - [Testing Guide](./docs/testing.md)
167
+ - [Test Report for 0.0.5](./docs/test-report-0.0.5.md)
225
168
  - [Product Overview](./docs/codex-switch-product-overview.md)
226
169
  - [Technical Architecture](./docs/codex-switch-technical-architecture.md)
227
- - [0.0.4 Design Doc](./docs/codex-switch-v0.0.4-design.md)
170
+ - [Design Doc 0.0.5](./docs/Design/codex-switch-v0.0.5-design.md)
171
+ - [Design Doc 0.0.4](./docs/Design/codex-switch-v0.0.4-design.md)
228
172
 
229
- ## License | 许可证
173
+ ## License
230
174
 
231
175
  MIT
@@ -4,9 +4,10 @@ exports.addProvider = addProvider;
4
4
  const config_1 = require("../domain/config");
5
5
  const providers_1 = require("../domain/providers");
6
6
  const errors_1 = require("../domain/errors");
7
- const config_repo_1 = require("../infra/config-repo");
8
- const fs_utils_1 = require("../infra/fs-utils");
9
- const providers_repo_1 = require("../infra/providers-repo");
7
+ const config_repo_1 = require("../storage/config-repo");
8
+ const fs_utils_1 = require("../storage/fs-utils");
9
+ const providers_repo_1 = require("../storage/providers-repo");
10
+ const auth_repo_1 = require("../storage/auth-repo");
10
11
  const run_mutation_1 = require("./run-mutation");
11
12
  /**
12
13
  * Adds a new provider record to the managed providers registry.
@@ -19,6 +20,7 @@ function addProvider(args) {
19
20
  }
20
21
  const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
21
22
  const existingProfile = document.profiles.find((profile) => profile.name === args.profile);
23
+ const existingModelProvider = document.modelProviders.find((entry) => entry.name === args.profile);
22
24
  if (!existingProfile && !args.createProfile) {
23
25
  throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${args.profile}" does not exist in config.toml.`, {
24
26
  profile: args.profile,
@@ -29,16 +31,29 @@ function addProvider(args) {
29
31
  ? {
30
32
  [args.profile]: (0, config_1.validateManagedProfileCreation)(args.profile, {
31
33
  model: args.model ?? undefined,
32
- baseUrl: args.baseUrl ?? undefined,
34
+ modelProvider: args.profile,
33
35
  }),
34
36
  }
35
37
  : undefined;
38
+ const upsertModelProviders = !existingModelProvider && args.createProfile
39
+ ? {
40
+ [args.profile]: {
41
+ baseUrl: args.baseUrl ?? undefined,
42
+ envKey: (0, config_1.buildManagedProfileEnvKey)(args.profile),
43
+ },
44
+ }
45
+ : undefined;
46
+ if (existingProfile) {
47
+ (0, config_repo_1.requireManagedProfileRuntime)(document, providers, args.profile);
48
+ }
49
+ const envKey = existingModelProvider?.envKey ?? (0, config_1.buildManagedProfileEnvKey)(args.profile);
36
50
  const next = {
37
51
  providers: {
38
52
  ...providers.providers,
39
53
  [args.providerName]: (0, providers_1.cleanProviderRecord)({
40
54
  profile: args.profile,
41
55
  apiKey: args.apiKey,
56
+ envKey,
42
57
  baseUrl: args.baseUrl ?? undefined,
43
58
  note: args.note ?? undefined,
44
59
  tags: args.tags,
@@ -53,18 +68,27 @@ function addProvider(args) {
53
68
  files: [
54
69
  { absolutePath: args.providersPath, relativePath: "providers.json" },
55
70
  { absolutePath: args.configPath, relativePath: "config.toml" },
71
+ { absolutePath: args.authPath, relativePath: "auth.json" },
56
72
  ],
57
73
  mutate: () => {
58
74
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
59
75
  upsertProfiles,
76
+ upsertModelProviders,
60
77
  });
61
78
  // Persist only the normalized provider payload so later reads are deterministic.
62
79
  (0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
63
80
  (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
81
+ if (document.activeProfile === args.profile) {
82
+ const activeProviderName = (0, config_repo_1.resolveActiveProviderName)(document, next);
83
+ const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
84
+ (0, auth_repo_1.writeAuthFile)(args.authPath, next.providers[activeProviderName], existingAuth ?? undefined);
85
+ }
64
86
  return {
65
87
  provider: args.providerName,
66
88
  profile: args.profile,
89
+ envKey,
67
90
  createdProfileSections: configPlan.createdProfileSections,
91
+ createdModelProviderSections: configPlan.createdModelProviderSections,
68
92
  deletedProfileSections: configPlan.deletedProfileSections,
69
93
  keptSharedProfiles: [],
70
94
  switchedActiveProfile: configPlan.switchedActiveProfile,
@@ -4,9 +4,10 @@ exports.editProvider = editProvider;
4
4
  const errors_1 = require("../domain/errors");
5
5
  const config_1 = require("../domain/config");
6
6
  const providers_1 = require("../domain/providers");
7
- const config_repo_1 = require("../infra/config-repo");
8
- const fs_utils_1 = require("../infra/fs-utils");
9
- const providers_repo_1 = require("../infra/providers-repo");
7
+ const config_repo_1 = require("../storage/config-repo");
8
+ const fs_utils_1 = require("../storage/fs-utils");
9
+ const providers_repo_1 = require("../storage/providers-repo");
10
+ const auth_repo_1 = require("../storage/auth-repo");
10
11
  const run_mutation_1 = require("./run-mutation");
11
12
  /**
12
13
  * Updates selected fields on a single managed provider.
@@ -23,13 +24,7 @@ function editProvider(args) {
23
24
  });
24
25
  }
25
26
  const updatedFields = [];
26
- const nextRecord = (0, providers_1.cleanProviderRecord)({
27
- profile: args.profile ?? current.profile,
28
- apiKey: args.apiKey ?? current.apiKey,
29
- baseUrl: args.baseUrl === null ? undefined : args.baseUrl ?? current.baseUrl,
30
- note: args.note === null ? undefined : args.note ?? current.note,
31
- tags: args.tags ?? current.tags,
32
- });
27
+ const nextProfile = args.profile ?? current.profile;
33
28
  if (args.profile !== undefined && args.profile !== current.profile) {
34
29
  updatedFields.push("profile");
35
30
  }
@@ -46,10 +41,12 @@ function editProvider(args) {
46
41
  updatedFields.push("tags");
47
42
  }
48
43
  const oldProfile = current.profile;
49
- const newProfile = nextRecord.profile;
44
+ const newProfile = nextProfile;
50
45
  const targetSection = document.profiles.find((profile) => profile.name === newProfile) ?? null;
46
+ const targetModelProviderSection = document.modelProviders.find((entry) => entry.name === newProfile) ?? null;
51
47
  const targetProfileExists = Boolean(targetSection);
52
48
  let upsertProfiles;
49
+ let upsertModelProviders;
53
50
  if (!targetProfileExists) {
54
51
  if (!args.createProfile) {
55
52
  throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${newProfile}" does not exist in config.toml.`, {
@@ -60,24 +57,44 @@ function editProvider(args) {
60
57
  upsertProfiles = {
61
58
  [newProfile]: (0, config_1.validateManagedProfileCreation)(newProfile, {
62
59
  model: args.model ?? undefined,
63
- baseUrl: args.baseUrl ?? undefined,
60
+ modelProvider: newProfile,
64
61
  }),
65
62
  };
63
+ upsertModelProviders = {
64
+ [newProfile]: {
65
+ baseUrl: args.baseUrl ?? undefined,
66
+ envKey: (0, config_1.buildManagedProfileEnvKey)(newProfile),
67
+ },
68
+ };
66
69
  }
67
- else if (args.model !== undefined || args.baseUrl !== undefined) {
70
+ else {
71
+ (0, config_repo_1.requireManagedProfileRuntime)(document, providers, newProfile);
72
+ }
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
+ const nextRecord = (0, providers_1.cleanProviderRecord)({
80
+ profile: newProfile,
81
+ apiKey: args.apiKey ?? current.apiKey,
82
+ envKey: nextEnvKey,
83
+ baseUrl: args.baseUrl === null ? undefined : args.baseUrl ?? current.baseUrl,
84
+ note: args.note === null ? undefined : args.note ?? current.note,
85
+ tags: args.tags ?? current.tags,
86
+ });
87
+ if (targetProfileExists && args.model !== undefined) {
68
88
  upsertProfiles = {
69
89
  [newProfile]: {
70
90
  ...(args.model !== undefined && args.model !== null ? { model: args.model } : {}),
71
- ...(args.baseUrl !== undefined && args.baseUrl !== null ? { baseUrl: args.baseUrl } : {}),
72
91
  },
73
92
  };
74
93
  if (args.model !== undefined && (targetSection?.model !== args.model) && !updatedFields.includes("model")) {
75
94
  updatedFields.push("model");
76
95
  }
77
- if (args.baseUrl !== undefined && targetSection?.baseUrl !== args.baseUrl && !updatedFields.includes("baseUrl")) {
78
- updatedFields.push("baseUrl");
79
- }
80
96
  }
97
+ // Compute profile link ownership after the edit so lifecycle planning can decide whether sections stay, move, or delete.
81
98
  const remainingLinksByProfile = new Map();
82
99
  for (const [name, provider] of Object.entries(providers.providers)) {
83
100
  if (name === args.providerName) {
@@ -108,24 +125,35 @@ function editProvider(args) {
108
125
  files: [
109
126
  { absolutePath: args.providersPath, relativePath: "providers.json" },
110
127
  { absolutePath: args.configPath, relativePath: "config.toml" },
128
+ { absolutePath: args.authPath, relativePath: "auth.json" },
111
129
  ],
112
130
  mutate: () => {
113
131
  const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
114
132
  upsertProfiles,
133
+ upsertModelProviders,
115
134
  deleteProfiles: lifecycle.deletedProfileSections,
116
135
  setActiveProfile: lifecycle.nextActiveProfile,
117
136
  });
118
- (0, providers_repo_1.writeProvidersFile)(args.providersPath, {
137
+ const nextProviders = {
119
138
  providers: {
120
139
  ...providers.providers,
121
140
  [args.providerName]: nextRecord,
122
141
  },
123
- });
142
+ };
143
+ // Write providers first so the registry and config move together inside the managed backup boundary.
144
+ (0, providers_repo_1.writeProvidersFile)(args.providersPath, nextProviders);
124
145
  (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
+ }
125
152
  return {
126
153
  provider: args.providerName,
127
154
  updatedFields,
128
155
  createdProfileSections: configPlan.createdProfileSections,
156
+ createdModelProviderSections: configPlan.createdModelProviderSections,
129
157
  deletedProfileSections: configPlan.deletedProfileSections,
130
158
  keptSharedProfiles: lifecycle.keptSharedProfiles,
131
159
  switchedActiveProfile: lifecycle.switchedActiveProfile,
@@ -37,8 +37,8 @@ exports.exportProviders = exportProviders;
37
37
  const fs = __importStar(require("node:fs"));
38
38
  const path = __importStar(require("node:path"));
39
39
  const errors_1 = require("../domain/errors");
40
- const fs_utils_1 = require("../infra/fs-utils");
41
- const providers_repo_1 = require("../infra/providers-repo");
40
+ const fs_utils_1 = require("../storage/fs-utils");
41
+ const providers_repo_1 = require("../storage/providers-repo");
42
42
  /**
43
43
  * Exports the current providers registry to a user-specified file.
44
44
  */
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCurrentProfile = getCurrentProfile;
4
- const config_repo_1 = require("../infra/config-repo");
4
+ const config_repo_1 = require("../storage/config-repo");
5
5
  /**
6
6
  * Returns the currently active top-level Codex profile.
7
7
  */
@@ -37,12 +37,14 @@ 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 config_repo_1 = require("../infra/config-repo");
41
- const providers_repo_1 = require("../infra/providers-repo");
40
+ const providers_1 = require("../domain/providers");
41
+ const config_repo_1 = require("../storage/config-repo");
42
+ const providers_repo_1 = require("../storage/providers-repo");
43
+ const auth_repo_1 = require("../storage/auth-repo");
42
44
  /**
43
45
  * Reports the current on-disk runtime state and how it maps back to managed providers.
44
46
  */
45
- function getStatus(codexDir, configPath, providersPath) {
47
+ function getStatus(codexDir, configPath, providersPath, authPath) {
46
48
  const configExists = fs.existsSync(configPath);
47
49
  const providersExists = fs.existsSync(providersPath);
48
50
  let currentProfile = null;
@@ -50,6 +52,7 @@ function getStatus(codexDir, configPath, providersPath) {
50
52
  const providers = providersExists ? (0, providers_repo_1.readProvidersFile)(providersPath) : null;
51
53
  let configViews = [];
52
54
  let consistencyIssues = [];
55
+ const authState = (0, auth_repo_1.readManagedAuthState)(authPath);
53
56
  if (configExists) {
54
57
  const document = (0, config_repo_1.readStructuredConfig)(configPath);
55
58
  currentProfile = document.activeProfile;
@@ -60,6 +63,7 @@ function getStatus(codexDir, configPath, providersPath) {
60
63
  }
61
64
  }
62
65
  const liveState = (0, runtime_state_1.inspectLiveStateDrift)(currentProfile, providers);
66
+ const activeProviderCandidates = currentProfile && providers ? (0, providers_1.findProvidersByProfile)(providers, currentProfile) : [];
63
67
  if (liveState.canBackfillActiveProvider) {
64
68
  // Surface unmanaged live state without mutating anything during a read-only status call.
65
69
  warnings.push("Current config profile is not mapped in providers.json. Backfill would be required before treating live state as managed.");
@@ -74,7 +78,10 @@ function getStatus(codexDir, configPath, providersPath) {
74
78
  currentProfile,
75
79
  currentProfileMapped: liveState.profileMapped,
76
80
  provider: liveState.mappedProvider,
81
+ activeProviderResolvable: activeProviderCandidates.length === 1,
82
+ activeProviderCandidates,
77
83
  liveState,
84
+ auth: authState,
78
85
  configProfiles: configViews,
79
86
  issues: consistencyIssues,
80
87
  },
@@ -39,9 +39,9 @@ const path = __importStar(require("node:path"));
39
39
  const config_1 = require("../domain/config");
40
40
  const providers_1 = require("../domain/providers");
41
41
  const errors_1 = require("../domain/errors");
42
- const config_repo_1 = require("../infra/config-repo");
43
- const fs_utils_1 = require("../infra/fs-utils");
44
- const providers_repo_1 = require("../infra/providers-repo");
42
+ const config_repo_1 = require("../storage/config-repo");
43
+ const fs_utils_1 = require("../storage/fs-utils");
44
+ const providers_repo_1 = require("../storage/providers-repo");
45
45
  const run_mutation_1 = require("./run-mutation");
46
46
  /**
47
47
  * Imports provider definitions from an external JSON file into the managed registry.
@@ -83,13 +83,21 @@ function importProviders(args) {
83
83
  const missingViews = nextViews.filter((view) => view.source === "orphaned-reference");
84
84
  const repairedProfiles = [];
85
85
  const upsertProfiles = missingViews.reduce((accumulator, view) => {
86
- const repair = args.repairProfiles?.[view.name];
87
- if (!repair) {
88
- throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", "Import would create provider references to missing config profiles that need model and base_url.", {
86
+ const sourceView = currentViews.find((entry) => entry.name === view.name) ?? null;
87
+ if (!sourceView?.model) {
88
+ throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", "Import would create provider references to missing config profiles that need model and matching model_provider sections.", {
89
89
  profilesNeedingRepair: missingViews.map((entry) => entry.name).sort(),
90
90
  });
91
91
  }
92
- accumulator[view.name] = (0, config_1.validateManagedProfileCreation)(view.name, repair);
92
+ if (sourceView.modelProvider !== view.name || !sourceView.baseUrl) {
93
+ throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", "Import would create provider references to missing config profiles without matching model_provider runtime sections.", {
94
+ profilesNeedingRepair: missingViews.map((entry) => entry.name).sort(),
95
+ });
96
+ }
97
+ accumulator[view.name] = (0, config_1.validateManagedProfileCreation)(view.name, {
98
+ model: sourceView.model,
99
+ modelProvider: view.name,
100
+ });
93
101
  repairedProfiles.push(view.name);
94
102
  return accumulator;
95
103
  }, {});
@@ -0,0 +1,68 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initCodex = initCodex;
37
+ const fs = __importStar(require("node:fs"));
38
+ const errors_1 = require("../domain/errors");
39
+ const fs_utils_1 = require("../storage/fs-utils");
40
+ const providers_repo_1 = require("../storage/providers-repo");
41
+ /**
42
+ * Initializes a Codex directory for managed providers.json usage without requiring live Codex state.
43
+ */
44
+ function initCodex(args) {
45
+ const codexDirExists = fs.existsSync(args.codexDir);
46
+ if (!codexDirExists && !args.createCodexDir) {
47
+ throw (0, errors_1.cliError)("CODEX_DIR_NOT_FOUND", "The requested Codex directory does not exist.", {
48
+ codexDir: args.codexDir,
49
+ });
50
+ }
51
+ if (!codexDirExists) {
52
+ (0, fs_utils_1.ensureDir)(args.codexDir);
53
+ }
54
+ const providersExists = fs.existsSync(args.providersPath);
55
+ if (!providersExists) {
56
+ (0, providers_repo_1.writeProvidersFile)(args.providersPath, { providers: {} });
57
+ }
58
+ return {
59
+ data: {
60
+ codexDir: args.codexDir,
61
+ createdCodexDir: !codexDirExists,
62
+ createdProvidersFile: !providersExists,
63
+ providersAlreadyExisted: providersExists,
64
+ configExists: fs.existsSync(args.configPath),
65
+ authExists: fs.existsSync(args.authPath),
66
+ },
67
+ };
68
+ }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.listBackupEntries = listBackupEntries;
4
- const backup_repo_1 = require("../infra/backup-repo");
4
+ const backup_repo_1 = require("../storage/backup-repo");
5
5
  /**
6
6
  * Lists backup manifests available under the managed Codex backups directory.
7
7
  */