@minniexcode/codex-switch 0.0.3 → 0.0.4

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.
@@ -0,0 +1,874 @@
1
+ # codex-switch `0.0.4` 设计文档
2
+
3
+ ## 文档信息
4
+
5
+ - 文档类型:详细设计文档
6
+ - 适用版本:`0.0.4` 功能里程碑
7
+ - 对应目标 PRD:[`PRD/codex-switch-prd-v0.1.0.md`](./PRD/codex-switch-prd-v0.1.0.md)
8
+ - 历史基线 PRD:[`PRD/codex-switch-prd.md`](./PRD/codex-switch-prd.md)
9
+ - 当前架构基线:[`codex-switch-technical-architecture.md`](./codex-switch-technical-architecture.md)
10
+ - 当前命令基线:[`codex-switch-command-design.md`](./codex-switch-command-design.md)
11
+
12
+ ## 1. 文档目标
13
+
14
+ 这份文档回答的不是“未来想做什么”,而是 `0.0.4` 这批功能应该如何落地:
15
+
16
+ - 功能为什么进入 `0.0.4`
17
+ - 每个功能的职责边界是什么
18
+ - 命令行为、交互方式、失败语义如何定义
19
+ - 现有架构上应该怎样扩展
20
+ - 代码层面需要新增哪些模块
21
+ - 哪些内容明确不在 `0.0.4` 范围内
22
+
23
+ 目标是让后续实现阶段不需要再补关键设计决策。
24
+
25
+ ## 2. 背景与设计原则
26
+
27
+ ### 2.1 当前基线
28
+
29
+ 当前 `0.0.3` 已经具备:
30
+
31
+ - provider 基础管理:`list`、`add`、`remove`
32
+ - provider 切换:`switch`
33
+ - 文件导入导出:`import`、`export`
34
+ - 状态与诊断:`status`、`doctor`
35
+ - 安全恢复:`rollback`
36
+ - TTY 渐进式交互
37
+ - 统一 JSON envelope
38
+ - 写操作锁、备份、回滚模型
39
+
40
+ ### 2.2 `0.0.4` 进入的原因
41
+
42
+ 当前主要短板不是“能不能切换”,而是下面几类使用门槛:
43
+
44
+ - 首次使用需要人工准备 `providers.json`
45
+ - 缺少查看单个 provider 的命令
46
+ - 缺少修改单个 provider 的命令
47
+ - 只支持 latest rollback,不支持历史备份选择
48
+ - `import` 只有整体替换,没有 merge 模式
49
+ - 一部分错误码仍沿用 MVP 时代的宽泛复用
50
+
51
+ ### 2.3 设计原则
52
+
53
+ `0.0.4` 必须继续沿用现有设计原则:
54
+
55
+ - `CLI First`
56
+ - `Local First`
57
+ - `Safe by Default`
58
+ - `AI Friendly`
59
+ - `Split State Model`
60
+ - `Lightweight Transactions`
61
+
62
+ 在此基础上增加三条演进原则:
63
+
64
+ - 不破坏现有 JSON envelope
65
+ - 不复用语义不匹配的错误码
66
+ - 所有写命令默认纳入备份与回滚模型
67
+
68
+ ## 3. 范围与边界
69
+
70
+ ### 3.1 `0.0.4` 范围内
71
+
72
+ 本版本详细设计覆盖以下功能:
73
+
74
+ - `codexs setup`
75
+ - `codexs show <provider>`
76
+ - `codexs edit <provider>`
77
+ - `codexs backups list`
78
+ - `codexs rollback <backup-id>`
79
+ - `codexs import --merge`
80
+ - CLI 错误码与参数错误语义收紧
81
+
82
+ ### 3.2 明确不在 `0.0.4` 范围内
83
+
84
+ 下面这些内容不进入本设计:
85
+
86
+ - Copilot auth 接入
87
+ - 本地代理服务
88
+ - 第三方依赖动态安装
89
+ - GUI / MCP / daemon
90
+ - 远程同步或云端控制面
91
+ - 放宽 `providers.json` 为半初始化 provider 模型
92
+
93
+ ### 3.3 数据边界
94
+
95
+ `0.0.4` 仍然坚持当前状态模型:
96
+
97
+ - `providers.json`:管理态单一事实源
98
+ - `config.toml`:运行态配置
99
+ - `auth.json`:运行态认证文件
100
+ - `backups/` + `latest.json`:回滚态
101
+
102
+ 不引入数据库,不引入新的长期状态仓库。
103
+
104
+ ## 4. 功能总览
105
+
106
+ ### 4.1 `setup`
107
+
108
+ 目标:
109
+
110
+ - 完成首次初始化
111
+ - 从已有 Codex 目录引导生成或更新 `providers.json`
112
+ - 初始化结束后立刻用 `doctor` 验证状态
113
+
114
+ ### 4.2 `show`
115
+
116
+ 目标:
117
+
118
+ - 查看单个 provider 的完整详情
119
+ - 为人类和 AI 提供单条记录读取能力
120
+
121
+ ### 4.3 `edit`
122
+
123
+ 目标:
124
+
125
+ - 原位修改单个 provider
126
+ - 保留当前文件模型和备份模型
127
+
128
+ ### 4.4 `backups list`
129
+
130
+ 目标:
131
+
132
+ - 把历史备份从“仅 latest 隐式存在”提升为“可枚举资源”
133
+
134
+ ### 4.5 `rollback <backup-id>`
135
+
136
+ 目标:
137
+
138
+ - 从 latest rollback 扩展为显式历史恢复
139
+
140
+ ### 4.6 `import --merge`
141
+
142
+ 目标:
143
+
144
+ - 在不完全替换 registry 的情况下导入 provider 清单
145
+
146
+ ## 5. 用户与调用模型
147
+
148
+ ### 5.1 人类用户
149
+
150
+ 人类模式优先使用:
151
+
152
+ - 清晰帮助信息
153
+ - 渐进式 TTY 交互
154
+ - 危险写入确认
155
+ - 明确的恢复路径
156
+
157
+ ### 5.2 AI / 自动化调用
158
+
159
+ AI 模式优先使用:
160
+
161
+ - `--json`
162
+ - 显式参数
163
+ - 稳定 envelope
164
+ - 稳定错误码
165
+
166
+ 约束:
167
+
168
+ - `--json` 下不允许进入交互
169
+ - 非 TTY 环境不依赖 prompt 完成核心流程
170
+
171
+ ## 6. 公共接口与契约
172
+
173
+ ### 6.1 JSON Envelope 保持不变
174
+
175
+ 继续使用:
176
+
177
+ ```json
178
+ {
179
+ "ok": true,
180
+ "command": "command-name",
181
+ "data": {},
182
+ "warnings": [],
183
+ "error": null
184
+ }
185
+ ```
186
+
187
+ 规则:
188
+
189
+ - 顶层字段不改名
190
+ - 顶层字段不增删重排为语义信号
191
+ - 扩展字段统一进入 `data`
192
+ - 软提示进入 `warnings`
193
+ - 错误细节进入 `error.details`
194
+
195
+ ### 6.2 `providers.json` 保持完整 provider 模型
196
+
197
+ 当前 `0.0.4` 仍要求 managed provider 至少具备:
198
+
199
+ - `profile`
200
+ - `apiKey`
201
+
202
+ 允许补充:
203
+
204
+ - `baseUrl`
205
+ - `note`
206
+ - `tags`
207
+
208
+ 不允许:
209
+
210
+ - 写入缺少 `apiKey` 的正式 provider
211
+ - 在 `setup` 中默认创建半成品记录
212
+
213
+ ### 6.3 备份 ID 契约
214
+
215
+ `0.0.4` 引入显式 `backupId` 概念。
216
+
217
+ 规则:
218
+
219
+ - `backupId` 等于备份目录 basename
220
+ - 示例:`20260511-221457-switch`
221
+ - `backups list` 返回该 ID
222
+ - `rollback <backup-id>` 使用该 ID 定位 manifest
223
+
224
+ ## 7. 命令详细设计
225
+
226
+ ### 7.1 `codexs setup`
227
+
228
+ #### 用途
229
+
230
+ 初始化 codex-switch 的受管状态。
231
+
232
+ #### 命令形态
233
+
234
+ ```bash
235
+ codexs setup [--json] [--codex-dir <path>] [--merge|--overwrite]
236
+ ```
237
+
238
+ 说明:
239
+
240
+ - `--codex-dir` 显式指定目标目录
241
+ - `--merge` 和 `--overwrite` 只用于非交互模式显式决定已有 `providers.json` 的处理策略
242
+ - 默认不增加更多参数,避免首次引入就过重
243
+
244
+ #### 主流程
245
+
246
+ 1. 确认 `codex` CLI 是否可执行
247
+ 2. 确认 `codex` 版本是否满足最低门槛
248
+ 3. 确定目标 Codex 目录
249
+ 4. 读取目标 `config.toml`
250
+ 5. 解析 profile 列表和当前 active profile
251
+ 6. 基于 profile 列表构造初始化候选
252
+ 7. 为缺失 `apiKey` 的候选项补问必填字段
253
+ 8. 检测 `providers.json` 是否已存在
254
+ 9. 执行 `overwrite`、`merge` 或 `cancel`
255
+ 10. 通过统一 mutation 流程写入 `providers.json`
256
+ 11. 自动执行 `doctor`
257
+ 12. 输出 setup 结果和 doctor 结果摘要
258
+
259
+ #### 目录发现策略
260
+
261
+ 优先级:
262
+
263
+ 1. `--codex-dir` 显式指定
264
+ 2. 默认目录 `~/.codex`
265
+ 3. 未来可扩展的候选目录发现器返回的其他目录
266
+
267
+ 交互行为:
268
+
269
+ - 单候选目录:直接继续
270
+ - 多候选目录:TTY 下选择或手动输入
271
+ - 非交互且多候选目录:失败并返回 `CODEX_DIR_AMBIGUOUS`
272
+
273
+ #### 输入补问策略
274
+
275
+ `setup` 不从运行态猜测 API key。
276
+
277
+ TTY 下:
278
+
279
+ - 可为每个要纳入管理的 profile 补问 `providerName`
280
+ - 可补问 `apiKey`
281
+ - 可选补问 `baseUrl`、`note`、`tags`
282
+
283
+ 非交互下:
284
+
285
+ - 若无法构造完整 provider,直接失败
286
+ - 不写入部分完成的 registry
287
+
288
+ #### 已存在 `providers.json` 时的策略
289
+
290
+ TTY 下:
291
+
292
+ - 显示检测结果
293
+ - 提供 `overwrite`、`merge`、`cancel`
294
+
295
+ 非交互下:
296
+
297
+ - 只有显式 `--merge` 或 `--overwrite` 才继续
298
+ - 否则返回 `PROVIDERS_ALREADY_EXISTS`
299
+
300
+ #### 成功输出
301
+
302
+ 人类模式:
303
+
304
+ - 目标目录
305
+ - 初始化了多少 providers
306
+ - 采用了哪种写入策略
307
+ - 是否执行了 doctor
308
+ - doctor 是否发现问题
309
+
310
+ JSON 模式建议字段:
311
+
312
+ - `codexDir`
313
+ - `strategy`
314
+ - `providersInitialized`
315
+ - `providerNames`
316
+ - `doctor`
317
+
318
+ #### 失败错误码
319
+
320
+ - `CODEX_NOT_INSTALLED`
321
+ - `CODEX_VERSION_UNSUPPORTED`
322
+ - `CODEX_DIR_NOT_FOUND`
323
+ - `CODEX_DIR_AMBIGUOUS`
324
+ - `CONFIG_NOT_FOUND`
325
+ - `PROFILE_NOT_FOUND`
326
+ - `PROVIDERS_ALREADY_EXISTS`
327
+ - `PROMPT_CANCELLED`
328
+ - `BACKUP_FAILED`
329
+ - `ROLLBACK_FAILED`
330
+
331
+ #### 边界
332
+
333
+ - 不负责自动登录 provider
334
+ - 不负责从远程拉取任何配置
335
+ - 不负责创建复杂 profile 结构
336
+ - 不负责引入第三方 auth
337
+
338
+ ### 7.2 `codexs show <provider>`
339
+
340
+ #### 用途
341
+
342
+ 展示单个 provider 的完整记录。
343
+
344
+ #### 命令形态
345
+
346
+ ```bash
347
+ codexs show <provider> [--json] [--codex-dir <path>]
348
+ ```
349
+
350
+ #### 行为
351
+
352
+ - 读取 `providers.json`
353
+ - 查找目标 provider
354
+ - 返回完整字段
355
+ - 默认对敏感值做安全展示
356
+
357
+ #### 输出策略
358
+
359
+ 人类模式:
360
+
361
+ - 展示 `profile`、`baseUrl`、`note`、`tags`
362
+ - `apiKey` 默认只显示掩码摘要
363
+
364
+ JSON 模式:
365
+
366
+ - `data.provider` 返回完整结构
367
+ - `apiKey` 是否完整返回由当前安全策略决定;`0.0.4` 建议默认返回完整值给显式本地自动化调用,不在普通文本模式泄露
368
+
369
+ #### 失败错误码
370
+
371
+ - `PROVIDERS_NOT_FOUND`
372
+ - `PROVIDERS_PARSE_ERROR`
373
+ - `PROVIDER_NOT_FOUND`
374
+
375
+ #### 边界
376
+
377
+ - 不做模糊搜索
378
+ - 不支持按 tag 查询
379
+
380
+ ### 7.3 `codexs edit <provider>`
381
+
382
+ #### 用途
383
+
384
+ 修改单个 provider 记录。
385
+
386
+ #### 命令形态
387
+
388
+ ```bash
389
+ codexs edit <provider> [--profile <name>] [--api-key <key>] [--base-url <url>] [--note <text>] [--tag <tag> ...] [--json] [--codex-dir <path>]
390
+ ```
391
+
392
+ #### 设计取舍
393
+
394
+ 默认采用“显式参数优先 + TTY 补齐”的方式,而不是外部编辑器。
395
+
396
+ 原因:
397
+
398
+ - 参数契约更适合自动化
399
+ - 行为更容易测试
400
+ - 不引入平台级编辑器差异
401
+ - 保持和 `add` 的交互模型一致
402
+
403
+ #### 行为
404
+
405
+ - 读取现有 provider
406
+ - 将传入字段覆盖到原记录
407
+ - 未传入的字段保持原值
408
+ - TTY 下如果没有传任何可编辑字段,可进入字段选择式补问
409
+ - 最终写回 `providers.json`
410
+
411
+ #### tag 语义
412
+
413
+ `0.0.4` 建议先采用简单规则:
414
+
415
+ - 传入 `--tag` 时,用提供的 tag 列表整体替换原 tags
416
+ - 未传 `--tag` 时保留原 tags
417
+
418
+ 不在 `0.0.4` 引入复杂的 `--add-tag` / `--remove-tag`
419
+
420
+ #### 成功输出
421
+
422
+ - `provider`
423
+ - `backupPath`
424
+ - `updatedFields`
425
+
426
+ #### 失败错误码
427
+
428
+ - `PROVIDERS_NOT_FOUND`
429
+ - `PROVIDERS_PARSE_ERROR`
430
+ - `PROVIDER_NOT_FOUND`
431
+ - `INVALID_ARGUMENT`
432
+ - `BACKUP_FAILED`
433
+ - `ROLLBACK_FAILED`
434
+
435
+ #### 边界
436
+
437
+ - 不支持重命名 provider key
438
+ - 不支持编辑多个 provider
439
+ - 不引入字段级 patch 语法
440
+
441
+ ### 7.4 `codexs backups list`
442
+
443
+ #### 用途
444
+
445
+ 列出历史备份条目。
446
+
447
+ #### 命令形态
448
+
449
+ ```bash
450
+ codexs backups list [--json] [--codex-dir <path>]
451
+ ```
452
+
453
+ #### 行为
454
+
455
+ - 扫描 `backups/` 目录
456
+ - 忽略 `latest.json`
457
+ - 读取每个备份目录下的 `manifest.json`
458
+ - 按 `createdAt` 倒序输出
459
+
460
+ #### JSON 字段
461
+
462
+ - `backups`
463
+ - `backupId`
464
+ - `createdAt`
465
+ - `reason`
466
+ - `files`
467
+ - `backupPath`
468
+ - `count`
469
+
470
+ #### 失败错误码
471
+
472
+ - `BACKUP_NOT_FOUND`
473
+ - `PROVIDERS_PARSE_ERROR`
474
+
475
+ 说明:
476
+
477
+ - `BACKUP_NOT_FOUND` 在这里表示备份目录不存在或为空
478
+ - 若 manifest 结构损坏,可新增专门解析错误,`0.0.4` 先允许复用更具体的新错误码实现
479
+
480
+ #### 边界
481
+
482
+ - 不做分页
483
+ - 不做模糊过滤
484
+ - 不展示文件 diff
485
+
486
+ ### 7.5 `codexs rollback <backup-id>`
487
+
488
+ #### 用途
489
+
490
+ 恢复指定历史备份。
491
+
492
+ #### 命令形态
493
+
494
+ ```bash
495
+ codexs rollback [<backup-id>] [--json] [--codex-dir <path>]
496
+ ```
497
+
498
+ 说明:
499
+
500
+ - 不带参数时保持 current latest rollback 语义
501
+ - 带 `backup-id` 时按显式条目恢复
502
+
503
+ #### 行为
504
+
505
+ - 当未传 `backup-id`:沿用 `latest.json`
506
+ - 当传 `backup-id`:加载 `backups/<backup-id>/manifest.json`
507
+ - 恢复 manifest 中列出的所有文件
508
+ - 成功后返回 restored 文件清单
509
+
510
+ #### 交互
511
+
512
+ TTY 下:
513
+
514
+ - latest rollback 保持当前确认方式
515
+ - 指定 `backup-id` 时,也先展示目标备份摘要并确认
516
+
517
+ 非交互下:
518
+
519
+ - 不做确认
520
+
521
+ #### 失败错误码
522
+
523
+ - `BACKUP_NOT_FOUND`
524
+ - `ROLLBACK_FAILED`
525
+
526
+ #### 边界
527
+
528
+ - 不支持部分文件回滚
529
+ - 不在 `0.0.4` 做“预览 diff 后再恢复”
530
+
531
+ ### 7.6 `codexs import --merge`
532
+
533
+ #### 用途
534
+
535
+ 将外部 provider 文件并入当前 registry。
536
+
537
+ #### 命令形态
538
+
539
+ ```bash
540
+ codexs import <file> [--merge] [--json] [--codex-dir <path>]
541
+ ```
542
+
543
+ #### 行为
544
+
545
+ - 未带 `--merge`:保持现有整体替换语义
546
+ - 带 `--merge`:
547
+ - 读取外部文件
548
+ - 读取当前 `providers.json`
549
+ - 合并两边 providers
550
+ - 同名冲突时以导入文件为准
551
+ - 写回最终 registry
552
+
553
+ #### 成功输出
554
+
555
+ - `mode`
556
+ - `backupPath`
557
+ - `importedCount`
558
+ - `mergedCount`
559
+ - `replacedProviders`
560
+
561
+ #### 失败错误码
562
+
563
+ - `INVALID_IMPORT_FILE`
564
+ - `PROVIDERS_NOT_FOUND`
565
+ - `PROVIDERS_PARSE_ERROR`
566
+ - `BACKUP_FAILED`
567
+ - `ROLLBACK_FAILED`
568
+
569
+ #### 边界
570
+
571
+ - 不做交互式冲突逐条选择
572
+ - 不支持三方 merge
573
+
574
+ ## 8. 错误码设计
575
+
576
+ ### 8.1 问题背景
577
+
578
+ 当前实现里,`INVALID_IMPORT_FILE` 被复用到了多个不相干场景,例如:
579
+
580
+ - 参数缺失
581
+ - 未知命令
582
+ - 用户取消某些流程
583
+
584
+ 这会降低 AI 和脚本的判断质量。
585
+
586
+ ### 8.2 `0.0.4` 新错误码
587
+
588
+ 建议新增:
589
+
590
+ - `INVALID_ARGUMENT`
591
+ - `UNKNOWN_COMMAND`
592
+ - `PROMPT_CANCELLED`
593
+ - `CODEX_NOT_INSTALLED`
594
+ - `CODEX_VERSION_UNSUPPORTED`
595
+ - `CODEX_DIR_NOT_FOUND`
596
+ - `CODEX_DIR_AMBIGUOUS`
597
+ - `PROVIDERS_ALREADY_EXISTS`
598
+ - `BACKUP_NOT_FOUND`
599
+
600
+ ### 8.3 归类原则
601
+
602
+ - 参数问题:`INVALID_ARGUMENT`
603
+ - 命令分发问题:`UNKNOWN_COMMAND`
604
+ - 用户主动取消:`PROMPT_CANCELLED`
605
+ - 环境缺失 / 版本问题:`CODEX_*`
606
+ - 备份定位失败:`BACKUP_NOT_FOUND`
607
+ - 登录失败:`CODEX_LOGIN_FAILED`
608
+
609
+ ## 9. 架构与模块设计
610
+
611
+ ### 9.1 保持现有四层结构
612
+
613
+ 继续沿用:
614
+
615
+ - CLI 层
616
+ - Application 层
617
+ - Domain 层
618
+ - Infrastructure 层
619
+
620
+ 不在 `0.0.4` 引入新层次。
621
+
622
+ ### 9.2 建议新增模块
623
+
624
+ #### CLI 层
625
+
626
+ 建议新增:
627
+
628
+ - `src/cli/setup-interactive.ts`
629
+ - 负责 setup 的交互收集和已有 registry 处理策略选择
630
+ - `src/cli/backups-interactive.ts`
631
+ - 负责 rollback 目标摘要确认
632
+
633
+ 建议扩展:
634
+
635
+ - `src/cli/help.ts`
636
+ - `src/cli/output.ts`
637
+ - `src/cli/args.ts`
638
+
639
+ #### Application 层
640
+
641
+ 建议新增:
642
+
643
+ - `src/app/setup-codex.ts`
644
+ - `src/app/show-provider.ts`
645
+ - `src/app/edit-provider.ts`
646
+ - `src/app/list-backups.ts`
647
+ - `src/app/rollback-backup.ts`
648
+
649
+ 建议扩展:
650
+
651
+ - `src/app/import-providers.ts`
652
+ - `src/app/run-doctor.ts`
653
+ - `src/app/types.ts`
654
+
655
+ #### Domain 层
656
+
657
+ 建议新增:
658
+
659
+ - `src/domain/setup.ts`
660
+ - setup 过程中的数据模型和校验
661
+ - `src/domain/backups.ts`
662
+ - backup list / backup id 相关纯逻辑
663
+
664
+ 建议扩展:
665
+
666
+ - `src/domain/errors.ts`
667
+ - `src/domain/providers.ts`
668
+ - `src/domain/backup.ts`
669
+
670
+ #### Infrastructure 层
671
+
672
+ 建议新增:
673
+
674
+ - `src/infra/codex-discovery.ts`
675
+ - 负责候选 Codex 目录发现
676
+
677
+ 建议扩展:
678
+
679
+ - `src/infra/codex-cli.ts`
680
+ - 增加版本读取与版本门槛比较
681
+ - `src/infra/backup-repo.ts`
682
+ - 增加列举指定备份、读取指定 manifest
683
+ - `src/infra/providers-repo.ts`
684
+ - 增加单 provider 读取与 merge 写入辅助
685
+
686
+ ### 9.3 依赖方向
687
+
688
+ 保持当前依赖方向不变:
689
+
690
+ - `cli -> app`
691
+ - `app -> domain + infra`
692
+ - `infra -> domain`
693
+ - `domain -> none`
694
+
695
+ ### 9.4 setup 时序
696
+
697
+ ```text
698
+ argv
699
+ -> parseArgs
700
+ -> executeCommand("setup")
701
+ -> app/setup-codex
702
+ -> infra/codex-cli.checkAvailable + readVersion
703
+ -> infra/codex-discovery.findCandidates
704
+ -> cli/setup-interactive (TTY only)
705
+ -> infra/config-repo read config.toml
706
+ -> domain/setup build provider drafts
707
+ -> app/run-mutation
708
+ -> infra/providers-repo write providers.json
709
+ -> app/run-doctor
710
+ -> output
711
+ ```
712
+
713
+ ### 9.5 rollback with backup-id 时序
714
+
715
+ ```text
716
+ argv
717
+ -> parseArgs
718
+ -> executeCommand("rollback")
719
+ -> app/rollback-backup
720
+ -> infra/backup-repo.loadManifestById or loadLatestManifest
721
+ -> cli confirmation (TTY only)
722
+ -> infra/backup-repo.restoreManifest
723
+ -> output
724
+ ```
725
+
726
+ ## 10. 技术实现细节
727
+
728
+ ### 10.1 参数解析
729
+
730
+ `args.ts` 需要支持:
731
+
732
+ - 子命令 `backups list`
733
+ - `rollback` 的可选位置参数 `<backup-id>`
734
+ - `import --merge`
735
+ - `setup --merge`
736
+ - `setup --overwrite`
737
+
738
+ 建议:
739
+
740
+ - 保持当前轻量 parser,不引入 commander 等外部解析库
741
+ - 在 parser 结果里把 `backups list` 归一为单一 command key,例如 `backups-list`
742
+
743
+ ### 10.2 Codex 版本检查
744
+
745
+ `infra/codex-cli.ts` 需要新增:
746
+
747
+ - `readCodexVersion(): { ok: true; version: string } | { ok: false; cause: string }`
748
+ - `checkCodexVersion(minVersion: string): { ok: boolean; currentVersion?: string; cause?: string }`
749
+
750
+ 建议:
751
+
752
+ - 通过 `codex --version` 获取原始文本
753
+ - 在 domain 层做轻量版本字符串解析
754
+ - 最低版本门槛作为常量维护
755
+
756
+ ### 10.3 目录发现
757
+
758
+ `codex-discovery.ts` 的职责:
759
+
760
+ - 接收显式 `--codex-dir`
761
+ - 发现默认目录
762
+ - 未来可扩展更多候选目录策略
763
+
764
+ `0.0.4` 建议最小实现:
765
+
766
+ - 当前先支持默认目录 + 显式目录
767
+ - 保留接口为返回候选列表,避免以后重写调用链
768
+
769
+ ### 10.4 provider merge
770
+
771
+ `import --merge` 合并算法:
772
+
773
+ - 读取当前 registry
774
+ - 读取导入 registry
775
+ - 浅层按 provider name 合并
776
+ - 冲突时导入侧覆盖本地
777
+ - 使用 deterministic key ordering 写回
778
+
779
+ ### 10.5 show 的敏感字段输出
780
+
781
+ 文本模式:
782
+
783
+ - `apiKey` 只显示掩码,例如前 3 后 2
784
+
785
+ JSON 模式:
786
+
787
+ - 明确标注这是本地机器可读输出
788
+ - 默认允许返回完整 `apiKey`
789
+
790
+ ### 10.6 edit 的字段更新
791
+
792
+ 建议先使用“完整对象重写”而不是字段级 patch 存储。
793
+
794
+ 原因:
795
+
796
+ - 当前 `providers.json` 结构小
797
+ - 现有 repo 已有整体读写能力
798
+ - 更容易复用现有 backup/mutation 模型
799
+
800
+ ### 10.7 backup 枚举
801
+
802
+ `backup-repo.ts` 需要:
803
+
804
+ - 枚举 `backups/` 下所有目录
805
+ - 过滤非目录项和 `latest.json`
806
+ - 尝试读取每个 `manifest.json`
807
+ - 若某一条 manifest 损坏,建议该条以 warning 形式跳过,而不是让整个 `backups list` 失败
808
+
809
+ ## 11. 测试设计
810
+
811
+ ### 11.1 CLI 测试
812
+
813
+ 需要覆盖:
814
+
815
+ - `setup` 在非交互缺参数或多目录冲突时的失败 envelope
816
+ - `show` 成功和 provider 不存在
817
+ - `edit` 显式字段更新
818
+ - `backups list` 返回排序结果
819
+ - `rollback <backup-id>` 成功与目标不存在
820
+ - `import --merge` 冲突覆盖语义
821
+
822
+ ### 11.2 Application 测试
823
+
824
+ 需要覆盖:
825
+
826
+ - setup draft 生成与已有 registry 策略
827
+ - edit 更新字段集合计算
828
+ - merge 导入结果
829
+ - 指定 manifest 回滚
830
+
831
+ ### 11.3 Domain 测试
832
+
833
+ 需要覆盖:
834
+
835
+ - 版本比较
836
+ - backupId 解析
837
+ - provider merge 纯逻辑
838
+ - `apiKey` 掩码逻辑
839
+
840
+ ### 11.4 Fixture 设计
841
+
842
+ 建议新增 fixture:
843
+
844
+ - 多 profile config
845
+ - 已存在 providers 的 setup 场景
846
+ - 多个历史备份目录
847
+ - 损坏 manifest 场景
848
+
849
+ ## 12. 验收标准
850
+
851
+ `0.0.4` 设计落地后,至少应满足:
852
+
853
+ - 用户可以通过 `setup` 完成首次初始化
854
+ - `show` 能查看单个 provider
855
+ - `edit` 能稳定更新单个 provider
856
+ - `backups list` 能列出历史备份
857
+ - `rollback <backup-id>` 能恢复指定备份
858
+ - `import --merge` 能按导入侧覆盖策略写回
859
+ - 新增错误码足以覆盖参数、环境、取消、备份定位等核心场景
860
+ - 所有新增写命令继续沿用锁、备份、回滚模型
861
+
862
+ ## 13. 后续演进接口
863
+
864
+ 这份设计刻意为后续能力留了接口,但不在 `0.0.4` 落地:
865
+
866
+ - 更复杂的目录发现策略
867
+ - 更复杂的 tag patch 语法
868
+ - 交互式 import 冲突逐条选择
869
+ - 第三方 auth provider 接入
870
+ - 本地代理和依赖安装
871
+
872
+ ## 14. 结论
873
+
874
+ `0.0.4` 的本质不是再堆几个命令,而是把 codex-switch 从“已经能用的切换工具”推进到“具备初始化、精细查看编辑、历史恢复、结构化错误语义”的下一阶段。技术上不需要推翻当前四层结构,只需要围绕现有 parser、mutation orchestration、backup repo 和 provider repo 做有边界的增量扩展。