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
@@ -171,11 +171,20 @@ export declare class Analyzer {
171
171
  * 未采用其导入内容的 Conflict 都不写目标,记录只会保守化 reconcile 而无收益。
172
172
  */
173
173
  private shouldJournalStep;
174
- /** 文件类且可指纹 → 返回 home-relative 目标路径(posix);否则 null(不可指纹外部项)。
174
+ /** 文件类且可指纹 → 返回「根相对路径」(journal 的 ref 身份);否则 null(不可指纹外部项)。
175
175
  * profile 必须传入:plugins 分区的 pnpm-workspace.yaml / patch 文件位于
176
176
  * `profiles/<profile>/` 下,缺了它算出的相对路径不存在 → 指纹恒 null(issue #35)。 */
177
177
  private static fileRelFor;
178
- /** 读目标文件算 sha256(home-relative;经 HostContext.fs 使测试 mock 可注入)。失败返回 null。 */
178
+ /**
179
+ * 文件类且可指纹 → 返回「该分区自己的门面 + 绝对路径」。
180
+ *
181
+ * 为什么不能只拿 home-relative 路径去读:`pluginData` / `pluginAssets` 的根是**系统 home**
182
+ * (见 `core/backup.ts` 的 `fsForSection`),用 `ctx.fs` 读会落到 $DSH_HOME 下的错误位置
183
+ * 或被越界守卫拦下 —— 指纹会静默变成「读不到」,reconcile 因此永远判不出是否已应用。
184
+ * 宿主未提供该门面 / 路径不可解析 → null(保守:不可证明)。
185
+ */
186
+ private static fileTargetFor;
187
+ /** 读目标文件算 sha256(经该分区自己的门面,测试 mock 可注入)。失败返回 null。 */
179
188
  private fileFp;
180
189
  private applyOne;
181
190
  }
@@ -19,7 +19,7 @@ import { isAbsolutePath, applyPrefixMappings } from '../utils/paths.js';
19
19
  import { parseZip } from '../utils/zip.js';
20
20
  import { loadTombstones, isTombstoned } from '../schema/tombstones.js';
21
21
  import { DEFAULT_SENSITIVE_RELS, restoreVaultFiles } from '../security/vault.js';
22
- import { createSnapshot, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.js';
22
+ import { createSnapshot, fsForSection, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.js';
23
23
  import { isCredentialConfigured } from './credential-status.js';
24
24
  import { rollback } from './rollback.js';
25
25
  import { compatibilityReasons, computeCompatibility } from './validator.js';
@@ -790,7 +790,7 @@ export class Analyzer {
790
790
  let beforeFp = null;
791
791
  if (Analyzer.fileRelFor(item, importCtx.target.profile) !== null) {
792
792
  rel = Analyzer.fileRelFor(item, importCtx.target.profile);
793
- beforeFp = await this.fileFp(importCtx, rel);
793
+ beforeFp = await this.fileFp(importCtx, item);
794
794
  }
795
795
  beforeFpByItem.set(item.id, beforeFp);
796
796
  await sb.recordStep({
@@ -896,7 +896,7 @@ export class Analyzer {
896
896
  const rel = Analyzer.fileRelFor(item, importCtx.target.profile);
897
897
  let afterFp = null;
898
898
  if (rel !== null && outcome.executed.status === 'ok') {
899
- afterFp = await this.fileFp(importCtx, rel);
899
+ afterFp = await this.fileFp(importCtx, item);
900
900
  }
901
901
  await sb.recordStep({
902
902
  id: item.id,
@@ -1227,7 +1227,7 @@ export class Analyzer {
1227
1227
  shouldJournalStep(item) {
1228
1228
  return planItemWritesTarget(item);
1229
1229
  }
1230
- /** 文件类且可指纹 → 返回 home-relative 目标路径(posix);否则 null(不可指纹外部项)。
1230
+ /** 文件类且可指纹 → 返回「根相对路径」(journal 的 ref 身份);否则 null(不可指纹外部项)。
1231
1231
  * profile 必须传入:plugins 分区的 pnpm-workspace.yaml / patch 文件位于
1232
1232
  * `profiles/<profile>/` 下,缺了它算出的相对路径不存在 → 指纹恒 null(issue #35)。 */
1233
1233
  static fileRelFor(item, profile) {
@@ -1238,11 +1238,34 @@ export class Analyzer {
1238
1238
  return null;
1239
1239
  return resolveFileTargetRel(item.adapter, ref, profile);
1240
1240
  }
1241
- /** 读目标文件算 sha256(home-relative;经 HostContext.fs 使测试 mock 可注入)。失败返回 null。 */
1242
- async fileFp(ctx, rel) {
1241
+ /**
1242
+ * 文件类且可指纹 → 返回「该分区自己的门面 + 绝对路径」。
1243
+ *
1244
+ * 为什么不能只拿 home-relative 路径去读:`pluginData` / `pluginAssets` 的根是**系统 home**
1245
+ * (见 `core/backup.ts` 的 `fsForSection`),用 `ctx.fs` 读会落到 $DSH_HOME 下的错误位置
1246
+ * 或被越界守卫拦下 —— 指纹会静默变成「读不到」,reconcile 因此永远判不出是否已应用。
1247
+ * 宿主未提供该门面 / 路径不可解析 → null(保守:不可证明)。
1248
+ */
1249
+ static fileTargetFor(ctx, item) {
1250
+ if (!isFileSection(item.adapter))
1251
+ return null;
1252
+ const ref = item.target?.ref;
1253
+ if (ref === undefined || ref === '')
1254
+ return null;
1255
+ try {
1256
+ return { fs: fsForSection(ctx.target, item.adapter), abs: resolveFileTarget(ctx.target, item.adapter, ref) };
1257
+ }
1258
+ catch {
1259
+ return null;
1260
+ }
1261
+ }
1262
+ /** 读目标文件算 sha256(经该分区自己的门面,测试 mock 可注入)。失败返回 null。 */
1263
+ async fileFp(ctx, item) {
1264
+ const target = Analyzer.fileTargetFor(ctx, item);
1265
+ if (target === null)
1266
+ return null;
1243
1267
  try {
1244
- const data = await ctx.target.fs.readFile(rel);
1245
- return sha256Hex(data);
1268
+ return sha256Hex(await target.fs.readFile(target.abs));
1246
1269
  }
1247
1270
  catch {
1248
1271
  return null;
@@ -11,6 +11,16 @@ export declare const DEFAULT_BACKUP_SECTIONS: readonly SectionId[];
11
11
  export declare const OPT_IN_BACKUP_SECTIONS: readonly SectionId[];
12
12
  /** 全部可离线收集的分区(默认 ∪ 显式选中) */
13
13
  export declare const OFFLINE_BACKUP_SECTIONS: readonly SectionId[];
14
+ /**
15
+ * 离线 CLI **明确不收集**的分区(第三方插件数据)。
16
+ *
17
+ * 为什么不做:它的收集目标不是静态路径,而是「本机内置来源注册表 + 已安装插件自述声明」推导出来的
18
+ * 区域(见 `core/plugin-data-registry.ts`),还要尊重环境变量覆盖判定 —— 这套判定住在 adapter 里
19
+ * (走 HostContext 门面),而 CLI 的硬约束是**绝不 import DSH 门面**(peer 缺失也要能跑)。
20
+ * 在这里手抄一份区域表会立刻与注册表漂移(正是本文件开头反复说的那类缺陷),所以选择**显式不适用**:
21
+ * 用户显式 `--sections pluginData` 时给一条可执行的提示(改用 GUI 导出/导入),而不是静默不收。
22
+ */
23
+ export declare const OFFLINE_EXCLUDED_SECTIONS: readonly SectionId[];
14
24
  /**
15
25
  * 离线**无法**收集的分区(需要 DSH Service 门面读值/权威脱敏)。仅用于报告与参数校验,
16
26
  * 绝不写进归档:这些分区的权威形态是 GUI 导出(经 DSH `describe({redactSecrets:true})`
@@ -32,6 +32,8 @@
32
32
  * 目标越出 homeDir 或断链的链接跳过但**写进 warnings**——绝不静默少收内容;
33
33
  * 普通目录**超过层级上限**同样留痕(cli-F2),且告警措辞按**真实来源**分派(t37:链接类 vs 目录超深);
34
34
  * - 命中 `isReservedInternalRel` 的内部命名空间(locks/snapshots/transactions…)跳过;
35
+ * - 运行时依赖目录 node_modules(任意深度)**整棵跳过**(issue #76:与 plugins 分区的 npm pack 同口径,
36
+ * 依赖树可由包内 package.json / lock 重建),跳过了哪些目录、各多少文件一律写进 warnings;
35
37
  * - `pluginFiles`(`dsh-ssh.json` 等第三方插件自有文件)**默认不收**:该分区与 GUI
36
38
  * 侧同为 deviceSpecific「任意文件直通、无内容过滤」,`dsh-ssh.json` 里是明文主机
37
39
  * 密码——必须由用户显式 `--sections pluginFiles` 选中,并附带显式告警
@@ -39,6 +41,7 @@
39
41
  *
40
42
  * 零 DSH 运行时依赖:仅 `node:fs/promises` + `node:path` + 本仓库零依赖模块。
41
43
  */
44
+ import { engineMsg } from './messages-core.js';
42
45
  import fs from 'node:fs/promises';
43
46
  import path from 'node:path';
44
47
  import { listRecursiveFollowingLinks } from '../utils/recursive-walk.js';
@@ -60,6 +63,16 @@ export const OPT_IN_BACKUP_SECTIONS = ['pluginFiles'];
60
63
  export const OFFLINE_BACKUP_SECTIONS = [
61
64
  ...DEFAULT_BACKUP_SECTIONS, ...OPT_IN_BACKUP_SECTIONS,
62
65
  ];
66
+ /**
67
+ * 离线 CLI **明确不收集**的分区(第三方插件数据)。
68
+ *
69
+ * 为什么不做:它的收集目标不是静态路径,而是「本机内置来源注册表 + 已安装插件自述声明」推导出来的
70
+ * 区域(见 `core/plugin-data-registry.ts`),还要尊重环境变量覆盖判定 —— 这套判定住在 adapter 里
71
+ * (走 HostContext 门面),而 CLI 的硬约束是**绝不 import DSH 门面**(peer 缺失也要能跑)。
72
+ * 在这里手抄一份区域表会立刻与注册表漂移(正是本文件开头反复说的那类缺陷),所以选择**显式不适用**:
73
+ * 用户显式 `--sections pluginData` 时给一条可执行的提示(改用 GUI 导出/导入),而不是静默不收。
74
+ */
75
+ export const OFFLINE_EXCLUDED_SECTIONS = ['pluginData', 'pluginAssets'];
63
76
  /**
64
77
  * 离线**无法**收集的分区(需要 DSH Service 门面读值/权威脱敏)。仅用于报告与参数校验,
65
78
  * 绝不写进归档:这些分区的权威形态是 GUI 导出(经 DSH `describe({redactSecrets:true})`
@@ -71,7 +84,10 @@ export const OFFLINE_UNAVAILABLE_SECTIONS = [
71
84
  // 加载期自检(注册表派生,t30):三个离线清单里的 id 必须都是**已注册分区**。
72
85
  // 「离线可收集性」是注册表未建模的另一条轴(要脱离 DSH 读盘收集),故这三份清单仍是显式声明;
73
86
  // 但拼错 id / 分区改名过去会让该分区「静默永不收集、永不报告」,现在模块加载即失败。
74
- for (const id of [...DEFAULT_BACKUP_SECTIONS, ...OPT_IN_BACKUP_SECTIONS, ...OFFLINE_UNAVAILABLE_SECTIONS]) {
87
+ for (const id of [
88
+ ...DEFAULT_BACKUP_SECTIONS, ...OPT_IN_BACKUP_SECTIONS, ...OFFLINE_UNAVAILABLE_SECTIONS,
89
+ ...OFFLINE_EXCLUDED_SECTIONS,
90
+ ]) {
75
91
  requireSectionMeta(id);
76
92
  }
77
93
  /**
@@ -109,8 +125,8 @@ const SECTION_SPECS = {
109
125
  baseDir: '',
110
126
  files: ['dsh-ssh.json', 'pet.json'],
111
127
  dirs: ['plugin-config'],
112
- label: 'Plugin Files',
113
- risk: '第三方插件自有文件不受控(dsh-ssh.json 内含明文主机密码等),仅在你确认内容后才应纳入备份',
128
+ label: "Other Plugins' Loose Files",
129
+ get risk() { return engineMsg('core.backupPlan.pluginFilesRisk'); },
114
130
  },
115
131
  // adapters/self.ts(baseDir='dsh-config-manager',SELF_CONFIG_FILES 白名单)
116
132
  self: {
@@ -124,7 +140,7 @@ const SECTION_SPECS = {
124
140
  'dsh-config-manager/market/market-config.json',
125
141
  'dsh-config-manager/exports/.backup-notes.json',
126
142
  ],
127
- label: 'Plugin Self Config',
143
+ label: "This Plugin's Own Config",
128
144
  },
129
145
  };
130
146
  /**
@@ -134,13 +150,16 @@ const SECTION_SPECS = {
134
150
  * 跳过原因;而 `too-deep` 自 cli-F2 起对**普通目录**同样会记录(此前只有链接超深才记)—— 整块内容被
135
151
  * 深度上限裁掉时若仍写成「链接未进备份」,用户看到的原因就与真实来源(目录层级超出上限)不符。
136
152
  * 措辞里**不编具体层数**:上限常量住在 utils/recursive-walk.ts,本模块不 import 它的私有常量。
153
+ *
154
+ * 路径**不叠加分区前缀**:内核返回的 `s.path` 已是 homeDir 相对路径(含分区目录),再加一层
155
+ * 会打印成 `skills/skills/...`(真机规模探针实测),与相邻的「目录读取失败」行口径也不一致。
137
156
  */
138
- function skippedLinkWarning(label, s) {
139
- const at = `${label}/${s.path}(${s.reason})`;
157
+ function skippedLinkWarning(s) {
158
+ const at = engineMsg('core.backupPlan.pathWithReason', { path: s.path, reason: s.reason });
140
159
  if (s.reason === 'too-deep') {
141
- return `层级超过上限未进备份(目录或链接目标超出深度上限) / nesting limit exceeded, content NOT in this backup: ${at}`;
160
+ return engineMsg('core.backupPlan.tooDeep', { at });
142
161
  }
143
- return `链接未进备份 / link NOT in this backup: ${at}`;
162
+ return engineMsg('core.backupPlan.linkSkipped', { at });
144
163
  }
145
164
  /**
146
165
  * 递归列出目录下全部普通文件(**相对 homeDir** 的 posix 路径,已排序);目录不存在 → 空。
@@ -149,16 +168,23 @@ function skippedLinkWarning(label, s) {
149
168
  * 并把「跟随了多少链接」「哪些链接/目录没进来(及原因)」写进 warnings。
150
169
  * 旧实现遇到链接目录直接跳过且零告警,用户拿到的是「成功但缺斤少两」的备份。
151
170
  */
152
- async function listFilesRecursive(dirAbs, homeDir, warnings, label) {
171
+ async function listFilesRecursive(dirAbs, homeDir, warnings) {
153
172
  const listing = await listRecursiveFollowingLinks(dirAbs, homeDir);
154
173
  for (const s of listing.skippedLinks) {
155
- warnings.push(skippedLinkWarning(label, s));
174
+ warnings.push(skippedLinkWarning(s));
156
175
  }
157
176
  for (const dir of listing.unreadableDirs) {
158
- warnings.push(`目录读取失败,其内容未进备份 / directory unreadable, content NOT in this backup: ${dir}`);
177
+ warnings.push(engineMsg('core.backupPlan.dirUnreadable', { dir }));
178
+ }
179
+ // issue #76:运行时依赖目录整棵跳过(内容未进备份)——必须可见,绝不静默。
180
+ for (const dep of listing.skippedDepDirs) {
181
+ const files = dep.files === null
182
+ ? engineMsg('core.backupPlan.countNotMeasured')
183
+ : engineMsg('core.backupPlan.fileCount', { count: dep.files });
184
+ warnings.push(engineMsg('core.backupPlan.depDirSkipped', { path: dep.path, files }));
159
185
  }
160
186
  if (listing.followedLinks > 0) {
161
- warnings.push(`已跟随 ${listing.followedLinks} 个链接目录收集内容 / followed ${listing.followedLinks} linked director(ies)`);
187
+ warnings.push(engineMsg('core.backupPlan.followedLinks', { count: listing.followedLinks }));
162
188
  }
163
189
  return listing.paths;
164
190
  }
@@ -171,15 +197,20 @@ function toRelativePath(baseDir, homeRel) {
171
197
  }
172
198
  /** `--sections` 取值校验:返回选中分区或可读错误(未知 id 一律拒绝并列出合法值) */
173
199
  export function parseSectionsArg(raw) {
174
- const parts = raw.split(/[,,\s]+/).filter((p) => p !== '');
200
+ const parts = raw.split(/[,\uFF0C\s]+/).filter((p) => p !== '');
175
201
  if (parts.length === 0) {
176
- return { ok: false, error: '`--sections` 缺值 / missing value for `--sections`' };
202
+ return { ok: false, error: engineMsg('core.backupPlan.sectionsMissingValue') };
203
+ }
204
+ // 显式请求「不支持离线」的分区 → 单独一条可执行提示(不得当成「不存在的分区」)
205
+ const notOffline = parts.filter((p) => OFFLINE_EXCLUDED_SECTIONS.includes(p));
206
+ if (notOffline.length > 0) {
207
+ return { ok: false, error: engineMsg('core.backupPlan.sectionNotOffline', { section: notOffline.join(', ') }) };
177
208
  }
178
209
  const unknown = parts.filter((p) => !OFFLINE_BACKUP_SECTIONS.includes(p));
179
210
  if (unknown.length > 0) {
180
211
  return {
181
212
  ok: false,
182
- error: `未知分区 / unknown section(s): ${unknown.join(', ')}(可用 / available: ${OFFLINE_BACKUP_SECTIONS.join(', ')})`,
213
+ error: engineMsg('core.backupPlan.unknownSections', { unknown: unknown.join(', '), available: OFFLINE_BACKUP_SECTIONS.join(', ') }),
183
214
  };
184
215
  }
185
216
  // 去重并保持声明顺序,保证产物可复现
@@ -207,7 +238,7 @@ export async function collectBackupEntries(homeDir, only = []) {
207
238
  let excluded = 0;
208
239
  const push = (zipName, data) => {
209
240
  if (!isPathSafe(zipName)) {
210
- warnings.push(`跳过不安全条目名 / unsafe entry name skipped: ${zipName}`);
241
+ warnings.push(engineMsg('core.backupPlan.unsafeEntryName', { name: zipName }));
211
242
  excluded += 1;
212
243
  return;
213
244
  }
@@ -229,7 +260,7 @@ export async function collectBackupEntries(homeDir, only = []) {
229
260
  catch (err) {
230
261
  // 不存在 → 静默跳过(按需创建的文件,如 dsh-ssh.json)
231
262
  if (err.code !== 'ENOENT') {
232
- warnings.push(`探测失败 / stat failed: ${norm}(${err instanceof Error ? err.message : String(err)})`);
263
+ warnings.push(engineMsg('core.backupPlan.statFailed', { path: norm, reason: err instanceof Error ? err.message : String(err) }));
233
264
  }
234
265
  return;
235
266
  }
@@ -242,7 +273,7 @@ export async function collectBackupEntries(homeDir, only = []) {
242
273
  push(`${prefix}${toRelativePath(spec.baseDir, norm)}`, new Uint8Array(data));
243
274
  }
244
275
  catch (err) {
245
- warnings.push(`读取失败 / read failed: ${norm}(${err instanceof Error ? err.message : String(err)})`);
276
+ warnings.push(engineMsg('core.backupPlan.readFailed', { path: norm, reason: err instanceof Error ? err.message : String(err) }));
246
277
  }
247
278
  };
248
279
  // 1) 固定文件(存在才收)
@@ -251,7 +282,7 @@ export async function collectBackupEntries(homeDir, only = []) {
251
282
  // 2) 递归目录(跟随 junction/符号链接;未跟随的链接与不可读目录写进 warnings)
252
283
  for (const dirRel of spec.dirs ?? []) {
253
284
  const dirAbs = path.join(homeDir, ...normalizePath(dirRel).split('/'));
254
- const rels = await listFilesRecursive(dirAbs, homeDir, warnings, normalizePath(dirRel));
285
+ const rels = await listFilesRecursive(dirAbs, homeDir, warnings);
255
286
  for (const homeRel of rels)
256
287
  await consider(homeRel);
257
288
  }
@@ -264,7 +295,7 @@ export async function collectBackupEntries(homeDir, only = []) {
264
295
  }
265
296
  else {
266
297
  empty.push(sectionId);
267
- warnings.push(`分区无内容,未写入 / section empty, skipped: ${sectionId}`);
298
+ warnings.push(engineMsg('core.backupPlan.sectionEmpty', { section: sectionId }));
268
299
  }
269
300
  }
270
301
  return { entries, sections, warnings, included, empty };
@@ -28,6 +28,7 @@
28
28
  * (实测 `verifyChecksums` 只调用 `map.get()`,不会整体物化 Map)。
29
29
  * 对 MB 级的 DSH 配置备份完全可接受。
30
30
  */
31
+ import { engineMsg } from './messages-core.js';
31
32
  import fs from 'node:fs/promises';
32
33
  import { verifyChecksums } from '../utils/hashing.js';
33
34
  import { isENOENT } from '../utils/guards.js';
@@ -114,7 +115,7 @@ export async function verifyBackupZip(zipPath) {
114
115
  try {
115
116
  const stat = await fs.stat(zipPath);
116
117
  if (!stat.isFile()) {
117
- errors.push(`不是普通文件(目录或特殊文件)/ not a regular file: ${zipPath}`);
118
+ errors.push(engineMsg('core.backupVerify.notRegularFile', { zip: zipPath }));
118
119
  return makeResult(zipPath, 'MISSING', errors, warnings);
119
120
  }
120
121
  sizeBytes = stat.size;
@@ -122,11 +123,11 @@ export async function verifyBackupZip(zipPath) {
122
123
  }
123
124
  catch (err) {
124
125
  if (isENOENT(err)) {
125
- errors.push(`备份文件不存在 / backup file not found: ${zipPath}`);
126
+ errors.push(engineMsg('core.backupVerify.fileNotFound', { zip: zipPath }));
126
127
  return makeResult(zipPath, 'MISSING', errors, warnings);
127
128
  }
128
129
  // 文件在但读不动(EACCES / EBUSY / IO 错):这是自检自身的失败,不是「备份坏了」
129
- errors.push(`备份文件不可读 / backup file unreadable: ${msgOfError(err)}`);
130
+ errors.push(engineMsg('core.backupVerify.fileUnreadable', { reason: msgOfError(err) }));
130
131
  return makeResult(zipPath, 'VERIFY_ERROR', errors, warnings);
131
132
  }
132
133
  /** 已知字节数后的骨架(后续阶段失败时保留体积信息) */
@@ -137,7 +138,7 @@ export async function verifyBackupZip(zipPath) {
137
138
  /* ---------------- 2. 打开为 ZIP ---------------- */
138
139
  // 整体加密容器不是 ZIP(先识别,给出比「ZIP 损坏」准确得多的结论)
139
140
  if (isArchiveBlob(buf)) {
140
- errors.push('整体加密备份容器(DCA1)需先用密码解密为 ZIP 才能自检 / encrypted container must be decrypted first');
141
+ errors.push(engineMsg('core.backupVerify.encryptedContainer'));
141
142
  return withSize('UNSUPPORTED');
142
143
  }
143
144
  let archive;
@@ -145,7 +146,7 @@ export async function verifyBackupZip(zipPath) {
145
146
  archive = createHardenedZipParser()(buf);
146
147
  }
147
148
  catch (err) {
148
- errors.push(`ZIP 无法打开 / cannot open as ZIP: ${msgOfError(err)}`);
149
+ errors.push(engineMsg('core.backupVerify.cannotOpenZip', { reason: msgOfError(err) }));
149
150
  return withSize('CORRUPT');
150
151
  }
151
152
  const metas = archive.entries();
@@ -154,12 +155,12 @@ export async function verifyBackupZip(zipPath) {
154
155
  file: zipPath, verdict, sizeBytes, entryCount, errors, warnings,
155
156
  });
156
157
  if (entryCount === 0) {
157
- errors.push('空 ZIP(0 条目)/ empty archive');
158
+ errors.push(engineMsg('core.backupVerify.emptyArchive'));
158
159
  return withEntries('CORRUPT');
159
160
  }
160
161
  /* ---------------- 3. manifest.json ---------------- */
161
162
  if (!archive.has(MANIFEST_FILE)) {
162
- errors.push(`缺少 ${MANIFEST_FILE}(不是本插件导出的备份)/ ${MANIFEST_FILE} missing`);
163
+ errors.push(engineMsg('core.backupVerify.missingManifest', { file: MANIFEST_FILE }));
163
164
  return withEntries('CORRUPT');
164
165
  }
165
166
  let manifestRaw;
@@ -168,7 +169,7 @@ export async function verifyBackupZip(zipPath) {
168
169
  }
169
170
  catch (err) {
170
171
  // 条目本身读不出来(CRC 失败/解压失败)→ 归档损坏,而非 manifest 语义非法
171
- errors.push(`${MANIFEST_FILE} 无法读取(归档损坏)/ unreadable entry: ${msgOfError(err)}`);
172
+ errors.push(engineMsg('core.backupVerify.manifestUnreadable', { file: MANIFEST_FILE, reason: msgOfError(err) }));
172
173
  return withEntries('CORRUPT');
173
174
  }
174
175
  let manifest;
@@ -176,7 +177,7 @@ export async function verifyBackupZip(zipPath) {
176
177
  manifest = parseManifest(manifestRaw);
177
178
  }
178
179
  catch (err) {
179
- errors.push(`${MANIFEST_FILE} 非法 / invalid manifest: ${msgOfError(err)}`);
180
+ errors.push(engineMsg('core.backupVerify.invalidManifest', { file: MANIFEST_FILE, reason: msgOfError(err) }));
180
181
  return withEntries('CORRUPT');
181
182
  }
182
183
  const sections = Object.entries(manifest.sections ?? {})
@@ -187,12 +188,12 @@ export async function verifyBackupZip(zipPath) {
187
188
  });
188
189
  if (!isSupported(manifest.schemaVersion)) {
189
190
  // 版本超范围是「本插件读不了」,不是「备份坏了」——明确区分,避免误导用户重导
190
- errors.push(`schema 版本不受支持 / unsupported schema version: ${describeVersion(manifest.schemaVersion)}`);
191
+ errors.push(engineMsg('core.backupVerify.unsupportedSchema', { version: describeVersion(manifest.schemaVersion) }));
191
192
  return withManifest('UNSUPPORTED');
192
193
  }
193
194
  /* ---------------- 4. integrity/checksums.json ---------------- */
194
195
  if (!archive.has(CHECKSUMS_FILE)) {
195
- errors.push(`缺少 ${CHECKSUMS_FILE}(无法做完整性校验)/ ${CHECKSUMS_FILE} missing`);
196
+ errors.push(engineMsg('core.backupVerify.missingChecksums', { file: CHECKSUMS_FILE }));
196
197
  return withManifest('CORRUPT');
197
198
  }
198
199
  let table;
@@ -201,7 +202,7 @@ export async function verifyBackupZip(zipPath) {
201
202
  }
202
203
  catch (err) {
203
204
  // readEntryText 的 CRC/解压失败与「表结构非法」都归 CORRUPT(两者都说明备份不可信)
204
- errors.push(`${CHECKSUMS_FILE} 非法或不可读 / invalid or unreadable checksums table: ${msgOfError(err)}`);
205
+ errors.push(engineMsg('core.backupVerify.invalidChecksums', { file: CHECKSUMS_FILE, reason: msgOfError(err) }));
205
206
  return withManifest('CORRUPT');
206
207
  }
207
208
  // 4a. 完整性比对:**复用官方 verifyChecksums**(与导出侧 buildChecksums 同源同口径),
@@ -209,7 +210,7 @@ export async function verifyBackupZip(zipPath) {
209
210
  const unreadableNames = new Set();
210
211
  const unreadable = [];
211
212
  const lazyEntries = new LazyEntryMap(archive, unreadableNames, (name, reason) => {
212
- unreadable.push(`${name}(${reason})`);
213
+ unreadable.push(engineMsg('core.backupVerify.unreadableEntry', { name, reason }));
213
214
  });
214
215
  const tableKeys = Object.keys(table);
215
216
  const check = verifyChecksums(lazyEntries, table);
@@ -219,10 +220,10 @@ export async function verifyBackupZip(zipPath) {
219
220
  // 4b. 结构提示:未纳入校验表的条目(导出器只把 manifest 与校验表自身排除在外)
220
221
  if (tableKeys.length === 0) {
221
222
  if (entryCount > NON_DATA_ENTRIES.size) {
222
- errors.push('校验表为空但归档含数据条目 / empty checksums table with data entries');
223
+ errors.push(engineMsg('core.backupVerify.emptyTableWithData'));
223
224
  return withManifest('CORRUPT');
224
225
  }
225
- warnings.push('校验表为空(归档仅含 manifest 与校验表自身)/ empty checksums table');
226
+ warnings.push(engineMsg('core.backupVerify.emptyTable'));
226
227
  }
227
228
  const covered = new Set(tableKeys);
228
229
  const uncovered = metas
@@ -230,7 +231,7 @@ export async function verifyBackupZip(zipPath) {
230
231
  .map((m) => m.name);
231
232
  if (uncovered.length > 0) {
232
233
  const shown = uncovered.slice(0, 5).join(', ');
233
- warnings.push(`含未纳入校验表的条目 ${uncovered.length} 个 / entries not covered by checksums: ${shown}${uncovered.length > 5 ? ', …' : ''}`);
234
+ warnings.push(engineMsg('core.backupVerify.uncoveredEntries', { count: uncovered.length, shown, suffix: uncovered.length > 5 ? ', …' : '' }));
234
235
  }
235
236
  // 4c. 结构提示:manifest 声明包含的分区在归档内应有对应条目
236
237
  for (const id of sections) {
@@ -238,31 +239,31 @@ export async function verifyBackupZip(zipPath) {
238
239
  const jsonPath = SECTION_JSON_PATHS[id];
239
240
  if (prefix !== undefined) {
240
241
  if (!metas.some((m) => m.name.startsWith(prefix) && m.name !== prefix)) {
241
- warnings.push(`manifest 声明分区 ${id} 但归档内无对应文件 / declared section has no entries: ${id}`);
242
+ warnings.push(engineMsg('core.backupVerify.declaredSectionNoEntries', { id }));
242
243
  }
243
244
  }
244
245
  else if (jsonPath !== undefined && !archive.has(jsonPath)) {
245
- warnings.push(`manifest 声明分区 ${id} 但缺少 ${jsonPath} / declared section payload missing: ${id}`);
246
+ warnings.push(engineMsg('core.backupVerify.declaredSectionPayloadMissing', { id, path: jsonPath }));
246
247
  }
247
248
  }
248
249
  if (manifest.security?.encrypted === true && !metas.some((m) => SECRETS_ENC_RE.test(m.name))) {
249
- warnings.push('manifest 标记加密但归档内无凭据密文条目 / encrypted flag without secrets entry');
250
+ warnings.push(engineMsg('core.backupVerify.encryptedFlagNoSecrets'));
250
251
  }
251
252
  /* ---------------- 裁决 ---------------- */
252
253
  if (missing.length > 0) {
253
- errors.push(`校验表内条目缺失(${missing.length})/ missing entries: ${missing.join(', ')}`);
254
+ errors.push(engineMsg('core.backupVerify.missingEntries', { count: missing.length, list: missing.join(', ') }));
254
255
  }
255
256
  if (mismatched.length > 0) {
256
- errors.push(`条目 SHA-256 不符(${mismatched.length})/ hash mismatch: ${mismatched.join(', ')}`);
257
+ errors.push(engineMsg('core.backupVerify.hashMismatch', { count: mismatched.length, list: mismatched.join(', ') }));
257
258
  }
258
259
  if (unreadable.length > 0) {
259
- errors.push(`条目无法读取(${unreadable.length})/ unreadable entries: ${unreadable.join(', ')}`);
260
+ errors.push(engineMsg('core.backupVerify.unreadableEntries', { count: unreadable.length, list: unreadable.join(', ') }));
260
261
  }
261
262
  return withManifest(missing.length > 0 || mismatched.length > 0 || unreadable.length > 0 ? 'CORRUPT' : 'OK');
262
263
  }
263
264
  catch (err) {
264
265
  // 5. 自检自身异常:如实报 VERIFY_ERROR,绝不降级成 OK
265
- errors.push(`自检过程异常 / verification error: ${msgOfError(err)}`);
266
+ errors.push(engineMsg('core.backupVerify.verificationError', { reason: msgOfError(err) }));
266
267
  return withSize('VERIFY_ERROR');
267
268
  }
268
269
  }
@@ -1,5 +1,5 @@
1
1
  import type { SectionId } from '../schema/types.ts';
2
- import type { ConfigAdapter, HostContext, ImportPlan, PlanItem, Snapshot, SnapshotStatus, SnapshotStore } from './types.ts';
2
+ import type { ConfigAdapter, FileSystemFacade, HostContext, ImportPlan, PlanItem, Snapshot, SnapshotStatus, SnapshotStore } from './types.ts';
3
3
  /**
4
4
  * 「会写目标」的**单点事实源**:该计划项是否会被引擎交给 `adapter.applyItem`
5
5
  * (即可能产生目标写入)。
@@ -38,6 +38,25 @@ export declare function planItemWritesTarget(item: PlanItem, exec?: PlanItemExec
38
38
  * 行 id 是任意字符串,无法靠形状判断它是不是一个路径。
39
39
  */
40
40
  export declare const PLUGIN_PATCH_REF_PREFIX = "patchFile:";
41
+ /**
42
+ * 以**系统 home 为根**的文件类分区(ref 是系统 home 相对路径)。
43
+ *
44
+ * 为什么单独一组:`FileSystemFacade` 被限制在 $DSH_HOME 内,而第三方插件的数据常在 $DSH_HOME 之外
45
+ * (`~/.dsh-wallpaper-engine/`、`~/.mnemon/` …)。这两个分区的路径解析与 I/O 门面都换根,
46
+ * 其余分区一字不动 —— 单根不变量只在**这两个分区**上被显式放宽,且仍然只允许白名单区域。
47
+ */
48
+ export declare const OS_HOME_FILE_SECTIONS: ReadonlySet<SectionId>;
49
+ /** 该分区文件目标的根(`dsh` = $DSH_HOME;`osHome` = 系统 home) */
50
+ export declare function sectionFileRoot(adapter: SectionId): 'dsh' | 'osHome';
51
+ /** 系统 home:宿主显式给就用它,否则进程 `homedir()`(`~` 的定义,不是猜) */
52
+ export declare function osHomeDirOf(ctx: HostContext): string;
53
+ /**
54
+ * 该分区文件 I/O 应使用的门面。
55
+ *
56
+ * `osHome` 分区必须用 `homeFs`:缺了它**显式抛错**,绝不退回 `ctx.fs` —— 那会把
57
+ * `~/.mnemon/x.db` 写成 `$DSH_HOME/.mnemon/x.db`(看起来成功、实际写错位置)。
58
+ */
59
+ export declare function fsForSection(ctx: HostContext, adapter: SectionId): FileSystemFacade;
41
60
  /** 解析文件类目标的绝对路径(引擎通用快照与回滚共用) */
42
61
  export declare function resolveFileTarget(ctx: HostContext, adapter: SectionId, ref: string): string;
43
62
  /**
@@ -144,6 +163,11 @@ export interface FileSnapshotStoreOptions {
144
163
  * 未注入 → `fallbackPruneSelector`(只按 keepLast,等价既有 FIFO;分层字段被忽略 = 保守少删)。
145
164
  */
146
165
  pruneSelector?: PruneSelector;
166
+ /**
167
+ * 诊断回调(可选):保留清理**被放弃**或失败时的可见线索。
168
+ * 缺省 = 不记录(不改变行为);宿主接 `host.log.warn` —— 「这轮为什么没清理」必须能查。
169
+ */
170
+ onWarn?: (message: string) => void;
147
171
  }
148
172
  /** 文件快照存储:<dir>/<id>/snapshot.json + <dir>/<id>/blobs/* */
149
173
  export declare class FileSnapshotStore implements SnapshotStore {