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
@@ -0,0 +1,216 @@
1
+ /**
2
+ * src/utils/** 的领域消息目录(zh 源语言 / en 镜像)——「英文界面零中文」迁移(t5)。
3
+ *
4
+ * 口径(与 src/core/messages.ts 一致,见 docs/design/2026-10-08-i18n-en-hardcoding.md):
5
+ * - zh 值 = 迁移前的既有中文文案**逐字不变**(既有测试断言依赖它,只允许新增 en);
6
+ * - en 值类型为 Record<keyof typeof zh, string> —— 缺键/多键即编译错误;
7
+ * - 动态明细一律走 {param} 插值传参,不得丢信息;
8
+ * - 本目录经 registerMessageCatalog 注册(模块加载即注册),查找顺序在既有基础目录之后;
9
+ * - zh 对象字面量必须叫 `zh`(守卫 E3 目录 zh 区豁免;改名会让本文件自身变成 300+ 命中)。
10
+ *
11
+ * 使用者:`utilsMsg`(本域唯一出口)——它 import 了 engineMsg,因此 import 本函数即完成注册,
12
+ * 不会出现「用了键但目录没注册」的裸键名输出。
13
+ */
14
+ import { engineMsg, registerMessageCatalog } from '../core/engine-locale.js';
15
+ export const zh = {
16
+ // ---------- utils/json.ts:深度/体积保护 ----------
17
+ 'utils.json.depthDefault': 'JSON 嵌套深度超过上限 {max}',
18
+ 'utils.json.bytesExceeded': 'JSON 体积超过上限 {max} 字节',
19
+ 'utils.json.parseFailedDeep': 'JSON 解析失败(可能是嵌套过深): {reason}',
20
+ 'utils.json.depthExceeded': 'JSON 嵌套深度 {depth} 超过上限 {max}',
21
+ // ---------- utils/atomic-write.ts:tmp 名清洗 / symlink 策略 ----------
22
+ 'utils.atomicWrite.invalidBaseName': '原子写:非法目标文件名「{base}」',
23
+ 'utils.atomicWrite.illegalChars': '原子写:目标文件名含非法字符「{base}」',
24
+ 'utils.atomicWrite.controlChars': '原子写:目标文件名含控制字符「{base}」',
25
+ 'utils.atomicWrite.symlinkRejected': '原子写:目标为符号链接且策略为 reject,拒绝写入「{target}」',
26
+ 'utils.atomicWrite.symlinkRejectedSync': '原子写(同步):目标为符号链接且策略为 reject,拒绝写入「{target}」',
27
+ // ---------- utils/zip.ts:条目数 / 条目名 / 解压校验 ----------
28
+ 'utils.zip.entryCountWriteExceeded': 'ZIP 条目数 {count} 超过上限 {max}(导出上限与读取上限一致;请缩小导出分区范围后重试)',
29
+ 'utils.zip.unsafeEntryNameWrite': '拒绝写入不安全的条目名: {name}',
30
+ 'utils.zip.entryInflateFailed': '条目 "{name}" 解压失败: {reason}',
31
+ 'utils.zip.unknownCompression': '条目 "{name}" 使用未知压缩方法 {method}',
32
+ 'utils.zip.entryMissing': 'ZIP 中不存在条目: {name}',
33
+ 'utils.zip.localHeaderCorrupt': '条目 "{name}" 的本地文件头损坏',
34
+ 'utils.zip.dataOutOfRange': '条目 "{name}" 数据越界',
35
+ 'utils.zip.sizeMismatch': '条目 "{name}" 解压尺寸不符({actual} ≠ {expected})',
36
+ 'utils.zip.crcMismatch': '条目 "{name}" CRC32 校验失败(ZIP 已损坏)',
37
+ 'utils.zip.totalUncompressedExceeded': 'ZIP 解压总字节数超过上限',
38
+ 'utils.zip.ratioExceeded': '条目 "{name}" 压缩比 {ratio} 超过上限(疑似 zip bomb)',
39
+ 'utils.zip.tooSmall': '不是合法的 ZIP 文件(体积过小)',
40
+ 'utils.zip.missingEocd': '不是合法的 ZIP 文件(缺少中央目录结束记录)',
41
+ 'utils.zip.centralDirOutOfRange': '中央目录越界(ZIP 损坏)',
42
+ 'utils.zip.entryCountReadExceeded': 'ZIP 条目数 {count} 超过上限 {max}',
43
+ 'utils.zip.centralEntryOutOfRange': '中央目录条目越界(ZIP 损坏)',
44
+ 'utils.zip.centralSignatureCorrupt': '中央目录签名损坏',
45
+ 'utils.zip.unsafeEntryName': 'ZIP 条目名不安全: {name}',
46
+ 'utils.zip.duplicateEntryName': 'ZIP 条目名重复: {name}',
47
+ 'utils.zip.symlinkEntryRejected': 'ZIP 含符号链接条目,已拒绝: {name}',
48
+ 'utils.zip.localHeaderOutOfRange': '条目 "{name}" 本地文件头越界',
49
+ 'utils.zip.compressedTotalExceeded': 'ZIP 压缩数据总量超过上限',
50
+ 'utils.zip.extractTargetOutOfRange': '解压目标越界: {name}',
51
+ // ---------- utils/env-lock.ts:环境锁诊断 detail(内部诊断文案,供 CLI/日志/事故恢复展示) ----------
52
+ 'utils.envLock.probeFailed': '进程探测失败 pid={pid}: {reason}',
53
+ 'utils.envLock.probeUndetermined': '进程探测未确定 pid={pid}',
54
+ 'utils.envLock.probeFailedShort': '进程探测失败 pid={pid}',
55
+ 'utils.envLock.mkdirLocksDir': '创建锁目录 {dir}',
56
+ 'utils.envLock.openWxPermissionNoExisting': "open('wx') {path}: 权限/ACL 错误 ({code}),且未发现既有锁文件",
57
+ 'utils.envLock.writeOwner': '写入 owner {path}',
58
+ 'utils.envLock.releaseForeignToken': 'release: token 不匹配当前持有(foreign/已被接管),拒绝 unlink',
59
+ 'utils.envLock.releaseOwnershipUnreadable': 'release: 磁盘 ownership 无法读取/损坏(可能被异常恢复或人工修改),拒绝 unlink(ownership-lost)',
60
+ 'utils.envLock.releaseOwnershipMismatch': 'release: 磁盘 ownership.instanceId={diskId} !== 本 token {tokenId}(ownership-lost)',
61
+ 'utils.envLock.releaseUnlinkFailed': 'release: unlink {path} 失败: {reason}',
62
+ 'utils.envLock.ownershipMissing': 'ownership file 不存在(可能正被创建中)',
63
+ 'utils.envLock.ownershipCorrupt': 'ownership 存在但无有效 owner(可能崩溃残留空/损坏文件),不删除、不执行;可用 --recover-stale-lock 回收',
64
+ 'utils.envLock.heartbeatPermissionDenied': '无法读取 heartbeat(权限/ACL),保守拒绝',
65
+ 'utils.envLock.heartbeatReadFailed': 'heartbeat 读取失败,保守拒绝',
66
+ 'utils.envLock.ownerConfirmedDead': 'owner pid={pid} 确证不存在 (heartbeat expired)',
67
+ 'utils.envLock.ownerAliveUnverifiable': 'owner pid={pid} 存活但无法验证进程身份(可能是 PID 复用),且 heartbeat {longExpired} → 判定为残留锁,可显式回收',
68
+ 'utils.envLock.heartbeatExpiredNoIdentity': 'heartbeat 过期且 pid={pid} 存活,但无法可靠取得 OS process identity,保守拒绝删除',
69
+ 'utils.envLock.pidReuse': 'pid reuse:pid={pid} identity 与 recorded 不同',
70
+ 'utils.envLock.ownerAliveDegraded': 'owner 进程存活(heartbeat 可能 degraded),非 stale',
71
+ 'utils.envLock.notStaleRefuseRecovery': '非 stale,拒绝 recovery',
72
+ 'utils.envLock.ownershipGone': 'ownership 已消失(被他人接管/清除),停止 recovery',
73
+ 'utils.envLock.captureRenameFailed': '捕获 rename 失败(可能被他人接管),停止',
74
+ 'utils.envLock.captureReadFailed': '捕获文件读取失败,保留 recovering 供诊断',
75
+ 'utils.envLock.corruptReclaimAnomaly': 'corrupt 回收二次验证异常:captured 具有效 owner,保留 {name} quarantine;不覆盖当前 environment.lock',
76
+ 'utils.envLock.reverifyFailed': '二次验证失败:保留 {name} quarantine;不覆盖当前 environment.lock(若有 successor)',
77
+ 'utils.envLock.removeCapturedFailed': '删除 captured 失败',
78
+ 'utils.envLock.removedStaleOwnership': '已移除 stale ownership (op={op}, pid={pid})',
79
+ 'utils.envLock.removedCorruptOwnership': '已移除 corrupt/崩溃残留所有权文件',
80
+ 'utils.envLock.longExpired': '已过期 {age}(阈值 {threshold})',
81
+ 'utils.envLock.durationDays': '{value} 天',
82
+ 'utils.envLock.durationHours': '{value} 小时',
83
+ 'utils.envLock.durationMinutes': '{value} 分钟',
84
+ 'utils.envLock.durationSeconds': '{value} 秒',
85
+ 'utils.envLock.ioPermissionNotLock': '{what}: 权限/ACL 错误 ({code}),非锁占用',
86
+ // ---------- utils/env-lock.ts:锁被挡时给用户的分类文案(长 / 短两套,同源同分类) ----------
87
+ 'utils.envLock.block.locked': '另一个任务正在运行,请稍后重试。',
88
+ 'utils.envLock.block.blocked': '配置修改已被保护,请先处理恢复事项后再继续。',
89
+ 'utils.envLock.block.unavailable': '操作暂时无法执行,请稍后重试;若持续失败请查看日志。',
90
+ 'utils.envLock.block.stale': '检测到上次异常退出残留的配置锁(其持有进程已不存在),操作已被阻止。'
91
+ + '该锁不会自动清除,重试或重启 DSH 均无效:请在「事故恢复」中点击「回收残留锁」,'
92
+ + '或运行 dsh-config-manager recover-stale-lock 回收后再重试。',
93
+ 'utils.envLock.brief.locked': '环境锁被另一项任务占用',
94
+ 'utils.envLock.brief.blocked': '配置修改已被保护,需先处理恢复事项',
95
+ 'utils.envLock.brief.unavailable': '环境锁暂时不可用',
96
+ 'utils.envLock.brief.stale': '残留配置锁(持有进程已不存在),需先回收',
97
+ // ---------- utils/proxy.ts:代理隧道(保留原文的双语形态,zh 逐字不变) ----------
98
+ 'utils.proxy.connectTimeout': '代理连接超时 / proxy connect timed out ({ms}ms)',
99
+ 'utils.proxy.malformedConnectResponse': '代理 CONNECT 响应异常(头部过长)/ malformed CONNECT response',
100
+ 'utils.proxy.refusedConnect': '代理拒绝 CONNECT(HTTP {status})/ proxy refused CONNECT',
101
+ 'utils.proxy.closedDuringConnect': '代理连接在 CONNECT 完成前关闭 / proxy closed during CONNECT',
102
+ // ---------- utils/session-log-repair.ts:会话日志修复计划/告警 ----------
103
+ 'utils.sessionRepair.closerReferenced': '收尾块区间的 seq 被保留行引用 → 本次不丢弃该收尾块(避免把引用静默改指)',
104
+ 'utils.sessionRepair.truncateBreaksRefs': '截断会让保留行的引用失去目标或被改指(seq {seqs})→ 拒绝本次修复',
105
+ 'utils.sessionRepair.keptRefersDropped': '保留行引用到本次将被丢弃的行(seq {seqs})→ 拒绝本次修复',
106
+ 'utils.sessionRepair.danglingRef': '先前就存在的悬空引用:seq {seq}(本次修复未丢弃它)',
107
+ 'utils.sessionRepair.dropDuplicateRows': '丢弃 {rows} 行重放重复事件(字节相同 + seq 相同)',
108
+ 'utils.sessionRepair.dropSyntheticCloser': '丢弃 {rows} 行崩溃恢复补写的收尾块(turn {turn} 之后仍在续写,真实内容保留)',
109
+ 'utils.sessionRepair.truncateTail': '在首个异常处截断:丢弃其后 {rows} 行(**有损**,备份可回滚)',
110
+ };
111
+ export const en = {
112
+ // ---------- utils/json.ts ----------
113
+ 'utils.json.depthDefault': 'JSON nesting depth exceeds the limit of {max}',
114
+ 'utils.json.bytesExceeded': 'JSON payload exceeds the limit of {max} bytes',
115
+ 'utils.json.parseFailedDeep': 'JSON parsing failed (possibly nested too deeply): {reason}',
116
+ 'utils.json.depthExceeded': 'JSON nesting depth {depth} exceeds the limit of {max}',
117
+ // ---------- utils/atomic-write.ts ----------
118
+ 'utils.atomicWrite.invalidBaseName': 'Atomic write: invalid target file name "{base}"',
119
+ 'utils.atomicWrite.illegalChars': 'Atomic write: target file name contains illegal characters "{base}"',
120
+ 'utils.atomicWrite.controlChars': 'Atomic write: target file name contains control characters "{base}"',
121
+ 'utils.atomicWrite.symlinkRejected': 'Atomic write: target is a symbolic link and the policy is reject — refusing to write "{target}"',
122
+ 'utils.atomicWrite.symlinkRejectedSync': 'Atomic write (sync): target is a symbolic link and the policy is reject — refusing to write "{target}"',
123
+ // ---------- utils/zip.ts ----------
124
+ 'utils.zip.entryCountWriteExceeded': 'ZIP entry count {count} exceeds the limit of {max} (write and read limits are the same; narrow the exported section range and retry)',
125
+ 'utils.zip.unsafeEntryNameWrite': 'Refusing to write an unsafe entry name: {name}',
126
+ 'utils.zip.entryInflateFailed': 'Entry "{name}" failed to decompress: {reason}',
127
+ 'utils.zip.unknownCompression': 'Entry "{name}" uses an unknown compression method {method}',
128
+ 'utils.zip.entryMissing': 'No such entry in the ZIP: {name}',
129
+ 'utils.zip.localHeaderCorrupt': 'Entry "{name}" has a corrupt local file header',
130
+ 'utils.zip.dataOutOfRange': 'Entry "{name}" data is out of range',
131
+ 'utils.zip.sizeMismatch': 'Entry "{name}" decompressed size mismatch ({actual} ≠ {expected})',
132
+ 'utils.zip.crcMismatch': 'Entry "{name}" CRC32 check failed (the ZIP is corrupt)',
133
+ 'utils.zip.totalUncompressedExceeded': 'Total uncompressed ZIP bytes exceed the limit',
134
+ 'utils.zip.ratioExceeded': 'Entry "{name}" compression ratio {ratio} exceeds the limit (possible zip bomb)',
135
+ 'utils.zip.tooSmall': 'Not a valid ZIP file (too small)',
136
+ 'utils.zip.missingEocd': 'Not a valid ZIP file (missing end of central directory record)',
137
+ 'utils.zip.centralDirOutOfRange': 'Central directory is out of range (corrupt ZIP)',
138
+ 'utils.zip.entryCountReadExceeded': 'ZIP entry count {count} exceeds the limit of {max}',
139
+ 'utils.zip.centralEntryOutOfRange': 'Central directory entry is out of range (corrupt ZIP)',
140
+ 'utils.zip.centralSignatureCorrupt': 'Central directory signature is corrupt',
141
+ 'utils.zip.unsafeEntryName': 'Unsafe ZIP entry name: {name}',
142
+ 'utils.zip.duplicateEntryName': 'Duplicate ZIP entry name: {name}',
143
+ 'utils.zip.symlinkEntryRejected': 'ZIP contains a symbolic link entry — rejected: {name}',
144
+ 'utils.zip.localHeaderOutOfRange': 'Entry "{name}" local file header is out of range',
145
+ 'utils.zip.compressedTotalExceeded': 'Total compressed ZIP bytes exceed the limit',
146
+ 'utils.zip.extractTargetOutOfRange': 'Extraction target is out of range: {name}',
147
+ // ---------- utils/env-lock.ts ----------
148
+ 'utils.envLock.probeFailed': 'Process probe failed for pid={pid}: {reason}',
149
+ 'utils.envLock.probeUndetermined': 'Process probe for pid={pid} was inconclusive',
150
+ 'utils.envLock.probeFailedShort': 'Process probe failed for pid={pid}',
151
+ 'utils.envLock.mkdirLocksDir': 'create lock directory {dir}',
152
+ 'utils.envLock.openWxPermissionNoExisting': "open('wx') {path}: permission/ACL error ({code}), and no existing lock file was found",
153
+ 'utils.envLock.writeOwner': 'write owner {path}',
154
+ 'utils.envLock.releaseForeignToken': 'release: token does not match the current holder (foreign/taken over) — refusing to unlink',
155
+ 'utils.envLock.releaseOwnershipUnreadable': 'release: on-disk ownership is unreadable/corrupt (possibly recovered abnormally or edited by hand) — refusing to unlink (ownership-lost)',
156
+ 'utils.envLock.releaseOwnershipMismatch': 'release: on-disk ownership.instanceId={diskId} !== this token {tokenId} (ownership-lost)',
157
+ 'utils.envLock.releaseUnlinkFailed': 'release: unlink {path} failed: {reason}',
158
+ 'utils.envLock.ownershipMissing': 'ownership file does not exist (possibly being created right now)',
159
+ 'utils.envLock.ownershipCorrupt': 'ownership exists but has no valid owner (possibly an empty/corrupt file left by a crash); not deleted, not executed; reclaim with --recover-stale-lock',
160
+ 'utils.envLock.heartbeatPermissionDenied': 'Cannot read heartbeat (permission/ACL) — conservatively refusing',
161
+ 'utils.envLock.heartbeatReadFailed': 'Heartbeat read failed — conservatively refusing',
162
+ 'utils.envLock.ownerConfirmedDead': 'owner pid={pid} is confirmed gone (heartbeat expired)',
163
+ 'utils.envLock.ownerAliveUnverifiable': 'owner pid={pid} is alive but its process identity cannot be verified (possible PID reuse), and heartbeat {longExpired} → judged a stale lock, explicit reclaim allowed',
164
+ 'utils.envLock.heartbeatExpiredNoIdentity': 'heartbeat expired and pid={pid} is alive, but the OS process identity cannot be obtained reliably — conservatively refusing to delete',
165
+ 'utils.envLock.pidReuse': 'pid reuse: pid={pid} identity differs from the recorded one',
166
+ 'utils.envLock.ownerAliveDegraded': 'owner process is alive (heartbeat may be degraded), not stale',
167
+ 'utils.envLock.notStaleRefuseRecovery': 'not stale — refusing recovery',
168
+ 'utils.envLock.ownershipGone': 'ownership has disappeared (taken over/cleared by someone else) — stopping recovery',
169
+ 'utils.envLock.captureRenameFailed': 'capture rename failed (possibly taken over by someone else) — stopping',
170
+ 'utils.envLock.captureReadFailed': 'reading the captured file failed; keeping the recovering file for diagnosis',
171
+ 'utils.envLock.corruptReclaimAnomaly': 'corrupt reclaim second verification anomaly: the captured file has a valid owner; keeping {name} quarantined and not overwriting the current environment.lock',
172
+ 'utils.envLock.reverifyFailed': 'second verification failed: keeping {name} quarantined and not overwriting the current environment.lock (if any successor exists)',
173
+ 'utils.envLock.removeCapturedFailed': 'failed to delete the captured file',
174
+ 'utils.envLock.removedStaleOwnership': 'removed stale ownership (op={op}, pid={pid})',
175
+ 'utils.envLock.removedCorruptOwnership': 'removed the corrupt/crash-left ownership file',
176
+ 'utils.envLock.longExpired': 'expired {age} ago (threshold {threshold})',
177
+ 'utils.envLock.durationDays': '{value} days',
178
+ 'utils.envLock.durationHours': '{value} hours',
179
+ 'utils.envLock.durationMinutes': '{value} minutes',
180
+ 'utils.envLock.durationSeconds': '{value} seconds',
181
+ 'utils.envLock.ioPermissionNotLock': '{what}: permission/ACL error ({code}), not lock contention',
182
+ 'utils.envLock.block.locked': 'Another task is running, please retry later.',
183
+ 'utils.envLock.block.blocked': 'Configuration changes are protected — resolve the recovery items before continuing.',
184
+ 'utils.envLock.block.unavailable': 'The operation cannot run right now, please retry later; if it keeps failing, check the logs.',
185
+ 'utils.envLock.block.stale': 'A configuration lock left behind by a previous abnormal exit was detected (its owning process no longer exists), so the operation was blocked.'
186
+ + 'This lock will not clear itself — retrying or restarting DSH will not help: click "Reclaim stale lock" under "Incident recovery",'
187
+ + 'or run dsh-config-manager recover-stale-lock and then retry.',
188
+ 'utils.envLock.brief.locked': 'Environment lock held by another task',
189
+ 'utils.envLock.brief.blocked': 'Configuration changes are protected; resolve recovery items first',
190
+ 'utils.envLock.brief.unavailable': 'Environment lock temporarily unavailable',
191
+ 'utils.envLock.brief.stale': 'Stale configuration lock (owning process is gone); reclaim it first',
192
+ // ---------- utils/proxy.ts ----------
193
+ 'utils.proxy.connectTimeout': 'Proxy connection timed out ({ms}ms)',
194
+ 'utils.proxy.malformedConnectResponse': 'Malformed proxy CONNECT response (headers too long)',
195
+ 'utils.proxy.refusedConnect': 'Proxy refused CONNECT (HTTP {status})',
196
+ 'utils.proxy.closedDuringConnect': 'Proxy connection closed before CONNECT completed',
197
+ // ---------- utils/session-log-repair.ts ----------
198
+ 'utils.sessionRepair.closerReferenced': 'The seq range of the closer block is referenced by kept rows → this run will not drop that closer block (avoid silently redirecting references)',
199
+ 'utils.sessionRepair.truncateBreaksRefs': 'Truncation would leave references from kept rows without a target, or redirect them (seq {seqs}) → refusing this repair',
200
+ 'utils.sessionRepair.keptRefersDropped': 'Kept rows reference rows that would be dropped by this repair (seq {seqs}) → refusing this repair',
201
+ 'utils.sessionRepair.danglingRef': 'Pre-existing dangling reference: seq {seq} (this repair does not drop it)',
202
+ 'utils.sessionRepair.dropDuplicateRows': 'Drop {rows} row(s) that replay duplicate events (identical bytes + identical seq)',
203
+ 'utils.sessionRepair.dropSyntheticCloser': 'Drop {rows} row(s) of the crash-recovery closer block (writing continued after turn {turn}; real content is preserved)',
204
+ 'utils.sessionRepair.truncateTail': 'Truncate at the first anomaly: drop the following {rows} row(s) (**lossy**, a backup can roll it back)',
205
+ };
206
+ registerMessageCatalog({ id: 'utils', zh, en });
207
+ /**
208
+ * 本域唯一出口:取一条 src/utils/** 的文案(键名受类型约束,拼错即编译错误)。
209
+ *
210
+ * 为什么不让调用方直接 import engineMsg:import 本函数即保证目录已注册(模块加载期注册一次),
211
+ * 否则会出现「用了键但注册表里没有 → 输出裸键名」的静默失败。
212
+ */
213
+ export function utilsMsg(key, params) {
214
+ return engineMsg(key, params);
215
+ }
216
+ //# sourceMappingURL=messages-utils.js.map
@@ -23,10 +23,19 @@ import http from 'node:http';
23
23
  import https from 'node:https';
24
24
  import net from 'node:net';
25
25
  import tls from 'node:tls';
26
+ import { utilsMsg } from './messages-utils.js';
26
27
  /** 环境变量开关:`DSH_CONFIG_MANAGER_PROXY=off|none|0` 时即使配了代理也强制直连(调试/特殊情况)。 */
27
28
  const PROXY_OFF_VALUES = new Set(['off', 'none', '0', 'false', 'disable', 'disabled']);
28
29
  /** 隧道建立与单次请求的默认上限(避免代理不可达时无限挂起)。 */
29
30
  const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;
31
+ /**
32
+ * 默认 User-Agent(issue #86)。
33
+ *
34
+ * node 原生 `http`/`https` **不会**像 undici 那样自动补 UA,而本模块正是「配了代理」时唯一
35
+ * 的出站通道 —— 于是同一份代码出现「不配代理能用、配了代理反而 403」(GitHub 前置层直接拒绝
36
+ * 无 UA 请求)。统一兜底使两条通道的请求语义一致;调用方显式传入时绝不覆盖。
37
+ */
38
+ const DEFAULT_USER_AGENT = 'DSH-Config-Manager';
30
39
  /** 视为可跟随的重定向状态码(与 WebDAV 通道同一套语义) */
31
40
  const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
32
41
  /** 不能带响应体的状态码(`new Response(body, {status})` 会抛错) */
@@ -171,7 +180,7 @@ function connectToProxy(proxy, timeoutMs) {
171
180
  : net.connect({ host: proxy.hostname, port }, onConnect);
172
181
  socket.setTimeout(timeoutMs, () => {
173
182
  socket.destroy();
174
- reject(new Error(`代理连接超时 / proxy connect timed out (${timeoutMs}ms)`));
183
+ reject(new Error(utilsMsg('utils.proxy.connectTimeout', { ms: timeoutMs })));
175
184
  });
176
185
  socket.once('error', (err) => { reject(err); });
177
186
  });
@@ -207,7 +216,7 @@ async function openTunnel(target, proxy, timeoutMs) {
207
216
  if (end === -1) {
208
217
  if (buffered.length > 8192) {
209
218
  socket.off('data', onData);
210
- fail('代理 CONNECT 响应异常(头部过长)/ malformed CONNECT response');
219
+ fail(utilsMsg('utils.proxy.malformedConnectResponse'));
211
220
  }
212
221
  return;
213
222
  }
@@ -215,7 +224,7 @@ async function openTunnel(target, proxy, timeoutMs) {
215
224
  const head = buffered.slice(0, end);
216
225
  const status = Number((head.split('\r\n')[0] ?? '').split(' ')[1]);
217
226
  if (status !== 200) {
218
- fail(`代理拒绝 CONNECT(HTTP ${status})/ proxy refused CONNECT`);
227
+ fail(utilsMsg('utils.proxy.refusedConnect', { status }));
219
228
  return;
220
229
  }
221
230
  // CONNECT 响应头之后的字节已属隧道数据 → 回填,避免 TLS 握手丢包
@@ -226,7 +235,7 @@ async function openTunnel(target, proxy, timeoutMs) {
226
235
  };
227
236
  socket.on('data', onData);
228
237
  socket.once('error', (err) => { socket.off('data', onData); reject(err); });
229
- socket.once('close', () => { socket.off('data', onData); reject(new Error('代理连接在 CONNECT 完成前关闭 / proxy closed during CONNECT')); });
238
+ socket.once('close', () => { socket.off('data', onData); reject(new Error(utilsMsg('utils.proxy.closedDuringConnect'))); });
230
239
  socket.write(lines.join('\r\n'));
231
240
  });
232
241
  }
@@ -275,9 +284,13 @@ export async function requestOnce(options) {
275
284
  const payload = options.body === undefined
276
285
  ? undefined
277
286
  : Buffer.isBuffer(options.body) ? options.body : Buffer.from(options.body, 'utf8');
287
+ // 调用方显式传入的 User-Agent(大小写不敏感)优先;缺省补默认 UA(见 DEFAULT_USER_AGENT)。
288
+ const callerHeaders = options.headers ?? {};
289
+ const hasUserAgent = Object.keys(callerHeaders).some((key) => key.toLowerCase() === 'user-agent');
278
290
  const headers = {
279
291
  ...(payload !== undefined ? { 'content-length': String(payload.length) } : {}),
280
- ...(options.headers ?? {}),
292
+ ...(hasUserAgent ? {} : { 'user-agent': DEFAULT_USER_AGENT }),
293
+ ...callerHeaders,
281
294
  };
282
295
  /** url 非 null → 以 URL 形态发起(node 自行解析 host/port/path/TLS);否则按显式 options 发起。 */
283
296
  const send = (lib, url, requestOptions) => new Promise((resolve, reject) => {
@@ -6,11 +6,33 @@ export interface SkippedLink {
6
6
  * broken=断链;unreadable=目标既非文件也非目录(设备/FIFO 等);too-deep=超过深度上限 */
7
7
  reason: 'loop' | 'outside-home' | 'broken' | 'unreadable' | 'too-deep';
8
8
  }
9
+ /**
10
+ * 被整棵跳过的「运行时依赖目录」(任意深度的 `node_modules`,issue #76)。
11
+ *
12
+ * 为什么是硬规则:技能目录自带依赖时,`skills` 分区能被遍历出上万个条目,撞上导出侧的
13
+ * 10 000 条目闸门 → **定时备份永久失败且用户无解**(定时备份与「立即备份」都没有分区范围,
14
+ * `backup-schedule.json` 也没有排除项)。而 `plugins` 分区早就按 `npm pack` 的产物度量
15
+ * (`core/local-plugin-pack.ts` 明写「node_modules 由 npm pack 自身排除」)、宿主的
16
+ * `dirSizeBytes` 也显式跳过它 —— 同一个概念此前在通用递归里缺了同一排除项。
17
+ *
18
+ * 依赖树可由包内保留的 `package.json` / lock 重建,不属于用户配置,故不进备份;
19
+ * 但**必须留痕**(见 RecursiveListing.skippedDepDirs),绝不静默。
20
+ */
21
+ export declare const EXCLUDED_DEP_DIR_NAMES: readonly string[];
22
+ /** 被跳过的运行时依赖目录(内容未进备份;必须在报告里可见) */
23
+ export interface SkippedDepDir {
24
+ /** homeDir 相对路径(被跳过的目录本身) */
25
+ path: string;
26
+ /** 该目录下递归的普通文件数;null = 计数预算耗尽未统计(不猜,也不假装是 0) */
27
+ files: number | null;
28
+ }
9
29
  export interface RecursiveListing {
10
30
  /** homeDir 相对、斜杠分隔、已排序的文件路径 */
11
31
  paths: string[];
12
32
  /** 被跳过的链接(含原因);空 = 本次遍历无内容缺失 */
13
33
  skippedLinks: SkippedLink[];
34
+ /** 因「可重建的运行时依赖」整棵跳过的目录(EXCLUDED_DEP_DIR_NAMES);空 = 本次没有跳过 */
35
+ skippedDepDirs: SkippedDepDir[];
14
36
  /** 成功跟随(内容已收集)的链接数;用于「链接结构不会被还原」的提示 */
15
37
  followedLinks: number;
16
38
  /** 读取失败的目录(ACL / 竞态删除等)——这些目录下的内容同样没进备份,必须留痕 */
@@ -16,8 +16,29 @@
16
16
  import fs from 'node:fs/promises';
17
17
  import path from 'node:path';
18
18
  import { isSameOrChild } from './paths.js';
19
+ /**
20
+ * 被整棵跳过的「运行时依赖目录」(任意深度的 `node_modules`,issue #76)。
21
+ *
22
+ * 为什么是硬规则:技能目录自带依赖时,`skills` 分区能被遍历出上万个条目,撞上导出侧的
23
+ * 10 000 条目闸门 → **定时备份永久失败且用户无解**(定时备份与「立即备份」都没有分区范围,
24
+ * `backup-schedule.json` 也没有排除项)。而 `plugins` 分区早就按 `npm pack` 的产物度量
25
+ * (`core/local-plugin-pack.ts` 明写「node_modules 由 npm pack 自身排除」)、宿主的
26
+ * `dirSizeBytes` 也显式跳过它 —— 同一个概念此前在通用递归里缺了同一排除项。
27
+ *
28
+ * 依赖树可由包内保留的 `package.json` / lock 重建,不属于用户配置,故不进备份;
29
+ * 但**必须留痕**(见 RecursiveListing.skippedDepDirs),绝不静默。
30
+ */
31
+ export const EXCLUDED_DEP_DIR_NAMES = ['node_modules'];
19
32
  /** 深度上限:防御病态目录树 / realpath 去重失效时的栈爆炸 */
20
33
  const MAX_DEPTH = 64;
34
+ /**
35
+ * 依赖目录计数的**全局预算**(本次遍历合计;按**条目**扣,文件与目录都算)。
36
+ *
37
+ * 计数只走 readdir(不 stat、不读内容、不跟随链接),但病态树仍可能是成千上万次系统调用 ——
38
+ * 超预算即停,未统计的目录记 `files: null`。宁可少一个数字,也不让「跳过一个目录」变成
39
+ * 第二次全量遍历;按条目而非只按文件扣,是为了挡「上万层空目录」这种只要目录不要文件的树。
40
+ */
41
+ const MAX_DEP_FILES_COUNTED = 100_000;
21
42
  function normalizeSlashes(p) {
22
43
  return p.split(path.sep).join('/');
23
44
  }
@@ -29,6 +50,47 @@ async function realpathSafe(p) {
29
50
  return null;
30
51
  }
31
52
  }
53
+ /** 该路径(跟随链接)是否目录;读不到 → false(按「不是依赖目录」处理,宁可多带不可误丢) */
54
+ async function statIsDir(p) {
55
+ try {
56
+ return (await fs.stat(p)).isDirectory();
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ }
62
+ /**
63
+ * 只数文件个数的有界递归(不 stat、不读内容、不跟随链接;预算按条目扣,见 MAX_DEP_FILES_COUNTED)。
64
+ * @returns 完整计数;预算耗尽 → null(下界不冒充精确值)
65
+ */
66
+ async function countFilesBounded(dir, budget) {
67
+ let files = 0;
68
+ const stack = [dir];
69
+ while (stack.length > 0) {
70
+ const cur = stack.pop();
71
+ if (cur === undefined)
72
+ break;
73
+ let entries;
74
+ try {
75
+ entries = await fs.readdir(cur, { withFileTypes: true });
76
+ }
77
+ catch {
78
+ continue;
79
+ }
80
+ for (const entry of entries) {
81
+ if (budget.left <= 0)
82
+ return null;
83
+ budget.left -= 1;
84
+ if (entry.isDirectory()) {
85
+ stack.push(path.join(cur, entry.name));
86
+ continue;
87
+ }
88
+ if (entry.isFile())
89
+ files += 1;
90
+ }
91
+ }
92
+ return files;
93
+ }
32
94
  /**
33
95
  * 遍历 baseDir 下的所有文件(跟随目录链接)。
34
96
  * @param baseDir 绝对路径;不在 homeDir 内则返回空清单(与既有语义一致)
@@ -44,6 +106,8 @@ export async function listRecursiveFollowingLinks(baseDir, homeDir) {
44
106
  const homeReal = (await realpathSafe(home)) ?? home;
45
107
  const paths = [];
46
108
  const skippedLinks = [];
109
+ const skippedDepDirs = [];
110
+ const depFileBudget = { left: MAX_DEP_FILES_COUNTED };
47
111
  const unreadableDirs = [];
48
112
  let followedLinks = 0;
49
113
  /** 已进入过的目录 realpath(防环 / 防同一目标被两条链接重复收集) */
@@ -86,6 +150,16 @@ export async function listRecursiveFollowingLinks(baseDir, homeDir) {
86
150
  for (const entry of entries) {
87
151
  const abs = path.join(dir, entry.name);
88
152
  const entryRel = rel(abs);
153
+ // issue #76:运行时依赖目录**整棵跳过**(任意深度)。内容不进备份,但必须留痕(见 SkippedDepDir)。
154
+ // 链接形态也算(junction 指向的 node_modules 同样是依赖树);判不出类型时按普通目录继续走。
155
+ if (EXCLUDED_DEP_DIR_NAMES.includes(entry.name)
156
+ && (entry.isDirectory() || (entry.isSymbolicLink() && (await statIsDir(abs))))) {
157
+ skippedDepDirs.push({
158
+ path: entryRel,
159
+ files: depFileBudget.left > 0 ? await countFilesBounded(abs, depFileBudget) : null,
160
+ });
161
+ continue;
162
+ }
89
163
  if (entry.isDirectory()) {
90
164
  await walk(abs, depth + 1, null);
91
165
  }
@@ -128,6 +202,6 @@ export async function listRecursiveFollowingLinks(baseDir, homeDir) {
128
202
  return true;
129
203
  };
130
204
  await walk(base, 0, null);
131
- return { paths: paths.sort(), skippedLinks, followedLinks, unreadableDirs };
205
+ return { paths: paths.sort(), skippedLinks, skippedDepDirs, followedLinks, unreadableDirs };
132
206
  }
133
207
  //# sourceMappingURL=recursive-walk.js.map
@@ -19,6 +19,7 @@
19
19
  import fs from 'node:fs/promises';
20
20
  import { randomBytes } from 'node:crypto';
21
21
  import { basename, dirname, join } from 'node:path';
22
+ import { utilsMsg } from './messages-utils.js';
22
23
  import { isSessionLogName } from './session-log.js';
23
24
  import { collectSessionRowReferences, findSyntheticCloserRun, parseSessionRowFacts, referencesContain, sessionRowMayHaveReferences, walkSessionRowContinuity, } from './session-row-facts.js';
24
25
  import { decodeZstdFrame, encodeZstdFrame, scanZstdFrames, zstdAvailable } from './zstd-frame.js';
@@ -270,7 +271,7 @@ export function planSessionLogRepair(bytes, opts = {}) {
270
271
  const referencing = firstKeptRowReferencing(rows, keptWithoutCloser, candidateSeqs);
271
272
  if (referencing !== undefined) {
272
273
  closerSkippedByReference = true;
273
- warnings.push('收尾块区间的 seq 被保留行引用 → 本次不丢弃该收尾块(避免把引用静默改指)');
274
+ warnings.push(utilsMsg('utils.sessionRepair.closerReferenced'));
274
275
  }
275
276
  else {
276
277
  const anomalyAfter = walkRows(rows, keptWithoutCloser).firstAnomalyIndex;
@@ -304,9 +305,9 @@ export function planSessionLogRepair(bytes, opts = {}) {
304
305
  // 整个计划直接 refuse —— 绝不返回一个「注定在 apply 期被拒」的有损预览。
305
306
  const closure = compareReferenceClosure(rows, before, kept, keptAfterTruncation);
306
307
  if (closure.missing.length > 0 || closure.redirected.length > 0) {
307
- warnings.push('截断会让保留行的引用失去目标或被改指(seq ' +
308
- [...closure.missing, ...closure.redirected].sort((a, b) => a - b).join(', ') +
309
- ')→ 拒绝本次修复');
308
+ warnings.push(utilsMsg('utils.sessionRepair.truncateBreaksRefs', {
309
+ seqs: [...closure.missing, ...closure.redirected].sort((a, b) => a - b).join(', '),
310
+ }));
310
311
  return { ok: false, reason: 'dropped-ref', actions: [], lossy: false, warnings };
311
312
  }
312
313
  if (opts.allowLossy !== true) {
@@ -330,11 +331,11 @@ export function planSessionLogRepair(bytes, opts = {}) {
330
331
  // 先前就存在的悬空引用(引用的 seq 本来就不在日志里)**只告警**,绝不 refuse。
331
332
  const finalClosure = compareReferenceClosure(rows, before, kept, kept);
332
333
  if (finalClosure.missing.length > 0 || finalClosure.redirected.length > 0) {
333
- warnings.push('保留行引用到本次将被丢弃的行(seq ' + [...finalClosure.missing, ...finalClosure.redirected].join(', ') + ')→ 拒绝本次修复');
334
+ warnings.push(utilsMsg('utils.sessionRepair.keptRefersDropped', { seqs: [...finalClosure.missing, ...finalClosure.redirected].join(', ') }));
334
335
  return { ok: false, reason: 'dropped-ref', actions, lossy: false, warnings };
335
336
  }
336
337
  for (const seq of finalClosure.dangling)
337
- warnings.push('先前就存在的悬空引用:seq ' + String(seq) + '(本次修复未丢弃它)');
338
+ warnings.push(utilsMsg('utils.sessionRepair.danglingRef', { seq }));
338
339
  return {
339
340
  ok: true,
340
341
  actions,
@@ -353,7 +354,7 @@ function buildActions(dupRows, closerDropped, closerRun, truncateRows) {
353
354
  if (dupRows > 0) {
354
355
  actions.push({
355
356
  code: 'drop-duplicate-rows',
356
- detail: '丢弃 ' + String(dupRows) + ' 行重放重复事件(字节相同 + seq 相同)',
357
+ detail: utilsMsg('utils.sessionRepair.dropDuplicateRows', { rows: dupRows }),
357
358
  lossy: false,
358
359
  rows: dupRows,
359
360
  });
@@ -361,7 +362,7 @@ function buildActions(dupRows, closerDropped, closerRun, truncateRows) {
361
362
  if (closerDropped && closerRun !== undefined) {
362
363
  actions.push({
363
364
  code: 'drop-synthetic-closer',
364
- detail: '丢弃 ' + String(closerRun.end - closerRun.start) + ' 行崩溃恢复补写的收尾块(turn ' + String(closerRun.turn) + ' 之后仍在续写,真实内容保留)',
365
+ detail: utilsMsg('utils.sessionRepair.dropSyntheticCloser', { rows: closerRun.end - closerRun.start, turn: closerRun.turn }),
365
366
  lossy: false,
366
367
  rows: closerRun.end - closerRun.start,
367
368
  });
@@ -369,7 +370,7 @@ function buildActions(dupRows, closerDropped, closerRun, truncateRows) {
369
370
  if (truncateRows > 0) {
370
371
  actions.push({
371
372
  code: 'truncate-tail',
372
- detail: '在首个异常处截断:丢弃其后 ' + String(truncateRows) + ' 行(**有损**,备份可回滚)',
373
+ detail: utilsMsg('utils.sessionRepair.truncateTail', { rows: truncateRows }),
373
374
  lossy: true,
374
375
  rows: truncateRows,
375
376
  });