dsh-config-manager 0.1.71 → 0.1.73

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 (596) hide show
  1. package/README.md +149 -98
  2. package/README.zh-CN.md +245 -88
  3. package/lib/adapters/agent-instructions.js +1 -1
  4. package/lib/adapters/file-collection.js +1 -1
  5. package/lib/adapters/index.d.ts +2 -1
  6. package/lib/adapters/index.js +6 -1
  7. package/lib/adapters/link-report.d.ts +1 -0
  8. package/lib/adapters/link-report.js +27 -3
  9. package/lib/adapters/messages.d.ts +21 -0
  10. package/lib/adapters/messages.js +57 -0
  11. package/lib/adapters/plugin-data.d.ts +114 -0
  12. package/lib/adapters/plugin-data.js +309 -0
  13. package/lib/adapters/plugin-files.js +3 -2
  14. package/lib/adapters/plugins.js +4 -3
  15. package/lib/adapters/prompts.d.ts +1 -0
  16. package/lib/adapters/prompts.js +7 -1
  17. package/lib/adapters/self.d.ts +8 -2
  18. package/lib/adapters/self.js +3 -2
  19. package/lib/adapters/sessions.d.ts +1 -0
  20. package/lib/adapters/sessions.js +4 -2
  21. package/lib/adapters/test-helpers.d.ts +9 -0
  22. package/lib/adapters/test-helpers.js +14 -0
  23. package/lib/adapters/ui.d.ts +2 -2
  24. package/lib/adapters/ui.js +25 -25
  25. package/lib/adapters/workspaces.d.ts +1 -0
  26. package/lib/adapters/workspaces.js +7 -3
  27. package/lib/cli/actions.d.ts +9 -1
  28. package/lib/cli/actions.js +5 -2
  29. package/lib/cli/help.js +23 -10
  30. package/lib/cli/index.d.ts +23 -0
  31. package/lib/cli/index.js +90 -32
  32. package/lib/cli/sessions-inspect.js +3 -1
  33. package/lib/cli/sessions-repair.js +25 -6
  34. package/lib/cli/web/page.d.ts +12 -0
  35. package/lib/cli/web/page.js +21 -12
  36. package/lib/cli/web/routes.js +2 -2
  37. package/lib/cli/web/server.js +4 -4
  38. package/lib/client.d.ts +1442 -494
  39. package/lib/client.js +15326 -8520
  40. package/lib/core/analyzer.d.ts +11 -2
  41. package/lib/core/analyzer.js +31 -8
  42. package/lib/core/backup-plan.d.ts +10 -0
  43. package/lib/core/backup-plan.js +51 -20
  44. package/lib/core/backup-verify.js +24 -23
  45. package/lib/core/backup.d.ts +25 -1
  46. package/lib/core/backup.js +99 -28
  47. package/lib/core/boot-rescue.js +4 -3
  48. package/lib/core/boot-safety.js +2 -1
  49. package/lib/core/command-rollback.d.ts +182 -0
  50. package/lib/core/command-rollback.js +293 -0
  51. package/lib/core/consult-source.js +7 -6
  52. package/lib/core/disk-usage.d.ts +74 -10
  53. package/lib/core/disk-usage.js +67 -4
  54. package/lib/core/engine-locale.d.ts +87 -0
  55. package/lib/core/engine-locale.js +125 -0
  56. package/lib/core/exporter.js +3 -3
  57. package/lib/core/fork-reconcile.d.ts +128 -0
  58. package/lib/core/fork-reconcile.js +150 -0
  59. package/lib/core/journal.js +6 -5
  60. package/lib/core/local-plugin-host.js +3 -2
  61. package/lib/core/local-plugin-pack.js +11 -6
  62. package/lib/core/messages-core.d.ts +294 -0
  63. package/lib/core/messages-core.js +619 -0
  64. package/lib/core/messages.d.ts +61 -2
  65. package/lib/core/messages.js +118 -13
  66. package/lib/core/migration-consult.js +21 -20
  67. package/lib/core/migration-history.js +19 -5
  68. package/lib/core/model-tools.d.ts +0 -22
  69. package/lib/core/model-tools.js +50 -27
  70. package/lib/core/phase3-host.js +30 -5
  71. package/lib/core/plugin-cli.js +18 -19
  72. package/lib/core/plugin-data-registry.d.ts +124 -0
  73. package/lib/core/plugin-data-registry.js +198 -0
  74. package/lib/core/reconcile.d.ts +2 -14
  75. package/lib/core/reconcile.js +87 -17
  76. package/lib/core/recovery-orchestrator.js +19 -18
  77. package/lib/core/reinstall.d.ts +4 -0
  78. package/lib/core/reinstall.js +26 -21
  79. package/lib/core/restore.js +20 -17
  80. package/lib/core/rollback-gate.d.ts +33 -0
  81. package/lib/core/rollback-gate.js +57 -0
  82. package/lib/core/rollback-ignore-rules.d.ts +31 -0
  83. package/lib/core/rollback-ignore-rules.js +140 -0
  84. package/lib/core/rollback-ledger.d.ts +248 -0
  85. package/lib/core/rollback-ledger.js +1282 -0
  86. package/lib/core/rollback-point.d.ts +1391 -0
  87. package/lib/core/rollback-point.js +3503 -0
  88. package/lib/core/rollback.js +6 -4
  89. package/lib/core/run-registry.js +6 -5
  90. package/lib/core/session-health.d.ts +15 -0
  91. package/lib/core/session-meta.d.ts +42 -2
  92. package/lib/core/session-meta.js +140 -1
  93. package/lib/core/session-title.d.ts +28 -0
  94. package/lib/core/session-title.js +185 -0
  95. package/lib/core/startup-barrier.d.ts +24 -0
  96. package/lib/core/startup-barrier.js +22 -0
  97. package/lib/core/transaction-coordinator.js +4 -3
  98. package/lib/core/types.d.ts +37 -1
  99. package/lib/core/validator.js +5 -4
  100. package/lib/core/verify-recovery.js +31 -30
  101. package/lib/foreign/bundle.js +3 -2
  102. package/lib/foreign/continue.js +2 -1
  103. package/lib/foreign/crush.js +2 -1
  104. package/lib/foreign/gemini.js +2 -1
  105. package/lib/foreign/goose.js +2 -1
  106. package/lib/foreign/kilocode.js +2 -1
  107. package/lib/foreign/kimi.js +2 -1
  108. package/lib/foreign/messages.d.ts +15 -0
  109. package/lib/foreign/messages.js +40 -0
  110. package/lib/foreign/mimocode.js +2 -1
  111. package/lib/foreign/openclaw.js +2 -1
  112. package/lib/foreign/opencode.js +2 -1
  113. package/lib/foreign/pi.js +2 -1
  114. package/lib/foreign/qoder.js +2 -1
  115. package/lib/foreign/qwen.js +2 -1
  116. package/lib/foreign/registry.d.ts +1 -1
  117. package/lib/foreign/registry.js +8 -7
  118. package/lib/foreign/teleagent.js +2 -1
  119. package/lib/foreign/workbuddy.js +2 -1
  120. package/lib/foreign/zcode.js +2 -1
  121. package/lib/foreign/zed.js +2 -1
  122. package/lib/index.d.ts +227 -7
  123. package/lib/index.js +799 -54
  124. package/lib/market/git-file-writer.js +20 -19
  125. package/lib/market/github-repos.js +35 -19
  126. package/lib/market/index-parser.js +31 -30
  127. package/lib/market/messages-market.d.ts +138 -0
  128. package/lib/market/messages-market.js +305 -0
  129. package/lib/market/my-repo.d.ts +2 -2
  130. package/lib/market/my-repo.js +32 -29
  131. package/lib/market/prepare.js +18 -15
  132. package/lib/market/security.js +20 -19
  133. package/lib/market/types.js +2 -1
  134. package/lib/market/url.js +2 -1
  135. package/lib/migrations/index.js +6 -5
  136. package/lib/migrations/messages-migrations.d.ts +32 -0
  137. package/lib/migrations/messages-migrations.js +58 -0
  138. package/lib/profiles/dsh-profile-launcher.js +2 -1
  139. package/lib/profiles/dsh-profile-runtime.js +2 -1
  140. package/lib/profiles/messages.d.ts +7 -0
  141. package/lib/profiles/messages.js +27 -0
  142. package/lib/routes/backup.js +18 -2
  143. package/lib/routes/import.js +3 -3
  144. package/lib/routes/index.d.ts +1 -1
  145. package/lib/routes/kit.d.ts +3 -2
  146. package/lib/routes/kit.js +6 -1
  147. package/lib/routes/market.js +1 -1
  148. package/lib/routes/messages.d.ts +43 -0
  149. package/lib/routes/messages.js +102 -0
  150. package/lib/routes/prefs.js +26 -3
  151. package/lib/routes/profiles.js +12 -6
  152. package/lib/routes/recovery.d.ts +21 -0
  153. package/lib/routes/recovery.js +478 -94
  154. package/lib/routes/snapshots.js +51 -4
  155. package/lib/routes/sync.js +79 -17
  156. package/lib/schema/config.d.ts +11 -1
  157. package/lib/schema/config.js +52 -20
  158. package/lib/schema/index.d.ts +8 -6
  159. package/lib/schema/index.js +8 -6
  160. package/lib/schema/manifest.js +19 -18
  161. package/lib/schema/messages.d.ts +47 -0
  162. package/lib/schema/messages.js +110 -0
  163. package/lib/schema/section-registry.d.ts +42 -15
  164. package/lib/schema/section-registry.js +67 -32
  165. package/lib/schema/types.d.ts +1 -1
  166. package/lib/schema/versions.js +6 -5
  167. package/lib/security/encryption.js +16 -15
  168. package/lib/security/integrity.js +8 -7
  169. package/lib/security/messages.d.ts +30 -0
  170. package/lib/security/messages.js +77 -0
  171. package/lib/security/redaction.js +12 -0
  172. package/lib/security/secret-scanner.js +16 -13
  173. package/lib/security/vault.js +2 -1
  174. package/lib/security/zip-security.js +4 -3
  175. package/lib/sync/ancestor.js +2 -1
  176. package/lib/sync/autosync-config.d.ts +11 -0
  177. package/lib/sync/autosync-config.js +12 -0
  178. package/lib/sync/autosync-scheduler.d.ts +11 -1
  179. package/lib/sync/autosync-scheduler.js +115 -76
  180. package/lib/sync/backup-files.js +3 -2
  181. package/lib/sync/backup-scheduler.js +6 -5
  182. package/lib/sync/blob-store.js +2 -1
  183. package/lib/sync/git/git-transport.d.ts +55 -0
  184. package/lib/sync/git/git-transport.js +152 -2
  185. package/lib/sync/github-auth.js +10 -9
  186. package/lib/sync/layout.js +13 -12
  187. package/lib/sync/messages.d.ts +63 -0
  188. package/lib/sync/messages.js +149 -0
  189. package/lib/sync/review-queue.js +6 -5
  190. package/lib/sync/s3/messages.d.ts +7 -0
  191. package/lib/sync/s3/messages.js +16 -0
  192. package/lib/sync/s3/s3-providers.d.ts +4 -1
  193. package/lib/sync/s3/s3-providers.js +5 -5
  194. package/lib/sync/s3/s3-transport.js +1 -1
  195. package/lib/sync/s3/sigv4.js +2 -1
  196. package/lib/sync/snapshot-crypto.js +6 -5
  197. package/lib/sync/snapshot-json.js +3 -2
  198. package/lib/sync/sync-engine.d.ts +58 -22
  199. package/lib/sync/sync-engine.js +119 -48
  200. package/lib/sync/sync-history.js +7 -6
  201. package/lib/sync/sync-selection.d.ts +15 -4
  202. package/lib/sync/sync-selection.js +17 -5
  203. package/lib/sync/ui-prefs.d.ts +22 -0
  204. package/lib/sync/ui-prefs.js +28 -0
  205. package/lib/sync/webdav/webdav-transport.d.ts +1 -0
  206. package/lib/sync/webdav/webdav-transport.js +4 -1
  207. package/lib/ui/artifact-view.js +4 -3
  208. package/lib/ui/commands.d.ts +1 -1
  209. package/lib/ui/commands.js +10 -0
  210. package/lib/ui/diff-view.d.ts +10 -0
  211. package/lib/ui/diff-view.js +17 -0
  212. package/lib/ui/disk-usage-view.d.ts +33 -4
  213. package/lib/ui/disk-usage-view.js +29 -1
  214. package/lib/ui/errors.d.ts +1 -1
  215. package/lib/ui/errors.js +4 -3
  216. package/lib/ui/export-flow.js +26 -20
  217. package/lib/ui/foreign-view.d.ts +5 -1
  218. package/lib/ui/foreign-view.js +26 -2
  219. package/lib/ui/i18n.d.ts +68 -1
  220. package/lib/ui/i18n.js +120 -2
  221. package/lib/ui/import-wizard.d.ts +7 -0
  222. package/lib/ui/import-wizard.js +13 -10
  223. package/lib/ui/progress.d.ts +2 -2
  224. package/lib/ui/progress.js +1 -2
  225. package/lib/ui/report.d.ts +5 -5
  226. package/lib/ui/report.js +6 -6
  227. package/lib/ui/rollback-view.d.ts +815 -0
  228. package/lib/ui/rollback-view.js +738 -0
  229. package/lib/ui/selection-model.d.ts +41 -0
  230. package/lib/ui/selection-model.js +30 -0
  231. package/lib/ui/session-inventory-view.d.ts +97 -3
  232. package/lib/ui/session-inventory-view.js +76 -2
  233. package/lib/ui/session-issue-labels.d.ts +43 -0
  234. package/lib/ui/session-issue-labels.js +48 -0
  235. package/lib/ui/sync-settings-view.d.ts +33 -0
  236. package/lib/ui/sync-settings-view.js +40 -0
  237. package/lib/ui/test-helpers.js +2 -1
  238. package/lib/ui/types.d.ts +8 -3
  239. package/lib/ui/types.js +1 -1
  240. package/lib/utils/atomic-write.js +6 -5
  241. package/lib/utils/bundle-scan.d.ts +11 -1
  242. package/lib/utils/bundle-scan.js +12 -1
  243. package/lib/utils/env-lock.d.ts +10 -0
  244. package/lib/utils/env-lock.js +73 -46
  245. package/lib/utils/json.js +6 -5
  246. package/lib/utils/messages-utils.d.ts +96 -0
  247. package/lib/utils/messages-utils.js +216 -0
  248. package/lib/utils/proxy.js +18 -5
  249. package/lib/utils/recursive-walk.d.ts +22 -0
  250. package/lib/utils/recursive-walk.js +75 -1
  251. package/lib/utils/session-log-repair.js +10 -9
  252. package/lib/utils/zip.js +27 -26
  253. package/package.json +12 -2
  254. package/src/adapters/agent-instructions.ts +1 -1
  255. package/src/adapters/file-collection.test.ts +1 -0
  256. package/src/adapters/file-collection.ts +1 -1
  257. package/src/adapters/files.test.ts +27 -0
  258. package/src/adapters/index.ts +6 -1
  259. package/src/adapters/link-report.test.ts +22 -2
  260. package/src/adapters/link-report.ts +29 -4
  261. package/src/adapters/messages.ts +61 -0
  262. package/src/adapters/plugin-data.test.ts +225 -0
  263. package/src/adapters/plugin-data.ts +351 -0
  264. package/src/adapters/plugin-files.ts +3 -2
  265. package/src/adapters/plugins.ts +4 -3
  266. package/src/adapters/prompts.ts +7 -1
  267. package/src/adapters/self.test.ts +26 -0
  268. package/src/adapters/self.ts +10 -3
  269. package/src/adapters/sessions.ts +4 -2
  270. package/src/adapters/settings.test.ts +38 -1
  271. package/src/adapters/test-helpers.ts +15 -0
  272. package/src/adapters/ui.test.ts +2 -2
  273. package/src/adapters/ui.ts +27 -26
  274. package/src/adapters/workspaces.ts +7 -3
  275. package/src/cli/actions.test.ts +28 -0
  276. package/src/cli/actions.ts +11 -2
  277. package/src/cli/help.ts +23 -10
  278. package/src/cli/index-guards.test.ts +193 -0
  279. package/src/cli/index.ts +106 -31
  280. package/src/cli/sessions-inspect.test.ts +15 -1
  281. package/src/cli/sessions-inspect.ts +3 -1
  282. package/src/cli/sessions-repair.test.ts +84 -0
  283. package/src/cli/sessions-repair.ts +33 -7
  284. package/src/cli/web/console-route-parity.test.ts +115 -0
  285. package/src/cli/web/page.ts +23 -12
  286. package/src/cli/web/routes.ts +2 -1
  287. package/src/cli/web/server.ts +4 -4
  288. package/src/cli/web/web.test.ts +2 -2
  289. package/src/client/ConfigManagerSection.tsx +30 -2
  290. package/src/client/about/AboutPanel.tsx +2 -2
  291. package/src/client/about/PluginUpdateDialog.tsx +1 -1
  292. package/src/client/about/ReleaseNotesDialog.tsx +2 -2
  293. package/src/client/about/about-view.test.ts +13 -3
  294. package/src/client/about/about-view.ts +10 -6
  295. package/src/client/about/release-notes-view.test.ts +14 -4
  296. package/src/client/about/release-notes-view.ts +4 -2
  297. package/src/client/api.ts +18 -6
  298. package/src/client/chat/ChatEntryView.tsx +411 -0
  299. package/src/client/chat/RollbackDiffTab.tsx +108 -0
  300. package/src/client/chat/RollbackFileIcon.tsx +96 -0
  301. package/src/client/chat/RollbackReviewDialog.tsx +607 -0
  302. package/src/client/chat/chat-entry-view.test.ts +1174 -0
  303. package/src/client/chat/chat-entry-view.ts +758 -0
  304. package/src/client/chat/chat-locales.ts +152 -0
  305. package/src/client/chat/chat-node-registration.ts +64 -0
  306. package/src/client/chat/chat-point-window.ts +183 -0
  307. package/src/client/chat/chat-slots.ts +107 -0
  308. package/src/client/chat/rollback-diff-tab.test.ts +105 -0
  309. package/src/client/chat/rollback-diff-tab.ts +174 -0
  310. package/src/client/chat/rollback-entry-view.test.ts +1469 -0
  311. package/src/client/chat/rollback-entry-view.ts +946 -0
  312. package/src/client/chat/sidebar-slots.ts +59 -0
  313. package/src/client/common/ConfirmDialog.tsx +7 -3
  314. package/src/client/common/ContentPicker.tsx +48 -10
  315. package/src/client/common/DiffTable.tsx +133 -0
  316. package/src/client/common/ErrorBanner.tsx +4 -4
  317. package/src/client/common/Icon.tsx +2 -0
  318. package/src/client/common/Modal.tsx +8 -1
  319. package/src/client/common/ProgressBar.tsx +8 -2
  320. package/src/client/common/ReportView.tsx +4 -4
  321. package/src/client/common/RunsCenter.tsx +1 -0
  322. package/src/client/common/client-route-parity.test.ts +84 -0
  323. package/src/client/common/info-hint-guard.test.ts +46 -13
  324. package/src/client/common/picker-lazy-units.test.ts +42 -0
  325. package/src/client/common/plan-text-redaction.test.ts +17 -6
  326. package/src/client/common/progress-view-edge.test.ts +8 -7
  327. package/src/client/common/progress-view.test.ts +9 -9
  328. package/src/client/common/progress-view.ts +2 -2
  329. package/src/client/common/routes.ts +38 -0
  330. package/src/client/common/section-labels.test.ts +1 -0
  331. package/src/client/common/section-labels.ts +2 -0
  332. package/src/client/common/ui.tsx +73 -10
  333. package/src/client/config-manager.module.css +1109 -31
  334. package/src/client/css-scale-guard.test.ts +30 -0
  335. package/src/client/environment/EnvironmentPanel.tsx +11 -4
  336. package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
  337. package/src/client/export/ExportView.tsx +13 -8
  338. package/src/client/history/HistoryPanel.tsx +4 -3
  339. package/src/client/history/history-api.ts +2 -2
  340. package/src/client/history/history-locales.ts +5 -1
  341. package/src/client/home/HomePanel.tsx +29 -8
  342. package/src/client/import/ForeignImportView.tsx +4 -5
  343. package/src/client/import/ImportWizardView.tsx +5 -3
  344. package/src/client/import/import-wizard-split.test.ts +3 -1
  345. package/src/client/import/import-wizard-steps.tsx +9 -9
  346. package/src/client/index.ts +232 -3
  347. package/src/client/library/LibraryActions.tsx +32 -7
  348. package/src/client/library/LibraryPanel.tsx +23 -3
  349. package/src/client/library/RestorePlanView.tsx +9 -66
  350. package/src/client/locales.ts +62 -9
  351. package/src/client/lucide-icons.d.ts +1 -0
  352. package/src/client/market/MarketPanel.tsx +13 -2
  353. package/src/client/market/MyConfigsView.tsx +5 -4
  354. package/src/client/market/MyConfigsWizard.tsx +10 -8
  355. package/src/client/market/market-api.test.ts +9 -8
  356. package/src/client/market/market-api.ts +2 -2
  357. package/src/client/market/market-locales.ts +3 -0
  358. package/src/client/market/market-view.test.ts +6 -6
  359. package/src/client/market/market-view.ts +3 -3
  360. package/src/client/market/my-configs-api.ts +2 -2
  361. package/src/client/market/my-configs-view.test.ts +23 -22
  362. package/src/client/market/my-configs-view.ts +8 -8
  363. package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
  364. package/src/client/recovery/RecoveryPanel.tsx +442 -283
  365. package/src/client/recovery/RollbackCard.tsx +859 -0
  366. package/src/client/recovery/RollbackDiffPane.tsx +69 -0
  367. package/src/client/recovery/RollbackFileChanges.tsx +163 -0
  368. package/src/client/recovery/incident-api.ts +2 -2
  369. package/src/client/recovery/recovery-api.ts +144 -6
  370. package/src/client/recovery/recovery-locales.test.ts +299 -0
  371. package/src/client/recovery/recovery-locales.ts +520 -149
  372. package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
  373. package/src/client/recovery/rollback-enabled.ts +41 -0
  374. package/src/client/recovery/rollback-file-changes.test.ts +114 -0
  375. package/src/client/recovery/session-dialog-mount.test.ts +54 -0
  376. package/src/client/run-store.ts +84 -21
  377. package/src/client/sync/AutosyncCard.tsx +25 -1
  378. package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
  379. package/src/client/sync/ChannelConfigDialog.tsx +10 -10
  380. package/src/client/sync/DecryptPasswordCard.tsx +2 -2
  381. package/src/client/sync/SecurityOptionsCard.tsx +27 -17
  382. package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
  383. package/src/client/sync/SyncConfirmView.tsx +46 -8
  384. package/src/client/sync/SyncLogList.tsx +1 -1
  385. package/src/client/sync/SyncPanel.tsx +31 -23
  386. package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
  387. package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
  388. package/src/client/sync/SyncSettingsView.test.ts +20 -1
  389. package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
  390. package/src/client/sync/WorkDirCard.tsx +112 -0
  391. package/src/client/sync/history-model.test.ts +42 -9
  392. package/src/client/sync/history-model.ts +30 -15
  393. package/src/client/sync/sync-api.test.ts +57 -33
  394. package/src/client/sync/sync-api.ts +80 -6
  395. package/src/client/sync/sync-locales.ts +104 -3
  396. package/src/client/sync/sync-view-v2.test.ts +65 -13
  397. package/src/client/sync/sync-view.test.ts +107 -52
  398. package/src/client/sync/sync-view.ts +124 -41
  399. package/src/core/analyzer.ts +31 -9
  400. package/src/core/backup-plan.test.ts +48 -5
  401. package/src/core/backup-plan.ts +51 -20
  402. package/src/core/backup-verify.ts +24 -23
  403. package/src/core/backup.test.ts +76 -0
  404. package/src/core/backup.ts +105 -29
  405. package/src/core/boot-rescue.ts +4 -3
  406. package/src/core/boot-safety.ts +2 -1
  407. package/src/core/command-rollback.test.ts +536 -0
  408. package/src/core/command-rollback.ts +406 -0
  409. package/src/core/consult-source.ts +7 -6
  410. package/src/core/disk-usage.test.ts +257 -1
  411. package/src/core/disk-usage.ts +134 -14
  412. package/src/core/engine-locale.test.ts +593 -0
  413. package/src/core/engine-locale.ts +175 -0
  414. package/src/core/exporter.ts +3 -3
  415. package/src/core/fork-reconcile.test.ts +279 -0
  416. package/src/core/fork-reconcile.ts +234 -0
  417. package/src/core/journal.ts +6 -5
  418. package/src/core/local-plugin-host.ts +3 -2
  419. package/src/core/local-plugin-pack.ts +11 -6
  420. package/src/core/messages-core.ts +661 -0
  421. package/src/core/messages.ts +125 -17
  422. package/src/core/migration-consult.ts +21 -20
  423. package/src/core/migration-history.ts +19 -5
  424. package/src/core/model-tools.ts +28 -27
  425. package/src/core/phase3-host.ts +21 -5
  426. package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
  427. package/src/core/plugin-cli.ts +18 -19
  428. package/src/core/plugin-data-registry.test.ts +113 -0
  429. package/src/core/plugin-data-registry.ts +261 -0
  430. package/src/core/reconcile.test.ts +117 -0
  431. package/src/core/reconcile.ts +85 -17
  432. package/src/core/recovery-orchestrator.ts +19 -18
  433. package/src/core/reinstall.ts +26 -21
  434. package/src/core/restore.ts +20 -17
  435. package/src/core/rollback-file-diff.test.ts +159 -0
  436. package/src/core/rollback-gate.test.ts +66 -0
  437. package/src/core/rollback-gate.ts +61 -0
  438. package/src/core/rollback-ignore-rules.ts +126 -0
  439. package/src/core/rollback-ledger.test.ts +655 -0
  440. package/src/core/rollback-ledger.ts +1314 -0
  441. package/src/core/rollback-point.test.ts +2853 -0
  442. package/src/core/rollback-point.ts +4340 -0
  443. package/src/core/rollback.ts +5 -3
  444. package/src/core/run-registry.ts +6 -5
  445. package/src/core/session-health.ts +15 -0
  446. package/src/core/session-meta.test.ts +117 -1
  447. package/src/core/session-meta.ts +149 -2
  448. package/src/core/session-title.test.ts +127 -0
  449. package/src/core/session-title.ts +189 -0
  450. package/src/core/startup-barrier.test.ts +69 -0
  451. package/src/core/startup-barrier.ts +27 -0
  452. package/src/core/transaction-coordinator.ts +4 -3
  453. package/src/core/types.ts +37 -1
  454. package/src/core/validator.ts +5 -4
  455. package/src/core/verify-recovery.ts +31 -30
  456. package/src/foreign/bundle.ts +3 -2
  457. package/src/foreign/continue.ts +2 -1
  458. package/src/foreign/crush.ts +2 -1
  459. package/src/foreign/file-budget.test.ts +2 -0
  460. package/src/foreign/gemini.ts +2 -1
  461. package/src/foreign/goose.ts +2 -1
  462. package/src/foreign/kilocode.ts +2 -1
  463. package/src/foreign/kimi.ts +2 -1
  464. package/src/foreign/messages.ts +44 -0
  465. package/src/foreign/mimocode.ts +2 -1
  466. package/src/foreign/openclaw.ts +2 -1
  467. package/src/foreign/opencode.ts +2 -1
  468. package/src/foreign/pi.ts +2 -1
  469. package/src/foreign/qoder.ts +2 -1
  470. package/src/foreign/qwen.ts +2 -1
  471. package/src/foreign/registry.test.ts +1 -1
  472. package/src/foreign/registry.ts +8 -7
  473. package/src/foreign/teleagent.ts +2 -1
  474. package/src/foreign/workbuddy.ts +2 -1
  475. package/src/foreign/zcode.ts +2 -1
  476. package/src/foreign/zed.ts +2 -1
  477. package/src/index.facade.test.ts +340 -1
  478. package/src/index.ts +876 -57
  479. package/src/market/git-file-writer.ts +20 -19
  480. package/src/market/github-repos.test.ts +23 -0
  481. package/src/market/github-repos.ts +37 -19
  482. package/src/market/index-parser.ts +31 -30
  483. package/src/market/messages-market.ts +325 -0
  484. package/src/market/my-repo.ts +32 -29
  485. package/src/market/prepare.ts +18 -15
  486. package/src/market/security.ts +20 -19
  487. package/src/market/types.ts +2 -1
  488. package/src/market/url.ts +2 -1
  489. package/src/migrations/index.ts +6 -5
  490. package/src/migrations/messages-migrations.ts +59 -0
  491. package/src/profiles/dsh-profile-launcher.ts +2 -1
  492. package/src/profiles/dsh-profile-runtime.ts +2 -1
  493. package/src/profiles/messages.ts +31 -0
  494. package/src/routes/backup.ts +18 -2
  495. package/src/routes/import.ts +3 -3
  496. package/src/routes/index.ts +1 -1
  497. package/src/routes/kit.ts +9 -3
  498. package/src/routes/market.ts +1 -1
  499. package/src/routes/messages.ts +106 -0
  500. package/src/routes/prefs.ts +31 -2
  501. package/src/routes/profiles.ts +12 -6
  502. package/src/routes/recovery.test.ts +405 -1
  503. package/src/routes/recovery.ts +477 -94
  504. package/src/routes/snapshots.ts +51 -4
  505. package/src/routes/sync.ts +82 -17
  506. package/src/schema/config.ts +53 -20
  507. package/src/schema/index.ts +7 -5
  508. package/src/schema/manifest-schema.test.ts +4 -4
  509. package/src/schema/manifest.ts +19 -18
  510. package/src/schema/messages.ts +120 -0
  511. package/src/schema/registry.test.ts +28 -7
  512. package/src/schema/section-registry.ts +96 -42
  513. package/src/schema/types.ts +2 -1
  514. package/src/schema/versions.ts +6 -5
  515. package/src/security/encryption.ts +16 -15
  516. package/src/security/integrity.ts +8 -7
  517. package/src/security/messages.ts +87 -0
  518. package/src/security/redaction.test.ts +17 -1
  519. package/src/security/redaction.ts +13 -0
  520. package/src/security/secret-scanner.ts +19 -12
  521. package/src/security/security.test.ts +47 -0
  522. package/src/security/vault.ts +2 -1
  523. package/src/security/zip-security.ts +4 -3
  524. package/src/sync/ancestor.ts +2 -1
  525. package/src/sync/autosync-config.ts +21 -0
  526. package/src/sync/autosync-scheduler.test.ts +63 -7
  527. package/src/sync/autosync-scheduler.ts +118 -78
  528. package/src/sync/backup-files.ts +3 -2
  529. package/src/sync/backup-scheduler.ts +6 -5
  530. package/src/sync/blob-store.ts +2 -1
  531. package/src/sync/git/git-transport.test.ts +170 -8
  532. package/src/sync/git/git-transport.ts +176 -2
  533. package/src/sync/github-auth.ts +10 -9
  534. package/src/sync/layout.ts +13 -12
  535. package/src/sync/messages.ts +153 -0
  536. package/src/sync/review-queue.ts +6 -5
  537. package/src/sync/s3/messages.ts +16 -0
  538. package/src/sync/s3/s3-providers.ts +9 -6
  539. package/src/sync/s3/s3-transport.ts +1 -1
  540. package/src/sync/s3/sigv4.ts +3 -1
  541. package/src/sync/snapshot-crypto.ts +6 -5
  542. package/src/sync/snapshot-json.ts +3 -2
  543. package/src/sync/sync-credentials.test.ts +119 -0
  544. package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
  545. package/src/sync/sync-engine.test.ts +226 -8
  546. package/src/sync/sync-engine.ts +154 -52
  547. package/src/sync/sync-history.ts +7 -6
  548. package/src/sync/sync-selection.test.ts +12 -2
  549. package/src/sync/sync-selection.ts +18 -5
  550. package/src/sync/transport.test.ts +1 -0
  551. package/src/sync/ui-prefs.test.ts +44 -1
  552. package/src/sync/ui-prefs.ts +39 -0
  553. package/src/sync/webdav/webdav-transport.ts +4 -1
  554. package/src/ui/artifact-view.ts +4 -3
  555. package/src/ui/commands.test.ts +1 -0
  556. package/src/ui/commands.ts +11 -1
  557. package/src/ui/diff-view.test.ts +9 -1
  558. package/src/ui/diff-view.ts +22 -0
  559. package/src/ui/disk-usage-view.test.ts +113 -6
  560. package/src/ui/disk-usage-view.ts +63 -7
  561. package/src/ui/errors.ts +4 -3
  562. package/src/ui/export-flow.ts +26 -20
  563. package/src/ui/foreign-view.test.ts +43 -0
  564. package/src/ui/foreign-view.ts +30 -3
  565. package/src/ui/i18n.ts +131 -2
  566. package/src/ui/import-wizard.ts +18 -10
  567. package/src/ui/progress.ts +2 -2
  568. package/src/ui/report.ts +7 -6
  569. package/src/ui/rollback-view.test.ts +836 -0
  570. package/src/ui/rollback-view.ts +1405 -0
  571. package/src/ui/selection-model.test.ts +33 -1
  572. package/src/ui/selection-model.ts +53 -0
  573. package/src/ui/session-inventory-view.test.ts +109 -0
  574. package/src/ui/session-inventory-view.ts +138 -3
  575. package/src/ui/session-issue-labels.test.ts +51 -0
  576. package/src/ui/session-issue-labels.ts +70 -0
  577. package/src/ui/sync-settings-view.test.ts +36 -0
  578. package/src/ui/sync-settings-view.ts +49 -0
  579. package/src/ui/test-helpers.ts +2 -1
  580. package/src/ui/types.ts +10 -4
  581. package/src/utils/atomic-write.ts +6 -5
  582. package/src/utils/bundle-scan.test.ts +2 -2
  583. package/src/utils/bundle-scan.ts +12 -1
  584. package/src/utils/env-lock.ts +83 -49
  585. package/src/utils/json.ts +7 -5
  586. package/src/utils/messages-utils.ts +232 -0
  587. package/src/utils/proxy.test.ts +25 -0
  588. package/src/utils/proxy.ts +19 -5
  589. package/src/utils/recursive-walk.test.ts +43 -0
  590. package/src/utils/recursive-walk.ts +75 -1
  591. package/src/utils/session-log-repair.ts +10 -9
  592. package/src/utils/zip.ts +27 -26
  593. package/lib/core/checkpoint.d.ts +0 -362
  594. package/lib/core/checkpoint.js +0 -1309
  595. package/src/core/checkpoint.test.ts +0 -665
  596. package/src/core/checkpoint.ts +0 -1614
@@ -7,6 +7,7 @@ import assert from 'node:assert/strict';
7
7
 
8
8
  import { ConfigManagerApiError } from '../api.ts';
9
9
  import { SYNC_API, SYNC_WEBDAV_CREDENTIAL_REF, SyncApi } from './sync-api.ts';
10
+ import { zhUiT } from '../../ui/i18n.ts';
10
11
 
11
12
  interface FetchCall {
12
13
  url: string;
@@ -57,7 +58,7 @@ test('S-01 api.status():GET 到 /sync/status,解析配置/凭据/上次同
57
58
  // 经读取函数解除闭包赋值导致的 CFA 窄化(called 在回调内赋值,外部保持 null 窄化)
58
59
  const lastCall = (): FetchCall | null => called;
59
60
 
60
- const api = new SyncApi();
61
+ const api = new SyncApi(zhUiT);
61
62
  const result = await api.status();
62
63
  assert.equal(result.configured, true);
63
64
  assert.equal(result.repoUrl, 'https://github.com/u/r.git');
@@ -73,7 +74,7 @@ test('S-02 api.push():POST /sync/push,请求体携带 repoUrl/token', async
73
74
  return jsonResponse(200, { ok: true, snapshotId: 'sync-1', sections: ['settings'], warnings: [] });
74
75
  });
75
76
 
76
- const api = new SyncApi();
77
+ const api = new SyncApi(zhUiT);
77
78
  const result = await api.push({ repoUrl: 'https://github.com/u/r.git', token: 'ghp_secret' });
78
79
  assert.equal(result.ok, true);
79
80
  assert.equal(result.snapshotId, 'sync-1');
@@ -97,7 +98,7 @@ test('S-03 api.pull():POST /sync/pull,strategy 缺省透传 merge,响应
97
98
  });
98
99
  });
99
100
 
100
- const api = new SyncApi();
101
+ const api = new SyncApi(zhUiT);
101
102
  const result = await api.pull({ repoUrl: 'https://github.com/u/r.git', strategy: 'merge' });
102
103
  assert.equal(result.changes.length, 1);
103
104
  assert.equal(result.changes[0]?.kind, 'Conflict');
@@ -109,7 +110,7 @@ test('S-03 api.pull():POST /sync/pull,strategy 缺省透传 merge,响应
109
110
 
110
111
  test('S-04 错误映射:4xx 携带 error → ConfigManagerApiError', async () => {
111
112
  installFetchMock(() => jsonResponse(400, { error: 'repoUrl is required' }));
112
- const api = new SyncApi();
113
+ const api = new SyncApi(zhUiT);
113
114
  await assert.rejects(api.push({ repoUrl: '' }), (err: unknown) => {
114
115
  assert.ok(err instanceof ConfigManagerApiError);
115
116
  assert.match(err.message, /repoUrl is required/);
@@ -120,7 +121,7 @@ test('S-04 错误映射:4xx 携带 error → ConfigManagerApiError', async ()
120
121
  test('S-05 服务未挂载:404(非 JSON 体)→ ConfigManagerApiError 提示插件未加载', async () => {
121
122
  // 路由不存在时宿主返回 404 但无 JSON error → readJson 兜底为「插件未挂载」提示
122
123
  installFetchMock(() => new Response('Not Found', { status: 404 }));
123
- const api = new SyncApi();
124
+ const api = new SyncApi(zhUiT);
124
125
  await assert.rejects(api.status(), (err: unknown) => {
125
126
  assert.ok(err instanceof ConfigManagerApiError);
126
127
  assert.match(err.message, /未挂载/);
@@ -141,7 +142,7 @@ test('S-06 api.githubStart():POST /sync/github/start,解析 flowId/userCode/
141
142
  return jsonResponse(200, body);
142
143
  });
143
144
 
144
- const api = new SyncApi();
145
+ const api = new SyncApi(zhUiT);
145
146
  const result = await api.githubStart();
146
147
  assert.equal(result.flowId, 'flow-1');
147
148
  assert.equal(result.userCode, 'ABCD-EFGH');
@@ -157,7 +158,7 @@ test('S-07 api.githubPoll():POST /sync/github/poll 携带 flowId;pending 透
157
158
  return jsonResponse(200, { status: 'pending', pollDelayMs: 5000 });
158
159
  });
159
160
 
160
- const api = new SyncApi();
161
+ const api = new SyncApi(zhUiT);
161
162
  const result = await api.githubPoll('flow-1');
162
163
  assert.equal(result.status, 'pending');
163
164
  assert.equal(result.pollDelayMs, 5000);
@@ -168,7 +169,7 @@ test('S-07 api.githubPoll():POST /sync/github/poll 携带 flowId;pending 透
168
169
 
169
170
  test('S-08 api.githubPoll() 成功 → 响应只含状态,token 永不回传', async () => {
170
171
  installFetchMock(() => jsonResponse(200, { status: 'success', credentialConfigured: true }));
171
- const api = new SyncApi();
172
+ const api = new SyncApi(zhUiT);
172
173
  const result = await api.githubPoll('flow-1');
173
174
  assert.equal(result.status, 'success');
174
175
  assert.equal(result.credentialConfigured, true);
@@ -182,7 +183,7 @@ test('S-09 api.githubCancel():POST /sync/github/cancel 携带 flowId', async (
182
183
  calls.push(call);
183
184
  return jsonResponse(200, { ok: true });
184
185
  });
185
- const api = new SyncApi();
186
+ const api = new SyncApi(zhUiT);
186
187
  const result = await api.githubCancel('flow-1');
187
188
  assert.equal(result.ok, true);
188
189
  assert.equal(calls[0]?.url, SYNC_API.githubCancel);
@@ -206,7 +207,7 @@ test('S-10 api.snapshotsList():POST /sync/snapshots-list,解析倒序快照
206
207
  calls.push(call);
207
208
  return jsonResponse(200, body);
208
209
  });
209
- const api = new SyncApi();
210
+ const api = new SyncApi(zhUiT);
210
211
  const result = await api.snapshotsList({ repoUrl: 'https://github.com/u/r.git' });
211
212
  assert.equal(result.ok, true);
212
213
  assert.equal(result.snapshots.length, 2);
@@ -230,7 +231,7 @@ test('S-11 api.sync():POST /sync/sync,请求体携带 snapshotId;响应含
230
231
  calls.push(call);
231
232
  return jsonResponse(200, body);
232
233
  });
233
- const api = new SyncApi();
234
+ const api = new SyncApi(zhUiT);
234
235
  const result = await api.sync({ repoUrl: 'https://github.com/u/r.git', snapshotId: 'sync-3' });
235
236
  assert.equal(result.syncSessionId, 'sess-1');
236
237
  assert.equal(result.items.length, 1);
@@ -250,7 +251,7 @@ test('S-12 api.applyItems():POST /sync/apply-items,携带 adoptions(含 Co
250
251
  calls.push(call);
251
252
  return jsonResponse(200, body);
252
253
  });
253
- const api = new SyncApi();
254
+ const api = new SyncApi(zhUiT);
254
255
  const result = await api.applyItems({
255
256
  syncSessionId: 'sess-1',
256
257
  adoptions: [
@@ -273,7 +274,7 @@ test('S-13 api.cancel():POST /sync/cancel 携带 syncSessionId', async () => {
273
274
  calls.push(call);
274
275
  return jsonResponse(200, { ok: true });
275
276
  });
276
- const api = new SyncApi();
277
+ const api = new SyncApi(zhUiT);
277
278
  const result = await api.cancel('sess-1');
278
279
  assert.equal(result.ok, true);
279
280
  assert.equal(calls[0]?.url, SYNC_API.cancel);
@@ -297,7 +298,7 @@ test('S-14 api.autosyncStatus():GET /sync/autosync 返回 { git, webdav },
297
298
  return jsonResponse(200, body);
298
299
  });
299
300
  const lastCall = (): FetchCall | null => called;
300
- const api = new SyncApi();
301
+ const api = new SyncApi(zhUiT);
301
302
  const result = await api.autosyncStatus('git');
302
303
  assert.equal(result.enabled, true);
303
304
  assert.equal(result.interval, '30m');
@@ -319,7 +320,7 @@ test('S-15 api.autosyncUpdate():POST /sync/autosync,请求体携带 transpor
319
320
  calls.push(call);
320
321
  return jsonResponse(200, body);
321
322
  });
322
- const api = new SyncApi();
323
+ const api = new SyncApi(zhUiT);
323
324
  const result = await api.autosyncUpdate({ transport: 'webdav', enabled: true, interval: '60m' });
324
325
  assert.equal(result.enabled, true);
325
326
  assert.equal(result.interval, '60m');
@@ -345,7 +346,7 @@ test('S-16 api.history():GET /sync/history,解析 { entries }(含 autosync
345
346
  ],
346
347
  };
347
348
  installFetchMock(() => jsonResponse(200, body));
348
- const api = new SyncApi();
349
+ const api = new SyncApi(zhUiT);
349
350
  const result = await api.history();
350
351
  assert.equal(result.entries.length, 2);
351
352
  assert.equal(result.entries[0]?.kind, 'apply');
@@ -374,7 +375,7 @@ test('S-18 api.status():webdav 通道 → 解析 webdav 配置状态(url/use
374
375
  return jsonResponse(200, body);
375
376
  });
376
377
  const lastCall = (): FetchCall | null => called;
377
- const api = new SyncApi();
378
+ const api = new SyncApi(zhUiT);
378
379
  const result = await api.status();
379
380
  assert.equal(result.webdav?.url, 'https://dav.example.com/dav/config');
380
381
  assert.equal(result.webdav?.username, 'alice', 'status 可回传 username 值(非敏感,供表单回填)');
@@ -390,7 +391,7 @@ test('S-19 api.push():transport=webdav → 请求体携带顶层扁平 url/use
390
391
  calls.push(call);
391
392
  return jsonResponse(200, { ok: true, snapshotId: 'sync-w1', sections: [], warnings: [] });
392
393
  });
393
- const api = new SyncApi();
394
+ const api = new SyncApi(zhUiT);
394
395
  const result = await api.push({
395
396
  transport: 'webdav',
396
397
  url: 'https://dav.example.com/dav/config', username: 'alice', password: 'secret-pass',
@@ -413,7 +414,7 @@ test('S-20 api.pull():transport=webdav 请求体透传扁平 webdav 配置', a
413
414
  calls.push(call);
414
415
  return jsonResponse(200, { ok: true, snapshotId: 'sync-w1', changes: [], needsReview: false });
415
416
  });
416
- const api = new SyncApi();
417
+ const api = new SyncApi(zhUiT);
417
418
  await api.pull({ transport: 'webdav', url: 'https://dav.example.com/dav/config', username: 'alice', password: 'secret-pass' });
418
419
  assert.equal(calls[0]?.url, SYNC_API.pull);
419
420
  const sent = JSON.parse(String(calls[0]?.init?.body ?? '{}')) as Record<string, unknown>;
@@ -428,7 +429,7 @@ test('S-21 git 通道缺省:不带 transport → 请求体仍只含 git 字段
428
429
  calls.push(call);
429
430
  return jsonResponse(200, { ok: true, snapshotId: 'sync-1', sections: [], warnings: [] });
430
431
  });
431
- const api = new SyncApi();
432
+ const api = new SyncApi(zhUiT);
432
433
  await api.push({ repoUrl: 'https://github.com/u/r.git', token: 't' });
433
434
  const sent = JSON.parse(String(calls[0]?.init?.body ?? '{}')) as Record<string, unknown>;
434
435
  assert.equal(sent['transport'], undefined, '缺省不声明 transport(Host 视为 git)');
@@ -442,7 +443,7 @@ test('S-22 api.push():高级(自定义导出)模式 → 请求体携带 se
442
443
  calls.push(call);
443
444
  return jsonResponse(200, { ok: true, snapshotId: 'sync-sel', sections: ['settings'], warnings: [] });
444
445
  });
445
- const api = new SyncApi();
446
+ const api = new SyncApi(zhUiT);
446
447
  // 高级模式:传勾选分区
447
448
  await api.push({ repoUrl: 'https://github.com/u/r.git', sections: ['settings', 'skills'] });
448
449
  assert.equal(calls.length, 1);
@@ -474,7 +475,7 @@ test('S-23 api.status():解析 syncSections(可同步分区目录,无 secr
474
475
  return jsonResponse(200, body);
475
476
  });
476
477
  const lastCall = (): FetchCall | null => called;
477
- const api = new SyncApi();
478
+ const api = new SyncApi(zhUiT);
478
479
  const result = await api.status();
479
480
  assert.equal(result.syncSections?.length, 2);
480
481
  assert.equal(result.syncSections?.[0]?.id, 'settings');
@@ -493,7 +494,7 @@ test('S-24 api.status():解析 syncSelection(持久化分区选择,UI 回
493
494
  return jsonResponse(200, body);
494
495
  });
495
496
  const lastCall = (): FetchCall | null => called;
496
- const api = new SyncApi();
497
+ const api = new SyncApi(zhUiT);
497
498
  const result = await api.status();
498
499
  assert.equal(result.syncSelection?.mode, 'advanced');
499
500
  assert.deepEqual(result.syncSelection?.sections, ['settings', 'skills']);
@@ -506,7 +507,7 @@ test('S-25 api.saveSelection():POST /sync/selection 携带 transport + mode +
506
507
  calls.push(call);
507
508
  return jsonResponse(200, { ok: true, transport: 'webdav', mode: 'advanced', sections: ['settings', 'skills'] });
508
509
  });
509
- const api = new SyncApi();
510
+ const api = new SyncApi(zhUiT);
510
511
  const result = await api.saveSelection({ transport: 'webdav', mode: 'advanced', sections: ['settings', 'skills'] });
511
512
  assert.equal(result.mode, 'advanced');
512
513
  assert.equal(calls.length, 1);
@@ -524,7 +525,7 @@ test('S-26 api.push():加密快照 → 请求体携带 encrypt/encryptPassword
524
525
  calls.push(call);
525
526
  return jsonResponse(200, { ok: true, snapshotId: 'sync-enc', sections: ['settings'], warnings: [] });
526
527
  });
527
- const api = new SyncApi();
528
+ const api = new SyncApi(zhUiT);
528
529
  await api.push({
529
530
  repoUrl: 'https://github.com/u/r.git',
530
531
  encrypt: true,
@@ -544,7 +545,7 @@ test('S-27 api.pull():拉取加密快照 → 请求体携带 decryptPassword
544
545
  calls.push(call);
545
546
  return jsonResponse(200, { ok: true, snapshotId: 'sync-enc', changes: [], needsReview: false });
546
547
  });
547
- const api = new SyncApi();
548
+ const api = new SyncApi(zhUiT);
548
549
  await api.pull({ repoUrl: 'https://github.com/u/r.git', decryptPassword: 'pw-12345678' });
549
550
  assert.equal(calls[0]?.url, SYNC_API.pull);
550
551
  const sent = JSON.parse(String(calls[0]?.init?.body ?? '{}')) as Record<string, unknown>;
@@ -557,7 +558,7 @@ test('S-28 api.status():syncSelection 含 encrypt/includeSecrets 开关(UI
557
558
  syncSelection: { mode: 'advanced', sections: ['settings'], encrypt: true, includeSecrets: true },
558
559
  };
559
560
  installFetchMock(() => jsonResponse(200, body));
560
- const api = new SyncApi();
561
+ const api = new SyncApi(zhUiT);
561
562
  const result = await api.status();
562
563
  assert.equal(result.syncSelection?.encrypt, true);
563
564
  assert.equal(result.syncSelection?.includeSecrets, true);
@@ -576,7 +577,7 @@ test('S-28b api.status():syncSelectionByChannel / autosyncByChannel 按通道
576
577
  },
577
578
  };
578
579
  installFetchMock(() => jsonResponse(200, body));
579
- const api = new SyncApi();
580
+ const api = new SyncApi(zhUiT);
580
581
  const result = await api.status();
581
582
  assert.equal(result.syncSelectionByChannel?.git.mode, 'default', 'git 通道选择独立');
582
583
  assert.equal(result.syncSelectionByChannel?.webdav.mode, 'advanced');
@@ -592,7 +593,7 @@ test('S-29 api.saveUiPrefs():POST /sync/ui-prefs 携带 lastSyncChannel(磁
592
593
  calls.push(call);
593
594
  return jsonResponse(200, { ok: true, lastSyncChannel: 'webdav' });
594
595
  });
595
- const api = new SyncApi();
596
+ const api = new SyncApi(zhUiT);
596
597
  const result = await api.saveUiPrefs({ lastSyncChannel: 'webdav' });
597
598
  assert.equal(result.lastSyncChannel, 'webdav');
598
599
  assert.equal(calls.length, 1);
@@ -608,7 +609,7 @@ test('S-30 api.status():lastSyncChannel 回填(磁盘 ui-prefs;UI 通道
608
609
  lastSyncChannel: 'webdav',
609
610
  };
610
611
  installFetchMock(() => jsonResponse(200, body));
611
- const api = new SyncApi();
612
+ const api = new SyncApi(zhUiT);
612
613
  const result = await api.status();
613
614
  assert.equal(result.lastSyncChannel, 'webdav');
614
615
  });
@@ -619,7 +620,7 @@ test('S-31 api.githubListRepositories():GET 列举仓库(不设 method,只
619
620
  calls.push(call)
620
621
  return jsonResponse(200, { ok: true, repos: [REPO_SUMMARY] })
621
622
  })
622
- const api = new SyncApi()
623
+ const api = new SyncApi(zhUiT)
623
624
  const result = await api.githubListRepositories()
624
625
  assert.equal(calls.length, 1)
625
626
  assert.equal(calls[0]?.url, SYNC_API.githubRepositories)
@@ -634,7 +635,7 @@ test('S-32 api.githubCreateRepository():POST 新建仓库,请求体不含 pr
634
635
  calls.push(call)
635
636
  return jsonResponse(200, { ok: true, repo: REPO_SUMMARY })
636
637
  })
637
- const api = new SyncApi()
638
+ const api = new SyncApi(zhUiT)
638
639
  const result = await api.githubCreateRepository(' dsh-configs ', ' 我的配置 ')
639
640
  assert.equal(calls[0]?.url, SYNC_API.githubRepositories)
640
641
  assert.equal(calls[0]?.init?.method, 'POST')
@@ -651,8 +652,31 @@ test('S-33 api.githubCreateRepository():描述为空时不发送 description
651
652
  calls.push(call)
652
653
  return jsonResponse(200, { ok: true, repo: REPO_SUMMARY })
653
654
  })
654
- const api = new SyncApi()
655
+ const api = new SyncApi(zhUiT)
655
656
  await api.githubCreateRepository('dsh-configs', ' ')
656
657
  const sent = JSON.parse(String(calls[0]?.init?.body ?? '{}')) as Record<string, unknown>
657
658
  assert.ok(!('description' in sent), '空描述不占字段(与 GitHub 默认描述口径一致)')
658
659
  });
660
+
661
+ test('S-34 api.rollback():POST /sync/rollback 透传 full=false 与 failed(未回滚项 + 人工提示)', async () => {
662
+ const body = {
663
+ ok: true,
664
+ full: false,
665
+ failed: [{ item: 'credentialsStatus:DEEPSEEK_API_KEY', reason: 'DSH 不回读凭据值,无法自动恢复', manualHint: '请重新补录该凭据' }],
666
+ }
667
+ const calls: FetchCall[] = []
668
+ installFetchMock((call) => {
669
+ calls.push(call)
670
+ return jsonResponse(200, body)
671
+ })
672
+ const api = new SyncApi(zhUiT)
673
+ const result = await api.rollback({ restoreId: 'rest-9' })
674
+ assert.equal(result.full, false)
675
+ assert.equal(result.failed?.length, 1, '未回滚项必须透传(旧实现只回 ok/full,界面据此误报「已回滚」)')
676
+ assert.equal(result.failed?.[0]?.item, 'credentialsStatus:DEEPSEEK_API_KEY')
677
+ assert.equal(result.failed?.[0]?.manualHint, '请重新补录该凭据')
678
+ assert.equal(calls[0]?.url, SYNC_API.rollback)
679
+ assert.equal(calls[0]?.init?.method, 'POST')
680
+ const sent = JSON.parse(String(calls[0]?.init?.body ?? '{}')) as Record<string, unknown>
681
+ assert.equal(sent['restoreId'], 'rest-9')
682
+ });
@@ -37,7 +37,7 @@ import type { ConsultReport } from '../../core/migration-consult.ts';
37
37
  import { getJson, LONG_REQUEST_TIMEOUT_MS, postJson, type RequestOptions } from '../common/http.ts';
38
38
  import { CONFIG_MANAGER_API, SYNC_API } from '../common/routes.ts';
39
39
  import type { ExportPreviewResponse } from '../api.ts';
40
- import { zhUiT, type UiT } from '../../ui/i18n.ts';
40
+ import type { UiT } from '../../ui/i18n.ts';
41
41
  import { repoCreateBody } from '../../ui/sync-repository-picker.ts';
42
42
 
43
43
  /** 同步端点常量:**唯一来源** = `common/routes.ts`(W4 单点化),此处重导出保持既有导入面。 */
@@ -97,6 +97,30 @@ export interface SyncStatusResponse {
97
97
  autosync?: AutosyncStatusResponse;
98
98
  /** 全部通道的自动同步状态(git/webdav 各自独立;UI 按当前 tab 取对应通道) */
99
99
  autosyncByChannel?: Record<SyncTransportType, AutosyncStatusResponse>;
100
+ /**
101
+ * issue #79:git 工作副本与配置地址的关系(host 只读自检)。
102
+ * - `{ facts }` = 自检成功(origin 是否已跟随 repoUrl)
103
+ * - `{ error }` = 自检读不出来(git 不可用 / 工作副本异常)——**如实回传**,不伪装成「不存在」
104
+ */
105
+ workDir?: { facts: SyncWorkDirFacts } | { error: string };
106
+ }
107
+
108
+ /** git 工作副本自检事实(与宿主 GitWorkDirFacts 同形;无任何敏感值)。 */
109
+ export interface SyncWorkDirFacts {
110
+ /** 工作副本路径 */
111
+ path: string;
112
+ /** `.git` 是否存在 */
113
+ exists: boolean;
114
+ /** 当前 origin(读不出 → null) */
115
+ origin: string | null;
116
+ /** 单独配置的 push URL(未单独配置 → null) */
117
+ pushUrl: string | null;
118
+ /** fetch / push 地址是否都已跟随 repoUrl */
119
+ match: boolean;
120
+ /** 未上行的本地提交数(仅 detail 自检时给出,否则 null) */
121
+ ahead: number | null;
122
+ /** 工作树是否有未提交改动(仅 detail 自检时给出,否则 null) */
123
+ dirty: boolean | null;
100
124
  }
101
125
 
102
126
  /** 同步密码的保存状态(只回布尔;密码值永不回传浏览器)。 */
@@ -384,9 +408,39 @@ export interface ApplyItemsResponse {
384
408
  * 旧宿主不返回 → undefined(按「无未生效项」处理,不误报)。
385
409
  */
386
410
  ineffective?: { itemId: string; adapter: string; message?: string }[];
411
+ /**
412
+ * 「整体回滚是否完整」(诚实性,2026-10)。`rolledBack=true` 只说「触发了整体回滚」,
413
+ * 不代表每一项都补偿成功(典型:原本已存在的凭据值 DSH 不可回读 → 无法自动恢复)。
414
+ * 此时引擎把失败项并入 `warnings`,并置 `rollbackFull=false`;界面据此**不得**
415
+ * 显示成「已整体回滚」,必须列出未恢复项。
416
+ * 旧宿主不返回 → undefined(按旧的「已整体回滚」文案处理,不误报)。
417
+ */
418
+ rollbackFull?: boolean;
387
419
  result: unknown;
388
420
  }
389
421
 
422
+ /** POST /sync/rollback 响应:一键回滚的诚实结果(full=false 时 failed 列出未回滚项)。 */
423
+ export interface SyncRollbackResponse {
424
+ ok: boolean;
425
+ /** 是否**每一项**都补偿成功(false = 部分回滚,配置可能停留在中间状态) */
426
+ full: boolean;
427
+ /**
428
+ * 未回滚项(full=false 时非空)。`reason` = 为什么没能恢复;`manualHint` = 人工怎么补
429
+ * (如「请重新补录该凭据」)。旧宿主不返回 → undefined(按「无未回滚项」处理)。
430
+ */
431
+ failed?: SyncRollbackFailedItem[];
432
+ /** 同步历史写入失败(回滚本身已完成;仅提示) */
433
+ historyWriteError?: string;
434
+ }
435
+
436
+ /** 单条未回滚项。 */
437
+ export interface SyncRollbackFailedItem {
438
+ /** 形如 `<adapter>:<ref>` */
439
+ item: string;
440
+ reason: string;
441
+ manualHint?: string;
442
+ }
443
+
390
444
  /* ---------------------------------------------------------------- 自动同步 */
391
445
 
392
446
  /** 统一间隔类型。 */
@@ -402,6 +456,14 @@ export interface AutosyncStatusResponse {
402
456
  lastRunAt?: string;
403
457
  lastRunStatus?: AutosyncRunStatus;
404
458
  lastRunMessage?: string;
459
+ /**
460
+ * issue #79:本轮跳过的**结构化原因码**(界面按码取字典,绝不渲染裸枚举)。
461
+ * 取值与同步历史的 skipReason 同一套:conflict / mutation-locked / encrypted /
462
+ * unconfigured / upToDate / unchanged。
463
+ */
464
+ lastRunSkipReason?: string;
465
+ /** 跳过涉及的分区数(当前仅 conflict:远端另有 N 个分区待确认) */
466
+ lastRunSkipCount?: number;
405
467
  consecutiveFailures: number;
406
468
  /** 距上次自动同步已过 ms(host 计算,供 UI 倒计时/立即触发判断) */
407
469
  elapsedMs: number;
@@ -539,15 +601,27 @@ const SYNC_OPTS: RequestOptions = { timeoutMs: LONG_REQUEST_TIMEOUT_MS, timeoutK
539
601
  /** 远程同步浏览器半数据入口(备份与迁移页第 4 个 tab 的注入业务面) */
540
602
  export class SyncApi {
541
603
  readonly t: UiT
542
- constructor(t: UiT = zhUiT) {
604
+ constructor(t: UiT) {
543
605
  this.t = t
544
606
  }
545
607
 
546
- /** 读取同步状态(配置 / 凭据 / 上次同步时间 / 分区数) */
608
+ /** 读取同步状态(配置 / 凭据 / 上次同步时间 / 分区数 / 工作副本自检) */
547
609
  async status(): Promise<SyncStatusResponse> {
548
610
  return getJson<SyncStatusResponse>(SYNC_API.status, this.t, SYNC_OPTS);
549
611
  }
550
612
 
613
+ /**
614
+ * 重建 git 工作副本(issue #79 的自救入口)。
615
+ *
616
+ * - `relink`:只把 origin / push URL 重指向当前配置地址(非破坏性,不碰工作树与本地提交);
617
+ * - `reclone`:删除工作副本后重新 clone(**丢弃未上行的本地提交**)→ 必须带 `confirm: true`。
618
+ *
619
+ * 写动作:host 侧过 mutation gate(SAFE MODE / 环境锁)。
620
+ */
621
+ async workdirReset(payload: { mode: 'relink' | 'reclone'; confirm?: boolean }): Promise<{ ok: boolean; mode: string; workDir: { facts: SyncWorkDirFacts } }> {
622
+ return postJson<{ ok: boolean; mode: string; workDir: { facts: SyncWorkDirFacts } }>(SYNC_API.workdirReset, payload, this.t, SYNC_OPTS);
623
+ }
624
+
551
625
  /**
552
626
  * 分区条目清单(复用 /export-preview;**只读**,不写任何内容)。
553
627
  *
@@ -698,9 +772,9 @@ export class SyncApi {
698
772
  return postJson<{ ok: boolean; lastSyncChannel?: SyncTransportType }>(SYNC_API.uiPrefs, payload, this.t, SYNC_OPTS);
699
773
  }
700
774
 
701
- /** 一键回滚:按 restoreId 调用 backup→rollback */
702
- async rollback(payload: { restoreId: string }): Promise<{ ok: boolean; full: boolean }> {
703
- return postJson<{ ok: boolean; full: boolean }>(SYNC_API.rollback, payload, this.t, SYNC_OPTS);
775
+ /** 一键回滚:按 restoreId 调用 backup→rollback(full=false 时 failed 列出未回滚项与人工提示) */
776
+ async rollback(payload: { restoreId: string }): Promise<SyncRollbackResponse> {
777
+ return postJson<SyncRollbackResponse>(SYNC_API.rollback, payload, this.t, SYNC_OPTS);
704
778
  }
705
779
 
706
780
  /** Phase 7 迁移前咨询(只读健康评分 + 建议):对远端快照生成咨询报告。 */