@minniexcode/codex-switch 0.0.4 → 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/dist/app/add-provider.js +32 -1
- package/dist/app/edit-provider.js +74 -1
- package/dist/app/get-status.js +9 -2
- package/dist/app/import-providers.js +37 -1
- package/dist/app/list-config-profiles.js +29 -0
- package/dist/app/remove-provider.js +34 -2
- package/dist/app/run-doctor.js +22 -21
- package/dist/app/setup-codex.js +33 -16
- package/dist/app/show-config.js +34 -0
- package/dist/app/switch-provider.js +5 -2
- package/dist/cli/args.js +13 -2
- package/dist/cli/help.js +42 -5
- package/dist/cli/interactive.js +56 -0
- package/dist/cli/output.js +22 -0
- package/dist/cli.js +101 -12
- package/dist/domain/config.js +471 -39
- package/dist/infra/codex-cli.js +18 -3
- package/dist/infra/codex-discovery.js +3 -41
- package/dist/infra/codex-paths.js +1 -1
- package/dist/infra/config-repo.js +102 -9
- 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 +210 -260
- package/package.json +1 -1
- /package/docs/{codex-switch-v0.0.4-design.md → Design/codex-switch-v0.0.4-design.md} +0 -0
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
# codex-switch `0.0.5` 设计文档
|
|
2
|
+
|
|
3
|
+
## 文档信息
|
|
4
|
+
|
|
5
|
+
- 文档类型:详细设计文档
|
|
6
|
+
- 适用版本:`0.0.5`
|
|
7
|
+
- 目标范围:`0.0.4 -> 0.0.5`
|
|
8
|
+
- 主对齐 PRD:[`../PRD/codex-switch-prd-v0.1.0.md`](../PRD/codex-switch-prd-v0.1.0.md)
|
|
9
|
+
- 远期边界参考:[`../PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md`](../PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md)
|
|
10
|
+
- 风格基线:[`codex-switch-v0.0.4-design.md`](./codex-switch-v0.0.4-design.md)
|
|
11
|
+
|
|
12
|
+
## 1. 文档目标
|
|
13
|
+
|
|
14
|
+
这份文档回答的是 `0.0.5` 应该怎样落地,而不是继续讨论长期愿景:
|
|
15
|
+
|
|
16
|
+
- `config.toml` 怎样从字符串级切换升级为结构化读取与局部受控写回
|
|
17
|
+
- provider 管理命令怎样与 linked profile sections 保持一致
|
|
18
|
+
- `config show` / `config list-profiles` 的命令契约和返回边界是什么
|
|
19
|
+
- 历史状态、不一致状态、adopt / repair 流程怎样进入现有 CLI
|
|
20
|
+
- 当前代码结构上具体改哪些模块、补哪些错误码、补哪些测试
|
|
21
|
+
|
|
22
|
+
目标是让实现阶段不再重复拍板关键技术决策。
|
|
23
|
+
|
|
24
|
+
## 2. 版本定位与设计原则
|
|
25
|
+
|
|
26
|
+
### 2.1 当前基线
|
|
27
|
+
|
|
28
|
+
当前 `0.0.4` 已具备:
|
|
29
|
+
|
|
30
|
+
- provider registry 管理:`list`、`show`、`add`、`edit`、`remove`
|
|
31
|
+
- 运行态切换:`current`、`switch`
|
|
32
|
+
- 导入导出:`import`、`import --merge`、`export`
|
|
33
|
+
- 初始化与诊断:`setup`、`status`、`doctor`
|
|
34
|
+
- 备份恢复:`backups list`、`rollback`
|
|
35
|
+
- 统一 JSON envelope
|
|
36
|
+
- 写操作统一走锁、备份、失败回滚
|
|
37
|
+
|
|
38
|
+
当前实现短板主要在 `config.toml`:
|
|
39
|
+
|
|
40
|
+
- 仅支持浅层读取顶层 active `profile`
|
|
41
|
+
- profile section 仍靠轻量字符串匹配
|
|
42
|
+
- 不能安全维护注释、空行和未受管内容
|
|
43
|
+
- provider 写命令没有稳定的 provider-config 双写一致性模型
|
|
44
|
+
|
|
45
|
+
### 2.2 `0.0.5` 的一句话定义
|
|
46
|
+
|
|
47
|
+
`0.0.5` 的核心不是再堆一批命令,而是建立 `config.toml` 的结构化读取、provider-linked profile 管理和一致性诊断能力。
|
|
48
|
+
|
|
49
|
+
### 2.3 设计原则
|
|
50
|
+
|
|
51
|
+
`0.0.5` 继续沿用现有工程原则:
|
|
52
|
+
|
|
53
|
+
- `CLI First`
|
|
54
|
+
- `Local First`
|
|
55
|
+
- `Safe by Default`
|
|
56
|
+
- `AI Friendly`
|
|
57
|
+
- `Split State Model`
|
|
58
|
+
- `Lightweight Transactions`
|
|
59
|
+
|
|
60
|
+
在此基础上新增四条版本原则:
|
|
61
|
+
|
|
62
|
+
- 不把 `config.toml` 提升为 full config editor
|
|
63
|
+
- 不新增独立 `repair` 命令
|
|
64
|
+
- 不做全量 TOML parse -> stringify 写回
|
|
65
|
+
- 所有 provider-config 双写仍必须纳入同一备份与回滚事务
|
|
66
|
+
|
|
67
|
+
## 3. 范围与边界
|
|
68
|
+
|
|
69
|
+
### 3.1 `0.0.5` 范围内
|
|
70
|
+
|
|
71
|
+
本设计覆盖:
|
|
72
|
+
|
|
73
|
+
- `config.toml` 结构化读取
|
|
74
|
+
- `[profiles.<name>]` 的最小受管写入能力
|
|
75
|
+
- 顶层 active `profile` 与 provider-linked profile section 的一致性
|
|
76
|
+
- `config show`
|
|
77
|
+
- `config list-profiles`
|
|
78
|
+
- `add` / `edit` / `remove` / `setup` / `import --merge` 的 config-aware 升级
|
|
79
|
+
- `doctor` / `status` 的一致性信号升级
|
|
80
|
+
- 多候选 Codex 目录发现与交互选择
|
|
81
|
+
|
|
82
|
+
### 3.2 明确不在 `0.0.5` 范围内
|
|
83
|
+
|
|
84
|
+
下面这些内容不进入本设计:
|
|
85
|
+
|
|
86
|
+
- 通用 `config edit` 命令族
|
|
87
|
+
- `model` / `base_url` 之外的大 profile schema 首版规格
|
|
88
|
+
- 独立 `repair` 命令
|
|
89
|
+
- 第三方 auth / extension 集成
|
|
90
|
+
- 任意顶层 TOML 键的自由增删改
|
|
91
|
+
- 自动化的“全量历史 profile 收编”
|
|
92
|
+
|
|
93
|
+
### 3.3 数据边界
|
|
94
|
+
|
|
95
|
+
`0.0.5` 继续坚持状态分层:
|
|
96
|
+
|
|
97
|
+
- `providers.json`:provider registry 的单一事实源
|
|
98
|
+
- `config.toml`:运行态配置投影,部分受管
|
|
99
|
+
- `auth.json`:认证态
|
|
100
|
+
- `backups/` + `latest.json`:恢复态
|
|
101
|
+
|
|
102
|
+
不引入数据库,不引入新的长期状态仓库。
|
|
103
|
+
|
|
104
|
+
## 4. `0.0.5` 功能总览
|
|
105
|
+
|
|
106
|
+
`0.0.5` 需要完成四件事:
|
|
107
|
+
|
|
108
|
+
- 引入结构化 TOML 读取与非破坏性局部 patch 写回能力
|
|
109
|
+
- 增加稳定的 config 读取命令,供人类、AI 和脚本消费
|
|
110
|
+
- 让 provider 写命令同步维护 linked profile sections
|
|
111
|
+
- 补齐历史状态、不一致状态、共享 profile 和安全删除规则
|
|
112
|
+
|
|
113
|
+
## 5. 数据模型设计
|
|
114
|
+
|
|
115
|
+
### 5.1 `ManagedProfileFields`
|
|
116
|
+
|
|
117
|
+
`ManagedProfileFields` 表示真正写入 `[profiles.<name>]` 的受管字段。
|
|
118
|
+
|
|
119
|
+
`0.0.5` 只正式锁定:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
type ManagedProfileFields = {
|
|
123
|
+
model: string;
|
|
124
|
+
baseUrl: string;
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
规则:
|
|
129
|
+
|
|
130
|
+
- `model` 和 `base_url` 是当前唯一正式受管字段
|
|
131
|
+
- 写命令创建 profile section 时,必须同时具备 `model` 和 `base_url`
|
|
132
|
+
- 未提供任一必需字段时,不允许创建新的受管 section
|
|
133
|
+
- `apiKey` 继续只保存在 `providers.json`
|
|
134
|
+
- `note`、`tags` 等 provider 管理字段不进入 `config.toml`
|
|
135
|
+
|
|
136
|
+
字段归属直接锁定如下:
|
|
137
|
+
|
|
138
|
+
- `[profiles.<name>]`:`model`、`base_url`
|
|
139
|
+
- `providers.json`:`profile`、`apiKey`、可选 `note`、`tags`
|
|
140
|
+
- `auth.json`:当前激活 provider 对应的运行态认证内容
|
|
141
|
+
|
|
142
|
+
设计原因:
|
|
143
|
+
|
|
144
|
+
- 你的使用场景是“中转站”,`model` 和 `base_url` 共同定义真正的上游路由
|
|
145
|
+
- 如果 `base_url` 只放在 `providers.json`,而 `config.toml` 的 profile section 不受控,切换后就可能出现 profile 名变了但请求仍落到旧 endpoint 的分裂状态
|
|
146
|
+
- 因此 `0.0.5` 必须把 `base_url` 与 `model` 一起视为 profile runtime projection 的正式字段
|
|
147
|
+
|
|
148
|
+
### 5.2 `ManagedProfileView`
|
|
149
|
+
|
|
150
|
+
`ManagedProfileView` 是读取命令返回的稳定视图,不等同于持久化结构。
|
|
151
|
+
|
|
152
|
+
建议内部和输出层统一围绕以下字段:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
type ManagedProfileView = {
|
|
156
|
+
name: string;
|
|
157
|
+
managed: boolean;
|
|
158
|
+
isActive: boolean;
|
|
159
|
+
linkedProviders: string[];
|
|
160
|
+
model: string | null;
|
|
161
|
+
baseUrl: string | null;
|
|
162
|
+
managedFields: string[];
|
|
163
|
+
source: "managed" | "unmanaged" | "orphaned-reference";
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
字段语义:
|
|
168
|
+
|
|
169
|
+
- `name`:profile 名
|
|
170
|
+
- `managed`:是否至少被一个 provider 引用
|
|
171
|
+
- `isActive`:是否为顶层 active profile
|
|
172
|
+
- `linkedProviders`:引用该 profile 的 provider 名列表
|
|
173
|
+
- `model`:可识别的受管 `model` 值;不存在或不受管时为 `null`
|
|
174
|
+
- `baseUrl`:可识别的受管 `base_url` 值;不存在或不受管时为 `null`
|
|
175
|
+
- `managedFields`:当前识别到并纳入正式受管的字段名数组;`0.0.5` 只可能为 `[]`、`["model"]`、`["base_url"]` 或 `["model", "base_url"]`
|
|
176
|
+
- `source`:
|
|
177
|
+
- `managed`:section 存在且被 provider 引用
|
|
178
|
+
- `unmanaged`:section 存在但没有 provider 引用
|
|
179
|
+
- `orphaned-reference`:provider 引用了该 profile,但 `config.toml` 中缺少对应 section
|
|
180
|
+
|
|
181
|
+
### 5.3 `ConfigConsistencyIssue`
|
|
182
|
+
|
|
183
|
+
`ConfigConsistencyIssue` 是 `doctor` / `status` 的问题抽象。
|
|
184
|
+
|
|
185
|
+
最少覆盖以下问题类型:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
type ConfigConsistencyIssue =
|
|
189
|
+
| { code: "ORPHANED_PROFILE_REFERENCE"; profile: string; providers: string[] }
|
|
190
|
+
| { code: "UNMANAGED_ACTIVE_PROFILE"; profile: string }
|
|
191
|
+
| { code: "SHARED_PROFILE_REFERENCE"; profile: string; providers: string[] }
|
|
192
|
+
| { code: "ORPHANED_PROFILE_SECTION"; profile: string }
|
|
193
|
+
| { code: "DESTRUCTIVE_REMOVE_BLOCKED"; profile: string; provider: string; activeProfile: string };
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
说明:
|
|
197
|
+
|
|
198
|
+
- `ORPHANED_PROFILE_REFERENCE`:`providers.json` 引用了不存在的 profile section
|
|
199
|
+
- `UNMANAGED_ACTIVE_PROFILE`:当前 active profile 存在,但没有 provider 映射
|
|
200
|
+
- `SHARED_PROFILE_REFERENCE`:多个 provider 指向同一 profile;本身不一定是错误,但必须被识别
|
|
201
|
+
- `ORPHANED_PROFILE_SECTION`:`config.toml` 存在 profile section,但没有 provider 引用
|
|
202
|
+
- `DESTRUCTIVE_REMOVE_BLOCKED`:删除 provider 会导致 active profile 悬空,必须先切换
|
|
203
|
+
|
|
204
|
+
### 5.4 配置文档内部抽象
|
|
205
|
+
|
|
206
|
+
为了支撑非破坏性写回,`0.0.5` 在 infra 层引入下列内部抽象:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
type ParsedConfigDocument = {
|
|
210
|
+
rawText: string;
|
|
211
|
+
lineEnding: "\n" | "\r\n";
|
|
212
|
+
activeProfile: string | null;
|
|
213
|
+
profiles: ProfileSectionRef[];
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
type ProfileSectionRef = {
|
|
217
|
+
name: string;
|
|
218
|
+
headerStart: number;
|
|
219
|
+
sectionStart: number;
|
|
220
|
+
sectionEnd: number;
|
|
221
|
+
modelValueRange: { start: number; end: number } | null;
|
|
222
|
+
baseUrlValueRange: { start: number; end: number } | null;
|
|
223
|
+
model: string | null;
|
|
224
|
+
baseUrl: string | null;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
type ConfigPatchOperation =
|
|
228
|
+
| { kind: "replace-range"; start: number; end: number; text: string }
|
|
229
|
+
| { kind: "insert-at"; index: number; text: string }
|
|
230
|
+
| { kind: "delete-range"; start: number; end: number };
|
|
231
|
+
|
|
232
|
+
type ConfigMutationPlan = {
|
|
233
|
+
operations: ConfigPatchOperation[];
|
|
234
|
+
createdProfileSections: string[];
|
|
235
|
+
deletedProfileSections: string[];
|
|
236
|
+
updatedProfiles: string[];
|
|
237
|
+
switchedActiveProfile: boolean;
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
这些抽象只作为实现内部契约,不直接暴露给 CLI 输出。
|
|
242
|
+
|
|
243
|
+
## 6. TOML 处理路线
|
|
244
|
+
|
|
245
|
+
### 6.1 技术决策
|
|
246
|
+
|
|
247
|
+
`0.0.5` 直接锁定 TOML 技术路线:
|
|
248
|
+
|
|
249
|
+
- 采用 `@toml-tools/parser` 一类的 CST / 结构化 parser 路线
|
|
250
|
+
- 目标是获得 section / field 的稳定位置边界
|
|
251
|
+
- 最终对原始文本执行局部 patch 写回
|
|
252
|
+
|
|
253
|
+
不采用:
|
|
254
|
+
|
|
255
|
+
- 全量 parse -> stringify
|
|
256
|
+
- 重新序列化整个 `config.toml`
|
|
257
|
+
- 继续只靠字符串正则拼接
|
|
258
|
+
|
|
259
|
+
### 6.2 为什么不做全量 stringify
|
|
260
|
+
|
|
261
|
+
全量 stringify 不符合当前产品目标,因为它会带来下面这些不可接受的副作用:
|
|
262
|
+
|
|
263
|
+
- 破坏注释
|
|
264
|
+
- 破坏空行
|
|
265
|
+
- 改写未受管内容的顺序
|
|
266
|
+
- 让用户手工维护的 config 漂移过大
|
|
267
|
+
|
|
268
|
+
`0.0.5` 的目标是受控管理 provider-linked sections,而不是接管整份配置文件。
|
|
269
|
+
|
|
270
|
+
### 6.3 Patch 规则
|
|
271
|
+
|
|
272
|
+
patch 规则直接锁定:
|
|
273
|
+
|
|
274
|
+
- 所有 patch 操作基于原始文本坐标生成
|
|
275
|
+
- 应用时按文本区间从后往前执行
|
|
276
|
+
- 这样可以避免前面 patch 影响后面 patch 的 offset
|
|
277
|
+
|
|
278
|
+
示例策略:
|
|
279
|
+
|
|
280
|
+
1. 先生成完整 `ConfigMutationPlan`
|
|
281
|
+
2. 将 `replace-range` / `delete-range` / `insert-at` 按起始偏移倒序排序
|
|
282
|
+
3. 统一在一次写回中应用
|
|
283
|
+
|
|
284
|
+
### 6.4 结构化读取边界
|
|
285
|
+
|
|
286
|
+
`0.0.5` 结构化识别的范围只包括:
|
|
287
|
+
|
|
288
|
+
- 顶层 `profile = "..."`
|
|
289
|
+
- `[profiles.<name>]`
|
|
290
|
+
- `model = "..."`
|
|
291
|
+
- `base_url = "..."`
|
|
292
|
+
|
|
293
|
+
其余字段允许原样存在,但:
|
|
294
|
+
|
|
295
|
+
- 不在 `0.0.5` 的正式受管写入范围内
|
|
296
|
+
- 不要求被完整解析成稳定 schema
|
|
297
|
+
- 不允许在写入时被误删或重排
|
|
298
|
+
|
|
299
|
+
## 7. 目录发现与 `setup` 候选策略
|
|
300
|
+
|
|
301
|
+
### 7.1 候选集算法
|
|
302
|
+
|
|
303
|
+
`setup` 的目录发现采用保守候选集,不做全盘扫描。
|
|
304
|
+
|
|
305
|
+
优先规则:
|
|
306
|
+
|
|
307
|
+
- 若显式传入 `--codex-dir`,只使用该目录
|
|
308
|
+
- 否则候选集来自:
|
|
309
|
+
- `CODEXS_CODEX_DIR`
|
|
310
|
+
- `dev-codex/local-sandbox`,但仅在 `NODE_ENV=development`
|
|
311
|
+
- `~/.codex`
|
|
312
|
+
|
|
313
|
+
候选处理:
|
|
314
|
+
|
|
315
|
+
- 去重
|
|
316
|
+
- 过滤不存在路径
|
|
317
|
+
- 保留顺序,形成最终候选列表
|
|
318
|
+
|
|
319
|
+
### 7.2 交互规则
|
|
320
|
+
|
|
321
|
+
TTY 模式:
|
|
322
|
+
|
|
323
|
+
- 单候选:自动继续
|
|
324
|
+
- 多候选:让用户选择现有候选或手动输入
|
|
325
|
+
- 无候选:允许手动输入
|
|
326
|
+
|
|
327
|
+
非交互模式:
|
|
328
|
+
|
|
329
|
+
- 多候选:返回 `CODEX_DIR_AMBIGUOUS`
|
|
330
|
+
- 无候选:返回 `CODEX_DIR_NOT_FOUND`
|
|
331
|
+
|
|
332
|
+
### 7.3 设计取舍
|
|
333
|
+
|
|
334
|
+
这里明确不做:
|
|
335
|
+
|
|
336
|
+
- 扫描整个用户目录
|
|
337
|
+
- 递归搜索所有潜在 config 路径
|
|
338
|
+
- 推断多个工作区下的任意历史目录
|
|
339
|
+
|
|
340
|
+
原因是这类扫描成本高、噪声大、可预测性差,不适合作为稳定 CLI 契约。
|
|
341
|
+
|
|
342
|
+
## 8. 命令设计
|
|
343
|
+
|
|
344
|
+
### 8.1 新增命令面
|
|
345
|
+
|
|
346
|
+
`0.0.5` 新增:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
codexs config show [profile] [--json] [--codex-dir <path>]
|
|
350
|
+
codexs config list-profiles [--json] [--codex-dir <path>]
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 8.2 现有命令新增 flags
|
|
354
|
+
|
|
355
|
+
`add <provider>` 新增:
|
|
356
|
+
|
|
357
|
+
- `--create-profile`
|
|
358
|
+
- `--model <name>`
|
|
359
|
+
- `--base-url <url>`
|
|
360
|
+
|
|
361
|
+
`edit <provider>` 新增:
|
|
362
|
+
|
|
363
|
+
- `--create-profile`
|
|
364
|
+
- `--model <name>`
|
|
365
|
+
- `--base-url <url>`
|
|
366
|
+
- 继续支持 `--profile`
|
|
367
|
+
|
|
368
|
+
`remove <provider>` 新增:
|
|
369
|
+
|
|
370
|
+
- `--switch-to <profile>`
|
|
371
|
+
|
|
372
|
+
### 8.3 不新增命令
|
|
373
|
+
|
|
374
|
+
`0.0.5` 明确不新增独立 `repair` 命令。
|
|
375
|
+
|
|
376
|
+
repair / adopt 路径通过以下方式承接:
|
|
377
|
+
|
|
378
|
+
- `doctor` 暴露问题和建议动作
|
|
379
|
+
- `setup` 在交互模式下承接 adopt
|
|
380
|
+
- `import --merge` 在交互模式下承接 adopt / repair
|
|
381
|
+
- 现有写命令在交互模式下承接必要确认
|
|
382
|
+
|
|
383
|
+
## 9. 读取命令契约
|
|
384
|
+
|
|
385
|
+
### 9.1 `config show`
|
|
386
|
+
|
|
387
|
+
#### 用途
|
|
388
|
+
|
|
389
|
+
返回结构化 config 视图,可选聚焦单个 profile。
|
|
390
|
+
|
|
391
|
+
#### 命令形态
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
codexs config show [profile] [--json] [--codex-dir <path>]
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
#### 返回边界
|
|
398
|
+
|
|
399
|
+
默认返回 `config.toml` 中全部可识别 profiles,而不是只返回 managed profiles。
|
|
400
|
+
|
|
401
|
+
JSON 最小字段:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"activeProfile": "packycode",
|
|
406
|
+
"selectedProfile": null,
|
|
407
|
+
"profiles": [
|
|
408
|
+
{
|
|
409
|
+
"name": "packycode",
|
|
410
|
+
"managed": true,
|
|
411
|
+
"isActive": true,
|
|
412
|
+
"linkedProviders": ["packycode"],
|
|
413
|
+
"model": "gpt-5",
|
|
414
|
+
"baseUrl": "https://relay.example.com/v1",
|
|
415
|
+
"managedFields": ["model", "base_url"],
|
|
416
|
+
"source": "managed"
|
|
417
|
+
}
|
|
418
|
+
]
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
若传入 `[profile]`:
|
|
423
|
+
|
|
424
|
+
- `selectedProfile` 返回目标 profile
|
|
425
|
+
- `profiles` 仍建议保持数组 shape,但只包含目标视图
|
|
426
|
+
- 若目标来自 orphaned reference,也允许返回 `source = "orphaned-reference"` 的单条视图
|
|
427
|
+
|
|
428
|
+
#### 失败语义
|
|
429
|
+
|
|
430
|
+
- 读取失败:`CONFIG_NOT_FOUND` 或 `CONFIG_PARSE_ERROR`
|
|
431
|
+
- 指定 profile 不可识别:`PROFILE_NOT_FOUND`
|
|
432
|
+
|
|
433
|
+
### 9.2 `config list-profiles`
|
|
434
|
+
|
|
435
|
+
#### 用途
|
|
436
|
+
|
|
437
|
+
返回 profile 的轻量列表视图。
|
|
438
|
+
|
|
439
|
+
#### 命令形态
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
codexs config list-profiles [--json] [--codex-dir <path>]
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### 返回边界
|
|
446
|
+
|
|
447
|
+
也必须返回全部可识别 profiles,而不是只返回 managed profiles。
|
|
448
|
+
|
|
449
|
+
最小字段:
|
|
450
|
+
|
|
451
|
+
- `name`
|
|
452
|
+
- `managed`
|
|
453
|
+
- `isActive`
|
|
454
|
+
- `linkedProviders`
|
|
455
|
+
- `model`
|
|
456
|
+
- `baseUrl`
|
|
457
|
+
- `source`
|
|
458
|
+
|
|
459
|
+
与 `config show` 的差异:
|
|
460
|
+
|
|
461
|
+
- `list-profiles` 是轻量列表
|
|
462
|
+
- `show` 允许更完整的单 profile 语义和问题上下文
|
|
463
|
+
|
|
464
|
+
## 10. 写命令一致性规则
|
|
465
|
+
|
|
466
|
+
### 10.1 `add <provider>`
|
|
467
|
+
|
|
468
|
+
规则锁定如下:
|
|
469
|
+
|
|
470
|
+
- 当目标 profile 已存在时,只建立 provider -> profile 映射
|
|
471
|
+
- 当目标 profile 缺失时,只有同时传入 `--create-profile --model <name> --base-url <url>` 才允许创建 section
|
|
472
|
+
- 只传 `--create-profile` 但缺少 `--model` 或 `--base-url`,返回 `MANAGED_PROFILE_FIELDS_MISSING`
|
|
473
|
+
- 不允许写出 provider 指向缺失 profile 的新状态
|
|
474
|
+
|
|
475
|
+
### 10.2 `edit <provider>`
|
|
476
|
+
|
|
477
|
+
规则锁定如下:
|
|
478
|
+
|
|
479
|
+
- 改绑到已有 profile:更新 provider 映射并重新计算 active / shared 关系
|
|
480
|
+
- 改绑到缺失 profile:只有 `--create-profile --model <name> --base-url <url>` 才允许
|
|
481
|
+
- 旧 section 不做隐式 rename
|
|
482
|
+
- 旧 section 不做隐式 copy
|
|
483
|
+
- 旧 section 不做隐式 `model` / `base_url` 迁移
|
|
484
|
+
|
|
485
|
+
删除旧 section 的规则:
|
|
486
|
+
|
|
487
|
+
- 若旧 profile 仍被其他 provider 引用:保留
|
|
488
|
+
- 若旧 profile 无其他引用且不是 active profile:可删除
|
|
489
|
+
- 若旧 profile 无其他引用但仍是 active profile:必须先切换或交互确认后显式切换
|
|
490
|
+
|
|
491
|
+
### 10.3 `remove <provider>`
|
|
492
|
+
|
|
493
|
+
规则锁定如下:
|
|
494
|
+
|
|
495
|
+
- 若删除后 profile 仍被其他 provider 引用:保留 section
|
|
496
|
+
- 若删除后已无任何 provider 引用:允许删除 section
|
|
497
|
+
- 若会删掉当前 active profile 且这是最后一个引用:必须先 `switch` 或显式 `--switch-to`
|
|
498
|
+
- 不允许把 active profile 留成悬空状态
|
|
499
|
+
|
|
500
|
+
这里新增明确错误码:
|
|
501
|
+
|
|
502
|
+
- `PROFILE_IN_USE`:对共享 profile 或 active profile 进行危险删除时阻止继续
|
|
503
|
+
|
|
504
|
+
### 10.4 `switch <provider>`
|
|
505
|
+
|
|
506
|
+
`switch` 在 `0.0.5` 继续只负责:
|
|
507
|
+
|
|
508
|
+
- 修改顶层 active `profile`
|
|
509
|
+
|
|
510
|
+
它不负责:
|
|
511
|
+
|
|
512
|
+
- 修复 profile section 内容
|
|
513
|
+
- 创建缺失 section
|
|
514
|
+
- 迁移 profile schema
|
|
515
|
+
|
|
516
|
+
### 10.5 `setup`
|
|
517
|
+
|
|
518
|
+
`setup` 在 `0.0.5` 的要求:
|
|
519
|
+
|
|
520
|
+
- 支持多候选目录发现与 TTY 选择
|
|
521
|
+
- 支持 adopt 现有 unmanaged profiles
|
|
522
|
+
- 不要求一次性把所有历史 profile 全部变为 managed
|
|
523
|
+
- 不能制造新的 registry-config 不一致
|
|
524
|
+
|
|
525
|
+
当发现 unmanaged profile 时:
|
|
526
|
+
|
|
527
|
+
- 交互模式可选择 adopt 或跳过
|
|
528
|
+
- 非交互模式不得静默 adopt;只有在输入已足够明确时才继续
|
|
529
|
+
|
|
530
|
+
### 10.6 `import --merge`
|
|
531
|
+
|
|
532
|
+
`import --merge` 继续保持“导入侧覆盖本地同名 provider”的语义。
|
|
533
|
+
|
|
534
|
+
新增一致性规则:
|
|
535
|
+
|
|
536
|
+
- 如果导入结果引用缺失 profile,进入与 `add` / `edit` 相同的 create / adopt 规则
|
|
537
|
+
- 非交互模式下,无法满足 create 条件则失败
|
|
538
|
+
- 交互模式下,可进入 adopt / repair 辅助流
|
|
539
|
+
- 最终写入结果不得留下新的 orphaned reference
|
|
540
|
+
|
|
541
|
+
## 11. 写结果契约
|
|
542
|
+
|
|
543
|
+
`0.0.5` 要求以下写命令在 JSON `data` 中稳定返回新字段:
|
|
544
|
+
|
|
545
|
+
- `add`
|
|
546
|
+
- `edit`
|
|
547
|
+
- `remove`
|
|
548
|
+
- `setup`
|
|
549
|
+
- `import --merge`
|
|
550
|
+
|
|
551
|
+
新增字段:
|
|
552
|
+
|
|
553
|
+
```json
|
|
554
|
+
{
|
|
555
|
+
"createdProfileSections": [],
|
|
556
|
+
"deletedProfileSections": [],
|
|
557
|
+
"keptSharedProfiles": [],
|
|
558
|
+
"switchedActiveProfile": false,
|
|
559
|
+
"adoptedProfiles": [],
|
|
560
|
+
"repairedProfiles": []
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
约束:
|
|
565
|
+
|
|
566
|
+
- 未发生时返回空数组或 `false`
|
|
567
|
+
- 不用缺省省略字段
|
|
568
|
+
- 顶层 envelope 结构不变
|
|
569
|
+
|
|
570
|
+
## 12. 错误语义
|
|
571
|
+
|
|
572
|
+
### 12.1 `0.0.5` 需要新增的错误码
|
|
573
|
+
|
|
574
|
+
设计明确要求实现中新增:
|
|
575
|
+
|
|
576
|
+
- `CONFIG_PARSE_ERROR`
|
|
577
|
+
- `PROFILE_IN_USE`
|
|
578
|
+
- `MANAGED_PROFILE_FIELDS_MISSING`
|
|
579
|
+
|
|
580
|
+
继续使用已有:
|
|
581
|
+
|
|
582
|
+
- `CODEX_DIR_NOT_FOUND`
|
|
583
|
+
- `CODEX_DIR_AMBIGUOUS`
|
|
584
|
+
- `CONFIG_NOT_FOUND`
|
|
585
|
+
- `PROFILE_NOT_FOUND`
|
|
586
|
+
- `PROVIDERS_NOT_FOUND`
|
|
587
|
+
- `PROVIDERS_PARSE_ERROR`
|
|
588
|
+
- `BACKUP_FAILED`
|
|
589
|
+
- `ROLLBACK_FAILED`
|
|
590
|
+
|
|
591
|
+
### 12.2 错误码语义
|
|
592
|
+
|
|
593
|
+
`CONFIG_PARSE_ERROR`:
|
|
594
|
+
|
|
595
|
+
- `config.toml` 存在,但结构化读取失败
|
|
596
|
+
- 应包含文件路径和 parser 原因
|
|
597
|
+
|
|
598
|
+
`PROFILE_IN_USE`:
|
|
599
|
+
|
|
600
|
+
- 删除 / 改绑操作会破坏共享 profile 或 active profile 安全约束
|
|
601
|
+
- 应包含 `profile`、`provider`、`activeProfile`、`linkedProviders`
|
|
602
|
+
|
|
603
|
+
`MANAGED_PROFILE_FIELDS_MISSING`:
|
|
604
|
+
|
|
605
|
+
- 需要创建新的受管 profile section,但缺少最小字段
|
|
606
|
+
- `0.0.5` 至少用于缺少 `model` 或 `base_url`
|
|
607
|
+
|
|
608
|
+
### 12.3 明确不进入 `0.0.5` 的错误语义
|
|
609
|
+
|
|
610
|
+
`IMPORT_MERGE_CONFLICT` 不进入 `0.0.5`。
|
|
611
|
+
|
|
612
|
+
原因:
|
|
613
|
+
|
|
614
|
+
- `import --merge` 继续采用导入侧覆盖策略
|
|
615
|
+
- 不引入逐条冲突解决语义
|
|
616
|
+
|
|
617
|
+
## 13. 模块设计与代码落点
|
|
618
|
+
|
|
619
|
+
### 13.1 总体结构
|
|
620
|
+
|
|
621
|
+
保持当前四层结构不变:
|
|
622
|
+
|
|
623
|
+
- CLI 层
|
|
624
|
+
- Application 层
|
|
625
|
+
- Domain 层
|
|
626
|
+
- Infrastructure 层
|
|
627
|
+
|
|
628
|
+
### 13.2 `src/domain/config.ts`
|
|
629
|
+
|
|
630
|
+
当前 `src/domain/config.ts` 主要还是字符串 helper。
|
|
631
|
+
|
|
632
|
+
`0.0.5` 升级为 config 领域规则入口,负责:
|
|
633
|
+
|
|
634
|
+
- active profile 规则
|
|
635
|
+
- managed / unmanaged / orphaned 视图拼装
|
|
636
|
+
- 写操作前校验
|
|
637
|
+
- shared profile 与 destructive remove 规则判断
|
|
638
|
+
|
|
639
|
+
建议新增:
|
|
640
|
+
|
|
641
|
+
- `buildManagedProfileViews(...)`
|
|
642
|
+
- `collectConfigConsistencyIssues(...)`
|
|
643
|
+
- `validateManagedProfileCreation(...)`
|
|
644
|
+
- `planProfileLifecycleOutcome(...)`
|
|
645
|
+
|
|
646
|
+
### 13.3 `src/infra/config-repo.ts`
|
|
647
|
+
|
|
648
|
+
当前 `src/infra/config-repo.ts` 主要负责读取 active profile、列出 section 名、改写顶层 profile。
|
|
649
|
+
|
|
650
|
+
`0.0.5` 升级为结构化读取 + patch 应用中心,至少暴露:
|
|
651
|
+
|
|
652
|
+
- `readStructuredConfig`
|
|
653
|
+
- `listStructuredProfiles`
|
|
654
|
+
- `planConfigMutation`
|
|
655
|
+
- `applyConfigMutation`
|
|
656
|
+
- `findCodexDirCandidates`
|
|
657
|
+
|
|
658
|
+
职责包括:
|
|
659
|
+
|
|
660
|
+
- 读取原始 `config.toml`
|
|
661
|
+
- 通过 CST parser 建立 section / field 边界
|
|
662
|
+
- 生成 `ConfigMutationPlan`
|
|
663
|
+
- 应用局部 patch
|
|
664
|
+
- 保持注释、空行和未受管内容稳定
|
|
665
|
+
|
|
666
|
+
说明:
|
|
667
|
+
|
|
668
|
+
- 当前仓库已有 `src/infra/codex-discovery.ts` 的目录发现逻辑
|
|
669
|
+
- `0.0.5` 设计上允许把候选目录规则下沉整合进 config-aware 路径解析,但不要求强行删除旧文件
|
|
670
|
+
- 实现时可以保留 `codex-discovery.ts` 作为薄封装,底层委托给 `config-repo` 或共用 helper
|
|
671
|
+
|
|
672
|
+
### 13.4 `src/app/`
|
|
673
|
+
|
|
674
|
+
新增:
|
|
675
|
+
|
|
676
|
+
- `src/app/show-config.ts`
|
|
677
|
+
- `src/app/list-config-profiles.ts`
|
|
678
|
+
|
|
679
|
+
更新:
|
|
680
|
+
|
|
681
|
+
- `src/app/add-provider.ts`
|
|
682
|
+
- `src/app/edit-provider.ts`
|
|
683
|
+
- `src/app/remove-provider.ts`
|
|
684
|
+
- `src/app/setup-codex.ts`
|
|
685
|
+
- `src/app/import-providers.ts`
|
|
686
|
+
- `src/app/get-status.ts`
|
|
687
|
+
- `src/app/run-doctor.ts`
|
|
688
|
+
|
|
689
|
+
应用层需要承担:
|
|
690
|
+
|
|
691
|
+
- 组合 provider repo 与 config repo
|
|
692
|
+
- 组装双写事务输入
|
|
693
|
+
- 生成写结果契约字段
|
|
694
|
+
- 把 doctor / status 的 issue 转换为稳定输出结构
|
|
695
|
+
|
|
696
|
+
### 13.5 `src/cli.ts`
|
|
697
|
+
|
|
698
|
+
需要新增:
|
|
699
|
+
|
|
700
|
+
- `config show` 分派
|
|
701
|
+
- `config list-profiles` 分派
|
|
702
|
+
|
|
703
|
+
同时更新现有:
|
|
704
|
+
|
|
705
|
+
- `add` 参数接收 `--create-profile`、`--model`、`--base-url`
|
|
706
|
+
- `edit` 参数接收 `--create-profile`、`--model`、`--base-url`
|
|
707
|
+
- `remove` 参数接收 `--switch-to`
|
|
708
|
+
- `setup` 接入多候选目录交互
|
|
709
|
+
|
|
710
|
+
### 13.6 `src/cli/args.ts`
|
|
711
|
+
|
|
712
|
+
需要把新子命令归一化为稳定 command key,方式与现有 `backups list` 类似。
|
|
713
|
+
|
|
714
|
+
建议新增 command key:
|
|
715
|
+
|
|
716
|
+
- `config-show`
|
|
717
|
+
- `config-list-profiles`
|
|
718
|
+
|
|
719
|
+
### 13.7 `src/cli/help.ts` / `src/cli/output.ts` / `src/cli/interactive.ts`
|
|
720
|
+
|
|
721
|
+
需要增加:
|
|
722
|
+
|
|
723
|
+
- 新命令帮助文案
|
|
724
|
+
- `config show` / `config list-profiles` 的文本渲染
|
|
725
|
+
- `setup` 多候选目录选择交互
|
|
726
|
+
- adopt / repair 辅助交互的最小提示
|
|
727
|
+
|
|
728
|
+
## 14. 关键流程时序
|
|
729
|
+
|
|
730
|
+
### 14.1 `config show` 只读流程
|
|
731
|
+
|
|
732
|
+
```text
|
|
733
|
+
argv
|
|
734
|
+
-> parseArgs
|
|
735
|
+
-> executeCommand("config-show")
|
|
736
|
+
-> app/show-config
|
|
737
|
+
-> infra/config-repo.readStructuredConfig
|
|
738
|
+
-> infra/providers-repo.readProvidersFileIfExists
|
|
739
|
+
-> domain/config.buildManagedProfileViews
|
|
740
|
+
-> output
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### 14.2 `add` 创建缺失 profile 的双写流程
|
|
744
|
+
|
|
745
|
+
```text
|
|
746
|
+
argv
|
|
747
|
+
-> parseArgs
|
|
748
|
+
-> executeCommand("add")
|
|
749
|
+
-> app/add-provider
|
|
750
|
+
-> read providers.json + structured config
|
|
751
|
+
-> validate create-profile + model/base_url preconditions
|
|
752
|
+
-> domain/config.planProfileLifecycleOutcome
|
|
753
|
+
-> infra/config-repo.planConfigMutation
|
|
754
|
+
-> app/run-mutation
|
|
755
|
+
-> write providers.json + apply config patch
|
|
756
|
+
-> success result with createdProfileSections
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### 14.3 `edit --profile` 重绑定流程
|
|
760
|
+
|
|
761
|
+
```text
|
|
762
|
+
argv
|
|
763
|
+
-> parseArgs
|
|
764
|
+
-> executeCommand("edit")
|
|
765
|
+
-> app/edit-provider
|
|
766
|
+
-> load provider + structured config
|
|
767
|
+
-> resolve new profile target
|
|
768
|
+
-> create missing profile only when --create-profile --model --base-url is present
|
|
769
|
+
-> update provider mapping
|
|
770
|
+
-> keep or delete old section based on shared/active rules
|
|
771
|
+
-> single mutation transaction
|
|
772
|
+
-> success result with created/deleted/kept fields
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### 14.4 `remove --switch-to` 安全删除流程
|
|
776
|
+
|
|
777
|
+
```text
|
|
778
|
+
argv
|
|
779
|
+
-> parseArgs
|
|
780
|
+
-> executeCommand("remove")
|
|
781
|
+
-> app/remove-provider
|
|
782
|
+
-> load provider + structured config
|
|
783
|
+
-> detect whether target profile is last reference and active
|
|
784
|
+
-> require explicit switch target when destructive
|
|
785
|
+
-> switch active profile first in mutation plan when needed
|
|
786
|
+
-> delete provider mapping
|
|
787
|
+
-> delete profile section only if no remaining references
|
|
788
|
+
-> success result with switchedActiveProfile + deletedProfileSections
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### 14.5 `setup` 多候选目录 + adopt 流程
|
|
792
|
+
|
|
793
|
+
```text
|
|
794
|
+
argv
|
|
795
|
+
-> parseArgs
|
|
796
|
+
-> executeCommand("setup")
|
|
797
|
+
-> findCodexDirCandidates
|
|
798
|
+
-> TTY choose candidate or manual path
|
|
799
|
+
-> read structured config
|
|
800
|
+
-> collect unmanaged profiles and active profile
|
|
801
|
+
-> interactive adopt decision when applicable
|
|
802
|
+
-> build provider drafts
|
|
803
|
+
-> single mutation write
|
|
804
|
+
-> run doctor
|
|
805
|
+
-> output adopt/repair summary
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
## 15. 兼容、迁移与诊断
|
|
809
|
+
|
|
810
|
+
### 15.1 历史状态识别
|
|
811
|
+
|
|
812
|
+
`0.0.5` 需要识别至少四类历史状态:
|
|
813
|
+
|
|
814
|
+
- `providers.json` 可用,但 `config.toml` 只有手工维护 profile
|
|
815
|
+
- provider 引用了缺失 section
|
|
816
|
+
- `config.toml` 存在没有任何 provider 引用的历史 section
|
|
817
|
+
- 当前 active profile 指向 unmanaged profile
|
|
818
|
+
|
|
819
|
+
### 15.2 收敛路线
|
|
820
|
+
|
|
821
|
+
在没有独立 `repair` 命令的前提下,收敛路线明确为:
|
|
822
|
+
|
|
823
|
+
- `doctor` 识别问题并给出问题码
|
|
824
|
+
- `status` 给出浅层信号,不做静默修复
|
|
825
|
+
- `setup` / `import --merge` 在交互模式下承接 adopt / repair
|
|
826
|
+
- 非交互模式遇到不可自动收敛状态时失败,并返回明确原因
|
|
827
|
+
|
|
828
|
+
### 15.3 `doctor` 与 `status` 的责任差异
|
|
829
|
+
|
|
830
|
+
`status`:
|
|
831
|
+
|
|
832
|
+
- 给出当前 active profile、是否映射、是否存在浅层漂移信号
|
|
833
|
+
- 输出面向日常查看
|
|
834
|
+
|
|
835
|
+
`doctor`:
|
|
836
|
+
|
|
837
|
+
- 给出结构化 `ConfigConsistencyIssue[]`
|
|
838
|
+
- 明确问题码、上下文和建议动作
|
|
839
|
+
- 输出面向修复和自动化诊断
|
|
840
|
+
|
|
841
|
+
## 16. 测试设计
|
|
842
|
+
|
|
843
|
+
### 16.1 总体原则
|
|
844
|
+
|
|
845
|
+
测试继续采用当前 plain Node specs 模式:
|
|
846
|
+
|
|
847
|
+
- 不引入 Jest / Vitest
|
|
848
|
+
- fixture 继续放在 `tests/` / `dev-codex/` 现有模式中
|
|
849
|
+
|
|
850
|
+
### 16.2 CLI 测试
|
|
851
|
+
|
|
852
|
+
最少覆盖:
|
|
853
|
+
|
|
854
|
+
- `config show` 文本输出
|
|
855
|
+
- `config show --json` 输出
|
|
856
|
+
- `config list-profiles` 文本输出
|
|
857
|
+
- `config list-profiles --json` 输出
|
|
858
|
+
- `setup` 单候选目录
|
|
859
|
+
- `setup` 多候选目录 + TTY 选择
|
|
860
|
+
- `setup` 无候选目录 + TTY 手动输入
|
|
861
|
+
- `setup` 多候选目录 + 非交互失败 `CODEX_DIR_AMBIGUOUS`
|
|
862
|
+
- `add --create-profile --model --base-url`
|
|
863
|
+
- `edit --profile <missing> --create-profile --model --base-url`
|
|
864
|
+
- `remove --switch-to`
|
|
865
|
+
|
|
866
|
+
### 16.3 Application 测试
|
|
867
|
+
|
|
868
|
+
最少覆盖:
|
|
869
|
+
|
|
870
|
+
- provider + config 双写成功
|
|
871
|
+
- provider + config 双写失败整体回滚
|
|
872
|
+
- `import --merge` 后 linked section 一致性
|
|
873
|
+
- `setup` adopt unmanaged profile
|
|
874
|
+
|
|
875
|
+
### 16.4 Domain / Infra 测试
|
|
876
|
+
|
|
877
|
+
最少覆盖:
|
|
878
|
+
|
|
879
|
+
- structured config 读取
|
|
880
|
+
- patch 计划生成
|
|
881
|
+
- patch 应用后注释、空行、未受管内容保持
|
|
882
|
+
- managed / unmanaged / orphaned 视图计算
|
|
883
|
+
- `doctor` / `status` issue 计算
|
|
884
|
+
|
|
885
|
+
### 16.5 Fixture 设计
|
|
886
|
+
|
|
887
|
+
最少新增:
|
|
888
|
+
|
|
889
|
+
- 带注释和空行的 `config.toml`
|
|
890
|
+
- 共享 profile fixture
|
|
891
|
+
- orphaned reference fixture
|
|
892
|
+
- unmanaged active profile fixture
|
|
893
|
+
- 多候选 Codex 目录 fixture
|
|
894
|
+
|
|
895
|
+
## 17. Deferred 到 `0.1.0`
|
|
896
|
+
|
|
897
|
+
下面这些内容作为 `0.0.5 -> 0.1.0` 后续项单列,不混入当前实现:
|
|
898
|
+
|
|
899
|
+
- 更大的 profile schema
|
|
900
|
+
- 真正的 `config edit` 命令族
|
|
901
|
+
- 更强的 repair 自动化
|
|
902
|
+
- extensions / auth integration
|
|
903
|
+
|
|
904
|
+
## 18. 验收标准
|
|
905
|
+
|
|
906
|
+
`0.0.5` 设计落地后,至少应满足:
|
|
907
|
+
|
|
908
|
+
- `config show` 在文本和 `--json` 模式下返回稳定结构
|
|
909
|
+
- `config list-profiles` 返回全部可识别 profiles,并通过 `managed` / `source` 区分来源
|
|
910
|
+
- `add` / `edit` / `remove` 同步维护 linked profile sections
|
|
911
|
+
- 共享 profile 不会因单个 provider 操作被误删
|
|
912
|
+
- active profile 不会因删除或重绑定而悬空
|
|
913
|
+
- `setup` 的目录发现行为在 TTY / 非交互下可预测
|
|
914
|
+
- 结构化 TOML 写回后注释、空行和未受管内容保持稳定
|
|
915
|
+
- 双写失败时 `providers.json` 与 `config.toml` 能整体回滚
|
|
916
|
+
- `doctor` / `status` 能识别 orphaned reference、unmanaged active profile、shared profile 和 orphaned section
|
|
917
|
+
|
|
918
|
+
## 19. 结论
|
|
919
|
+
|
|
920
|
+
`0.0.5` 的本质,是把 `codex-switch` 从“能管理 provider registry 并做浅层 profile 切换”的工具,推进到“对 `config.toml` 有稳定结构化认知、能维护 provider-linked profile、一致性可诊断、双写可回滚”的下一阶段。
|
|
921
|
+
|
|
922
|
+
这一步不要求引入更大的命令体系,也不要求把 `config.toml` 变成通用编辑器;它要求的是把当前最容易产生漂移和隐式破坏的那部分能力,收敛成一套明确、可实现、可测试的实现规格。
|