dsh-config-manager 0.1.64 → 0.1.65

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 (168) hide show
  1. package/README.md +31 -6
  2. package/README.zh-CN.md +31 -6
  3. package/lib/cli/index.d.ts +11 -0
  4. package/lib/cli/index.js +23 -9
  5. package/lib/client.d.ts +204 -154
  6. package/lib/client.js +5139 -2886
  7. package/lib/core/analyzer.d.ts +4 -0
  8. package/lib/core/analyzer.js +8 -22
  9. package/lib/core/backup.d.ts +2 -2
  10. package/lib/core/boot-paths.d.ts +14 -0
  11. package/lib/core/boot-paths.js +28 -0
  12. package/lib/core/boot-rescue.d.ts +21 -0
  13. package/lib/core/boot-rescue.js +57 -1
  14. package/lib/core/boot-safety.js +2 -2
  15. package/lib/core/conflict-decisions.d.ts +16 -0
  16. package/lib/core/conflict-decisions.js +21 -0
  17. package/lib/core/crash-report.d.ts +15 -12
  18. package/lib/core/crash-report.js +33 -40
  19. package/lib/core/plugin-cli.js +21 -1
  20. package/lib/core/types.d.ts +9 -0
  21. package/lib/index.d.ts +4 -3
  22. package/lib/index.js +75 -187
  23. package/lib/profiles/dsh-profile-launcher.d.ts +141 -0
  24. package/lib/profiles/dsh-profile-launcher.js +396 -0
  25. package/lib/profiles/dsh-profile-manager.d.ts +49 -15
  26. package/lib/profiles/dsh-profile-manager.js +151 -48
  27. package/lib/profiles/dsh-profile-runtime.d.ts +83 -0
  28. package/lib/profiles/dsh-profile-runtime.js +194 -0
  29. package/lib/profiles/dsh-profile-shared.d.ts +117 -10
  30. package/lib/profiles/dsh-profile-shared.js +12 -0
  31. package/lib/profiles/index.d.ts +10 -3
  32. package/lib/profiles/index.js +10 -3
  33. package/lib/profiles/process-control.d.ts +37 -0
  34. package/lib/profiles/process-control.js +75 -0
  35. package/lib/routes/kit.d.ts +1 -0
  36. package/lib/routes/profiles.d.ts +1 -1
  37. package/lib/routes/profiles.js +140 -20
  38. package/lib/schema/section-registry.d.ts +24 -2
  39. package/lib/schema/section-registry.js +27 -0
  40. package/lib/ui/dsh-profiles-view.d.ts +52 -9
  41. package/lib/ui/dsh-profiles-view.js +80 -13
  42. package/lib/ui/i18n.d.ts +0 -1
  43. package/lib/ui/i18n.js +0 -2
  44. package/lib/ui/import-wizard.d.ts +30 -3
  45. package/lib/ui/import-wizard.js +30 -6
  46. package/lib/ui/select-model.d.ts +51 -0
  47. package/lib/ui/select-model.js +84 -0
  48. package/lib/ui/selection-model.d.ts +28 -1
  49. package/lib/ui/selection-model.js +48 -3
  50. package/lib/utils/env-lock.d.ts +39 -0
  51. package/lib/utils/env-lock.js +121 -25
  52. package/package.json +3 -1
  53. package/src/cli/index.ts +26 -9
  54. package/src/client/ConfigManagerSection.tsx +12 -39
  55. package/src/client/about/AboutPanel.tsx +22 -8
  56. package/src/client/about/about-view.test.ts +53 -6
  57. package/src/client/about/about-view.ts +26 -2
  58. package/src/client/api.ts +36 -6
  59. package/src/client/client-types.ts +2 -2
  60. package/src/client/common/ConfirmDialog.tsx +3 -2
  61. package/src/client/common/ContentPicker.tsx +21 -17
  62. package/src/client/common/CopyButton.tsx +74 -0
  63. package/src/client/common/ErrorBanner.tsx +3 -2
  64. package/src/client/common/Icon.tsx +116 -0
  65. package/src/client/common/Modal.tsx +29 -8
  66. package/src/client/common/Motion.tsx +70 -0
  67. package/src/client/common/ReportView.tsx +2 -2
  68. package/src/client/common/RunsCenter.tsx +3 -2
  69. package/src/client/common/Select.tsx +223 -0
  70. package/src/client/common/Skeleton.tsx +128 -0
  71. package/src/client/common/client-route-parity.test.ts +10 -10
  72. package/src/client/common/http-usage-guard.test.ts +1 -1
  73. package/src/client/common/http.test.ts +1 -1
  74. package/src/client/common/http.ts +2 -2
  75. package/src/client/common/morph-icons.test.ts +206 -0
  76. package/src/client/common/morph-icons.ts +89 -0
  77. package/src/client/common/routes.ts +10 -12
  78. package/src/client/common/ui.tsx +48 -5
  79. package/src/client/config-manager.module.css +545 -34
  80. package/src/client/history/HistoryPanel.tsx +30 -29
  81. package/src/client/icon-layer-guard.test.ts +137 -0
  82. package/src/client/import/ImportWizardView.tsx +15 -4
  83. package/src/client/import/use-import-wizard-controller.ts +24 -7
  84. package/src/client/index.ts +4 -4
  85. package/src/client/locales.ts +118 -123
  86. package/src/client/lucide-icons.d.ts +11 -0
  87. package/src/client/market/MarketPanel.tsx +39 -37
  88. package/src/client/market/MyConfigsInstall.tsx +3 -2
  89. package/src/client/market/MyConfigsList.tsx +2 -1
  90. package/src/client/market/MyConfigsLoginCard.tsx +3 -2
  91. package/src/client/overview/OverviewPanel.tsx +4 -39
  92. package/src/client/profiles/ProfilesPanel.tsx +422 -215
  93. package/src/client/recovery/RecoveryPanel.tsx +120 -3
  94. package/src/client/recovery/incident-api.ts +91 -0
  95. package/src/client/recovery/recovery-locales.ts +56 -0
  96. package/src/client/recovery/recovery-view.ts +71 -0
  97. package/src/client/run-store.test.ts +174 -2
  98. package/src/client/run-store.ts +147 -28
  99. package/src/client/snapshots/BackupScheduleCard.tsx +32 -35
  100. package/src/client/snapshots/RestorePlanView.tsx +8 -5
  101. package/src/client/snapshots/SnapshotsPanel.tsx +13 -4
  102. package/src/client/sync/AutosyncCard.tsx +6 -10
  103. package/src/client/sync/ChannelConfigDialog.tsx +8 -10
  104. package/src/client/sync/SyncHistoryView.tsx +3 -2
  105. package/src/client/sync/SyncSettingsView.tsx +17 -18
  106. package/src/core/analyzer.ts +8 -21
  107. package/src/core/backup.ts +2 -2
  108. package/src/core/boot-paths.test.ts +35 -0
  109. package/src/core/boot-paths.ts +29 -0
  110. package/src/core/boot-rescue.test.ts +82 -0
  111. package/src/core/boot-rescue.ts +57 -1
  112. package/src/core/boot-safety.ts +2 -2
  113. package/src/core/conflict-decisions.ts +34 -0
  114. package/src/core/crash-report.test.ts +25 -41
  115. package/src/core/crash-report.ts +34 -41
  116. package/src/core/incident-wiring.test.ts +96 -0
  117. package/src/core/plugin-cli.fs.test.ts +52 -0
  118. package/src/core/plugin-cli.ts +20 -1
  119. package/src/core/smoke.test.ts +4 -1
  120. package/src/core/types.ts +9 -0
  121. package/src/index.ts +79 -163
  122. package/src/profiles/dsh-profile-launcher.test.ts +359 -0
  123. package/src/profiles/dsh-profile-launcher.ts +473 -0
  124. package/src/profiles/dsh-profile-manager.test.ts +123 -59
  125. package/src/profiles/dsh-profile-manager.ts +148 -51
  126. package/src/profiles/dsh-profile-runtime.test.ts +196 -0
  127. package/src/profiles/dsh-profile-runtime.ts +228 -0
  128. package/src/profiles/dsh-profile-shared.ts +126 -9
  129. package/src/profiles/index.ts +30 -4
  130. package/src/profiles/process-control.ts +99 -0
  131. package/src/routes/kit.ts +1 -0
  132. package/src/routes/profiles.ts +139 -18
  133. package/src/schema/registry.test.ts +36 -0
  134. package/src/schema/section-registry.ts +42 -2
  135. package/src/ui/dsh-profiles-view.test.ts +87 -12
  136. package/src/ui/dsh-profiles-view.ts +106 -17
  137. package/src/ui/i18n.test.ts +0 -2
  138. package/src/ui/i18n.ts +0 -2
  139. package/src/ui/import-wizard.test.ts +40 -2
  140. package/src/ui/import-wizard.ts +41 -7
  141. package/src/ui/select-model.test.ts +75 -0
  142. package/src/ui/select-model.ts +92 -0
  143. package/src/ui/selection-model.test.ts +47 -5
  144. package/src/ui/selection-model.ts +50 -3
  145. package/src/utils/env-lock.test.ts +120 -0
  146. package/src/utils/env-lock.ts +140 -21
  147. package/lib/core/config-lifecycle.d.ts +0 -179
  148. package/lib/core/config-lifecycle.js +0 -479
  149. package/lib/core/config-snapshot.d.ts +0 -137
  150. package/lib/core/config-snapshot.js +0 -443
  151. package/lib/core/config-state.d.ts +0 -70
  152. package/lib/core/config-state.js +0 -177
  153. package/lib/core/undo.d.ts +0 -73
  154. package/lib/core/undo.js +0 -87
  155. package/lib/core/watcher.d.ts +0 -101
  156. package/lib/core/watcher.js +0 -219
  157. package/src/client/lifecycle/LifecyclePanel.tsx +0 -525
  158. package/src/client/lifecycle/lifecycle-api.ts +0 -178
  159. package/src/core/config-lifecycle.test.ts +0 -980
  160. package/src/core/config-lifecycle.ts +0 -571
  161. package/src/core/config-snapshot.test.ts +0 -559
  162. package/src/core/config-snapshot.ts +0 -538
  163. package/src/core/config-state.test.ts +0 -381
  164. package/src/core/config-state.ts +0 -207
  165. package/src/core/phase1-wiring.test.ts +0 -148
  166. package/src/core/undo.ts +0 -116
  167. package/src/core/watcher.test.ts +0 -326
  168. package/src/core/watcher.ts +0 -263
package/lib/client.d.ts CHANGED
@@ -195,6 +195,15 @@ interface PathIssue {
195
195
  kind: 'missing' | 'platformMismatch' | 'homeMismatch';
196
196
  value: string;
197
197
  mappedTo?: string;
198
+ /**
199
+ * 这条路径来自哪个分区(目前只有 workspaces / mcp 会被探测出路径问题)。
200
+ *
201
+ * 为什么要有它:导入向导的路径映射页必须**只问这次真的会写盘的路径** ——
202
+ * 用户没勾「工作区」时不该被要求为工作区路径指定新位置(真机反馈:明明没选工作区,
203
+ * 却被列了 7 条工作区路径)。core 只负责如实标注来源,是否要问由调用方结合本次选择决定。
204
+ * 缺省 = 来源未知(旧宿主未回传该字段)→ 调用方按「保守显示」处理。
205
+ */
206
+ section?: SectionId;
198
207
  }
199
208
  interface SnapshotTarget {
200
209
  adapter: SectionId;
@@ -652,21 +661,61 @@ interface DshProfileDetail extends DshProfileMeta {
652
661
  /** cordis.patch.yml 原文(不可读或过大 = null) */
653
662
  patch: string | null;
654
663
  }
655
- /** 「下次启动」标记的解析结果。 */
656
- interface DshProfileSelection {
664
+ /**
665
+ * 「用该档案启动」(独立实例)的结果。
666
+ *
667
+ * 为什么需要它:DSH 自身没有「默认 / 下次启动 profile」状态(profile 只由 `--profile` 决定),
668
+ * 所以「切换档案」唯一真正可用的形态是**另起一个实例**——本结构就是那条路径的回执。
669
+ */
670
+ interface DshProfileLaunchResult {
671
+ /** 被启动的档案名 */
657
672
  name: string;
658
- /** 目标 profile 是否仍然存在(被删除后 = false,UI 应提示重新选择) */
659
- exists: boolean;
660
- /** 目标是否就是当前运行中的 profile(= 无需重启) */
661
- isCurrent: boolean;
673
+ /** 目前只支持 web 形态(有浏览器 GUI) */
674
+ mode: 'web';
675
+ /** 新实例监听的端口(自动挑的空闲端口) */
676
+ port: number;
677
+ /** 带进程 token 的认证 URL(从子进程启动日志里抓;没抓到 = null,绝不臆造裸 URL) */
678
+ url: string | null;
679
+ /** 子进程 pid(拿不到 = null) */
680
+ pid: number | null;
681
+ /** 子进程日志文件绝对路径(未就绪时用户据此自查) */
682
+ logFile: string;
683
+ /** HTTP 探活是否已就绪 */
684
+ ready: boolean;
685
+ /** 非致命告警(UI 按码映射文案) */
686
+ warnings: DshProfileLaunchWarning[];
662
687
  }
663
- /** GET /profiles 的响应负载(列表 + 当前运行 + 下次启动 + 模板清单)。 */
688
+ /**
689
+ * 「运行中实例」的**合并视图**(UI 唯一消费形状):台账(本插件启动的) ∪ 心跳(任何在跑的实例)。
690
+ *
691
+ * 为什么必须合并:只认台账时,手动 `dsh web` 起来的实例对插件不可见 → 会在别的档案里被**再次启动**
692
+ * (用户实测:从 web 启动 cmtest 后,在 cmtest 里还能再启动 web,同名多开)。心跳让每个实例互相可见。
693
+ */
694
+ interface DshProfileRunningView {
695
+ name: string;
696
+ /** 实例 pid(停止用;缺心跳 pid 时为 null) */
697
+ pid: number | null;
698
+ /** web 端口(心跳/台账里记的;未知 = null) */
699
+ port: number | null;
700
+ /** 带 token 的认证 URL(**只有本插件启动的**才有:别人的 token 绝不落盘/回传) */
701
+ url: string | null;
702
+ startedAt: string;
703
+ /** 本插件启动的(台账里有它)→ 本 UI 负责停止 */
704
+ owned: boolean;
705
+ /** 就是当前正在跑本界面的这个实例(自己不能停自己) */
706
+ current: boolean;
707
+ }
708
+ /** 停止实例的结果码(gracious = 自己退了 / killed = 强杀 / already-stopped = 早就不在了)。 */
709
+ type DshProfileStopOutcome = 'graceful' | 'killed' | 'already-stopped';
710
+ /** 启动回执里的非致命告警码。 */
711
+ type DshProfileLaunchWarning = 'notReady' | 'urlNotFound';
712
+ /** GET /profiles 的响应负载(列表 + 当前运行 + 运行中实例 + 模板清单)。 */
664
713
  interface DshProfilesSnapshot {
665
714
  profiles: DshProfileMeta[];
666
715
  /** 当前运行中的 profile 名(宿主从 config/argv 解析) */
667
716
  current: string;
668
- /** 「下次启动」标记(无 = null) */
669
- selection: DshProfileSelection | null;
717
+ /** 在跑的实例(台账 ∪ 心跳;空数组 = 没有。UI 据此把「启动」换成「停止」/「当前运行」) */
718
+ running: DshProfileRunningView[];
670
719
  /** 可选的起步模板(新建档案时用) */
671
720
  templates: DshProfileTemplate[];
672
721
  }
@@ -677,6 +726,27 @@ interface DshProfileTemplate {
677
726
  bundles: string[];
678
727
  patchReload: DshProfilePatchReload;
679
728
  }
729
+ /** 复制档案的非致命告警码(UI 按码映射文案,绝不静默)。 */
730
+ type DshProfileCopyWarning =
731
+ /** 副本没有 node_modules 却声明了依赖:直接启动会解析不到 bundle,必须先装依赖 */
732
+ 'depsNotInstalled';
733
+ /**
734
+ * 「复制档案」的回执(UI 横幅用):副本名 + 是否带 node_modules + 耗时 + 告警。
735
+ *
736
+ * 为什么把 warnings 一路带回前端:includeNodeModules=false 出来的副本**不能直接启动**
737
+ * (bundles 里的树外插件解析不到),这条必须显式告诉用户并给出安装命令 —— 静默成功等于骗人。
738
+ */
739
+ interface DshProfileCopyResult {
740
+ /** 副本档案名 */
741
+ name: string;
742
+ /** 源档案名 */
743
+ sourceName: string;
744
+ /** 是否一并复制了 node_modules */
745
+ includeNodeModules: boolean;
746
+ /** 宿主侧实际耗时(毫秒;大档案含 node_modules 时可达数十秒) */
747
+ durationMs: number;
748
+ warnings: DshProfileCopyWarning[];
749
+ }
680
750
  //#endregion
681
751
  //#region src/core/run-registry.d.ts
682
752
  /** run 类型:导出 / 导入 / 自动同步 / 一键同步逐项应用 / 定时备份 / 快照恢复 / 配置档案切换 / recovery 恢复。 */
@@ -991,7 +1061,6 @@ declare const uiZh: {
991
1061
  readonly 'error.exportTimeout': '导出超时({minutes} 分钟):导出过程未完成,请重试;若反复超时请检查 DSH 状态';
992
1062
  readonly 'error.syncTimeout': '同步请求超时({minutes} 分钟):请检查网络与仓库可达性后重试';
993
1063
  readonly 'error.recoveryTimeout': '恢复请求超时({minutes} 分钟):恢复过程未完成,请重试;若反复超时请检查 DSH 状态';
994
- readonly 'error.lifecycleTimeout': '灾备请求超时({minutes} 分钟):撤销/重做未完成,请重试;若反复超时请检查 DSH 状态';
995
1064
  readonly 'error.settingsConflict.title': '配置已被并发修改';
996
1065
  readonly 'error.settingsConflict.action': '目标 DSH 的该配置在导入期间被其他进程修改,为避免覆盖请重新导出或手动核对后再试。';
997
1066
  readonly 'error.notConfirmed.title': '导入未确认';
@@ -1499,20 +1568,46 @@ declare class ConfigManagerApi {
1499
1568
  setSnapshotPinned(snapshotId: string, pinned: boolean): Promise<{
1500
1569
  pinned: boolean;
1501
1570
  }>;
1502
- /** 列出 DSH profile:列表 + 当前运行 + 「下次启动」标记 + 起步模板。 */
1571
+ /** 列出 DSH profile:列表 + 当前运行 + 在跑的实例(台账 ∪ 心跳)+ 起步模板。 */
1503
1572
  profilesList(): Promise<DshProfilesSnapshot>;
1504
1573
  /** 单个档案详情(package.json / cordis.patch.yml 原文;大文件原文截断为 null)。 */
1505
1574
  profileDetail(name: string): Promise<DshProfileDetail>;
1506
1575
  /** 新建档案(在 $DSH_HOME/profiles/<name> 写标准 profile 三件套;template = base/web/headless/sdk/…)。 */
1507
1576
  profileCreate(name: string, template: string): Promise<DshProfileMeta>;
1577
+ /**
1578
+ * 复制档案:整份拷贝成新档案(package.json 的 name 跟随改写)。
1579
+ * includeNodeModules 缺省 true —— 副本立刻可用,但 285 MB 级档案实测要三十秒上下(走长操作档位);
1580
+ * 传 false 只拷清单与 patch(秒级),回执 copy.warnings 会带 depsNotInstalled,调用方必须如实展示。
1581
+ */
1582
+ profileCopy(name: string, newName: string, opts?: {
1583
+ includeNodeModules?: boolean;
1584
+ }): Promise<{
1585
+ profile: DshProfileMeta;
1586
+ copy: DshProfileCopyResult;
1587
+ }>;
1508
1588
  /** 重命名档案(目录级移动;当前运行中的档案被 host 拒绝)。 */
1509
1589
  profileRename(name: string, newName: string): Promise<DshProfileMeta>;
1510
1590
  /** 物理删除档案目录(不可恢复;当前运行中的档案需显式 allowCurrent=true)。 */
1511
1591
  profileDelete(name: string, opts?: {
1512
1592
  allowCurrent?: boolean;
1513
1593
  }): Promise<void>;
1514
- /** 记录「下次启动」用哪个档案(name=null 清除标记)。DSH 不支持运行中切换,重启由用户完成。 */
1515
- profileSelect(name: string | null): Promise<DshProfileSelection | null>;
1594
+ /**
1595
+ * 停掉该档案的实例:本插件启动的走台账,**别处/手动启动的**走心跳(同样按 pid 停)。
1596
+ * 返回停止后的运行中实例列表 —— UI 据此把该行按钮换回「启动」。
1597
+ */
1598
+ profileStop(name: string): Promise<{
1599
+ result: DshProfileStopOutcome;
1600
+ running: DshProfileRunningView[];
1601
+ }>;
1602
+ /**
1603
+ * 用该档案启动一个**独立 DSH 实例**("切换档案"真正可用的形态;DSH 没有默认 profile 状态)。
1604
+ * 只支持 web 形态档案;host 要等子进程起来(最长 20s 探活)→ 长操作档位。
1605
+ * 同时返回启动后的 running 列表:UI 立刻把该行按钮换成「停止」,不必再 GET 一次。
1606
+ */
1607
+ profileLaunch(name: string): Promise<{
1608
+ launch: DshProfileLaunchResult;
1609
+ running: DshProfileRunningView[];
1610
+ }>;
1516
1611
  /** 读取定时备份配置(enabled / interval / 上次运行状态;无敏感字段)。 */
1517
1612
  backupSchedule(): Promise<BackupScheduleStatus>;
1518
1613
  /** 保存定时备份设置(enabled + interval);Host 校验后原子写 + 重排调度器。 */
@@ -2588,50 +2683,7 @@ declare class HistoryApi {
2588
2683
  private buildExportUrl;
2589
2684
  }
2590
2685
  //#endregion
2591
- //#region src/client/lifecycle/lifecycle-api.d.ts
2592
- /** 配置快照种类(与 core/undo.ts 的 ConfigSnapshotKind 同构)。 */
2593
- type LifecycleSnapshotKind = 'manual' | 'auto' | 'undo' | 'pre-restore' | 'baseline';
2594
- /** 单个配置状态快照的元信息(列表展示用;不含分区数据)。 */
2595
- interface LifecycleSnapshotMeta {
2596
- id: string;
2597
- createdAt: string;
2598
- kind: LifecycleSnapshotKind;
2599
- reason: string;
2600
- trigger: string | null;
2601
- sections: string[];
2602
- totalBytes: number;
2603
- note: string | null;
2604
- tags: string[];
2605
- pinned: boolean;
2606
- }
2607
- /** GET /lifecycle/status 响应。 */
2608
- interface LifecycleStatus {
2609
- canUndo: boolean;
2610
- canRedo: boolean;
2611
- total: number;
2612
- lastAutoAt: string | null;
2613
- watching: boolean;
2614
- snapshots: LifecycleSnapshotMeta[];
2615
- }
2616
- /** 撤销/重做的结果。失败时 `reason` 是稳定的枚举码(如 'already-at-state'),供 UI 分流文案。 */
2617
- interface LifecycleOutcome {
2618
- ok: boolean;
2619
- targetId: string | null;
2620
- reason: string | null;
2621
- report: {
2622
- applied?: string[];
2623
- skipped?: string[];
2624
- failed?: {
2625
- item: string;
2626
- reason: string;
2627
- }[];
2628
- invalidSections?: {
2629
- section: string;
2630
- reason: string;
2631
- }[];
2632
- needsRestart?: boolean;
2633
- } | null;
2634
- }
2686
+ //#region src/client/recovery/incident-api.d.ts
2635
2687
  /** 崩溃归因分类(与 core/crash-report.ts 的 CrashKind 同构)。 */
2636
2688
  type CrashKind = 'session-corrupt' | 'bundle-check' | 'patch-tree' | 'unknown';
2637
2689
  /** 建议动作(与 core/crash-report.ts 的 CrashAdvice 同构)。 */
@@ -2642,13 +2694,16 @@ interface CrashReport {
2642
2694
  crashReason: CrashKind | null;
2643
2695
  lastGoodAt: string | null;
2644
2696
  advice: CrashAdvice;
2645
- lastGoodSnapshotId: string | null;
2646
2697
  }
2647
- /** GET /rescue 响应。`stale` 为家目录指纹不匹配(换机/重建 home)后的自动降级标记。 */
2698
+ /**
2699
+ * GET /rescue 响应。stale 为家目录指纹不匹配(换机 / 重建 home)后的自动降级标记;
2700
+ * applied = 救援已在当前进程生效(本进程启动于进入救援之后,即用户已经重启过)。
2701
+ */
2648
2702
  interface RescueStatus {
2649
2703
  active: boolean;
2650
2704
  stale: boolean;
2651
2705
  enteredAt: string | null;
2706
+ applied: boolean;
2652
2707
  }
2653
2708
  /** POST /rescue 响应。 */
2654
2709
  interface RescueActionResult {
@@ -2660,47 +2715,20 @@ interface RescueActionResult {
2660
2715
  code?: string;
2661
2716
  message?: string;
2662
2717
  }
2663
- /** 新建快照的请求参数(全部可选)。 */
2664
- interface SnapshotRequest {
2665
- reason?: string;
2666
- note?: string;
2667
- tags?: string[];
2668
- }
2669
- /** 灾备浏览器半数据入口。 */
2670
- declare class LifecycleApi {
2718
+ /** 事故恢复(崩溃归因 + 救援模式)浏览器半数据入口。 */
2719
+ declare class IncidentApi {
2671
2720
  readonly t: UiT;
2672
2721
  constructor(t?: UiT);
2673
- /** GET /lifecycle/status:撤销/重做可用性 + 快照列表 + 自动快照状态。 */
2674
- status(): Promise<LifecycleStatus>;
2675
- /** POST /lifecycle/snapshot:立即保存一份手动快照。 */
2676
- snapshot(req?: SnapshotRequest): Promise<{
2677
- ok: boolean;
2678
- id: string;
2679
- kind: string;
2680
- totalBytes: number;
2681
- }>;
2682
- /**
2683
- * POST /lifecycle/undo:回退到内容不同的最近快照。
2684
- * 引擎会在撤销前自动落一份「撤销前存档」,因此该动作可重做。
2685
- */
2686
- undo(): Promise<LifecycleOutcome>;
2687
- /** POST /lifecycle/redo:重做上一次撤销(仅当撤销后没有新变更时可成功)。 */
2688
- redo(): Promise<LifecycleOutcome>;
2689
- /** POST /lifecycle/remove:删除单个配置状态快照。 */
2690
- remove(id: string): Promise<{
2691
- ok: boolean;
2692
- removed: boolean;
2693
- }>;
2694
- /** GET /crash:上次启动是否异常 + 归因 + 建议动作 + 最后正常快照 id。 */
2722
+ /** GET /crash:上次启动是否异常 + 归因 + 建议动作 + 最后正常时刻。 */
2695
2723
  crash(): Promise<CrashReport>;
2696
2724
  /** GET /rescue:救援模式状态。 */
2697
2725
  rescueStatus(): Promise<RescueStatus>;
2698
2726
  /**
2699
- * POST /rescue {action:'on', confirm:true}:进入救援模式。
2727
+ * POST /rescue {action:on, confirm:true}:进入救援模式。
2700
2728
  * confirm 必须由调用方在显式确认弹窗后传入 —— 本层绝不代填。
2701
2729
  */
2702
2730
  rescueOn(): Promise<RescueActionResult>;
2703
- /** POST /rescue {action:'off'}:退出救援模式(从备份还原)。 */
2731
+ /** POST /rescue {action:off}:退出救援模式(从备份完整还原)。 */
2704
2732
  rescueOff(): Promise<RescueActionResult>;
2705
2733
  }
2706
2734
  //#endregion
@@ -2724,8 +2752,8 @@ interface ConfigManagerSectionInjected {
2724
2752
  recoveryT: import('@deepseek-ai/dsh-client-ui-slots').TranslateNS<'config-manager-recovery'>;
2725
2753
  /** Migration History API(Phase 6:统一审计史;主 section 注册时注入) */
2726
2754
  historyApi: HistoryApi;
2727
- /** Phase 1 灾备 API(自动快照 / 撤销 / 重做 / 救援模式 / 崩溃归因;主 section 注册时注入) */
2728
- lifecycleApi: LifecycleApi;
2755
+ /** 事故恢复 API(崩溃归因 + 救援模式;主 section 注册时注入,RecoveryPanel 消费) */
2756
+ incidentApi: IncidentApi;
2729
2757
  /** Migration History locale(config-manager-history 命名空间,主 section 注册时注入) */
2730
2758
  historyT: import('@deepseek-ai/dsh-client-ui-slots').TranslateNS<'config-manager-history'>;
2731
2759
  }
@@ -3047,6 +3075,9 @@ declare const zh$3: {
3047
3075
  readonly 'backupSchedule.interval.7d': '每 7 天';
3048
3076
  readonly 'backupSchedule.interval.custom': '自定义(每周固定时刻)';
3049
3077
  readonly 'backupSchedule.customHint': '每周在选定星期与时刻各执行一次全量备份(错过的时间点不补跑)';
3078
+ readonly 'backupSchedule.weekday': '星期';
3079
+ readonly 'backupSchedule.hour': '小时';
3080
+ readonly 'backupSchedule.minute': '分钟';
3050
3081
  readonly 'backupSchedule.weekday.sunday': '周日';
3051
3082
  readonly 'backupSchedule.weekday.monday': '周一';
3052
3083
  readonly 'backupSchedule.weekday.tuesday': '周二';
@@ -3130,6 +3161,8 @@ declare const zh$3: {
3130
3161
  readonly 'about.repo': 'GitHub 仓库';
3131
3162
  readonly 'about.docs': '使用文档';
3132
3163
  readonly 'about.issues': '反馈问题';
3164
+ readonly 'about.feedbackHint': '遇到界面问题?复制环境信息,粘进 issue 就行';
3165
+ readonly 'about.copyEnv': '复制环境信息(插件版本 / DSH 版本 / 平台)';
3133
3166
  readonly 'about.authorLabel': '作者';
3134
3167
  readonly 'about.loading': '正在获取版本信息…';
3135
3168
  readonly 'about.retryStatus': '重新获取';
@@ -3162,17 +3195,40 @@ declare const zh$3: {
3162
3195
  readonly 'profiles.empty': '还没有任何档案。用「新建档案」创建第一个。';
3163
3196
  readonly 'profiles.refresh': '刷新';
3164
3197
  readonly 'profiles.current': '当前运行';
3165
- readonly 'profiles.nextLaunch': '下次启动';
3166
- readonly 'profiles.select': '设为下次启动';
3167
- readonly 'profiles.selectClear': '取消下次启动设置';
3168
- readonly 'profiles.selectDone': '已把「{name}」设为下次启动档案';
3169
- readonly 'profiles.selectCleared': '已取消下次启动档案设置';
3170
- readonly 'profiles.selectHint': 'DSH 无法在运行中切换档案:这里只记录下次启动用哪个,重启由你手动完成。';
3171
- readonly 'profiles.selectPending': '下次启动将使用「{name}」。重启命令:';
3172
- readonly 'profiles.selectCurrent': '「{name}」就是当前运行的档案,无需重启。';
3173
- readonly 'profiles.selectMissing': '下次启动设置指向的「{name}」已不存在,请重新选择。';
3198
+ readonly 'profiles.running.title': '运行状态';
3199
+ readonly 'profiles.running.none': '当前没有任何档案实例在跑。点档案行的「启动」会用该档案另起一个独立实例(自动挑空闲端口 + 自动打开浏览器);已经在跑的档案(含手动启动的)不会再被启动。';
3200
+ readonly 'profiles.running.port': '端口 {port}';
3201
+ readonly 'profiles.running.open': '打开';
3202
+ readonly 'profiles.running.badge': ':{port} 运行中';
3203
+ readonly 'profiles.running.originCurrent': '当前实例';
3204
+ readonly 'profiles.running.originOwned': '本插件启动';
3205
+ readonly 'profiles.running.originExternal': '其他实例';
3206
+ readonly 'profiles.running.badgePlain': '运行中';
3207
+ readonly 'profiles.stop': '停止';
3208
+ readonly 'profiles.stopping': '停止中…';
3209
+ readonly 'profiles.stop.hint': '停止本插件启动的这个实例(先请它自己退出,优雅期超时才强制终止进程树)';
3210
+ readonly 'profiles.stop.hintExternal': '该档案的实例由另一个 DSH 实例(或手动启动)运行——停止会结束那个进程';
3211
+ readonly 'profiles.stop.currentHint': '这就是你正在用的这个实例:不能在它自己的界面里停自己,请关闭该窗口/终端(或到启动它的那个实例里停止)';
3212
+ readonly 'profiles.stopMessageExternal': '确认停止档案「{name}」的实例(端口 {port})?它由另一个 DSH 实例(或手动启动)运行,停止会关闭那个实例。';
3213
+ readonly 'profiles.stopTitle': '停止实例';
3214
+ readonly 'profiles.stopMessage': '确认停止档案「{name}」的实例(端口 {port})?该实例里正在进行的会话会被中断。';
3215
+ readonly 'profiles.stop.done': '已停止档案「{name}」的实例';
3216
+ readonly 'profiles.stop.forced': '已停止档案「{name}」的实例(进程未在优雅期内退出,已结束其进程树)';
3217
+ readonly 'profiles.stop.gone': '档案「{name}」的实例早已不在(残留记录已清理)';
3174
3218
  readonly 'profiles.copy': '复制命令';
3175
3219
  readonly 'profiles.copied': '已复制重启命令';
3220
+ readonly 'profiles.launch': '启动';
3221
+ readonly 'profiles.launching': '启动中…';
3222
+ readonly 'profiles.launch.hint': '用该档案另起一个独立 DSH 实例(自动挑空闲端口、自动打开浏览器);当前实例不受影响。只支持 Web 形态档案。';
3223
+ readonly 'profiles.launch.notWeb': '「{name}」不是 Web 形态(bundles 里没有 @deepseek-ai/dsh-web-app),没有浏览器界面,无法作为独立实例启动。请在终端运行:';
3224
+ readonly 'profiles.launch.started': '已启动档案「{name}」的独立实例(端口 {port})';
3225
+ readonly 'profiles.launch.pending': '档案「{name}」的进程已启动(端口 {port}),但 {seconds} 秒内未确认就绪 —— 请看日志确认:';
3226
+ readonly 'profiles.launch.open': '打开新实例';
3227
+ readonly 'profiles.launch.copyUrl': '复制 URL';
3228
+ readonly 'profiles.launch.copiedUrl': '已复制认证 URL';
3229
+ readonly 'profiles.launch.warn.notReady': '未能确认 Web 服务已就绪(可能仍在启动,或启动失败)';
3230
+ readonly 'profiles.launch.warn.urlNotFound': '没能从启动日志里抓到带 token 的认证 URL — 请从日志里复制 URL 打开';
3231
+ readonly 'profiles.launch.log': '日志';
3176
3232
  readonly 'profiles.create.title': '新建档案';
3177
3233
  readonly 'profiles.create.hint': '在 $DSH_HOME/profiles/<name> 下创建标准 profile(package.json + cordis.patch.yml + pnpm-workspace.yaml);第三方插件需要在该档案里单独安装。';
3178
3234
  readonly 'profiles.create.nameLabel': '档案名';
@@ -3223,12 +3279,31 @@ declare const zh$3: {
3223
3279
  readonly 'profiles.delete.done': '已删除档案「{name}」';
3224
3280
  readonly 'profiles.deleteCurrentWarning': '这是当前运行中的档案:删除后本次运行仍可用,但重启会失败。';
3225
3281
  readonly 'profiles.deleteCurrentConfirm': '仍要删除当前档案';
3282
+ readonly 'profiles.duplicate': '复制';
3283
+ readonly 'profiles.duplicateHint': '把该档案整份复制成一份新档案(清单 / patch 层 / 依赖声明 / pnpm 锁文件;可连同已装的 node_modules 一起拷)';
3284
+ readonly 'profiles.duplicateTitle': '复制档案';
3285
+ readonly 'profiles.duplicateMessage': '把档案「{name}」复制成一份新档案:';
3286
+ readonly 'profiles.duplicate.nameLabel': '副本档案名';
3287
+ readonly 'profiles.duplicate.includeModules': '同时复制 node_modules(副本立刻可用)';
3288
+ readonly 'profiles.duplicate.includeModulesHint': '带上 node_modules,副本启动时才能解析到树外插件;代价是与原档案同体积、耗时较长(实测 285 MB ≈ 30 秒)。不勾选只复制清单与 patch(秒级完成),副本启动前需要先装依赖。';
3289
+ readonly 'profiles.duplicate.onlyManifest': '该档案还没有 node_modules:只会复制清单文件与 patch 层。';
3290
+ readonly 'profiles.duplicate.done': '已复制为档案「{name}」';
3291
+ readonly 'profiles.duplicate.warn.depsNotInstalled': '副本没有带 node_modules:直接启动会因解析不到 bundle 而失败,先在副本目录安装依赖:';
3292
+ readonly 'profiles.duplicate.copiedInstall': '已复制安装命令';
3293
+ readonly 'profiles.error.copyFailed': '复制档案失败';
3226
3294
  readonly 'profiles.error.exists': '同名档案已存在';
3227
3295
  readonly 'profiles.error.notFound': '档案不存在(可能已被删除)';
3228
3296
  readonly 'profiles.error.currentProfile': '该操作不能作用于当前运行中的档案';
3229
3297
  readonly 'profiles.error.invalidName': '档案名不合法';
3230
3298
  readonly 'profiles.error.reservedName': 'DSH 保留名不能手动创建';
3231
3299
  readonly 'profiles.error.unknownTemplate': '未知的起步模板';
3300
+ readonly 'profiles.error.notLaunchable': '该档案不是 Web 形态,没有浏览器界面(请用终端运行 dsh --profile <名>)';
3301
+ readonly 'profiles.error.launcherUnavailable': '定位不到 dsh 启动器(非标准启动方式),请手动运行 dsh --profile <名>';
3302
+ readonly 'profiles.error.launchFailed': '档案实例启动失败';
3303
+ readonly 'profiles.error.alreadyRunning': '该档案的实例已经在运行(先停止再启动)';
3304
+ readonly 'profiles.error.notRunning': '该档案没有正在运行的实例(可能已被手动关闭)';
3305
+ readonly 'profiles.error.stopFailed': '停止实例失败';
3306
+ readonly 'profiles.error.instanceRunning': '该档案的实例正在运行,请先停止再删除';
3232
3307
  readonly 'profiles.error.generic': '操作失败:{message}';
3233
3308
  readonly 'import.compatibility.sections': '{count} 个分区';
3234
3309
  readonly 'import.compatibility.plugins': '已装 {installed} · 待装 {toInstall}';
@@ -3246,58 +3321,6 @@ declare const zh$3: {
3246
3321
  readonly 'nav.sync': '同步';
3247
3322
  readonly 'nav.market': '市场';
3248
3323
  readonly 'nav.profiles': '档案';
3249
- readonly 'nav.recovery': '灾备';
3250
- readonly 'lifecycle.autoOn': '自动快照已开启';
3251
- readonly 'lifecycle.autoOff': '自动快照未开启';
3252
- readonly 'lifecycle.lastAuto': '最近自动快照';
3253
- readonly 'lifecycle.never': '从未';
3254
- readonly 'lifecycle.undo': '撤销';
3255
- readonly 'lifecycle.redo': '重做';
3256
- readonly 'lifecycle.undoTitle': '撤销上一步配置变更(回退到内容不同的最近快照)';
3257
- readonly 'lifecycle.redoTitle': '重做上一次撤销(仅在撤销后没有新变更时可用)';
3258
- readonly 'lifecycle.undoConfirm': '撤销会回滚配置到上一个不同状态。撤销前会自动保存当前状态,可随时重做。是否继续?';
3259
- readonly 'lifecycle.undoOk': '已撤销到 {id}';
3260
- readonly 'lifecycle.redoOk': '已重做到 {id}';
3261
- readonly 'lifecycle.undoNone': '没有可撤销的变化';
3262
- readonly 'lifecycle.redoNone': '没有可重做的撤销';
3263
- readonly 'lifecycle.snapshotNow': '立即快照';
3264
- readonly 'lifecycle.snapshotReason': '快照原因(可选)';
3265
- readonly 'lifecycle.snapshotCreated': '已创建快照 {id}';
3266
- readonly 'lifecycle.snapshots': '配置状态快照';
3267
- readonly 'lifecycle.empty': '还没有配置状态快照。开启自动快照后,配置变更会自动存档。';
3268
- readonly 'lifecycle.kind.auto': '自动';
3269
- readonly 'lifecycle.kind.manual': '手动';
3270
- readonly 'lifecycle.kind.undo': '撤销';
3271
- readonly 'lifecycle.kind.baseline': '基线';
3272
- readonly 'lifecycle.kind.pre-restore': '撤销前存档';
3273
- readonly 'lifecycle.sections': '{count} 个分区';
3274
- readonly 'lifecycle.delete': '删除';
3275
- readonly 'lifecycle.deleteConfirm': '删除快照 {id}?该快照将无法再用于回退。';
3276
- readonly 'lifecycle.crash.title': '上次启动未正常完成';
3277
- readonly 'lifecycle.crash.lastGood': '最后正常状态:{time}';
3278
- readonly 'lifecycle.crash.restoreLastGood': '回退到最后正常状态';
3279
- readonly 'lifecycle.crash.reason.session-corrupt': '会话文件损坏';
3280
- readonly 'lifecycle.crash.reason.bundle-check': '插件 bundle 无法解析';
3281
- readonly 'lifecycle.crash.reason.patch-tree': '插件加载失败(挂载树冲突)';
3282
- readonly 'lifecycle.crash.reason.unknown': '原因未知';
3283
- readonly 'lifecycle.crash.advice.restore-last-good': '建议回退到最后正常状态。';
3284
- readonly 'lifecycle.crash.advice.repair-session': '建议先用会话扫描修复损坏的会话文件。';
3285
- readonly 'lifecycle.crash.advice.check-bundles': '建议进入救援模式,或检查 package.json 的 dsh.profile.bundles。';
3286
- readonly 'lifecycle.crash.advice.check-patch-tree': '建议进入救援模式禁用插件后逐个排查。';
3287
- readonly 'lifecycle.rescue.title': '救援模式';
3288
- readonly 'lifecycle.rescue.desc': 'DSH 因插件起不来时,把插件挂载收窄到「DSH 核心 + 本插件」:改写两层 cordis.patch.yml,并把 dsh.profile.bundles 里的其它用户插件临时移出。不卸载任何包,退出时从备份完整还原。';
3289
- readonly 'lifecycle.rescue.active': '救援模式已开启({time})—— 请重启 DSH 使其生效。';
3290
- readonly 'lifecycle.rescue.enter': '进入救援模式';
3291
- readonly 'lifecycle.rescue.exit': '退出救援模式';
3292
- readonly 'lifecycle.rescue.restartHint': '操作后需要重启 DSH 才能生效。';
3293
- readonly 'lifecycle.rescue.confirm': '进入救援模式会改写 profile 的 cordis.patch.yml、置空 home 的 cordis.patch.yml,并把 dsh.profile.bundles 收窄为 DSH 核心与本插件(其它用户插件本次启动不挂载;包本身不删除)。退出时会从备份逐字节还原。请重启 DSH 使其生效。是否继续?';
3294
- readonly 'lifecycle.rescue.on': '已进入救援模式';
3295
- readonly 'lifecycle.rescue.off': '已退出救援模式';
3296
- readonly 'lifecycle.rescue.inactive': '救援模式未开启';
3297
- readonly 'lifecycle.rescue.stale': '救援标记已失效(换机或重建 home)';
3298
- readonly 'lifecycle.guided.title': '需要更完整的恢复流程?';
3299
- readonly 'lifecycle.guided.desc': '本页是一键回退(撤销/重做)。若 DSH 因中断的事务或崩溃而无法正常恢复,请用「备份」页的恢复向导逐步确认后执行 —— 它会先预览影响、再校验结果。';
3300
- readonly 'lifecycle.guided.action': '打开恢复向导';
3301
3324
  readonly 'nav.refresh': '刷新';
3302
3325
  readonly 'shell.status.idle': '就绪';
3303
3326
  readonly 'shell.status.running': '{count} 个任务进行中';
@@ -3927,6 +3950,33 @@ declare const zh: {
3927
3950
  readonly 'recovery.lock.refused': '未判定为残留锁,已拒绝回收(未做任何改动)';
3928
3951
  readonly 'recovery.lock.confirmTitle': '回收残留配置锁';
3929
3952
  readonly 'recovery.lock.confirmMessage': '将再次确认该锁的持有进程确已不存在,然后原子移除这把锁。若无法确认(例如锁仍被活跃进程持有),操作会被拒绝且不做任何改动。';
3953
+ readonly 'recovery.crash.title': '上次启动未正常完成';
3954
+ readonly 'recovery.crash.lastGood': '最后正常状态:{time}';
3955
+ readonly 'recovery.crash.guidance': '处置建议:进入救援模式让 DSH 先起来,随后到「备份文件」用最近一次备份恢复。';
3956
+ readonly 'recovery.crash.reason.session-corrupt': '会话文件损坏';
3957
+ readonly 'recovery.crash.reason.bundle-check': '插件 bundle 无法解析';
3958
+ readonly 'recovery.crash.reason.patch-tree': '插件加载失败(挂载树冲突)';
3959
+ readonly 'recovery.crash.reason.unknown': '原因未知';
3960
+ readonly 'recovery.crash.advice.none': '本模块没有额外建议动作。';
3961
+ readonly 'recovery.crash.advice.restore-last-good': '建议回到最后正常状态(灾备快照已下线,改用最近一次备份恢复)。';
3962
+ readonly 'recovery.crash.advice.repair-session': '建议先用会话扫描修复损坏的会话文件。';
3963
+ readonly 'recovery.crash.advice.check-bundles': '建议进入救援模式,或检查 package.json 的 dsh.profile.bundles。';
3964
+ readonly 'recovery.crash.advice.check-patch-tree': '建议进入救援模式禁用插件后逐个排查。';
3965
+ readonly 'recovery.rescue.title': '救援模式';
3966
+ readonly 'recovery.rescue.desc': 'DSH 因插件起不来时,把插件挂载收窄到「DSH 核心 + 本插件」:改写两层 cordis.patch.yml,并把 dsh.profile.bundles 里的其它用户插件临时移出。不卸载任何包,退出时从备份完整还原。';
3967
+ readonly 'recovery.rescue.inactive': '救援模式未开启';
3968
+ readonly 'recovery.rescue.activeBadge': '已开启';
3969
+ readonly 'recovery.rescue.active': '救援模式已开启({time})—— 请重启 DSH 使其生效。';
3970
+ readonly 'recovery.rescue.applied': '救援模式已开启({time})—— 本次启动只挂载了 DSH 核心与本插件。';
3971
+ readonly 'recovery.rescue.appliedHint': '其它用户插件本次未加载;退出救援模式后同样需要重启 DSH 才会恢复。';
3972
+ readonly 'recovery.rescue.restartHint': '操作后需要重启 DSH 才能生效。';
3973
+ readonly 'recovery.rescue.enter': '进入救援模式';
3974
+ readonly 'recovery.rescue.exit': '退出救援模式';
3975
+ readonly 'recovery.rescue.confirmTitle': '进入救援模式';
3976
+ readonly 'recovery.rescue.confirm': '进入救援模式会改写 profile 的 cordis.patch.yml、置空 home 的 cordis.patch.yml,并把 dsh.profile.bundles 收窄为 DSH 核心与本插件(其它用户插件本次启动不挂载;包本身不删除)。退出时会从备份逐字节还原。请重启 DSH 使其生效。是否继续?';
3977
+ readonly 'recovery.rescue.on': '已进入救援模式';
3978
+ readonly 'recovery.rescue.off': '已退出救援模式';
3979
+ readonly 'recovery.rescue.stale': '救援标记已失效(换机或重建 home)';
3930
3980
  };
3931
3981
  /** 字典键联合(注册处 compile-time 校验)。 */
3932
3982
  type RecoveryKey = keyof typeof zh;