@minniexcode/codex-switch 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.AI.md +8 -3
- package/README.md +160 -91
- package/dist/app/add-provider.js +32 -1
- package/dist/app/edit-provider.js +137 -0
- package/dist/app/get-status.js +9 -2
- package/dist/app/import-providers.js +47 -2
- package/dist/app/list-backups.js +17 -0
- package/dist/app/list-config-profiles.js +29 -0
- package/dist/app/remove-provider.js +34 -2
- package/dist/app/rollback-backup.js +30 -0
- package/dist/app/run-doctor.js +22 -21
- package/dist/app/setup-codex.js +155 -0
- package/dist/app/show-config.js +34 -0
- package/dist/app/show-provider.js +22 -0
- package/dist/app/switch-provider.js +5 -2
- package/dist/cli/add-interactive.js +25 -31
- package/dist/cli/args.js +19 -5
- package/dist/cli/help.js +109 -14
- package/dist/cli/interactive.js +123 -8
- package/dist/cli/output.js +56 -1
- package/dist/cli/prompt.js +19 -2
- package/dist/cli.js +250 -13
- package/dist/domain/backups.js +103 -0
- package/dist/domain/config.js +471 -39
- package/dist/domain/errors.js +3 -3
- package/dist/domain/providers.js +10 -0
- package/dist/domain/setup.js +30 -0
- package/dist/infra/backup-repo.js +65 -6
- package/dist/infra/codex-cli.js +79 -2
- package/dist/infra/codex-discovery.js +10 -0
- package/dist/infra/codex-paths.js +14 -1
- package/dist/infra/config-repo.js +102 -9
- package/dist/infra/providers-repo.js +29 -0
- package/docs/Design/codex-switch-v0.0.4-design.md +874 -0
- package/docs/Design/codex-switch-v0.0.5-design.md +922 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +308 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +343 -0
- package/docs/{codex-switch-prd.md → PRD/codex-switch-prd.md} +9 -5
- package/docs/cli-usage.md +580 -0
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +1 -1
- package/docs/codex-switch-product-research.md +2 -2
- package/docs/codex-switch-technical-architecture.md +1 -1
- package/package.json +1 -1
package/README.AI.md
CHANGED
|
@@ -17,16 +17,20 @@ Primary goals:
|
|
|
17
17
|
## Main Command Surface
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
+
codexs setup
|
|
20
21
|
codexs list
|
|
22
|
+
codexs show <provider>
|
|
21
23
|
codexs current
|
|
22
|
-
codexs switch <provider>
|
|
23
24
|
codexs status
|
|
25
|
+
codexs edit <provider>
|
|
26
|
+
codexs switch <provider>
|
|
24
27
|
codexs import <file>
|
|
25
28
|
codexs export <file>
|
|
26
29
|
codexs add <provider>
|
|
27
30
|
codexs remove <provider>
|
|
31
|
+
codexs backups list
|
|
28
32
|
codexs doctor
|
|
29
|
-
codexs rollback
|
|
33
|
+
codexs rollback [backup-id]
|
|
30
34
|
```
|
|
31
35
|
|
|
32
36
|
Shared flags:
|
|
@@ -88,11 +92,12 @@ codexs status --json
|
|
|
88
92
|
Current package version in this repository:
|
|
89
93
|
|
|
90
94
|
```text
|
|
91
|
-
0.0.
|
|
95
|
+
0.0.4
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
Recent version summary:
|
|
95
99
|
|
|
100
|
+
- `0.0.4`: setup/show/edit/backups list/specific rollback/import merge and clearer CLI semantics
|
|
96
101
|
- `0.0.3`: interactive TTY flows and improved help
|
|
97
102
|
- `0.0.2`: mutation orchestration, backups, rollback, locks, drift detection improvements
|
|
98
103
|
- `0.0.1`: initial TypeScript CLI and baseline docs
|
package/README.md
CHANGED
|
@@ -1,113 +1,173 @@
|
|
|
1
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
|
-
|
|
5
|
+
`codex-switch` 是一个本地优先的 CLI,用来安全地管理和切换 Codex 的 provider/profile 配置。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
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
8
|
|
|
9
|
-
|
|
9
|
+
它面向同时维护多个 Codex provider、API key 或 profile 的用户,目标是用可重复、先备份再写入的方式替代手动修改 `~/.codex/` 下的文件。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Overview | 简介
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
- back up config before writes
|
|
15
|
-
- roll back on failure
|
|
16
|
-
- support both humans in a terminal and AI/automation workflows
|
|
13
|
+
What it does:
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
- Initialize `providers.json` from an existing Codex directory
|
|
16
|
+
- List, show, add, edit, and remove provider records
|
|
17
|
+
- Switch the active provider/profile safely
|
|
18
|
+
- Import and export provider definitions
|
|
19
|
+
- Run diagnostics and detect local drift
|
|
20
|
+
- List backups and rollback to a previous managed state
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
它可以完成的事情:
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
codexs export <file>
|
|
29
|
-
codexs add <provider>
|
|
30
|
-
codexs remove <provider>
|
|
31
|
-
codexs doctor
|
|
32
|
-
codexs rollback
|
|
33
|
-
```
|
|
24
|
+
- 从现有 Codex 目录初始化 `providers.json`
|
|
25
|
+
- 查看、新增、编辑、删除 provider 记录
|
|
26
|
+
- 安全切换当前激活的 provider/profile
|
|
27
|
+
- 导入和导出 provider 配置
|
|
28
|
+
- 运行诊断并识别本地配置漂移
|
|
29
|
+
- 查看备份并回滚到之前的受管状态
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
Current version: `0.0.4`
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
- show the current active profile
|
|
39
|
-
- switch to another provider safely
|
|
40
|
-
- import and export provider mappings
|
|
41
|
-
- add or remove provider records
|
|
42
|
-
- detect config drift and common local issues
|
|
43
|
-
- back up managed files before mutation and roll back when needed
|
|
33
|
+
当前版本:`0.0.4`
|
|
44
34
|
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
### For Humans
|
|
35
|
+
## Install | 安装
|
|
48
36
|
|
|
49
37
|
Install globally:
|
|
50
38
|
|
|
51
|
-
```
|
|
39
|
+
```bash
|
|
52
40
|
npm install -g @minniexcode/codex-switch
|
|
53
41
|
```
|
|
54
42
|
|
|
55
|
-
Or run
|
|
43
|
+
Or run directly:
|
|
56
44
|
|
|
57
|
-
```
|
|
45
|
+
```bash
|
|
58
46
|
npx @minniexcode/codex-switch --help
|
|
59
47
|
```
|
|
60
48
|
|
|
61
|
-
|
|
49
|
+
全局安装:
|
|
62
50
|
|
|
63
|
-
```
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g @minniexcode/codex-switch
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
或者直接运行:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx @minniexcode/codex-switch --help
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
CLI entry:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
64
|
codexs --help
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
命令入口:
|
|
68
68
|
|
|
69
|
-
```
|
|
69
|
+
```bash
|
|
70
|
+
codexs --help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Start | 快速开始
|
|
74
|
+
|
|
75
|
+
Take over an existing Codex directory:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
codexs setup
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
接管当前已有的 Codex 目录:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
codexs setup
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Inspect managed providers:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
70
90
|
codexs list
|
|
71
|
-
codexs
|
|
91
|
+
codexs show my-provider
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
查看已管理的 provider:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
codexs list
|
|
98
|
+
codexs show my-provider
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Add and switch:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
72
104
|
codexs add my-provider --profile my-provider --api-key sk-xxx
|
|
73
105
|
codexs switch my-provider
|
|
74
|
-
codexs status
|
|
75
106
|
```
|
|
76
107
|
|
|
77
|
-
|
|
108
|
+
新增并切换:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
codexs add my-provider --profile my-provider --api-key sk-xxx
|
|
112
|
+
codexs switch my-provider
|
|
113
|
+
```
|
|
78
114
|
|
|
79
|
-
|
|
115
|
+
Check runtime state:
|
|
80
116
|
|
|
81
|
-
```
|
|
82
|
-
|
|
117
|
+
```bash
|
|
118
|
+
codexs current
|
|
119
|
+
codexs status
|
|
120
|
+
codexs doctor
|
|
83
121
|
```
|
|
84
122
|
|
|
85
|
-
|
|
123
|
+
检查当前运行状态:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
codexs current
|
|
127
|
+
codexs status
|
|
128
|
+
codexs doctor
|
|
129
|
+
```
|
|
86
130
|
|
|
87
|
-
|
|
131
|
+
## Common Commands | 常用命令
|
|
88
132
|
|
|
89
|
-
|
|
133
|
+
```bash
|
|
134
|
+
codexs setup
|
|
135
|
+
codexs list
|
|
136
|
+
codexs show <provider>
|
|
137
|
+
codexs current
|
|
138
|
+
codexs status
|
|
139
|
+
codexs add <provider> --profile <name> --api-key <key>
|
|
140
|
+
codexs edit <provider> [--profile <name>] [--api-key <key>]
|
|
141
|
+
codexs switch <provider>
|
|
142
|
+
codexs remove <provider>
|
|
143
|
+
codexs import <file> [--merge]
|
|
144
|
+
codexs export <file>
|
|
145
|
+
codexs backups list
|
|
146
|
+
codexs rollback [backup-id]
|
|
147
|
+
codexs doctor
|
|
148
|
+
```
|
|
90
149
|
|
|
91
|
-
|
|
150
|
+
Command help:
|
|
92
151
|
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
152
|
+
```bash
|
|
153
|
+
codexs help switch
|
|
154
|
+
codexs help setup
|
|
96
155
|
```
|
|
97
156
|
|
|
98
|
-
|
|
157
|
+
命令帮助:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
codexs help switch
|
|
161
|
+
codexs help setup
|
|
162
|
+
```
|
|
99
163
|
|
|
100
|
-
|
|
164
|
+
## How It Works | 工作方式
|
|
101
165
|
|
|
102
|
-
|
|
103
|
-
- `codexs switch` can show a provider selector when no provider is passed
|
|
104
|
-
- `codexs remove` supports interactive selection and confirmation
|
|
105
|
-
- `import`, `export`, and `rollback` ask for confirmation in interactive mode
|
|
106
|
-
- `--json` remains non-interactive for scripts and agents
|
|
166
|
+
By default, `codex-switch` operates on `~/.codex/`, and you can override the target with `--codex-dir`.
|
|
107
167
|
|
|
108
|
-
|
|
168
|
+
`codex-switch` 默认围绕 `~/.codex/` 工作,也可以通过 `--codex-dir` 指向其他目录。
|
|
109
169
|
|
|
110
|
-
|
|
170
|
+
Managed files:
|
|
111
171
|
|
|
112
172
|
```text
|
|
113
173
|
~/.codex/
|
|
@@ -117,46 +177,55 @@ The CLI supports both explicit commands and guided terminal flows.
|
|
|
117
177
|
backups/
|
|
118
178
|
```
|
|
119
179
|
|
|
120
|
-
|
|
180
|
+
说明:
|
|
121
181
|
|
|
122
|
-
- `providers.json` is the
|
|
123
|
-
- `config.toml` and `auth.json`
|
|
124
|
-
-
|
|
182
|
+
- `providers.json` is the managed provider registry
|
|
183
|
+
- `config.toml` and `auth.json` represent runtime state
|
|
184
|
+
- mutating commands back up before writing
|
|
185
|
+
- rollback is available after failed or undesired changes
|
|
125
186
|
|
|
126
|
-
`providers.json`
|
|
187
|
+
- `providers.json` 是受管理的 provider 注册表
|
|
188
|
+
- `config.toml` 和 `auth.json` 代表当前运行态
|
|
189
|
+
- 所有写操作都会先备份再写入
|
|
190
|
+
- 变更失败或结果不符合预期时可以回滚
|
|
127
191
|
|
|
128
|
-
##
|
|
192
|
+
## Automation | 自动化
|
|
129
193
|
|
|
130
|
-
|
|
194
|
+
This CLI supports both human TTY use and non-interactive automation.
|
|
131
195
|
|
|
132
|
-
|
|
133
|
-
- [AI README](./README.AI.md)
|
|
134
|
-
- [Product Overview](./docs/codex-switch-product-overview.md)
|
|
135
|
-
- [Product Research](./docs/codex-switch-product-research.md)
|
|
136
|
-
- [PRD](./docs/codex-switch-prd.md)
|
|
137
|
-
- [Technical Architecture](./docs/codex-switch-technical-architecture.md)
|
|
138
|
-
- [Command Design](./docs/codex-switch-command-design.md)
|
|
196
|
+
这个 CLI 同时支持人工交互和非交互自动化。
|
|
139
197
|
|
|
140
|
-
|
|
198
|
+
Recommended global flags:
|
|
141
199
|
|
|
142
|
-
|
|
200
|
+
```bash
|
|
201
|
+
--json
|
|
202
|
+
--codex-dir <path>
|
|
203
|
+
--help
|
|
204
|
+
--version
|
|
205
|
+
```
|
|
143
206
|
|
|
144
|
-
|
|
145
|
-
- Improved help output and command-specific guidance
|
|
146
|
-
- Expanded CLI test coverage for interactive and argument handling behavior
|
|
207
|
+
建议:
|
|
147
208
|
|
|
148
|
-
|
|
209
|
+
- use `--json` for stable machine-readable output
|
|
210
|
+
- pass all required arguments explicitly in scripts or CI
|
|
211
|
+
- use `--codex-dir <path>` for sandbox or test environments
|
|
149
212
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
213
|
+
- 在脚本或 CI 中使用 `--json` 获取稳定输出
|
|
214
|
+
- 在非交互环境中显式传入所有必需参数
|
|
215
|
+
- 在测试环境中优先配合 `--codex-dir <path>` 使用
|
|
153
216
|
|
|
154
|
-
|
|
217
|
+
## Documentation | 文档
|
|
155
218
|
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
219
|
+
- [Detailed CLI Usage](./docs/cli-usage.md)
|
|
220
|
+
- [详细 CLI 使用文档](./docs/cli-usage.md)
|
|
221
|
+
- [Changelog](./CHANGELOG.md)
|
|
222
|
+
- [更新日志](./CHANGELOG.md)
|
|
223
|
+
- [AI README](./README.AI.md)
|
|
224
|
+
- [中文 README(历史版)](./README.CN.md)
|
|
225
|
+
- [Product Overview](./docs/codex-switch-product-overview.md)
|
|
226
|
+
- [Technical Architecture](./docs/codex-switch-technical-architecture.md)
|
|
227
|
+
- [0.0.4 Design Doc](./docs/codex-switch-v0.0.4-design.md)
|
|
159
228
|
|
|
160
|
-
## License
|
|
229
|
+
## License | 许可证
|
|
161
230
|
|
|
162
231
|
MIT
|
package/dist/app/add-provider.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.addProvider = addProvider;
|
|
4
|
+
const config_1 = require("../domain/config");
|
|
4
5
|
const providers_1 = require("../domain/providers");
|
|
5
6
|
const errors_1 = require("../domain/errors");
|
|
7
|
+
const config_repo_1 = require("../infra/config-repo");
|
|
6
8
|
const fs_utils_1 = require("../infra/fs-utils");
|
|
7
9
|
const providers_repo_1 = require("../infra/providers-repo");
|
|
8
10
|
const run_mutation_1 = require("./run-mutation");
|
|
@@ -15,6 +17,22 @@ function addProvider(args) {
|
|
|
15
17
|
if (providers.providers[args.providerName]) {
|
|
16
18
|
throw (0, errors_1.cliError)("INVALID_IMPORT_FILE", `Provider "${args.providerName}" already exists.`);
|
|
17
19
|
}
|
|
20
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
21
|
+
const existingProfile = document.profiles.find((profile) => profile.name === args.profile);
|
|
22
|
+
if (!existingProfile && !args.createProfile) {
|
|
23
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${args.profile}" does not exist in config.toml.`, {
|
|
24
|
+
profile: args.profile,
|
|
25
|
+
provider: args.providerName,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const upsertProfiles = !existingProfile && args.createProfile
|
|
29
|
+
? {
|
|
30
|
+
[args.profile]: (0, config_1.validateManagedProfileCreation)(args.profile, {
|
|
31
|
+
model: args.model ?? undefined,
|
|
32
|
+
baseUrl: args.baseUrl ?? undefined,
|
|
33
|
+
}),
|
|
34
|
+
}
|
|
35
|
+
: undefined;
|
|
18
36
|
const next = {
|
|
19
37
|
providers: {
|
|
20
38
|
...providers.providers,
|
|
@@ -32,13 +50,26 @@ function addProvider(args) {
|
|
|
32
50
|
backupsDir: args.backupsDir,
|
|
33
51
|
latestBackupPath: args.latestBackupPath,
|
|
34
52
|
operation: "add",
|
|
35
|
-
files: [
|
|
53
|
+
files: [
|
|
54
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
55
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
56
|
+
],
|
|
36
57
|
mutate: () => {
|
|
58
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
59
|
+
upsertProfiles,
|
|
60
|
+
});
|
|
37
61
|
// Persist only the normalized provider payload so later reads are deterministic.
|
|
38
62
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
|
|
63
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
39
64
|
return {
|
|
40
65
|
provider: args.providerName,
|
|
41
66
|
profile: args.profile,
|
|
67
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
68
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
69
|
+
keptSharedProfiles: [],
|
|
70
|
+
switchedActiveProfile: configPlan.switchedActiveProfile,
|
|
71
|
+
adoptedProfiles: [],
|
|
72
|
+
repairedProfiles: [],
|
|
42
73
|
};
|
|
43
74
|
},
|
|
44
75
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.editProvider = editProvider;
|
|
4
|
+
const errors_1 = require("../domain/errors");
|
|
5
|
+
const config_1 = require("../domain/config");
|
|
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");
|
|
10
|
+
const run_mutation_1 = require("./run-mutation");
|
|
11
|
+
/**
|
|
12
|
+
* Updates selected fields on a single managed provider.
|
|
13
|
+
*/
|
|
14
|
+
function editProvider(args) {
|
|
15
|
+
(0, fs_utils_1.ensureDir)(args.codexDir);
|
|
16
|
+
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
17
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
18
|
+
const current = providers.providers[args.providerName];
|
|
19
|
+
if (!current) {
|
|
20
|
+
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${args.providerName}" was not found.`, {
|
|
21
|
+
provider: args.providerName,
|
|
22
|
+
file: args.providersPath,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
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
|
+
});
|
|
33
|
+
if (args.profile !== undefined && args.profile !== current.profile) {
|
|
34
|
+
updatedFields.push("profile");
|
|
35
|
+
}
|
|
36
|
+
if (args.apiKey !== undefined && args.apiKey !== current.apiKey) {
|
|
37
|
+
updatedFields.push("apiKey");
|
|
38
|
+
}
|
|
39
|
+
if (args.baseUrl !== undefined && (args.baseUrl ?? undefined) !== current.baseUrl) {
|
|
40
|
+
updatedFields.push("baseUrl");
|
|
41
|
+
}
|
|
42
|
+
if (args.note !== undefined && (args.note ?? undefined) !== current.note) {
|
|
43
|
+
updatedFields.push("note");
|
|
44
|
+
}
|
|
45
|
+
if (args.tags !== undefined) {
|
|
46
|
+
updatedFields.push("tags");
|
|
47
|
+
}
|
|
48
|
+
const oldProfile = current.profile;
|
|
49
|
+
const newProfile = nextRecord.profile;
|
|
50
|
+
const targetSection = document.profiles.find((profile) => profile.name === newProfile) ?? null;
|
|
51
|
+
const targetProfileExists = Boolean(targetSection);
|
|
52
|
+
let upsertProfiles;
|
|
53
|
+
if (!targetProfileExists) {
|
|
54
|
+
if (!args.createProfile) {
|
|
55
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${newProfile}" does not exist in config.toml.`, {
|
|
56
|
+
profile: newProfile,
|
|
57
|
+
provider: args.providerName,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
upsertProfiles = {
|
|
61
|
+
[newProfile]: (0, config_1.validateManagedProfileCreation)(newProfile, {
|
|
62
|
+
model: args.model ?? undefined,
|
|
63
|
+
baseUrl: args.baseUrl ?? undefined,
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else if (args.model !== undefined || args.baseUrl !== undefined) {
|
|
68
|
+
upsertProfiles = {
|
|
69
|
+
[newProfile]: {
|
|
70
|
+
...(args.model !== undefined && args.model !== null ? { model: args.model } : {}),
|
|
71
|
+
...(args.baseUrl !== undefined && args.baseUrl !== null ? { baseUrl: args.baseUrl } : {}),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
if (args.model !== undefined && (targetSection?.model !== args.model) && !updatedFields.includes("model")) {
|
|
75
|
+
updatedFields.push("model");
|
|
76
|
+
}
|
|
77
|
+
if (args.baseUrl !== undefined && targetSection?.baseUrl !== args.baseUrl && !updatedFields.includes("baseUrl")) {
|
|
78
|
+
updatedFields.push("baseUrl");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const remainingLinksByProfile = new Map();
|
|
82
|
+
for (const [name, provider] of Object.entries(providers.providers)) {
|
|
83
|
+
if (name === args.providerName) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const list = remainingLinksByProfile.get(provider.profile) ?? [];
|
|
87
|
+
list.push(name);
|
|
88
|
+
remainingLinksByProfile.set(provider.profile, list);
|
|
89
|
+
}
|
|
90
|
+
if (newProfile !== oldProfile) {
|
|
91
|
+
const list = remainingLinksByProfile.get(newProfile) ?? [];
|
|
92
|
+
list.push(args.providerName);
|
|
93
|
+
remainingLinksByProfile.set(newProfile, list);
|
|
94
|
+
}
|
|
95
|
+
const lifecycle = (0, config_1.planProfileLifecycleOutcome)({
|
|
96
|
+
providerName: args.providerName,
|
|
97
|
+
oldProfile,
|
|
98
|
+
newProfile,
|
|
99
|
+
activeProfile: document.activeProfile,
|
|
100
|
+
remainingLinksByProfile,
|
|
101
|
+
switchToProfile: args.switchToProfile ?? null,
|
|
102
|
+
});
|
|
103
|
+
return (0, run_mutation_1.runMutation)({
|
|
104
|
+
codexDir: args.codexDir,
|
|
105
|
+
backupsDir: args.backupsDir,
|
|
106
|
+
latestBackupPath: args.latestBackupPath,
|
|
107
|
+
operation: "edit",
|
|
108
|
+
files: [
|
|
109
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
110
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
111
|
+
],
|
|
112
|
+
mutate: () => {
|
|
113
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
114
|
+
upsertProfiles,
|
|
115
|
+
deleteProfiles: lifecycle.deletedProfileSections,
|
|
116
|
+
setActiveProfile: lifecycle.nextActiveProfile,
|
|
117
|
+
});
|
|
118
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, {
|
|
119
|
+
providers: {
|
|
120
|
+
...providers.providers,
|
|
121
|
+
[args.providerName]: nextRecord,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
125
|
+
return {
|
|
126
|
+
provider: args.providerName,
|
|
127
|
+
updatedFields,
|
|
128
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
129
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
130
|
+
keptSharedProfiles: lifecycle.keptSharedProfiles,
|
|
131
|
+
switchedActiveProfile: lifecycle.switchedActiveProfile,
|
|
132
|
+
adoptedProfiles: [],
|
|
133
|
+
repairedProfiles: [],
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
package/dist/app/get-status.js
CHANGED
|
@@ -37,6 +37,7 @@ 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");
|
|
40
41
|
const providers_repo_1 = require("../infra/providers-repo");
|
|
41
42
|
/**
|
|
42
43
|
* Reports the current on-disk runtime state and how it maps back to managed providers.
|
|
@@ -47,9 +48,13 @@ function getStatus(codexDir, configPath, providersPath) {
|
|
|
47
48
|
let currentProfile = null;
|
|
48
49
|
const warnings = [];
|
|
49
50
|
const providers = providersExists ? (0, providers_repo_1.readProvidersFile)(providersPath) : null;
|
|
51
|
+
let configViews = [];
|
|
52
|
+
let consistencyIssues = [];
|
|
50
53
|
if (configExists) {
|
|
51
|
-
const
|
|
52
|
-
currentProfile =
|
|
54
|
+
const document = (0, config_repo_1.readStructuredConfig)(configPath);
|
|
55
|
+
currentProfile = document.activeProfile;
|
|
56
|
+
configViews = (0, config_1.buildManagedProfileViews)(document, providers);
|
|
57
|
+
consistencyIssues = (0, config_1.collectConfigConsistencyIssues)(document, providers);
|
|
53
58
|
if (!currentProfile) {
|
|
54
59
|
warnings.push("config.toml exists but has no top-level profile.");
|
|
55
60
|
}
|
|
@@ -70,6 +75,8 @@ function getStatus(codexDir, configPath, providersPath) {
|
|
|
70
75
|
currentProfileMapped: liveState.profileMapped,
|
|
71
76
|
provider: liveState.mappedProvider,
|
|
72
77
|
liveState,
|
|
78
|
+
configProfiles: configViews,
|
|
79
|
+
issues: consistencyIssues,
|
|
73
80
|
},
|
|
74
81
|
};
|
|
75
82
|
}
|
|
@@ -36,8 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.importProviders = importProviders;
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const path = __importStar(require("node:path"));
|
|
39
|
+
const config_1 = require("../domain/config");
|
|
39
40
|
const providers_1 = require("../domain/providers");
|
|
40
41
|
const errors_1 = require("../domain/errors");
|
|
42
|
+
const config_repo_1 = require("../infra/config-repo");
|
|
41
43
|
const fs_utils_1 = require("../infra/fs-utils");
|
|
42
44
|
const providers_repo_1 = require("../infra/providers-repo");
|
|
43
45
|
const run_mutation_1 = require("./run-mutation");
|
|
@@ -58,16 +60,59 @@ function importProviders(args) {
|
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
(0, fs_utils_1.ensureDir)(args.codexDir);
|
|
63
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
61
64
|
return (0, run_mutation_1.runMutation)({
|
|
62
65
|
codexDir: args.codexDir,
|
|
63
66
|
backupsDir: args.backupsDir,
|
|
64
67
|
latestBackupPath: args.latestBackupPath,
|
|
65
68
|
operation: "import",
|
|
66
|
-
files: [
|
|
69
|
+
files: [
|
|
70
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
71
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
72
|
+
],
|
|
67
73
|
mutate: () => {
|
|
68
|
-
(0, providers_repo_1.
|
|
74
|
+
const current = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
75
|
+
const next = args.merge ? (0, providers_repo_1.mergeProviders)(current, imported) : imported;
|
|
76
|
+
const currentViews = (0, config_1.buildManagedProfileViews)(document, current);
|
|
77
|
+
const nextViews = (0, config_1.buildManagedProfileViews)(document, next);
|
|
78
|
+
const adoptedProfiles = currentViews
|
|
79
|
+
.filter((view) => view.source === "unmanaged" && view.linkedProviders.length === 0)
|
|
80
|
+
.filter((view) => nextViews.some((nextView) => nextView.name === view.name && nextView.managed))
|
|
81
|
+
.map((view) => view.name)
|
|
82
|
+
.sort();
|
|
83
|
+
const missingViews = nextViews.filter((view) => view.source === "orphaned-reference");
|
|
84
|
+
const repairedProfiles = [];
|
|
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.", {
|
|
89
|
+
profilesNeedingRepair: missingViews.map((entry) => entry.name).sort(),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
accumulator[view.name] = (0, config_1.validateManagedProfileCreation)(view.name, repair);
|
|
93
|
+
repairedProfiles.push(view.name);
|
|
94
|
+
return accumulator;
|
|
95
|
+
}, {});
|
|
96
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
97
|
+
upsertProfiles,
|
|
98
|
+
});
|
|
99
|
+
(0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
|
|
100
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
101
|
+
const replacedProviders = args.merge
|
|
102
|
+
? Object.keys(imported.providers).filter((name) => current.providers[name]).sort()
|
|
103
|
+
: [];
|
|
69
104
|
return {
|
|
105
|
+
mode: args.merge ? "merge" : "replace",
|
|
70
106
|
importedProviders: Object.keys(imported.providers).sort(),
|
|
107
|
+
importedCount: Object.keys(imported.providers).length,
|
|
108
|
+
mergedCount: Object.keys(next.providers).length,
|
|
109
|
+
replacedProviders,
|
|
110
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
111
|
+
deletedProfileSections: [],
|
|
112
|
+
keptSharedProfiles: nextViews.filter((view) => view.linkedProviders.length > 1).map((view) => view.name),
|
|
113
|
+
switchedActiveProfile: false,
|
|
114
|
+
adoptedProfiles,
|
|
115
|
+
repairedProfiles: repairedProfiles.sort(),
|
|
71
116
|
};
|
|
72
117
|
},
|
|
73
118
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listBackupEntries = listBackupEntries;
|
|
4
|
+
const backup_repo_1 = require("../infra/backup-repo");
|
|
5
|
+
/**
|
|
6
|
+
* Lists backup manifests available under the managed Codex backups directory.
|
|
7
|
+
*/
|
|
8
|
+
function listBackupEntries(backupsDir) {
|
|
9
|
+
const result = (0, backup_repo_1.listBackups)(backupsDir);
|
|
10
|
+
return {
|
|
11
|
+
data: {
|
|
12
|
+
backups: result.backups,
|
|
13
|
+
count: result.backups.length,
|
|
14
|
+
},
|
|
15
|
+
warnings: result.warnings,
|
|
16
|
+
};
|
|
17
|
+
}
|