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
@@ -34,13 +34,16 @@ import { MARKET_INDEX_SCHEMA_VERSION } from './types.js';
34
34
  import { stringifyJsonSafe } from '../utils/json.js';
35
35
  import { sha256Hex } from '../utils/hashing.js';
36
36
  import { redact } from '../security/redaction.js';
37
+ import { marketMsg } from './messages-market.js';
37
38
  /* ---------------------------------------------------------------- 常量(固定规则,无配置入口) */
38
39
  /** 官方收录目标仓库原样复用 t1 常量(github-repos.ts)
39
40
  * —— 与内置市场同一仓库,界面不提供任何修改入口(设计 §2.4 产品决策)。 */
40
41
  /** 用户公开仓库固定名:<login>/dsh-configs(系统按规则生成,不可配置) */
41
42
  export const USER_CONFIGS_REPO = 'dsh-configs';
42
- /** 用户仓库 index.json 的名称/描述(展示用) */
43
- export const USER_INDEX_NAME = '我的配置仓库';
43
+ /** 用户仓库 index.json 的名称/描述(展示用;按当前界面语言在**取用时**解析,不冻结在模块加载期) */
44
+ export function userIndexName() {
45
+ return marketMsg('market.myRepo.indexName');
46
+ }
44
47
  /** PR 分支固定名模式:dsh-market-sync/<itemId>(收录) */
45
48
  export const PR_BRANCH_PREFIX = 'dsh-market-sync';
46
49
  /** 下架 PR 分支固定名模式:dsh-market-delist/<itemId>(删除已收录条目;独立分支避免与收录分支混淆,
@@ -101,7 +104,7 @@ export function bumpVersion(version) {
101
104
  }
102
105
  /** 空用户索引(首次上传时创建) */
103
106
  function emptyIndex() {
104
- return { schemaVersion: MARKET_INDEX_SCHEMA_VERSION, name: USER_INDEX_NAME, items: [] };
107
+ return { schemaVersion: MARKET_INDEX_SCHEMA_VERSION, name: userIndexName(), items: [] };
105
108
  }
106
109
  /** upsert 条目:同 id 覆盖(保持原位置),否则追加 */
107
110
  function upsertIndexItem(index, item) {
@@ -168,10 +171,10 @@ export class MyRepoService {
168
171
  loginWriteQueues = new Map();
169
172
  constructor(options) {
170
173
  if (options.prepare === null || typeof options.prepare !== 'function') {
171
- throw new MyRepoError('prepare 必填');
174
+ throw new MyRepoError(marketMsg('market.myRepo.prepareRequired'));
172
175
  }
173
176
  if (options.rest === null || typeof options.rest !== 'object') {
174
- throw new MyRepoError('rest 必填');
177
+ throw new MyRepoError(marketMsg('market.myRepo.restRequired'));
175
178
  }
176
179
  this.prepare = options.prepare;
177
180
  this.rest = options.rest;
@@ -197,7 +200,7 @@ export class MyRepoService {
197
200
  return []; // 从未上传
198
201
  const parsed = parseMarketIndex(indexText);
199
202
  if (!parsed.ok || parsed.index === null) {
200
- throw new MyRepoError('用户仓库 index.json 无法解析(可能被外部修改)', 'index_invalid');
203
+ throw new MyRepoError(marketMsg('market.myRepo.userIndexUnparsable'), 'index_invalid');
201
204
  }
202
205
  // 收录状态:官方 index 含该 id → listed;否则按设计 §4.5 以 head=<login>:<branch>
203
206
  // 过滤 open PR(避免跨用户同名分支误报)→ pr-pending;否则 not-listed
@@ -284,7 +287,7 @@ export class MyRepoService {
284
287
  const { index } = await this.readUserIndex(login);
285
288
  const item = index.items.find((it) => it.id === itemId);
286
289
  if (item === undefined) {
287
- throw new MyRepoError(`未找到条目 ${itemId}(可能已被删除),请刷新列表后重试`, 'item_not_found');
290
+ throw new MyRepoError(marketMsg('market.myRepo.itemNotFound', { itemId }), 'item_not_found');
288
291
  }
289
292
  const repoUrl = userConfigsRepoUrl(login);
290
293
  this.launchListing({
@@ -305,7 +308,7 @@ export class MyRepoService {
305
308
  if (job !== undefined && job.status !== 'pending')
306
309
  return this.toListingStatus(job);
307
310
  if (Date.now() >= deadline) {
308
- throw new MyRepoError(`等待市场任务超时(${itemId})`, 'internal');
311
+ throw new MyRepoError(marketMsg('market.myRepo.listingTimeout', { itemId }), 'internal');
309
312
  }
310
313
  await new Promise((resolve) => setTimeout(resolve, 25));
311
314
  }
@@ -329,7 +332,7 @@ export class MyRepoService {
329
332
  const { index } = await this.readUserIndex(login);
330
333
  const item = index.items.find((it) => it.id === itemId);
331
334
  if (item === undefined) {
332
- throw new MyRepoError(`未找到要删除的条目 ${itemId}(可能已被删除),请刷新列表后重试`, 'item_not_found');
335
+ throw new MyRepoError(marketMsg('market.myRepo.itemToDeleteNotFound', { itemId }), 'item_not_found');
333
336
  }
334
337
  // ② 移除条目 + 删除 items/<itemId>/ 目录 → 写回 index.json(per-login 串行)
335
338
  const updatedIndex = removeIndexItem(index, itemId);
@@ -367,7 +370,7 @@ export class MyRepoService {
367
370
  delisted: true,
368
371
  prNumber: null,
369
372
  prUrl: null,
370
- warnings: ['该条目已收录官方市场,已自动提交下架 PR;合并前官方市场可能仍显示该条目'],
373
+ warnings: [marketMsg('market.myRepo.delistPrSubmitted')],
371
374
  };
372
375
  }
373
376
  // 未收录 → 关闭待审核的收录 PR(如有)
@@ -381,7 +384,7 @@ export class MyRepoService {
381
384
  delisted: false,
382
385
  prNumber: pr.number,
383
386
  prUrl: pr.htmlUrl,
384
- warnings: ['已关闭该条目的待审核收录 PR'],
387
+ warnings: [marketMsg('market.myRepo.pendingPrClosed')],
385
388
  };
386
389
  }
387
390
  return { ok: true, itemId, delisted: false, prNumber: null, prUrl: null, warnings: [] };
@@ -429,12 +432,12 @@ export class MyRepoService {
429
432
  }
430
433
  else if (explicitId !== null) {
431
434
  // 明确要求更新一个不存在的 id → 报错(避免静默新建导致用户以为更新成功)
432
- throw new MyRepoError(`未找到要更新的条目 ${explicitId}(可能已被移除),请刷新列表后重试`, 'item_not_found');
435
+ throw new MyRepoError(marketMsg('market.myRepo.itemToUpdateNotFound', { itemId: explicitId }), 'item_not_found');
433
436
  }
434
437
  else {
435
438
  itemId = uniqueItemId(baseId, ids); // 名称变化 → 作为新条目
436
439
  version = '1.0.0';
437
- warnings.push('未找到同名已上传条目,名称变化将作为新条目发布');
440
+ warnings.push(marketMsg('market.myRepo.nameChangedNewItem'));
438
441
  }
439
442
  }
440
443
  else {
@@ -509,7 +512,7 @@ export class MyRepoService {
509
512
  return { index: emptyIndex(), exists: false };
510
513
  const parsed = parseMarketIndex(text);
511
514
  if (!parsed.ok || parsed.index === null) {
512
- throw new MyRepoError('用户仓库 index.json 无法解析(可能被外部修改),已中止,请检查后重试', 'index_invalid');
515
+ throw new MyRepoError(marketMsg('market.myRepo.userIndexUnparsableAborted'), 'index_invalid');
513
516
  }
514
517
  return { index: parsed.index, exists: true };
515
518
  }
@@ -517,11 +520,11 @@ export class MyRepoService {
517
520
  async readOfficialIndex() {
518
521
  const text = await this.rest.readFile(MARKET_UPSTREAM_OWNER, MARKET_UPSTREAM_REPO, 'index.json');
519
522
  if (text === null) {
520
- throw new MyRepoError('官方市场 index.json 不存在', 'official_index_missing');
523
+ throw new MyRepoError(marketMsg('market.myRepo.officialIndexMissing'), 'official_index_missing');
521
524
  }
522
525
  const parsed = parseMarketIndex(text);
523
526
  if (!parsed.ok || parsed.index === null) {
524
- throw new MyRepoError('官方市场 index.json 无法解析', 'official_index_invalid');
527
+ throw new MyRepoError(marketMsg('market.myRepo.officialIndexUnparsable'), 'official_index_invalid');
525
528
  }
526
529
  return { index: parsed.index };
527
530
  }
@@ -529,7 +532,7 @@ export class MyRepoService {
529
532
  parsePreparedManifest(manifestText) {
530
533
  const parsed = parseMarketItemManifest(manifestText);
531
534
  if (!parsed.ok || parsed.manifest === null) {
532
- throw new MyRepoError('prepare 生成的 manifest 无效', 'internal');
535
+ throw new MyRepoError(marketMsg('market.myRepo.preparedManifestInvalid'), 'internal');
533
536
  }
534
537
  return parsed.manifest;
535
538
  }
@@ -546,7 +549,7 @@ export class MyRepoService {
546
549
  fork: false,
547
550
  };
548
551
  }
549
- return this.rest.createPublicRepo(USER_CONFIGS_REPO, '我的配置仓库(dsh-config-manager 自动发布)');
552
+ return this.rest.createPublicRepo(USER_CONFIGS_REPO, marketMsg('market.myRepo.repoDescription'));
550
553
  }
551
554
  /** ⑨ ensure PR:open PR(head=<login>:<branch>)存在 → 复用;否则 openPullRequest(base=main)。
552
555
  * action='delist' 时标题/正文为下架语义(分支用 delistBranchFor,head 过滤天然隔离)。 */
@@ -559,26 +562,26 @@ export class MyRepoService {
559
562
  return this.rest.openPullRequest({
560
563
  head,
561
564
  base: 'main',
562
- title: `市场下架:${itemId}`,
565
+ title: marketMsg('market.myRepo.delistPrTitle', { itemId }),
563
566
  body: [
564
- `由 dsh-config-manager「删除条目」自动提交。`,
567
+ marketMsg('market.myRepo.delistPrIntro'),
565
568
  '',
566
- `- 条目:${itemId}`,
567
- `- 操作:从官方市场 index.json 移除该条目(下架)`,
568
- `- 内容:原条目内容位于用户公开仓库,已随删除移除`,
569
+ marketMsg('market.myRepo.delistPrItemLine', { itemId }),
570
+ marketMsg('market.myRepo.delistPrAction'),
571
+ marketMsg('market.myRepo.delistPrContent'),
569
572
  ].join('\n'),
570
573
  });
571
574
  }
572
575
  return this.rest.openPullRequest({
573
576
  head,
574
577
  base: 'main',
575
- title: `市场收录:${itemId} v${version}`,
578
+ title: marketMsg('market.myRepo.listPrTitle', { itemId, version }),
576
579
  body: [
577
- `由 dsh-config-manager「一键上传」自动提交。`,
580
+ marketMsg('market.myRepo.listPrIntro'),
578
581
  '',
579
- `- 条目:${itemId} v${version}`,
580
- `- 内容托管:用户公开仓库(见 index.json 条目 repo 字段)`,
581
- `- 校验:dsh-config-market CI(validate.yml)将自动执行 8 道校验与秘密扫描`,
582
+ marketMsg('market.myRepo.listPrItemLine', { itemId, version }),
583
+ marketMsg('market.myRepo.listPrHosting'),
584
+ marketMsg('market.myRepo.listPrValidation'),
582
585
  ].join('\n'),
583
586
  });
584
587
  }
@@ -626,7 +629,7 @@ export class MyRepoService {
626
629
  const { index } = await this.readUserIndex(job.login);
627
630
  const item = index.items.find((it) => it.id === job.itemId);
628
631
  if (item === undefined) {
629
- throw new MyRepoError(`条目 ${job.itemId} 已不在用户仓库(可能已删除),任务终止`, 'item_not_found');
632
+ throw new MyRepoError(marketMsg('market.myRepo.itemGoneFromUserRepo', { itemId: job.itemId }), 'item_not_found');
630
633
  }
631
634
  updatedOfficial = upsertIndexItem(officialIndex.index, { ...item, repo: job.repoUrl });
632
635
  commitMsg = `market: ${item.version !== undefined && item.version !== '' ? 'update' : 'add'} ${job.itemId} v${job.version}`;
@@ -19,6 +19,7 @@ import { sectionMeta } from '../schema/section-registry.js';
19
19
  import { scanAndRedact, scanText } from '../security/secret-scanner.js';
20
20
  import { validateMarketRepoUrl } from './url.js';
21
21
  import { assertSafeItemId, BANNED_MARKET_SECTIONS, MARKET_ITEM_SCHEMA_VERSION, MAX_MARKET_ZIP_BYTES, } from './types.js';
22
+ import { marketMsg } from './messages-market.js';
22
23
  export class MarketPrepareError extends Error {
23
24
  constructor(message) {
24
25
  super(message);
@@ -38,19 +39,19 @@ export function prepareMarketItem(input) {
38
39
  // 1. id / name 基础校验
39
40
  assertSafeItemId(input.itemId);
40
41
  if (typeof input.name !== 'string' || input.name.trim() === '') {
41
- throw new MarketPrepareError('name 必填');
42
+ throw new MarketPrepareError(marketMsg('market.prepare.nameRequired'));
42
43
  }
43
44
  // 2. repoUrl 可选校验(强制 http(s),拒绝 userinfo / git@/ssh;非法即拒绝发布)
44
45
  const repoUrl = typeof input.repoUrl === 'string' && input.repoUrl.trim() !== '' ? input.repoUrl.trim() : undefined;
45
46
  if (repoUrl !== undefined) {
46
47
  const err = validateMarketRepoUrl(repoUrl);
47
48
  if (err !== null)
48
- throw new MarketPrepareError(`repoUrl 非法: ${err}`);
49
+ throw new MarketPrepareError(marketMsg('market.prepare.repoUrlInvalid', { reason: err }));
49
50
  }
50
51
  // 3. zip 体积上限(zip bomb 首道闸,与 security.ts 一致)
51
52
  const buf = Buffer.isBuffer(input.zipBytes) ? input.zipBytes : Buffer.from(input.zipBytes);
52
53
  if (buf.length > MAX_MARKET_ZIP_BYTES) {
53
- throw new MarketPrepareError(`config.zip 体积 ${buf.length} 超过上限 ${MAX_MARKET_ZIP_BYTES} 字节`);
54
+ throw new MarketPrepareError(marketMsg('market.prepare.zipTooLarge', { size: buf.length, max: MAX_MARKET_ZIP_BYTES }));
54
55
  }
55
56
  // 4. 加固解包(Zip Slip / 绝对路径 / 恶意条目;异常 → 拒绝)
56
57
  let archive;
@@ -58,39 +59,39 @@ export function prepareMarketItem(input) {
58
59
  archive = parseZipHardened(buf);
59
60
  }
60
61
  catch (err) {
61
- throw new MarketPrepareError(`config.zip 安全解析失败: ${err instanceof Error ? err.message : String(err)}`);
62
+ throw new MarketPrepareError(marketMsg('market.prepare.zipParseFailed', { reason: err instanceof Error ? err.message : String(err) }));
62
63
  }
63
64
  // 5. 内部 manifest + secrets 硬约束
64
65
  if (!archive.has(MANIFEST_FILE)) {
65
- throw new MarketPrepareError('config.zip 缺少内部 manifest.json(须为导出/备份格式)');
66
+ throw new MarketPrepareError(marketMsg('market.prepare.zipMissingManifest'));
66
67
  }
67
68
  let internalManifest;
68
69
  try {
69
70
  internalManifest = parseManifest(archive.readEntryText(MANIFEST_FILE));
70
71
  }
71
72
  catch (err) {
72
- throw new MarketPrepareError(`config.zip 内部 manifest 无效: ${err instanceof Error ? err.message : String(err)}`);
73
+ throw new MarketPrepareError(marketMsg('market.prepare.zipManifestInvalid', { reason: err instanceof Error ? err.message : String(err) }));
73
74
  }
74
75
  if (internalManifest.security.containsSecrets) {
75
- throw new MarketPrepareError('zip 声明 containsSecrets=true,市场通道永不携带秘密,拒绝发布');
76
+ throw new MarketPrepareError(marketMsg('market.prepare.zipContainsSecrets'));
76
77
  }
77
78
  // 6. sections = 内部 manifest 启用的分区(空 → 无可发布内容)
78
79
  const sections = Object.entries(internalManifest.sections)
79
80
  .filter(([, on]) => on)
80
81
  .map(([id]) => id);
81
82
  if (sections.length === 0) {
82
- throw new MarketPrepareError('zip 无启用分区,无可发布内容');
83
+ throw new MarketPrepareError(marketMsg('market.prepare.zipNoSections'));
83
84
  }
84
85
  // 6b. 市场条目禁止分区(BANNED_MARKET_SECTIONS:sessions 历史会话 / pluginFiles 任意文件 / self 本地环境)
85
86
  const bannedHit = sections.filter((s) => BANNED_MARKET_SECTIONS.includes(s));
86
87
  if (bannedHit.length > 0) {
87
- throw new MarketPrepareError(`zip 包含禁止分区 ${bannedHit.join(', ')}(sessions=历史会话 / pluginFiles=任意文件 / self=本地环境),市场条目禁止携带`);
88
+ throw new MarketPrepareError(marketMsg('market.prepare.bannedSections', { sections: bannedHit.join(', ') }));
88
89
  }
89
90
  // 6b2. 分享模式额外排除:deviceSpecific/platformSpecific 分区(比 BANNED 更严,与 BANNED 同语义——直接拒绝)
90
91
  if (isShare) {
91
92
  const nonPortable = sections.filter((s) => sectionMeta(s).portability !== 'portable');
92
93
  if (nonPortable.length > 0) {
93
- throw new MarketPrepareError(`分享模式禁止携带设备/平台相关分区 ${nonPortable.join(', ')}(含凭据状态 / 会话 / MCP / 工作区等),请仅导出通用可移植分区`);
94
+ throw new MarketPrepareError(marketMsg('market.prepare.shareNonPortableSections', { sections: nonPortable.join(', ') }));
94
95
  }
95
96
  }
96
97
  // 6b3. T1:plugins 分区禁止携带本地插件 tarball(localTarballs)。
@@ -103,12 +104,12 @@ export function prepareMarketItem(input) {
103
104
  : null;
104
105
  const tb = pluginsJson?.localTarballs;
105
106
  if (Array.isArray(tb) && tb.length > 0) {
106
- throw new MarketPrepareError(`zip 的 plugins 分区携带 ${tb.length} 个本地插件 tarball(localTarballs),禁止发布到市场(内嵌插件代码不可经公开仓库审阅;本地插件迁移请用自己的备份)`);
107
+ throw new MarketPrepareError(marketMsg('market.prepare.localTarballsRejected', { count: tb.length }));
107
108
  }
108
109
  // issue #35:patchFiles 同类拒绝(patch 在安装时改写依赖代码,同样不可经公开仓库审阅)
109
110
  const pfs = pluginsJson?.patchFiles;
110
111
  if (Array.isArray(pfs) && pfs.length > 0) {
111
- throw new MarketPrepareError(`zip 的 plugins 分区携带 ${pfs.length} 个 pnpm patch 文件(patchFiles),禁止发布到市场(会在安装时改写依赖代码;请用自己的备份迁移)`);
112
+ throw new MarketPrepareError(marketMsg('market.prepare.patchFilesRejected', { count: pfs.length }));
112
113
  }
113
114
  }
114
115
  // 6c. 内容级秘密扫描(纵深防御,不依赖导出 containsSecrets 标记):
@@ -152,7 +153,9 @@ export function prepareMarketItem(input) {
152
153
  if (scanHits.length > 0) {
153
154
  const limit = isShare ? 10 : 3;
154
155
  const suffix = scanHits.length > limit ? '…' : '';
155
- throw new MarketPrepareError(`检测到疑似敏感内容(${scanHits.slice(0, limit).join(';')}${suffix}),市场条目禁止携带凭据,请脱敏后重试`);
156
+ throw new MarketPrepareError(marketMsg('market.prepare.sensitiveContent', {
157
+ hits: scanHits.slice(0, limit).join(marketMsg('market.prepare.scanHitSeparator')) + suffix,
158
+ }));
156
159
  }
157
160
  // 7. SHA-256 + 生成 L2 manifest
158
161
  const sha256 = sha256Hex(buf);
@@ -178,8 +181,8 @@ export function prepareMarketItem(input) {
178
181
  sha256,
179
182
  sections,
180
183
  warnings: [
181
- '条目将发布至公共网络市场,未经官方审核(供应链警示)',
182
- '发布内容为不可信输入,下载方导入前会逐项核对',
184
+ marketMsg('market.prepare.warningPublicMarket'),
185
+ marketMsg('market.prepare.warningUntrustedInput'),
183
186
  ],
184
187
  };
185
188
  }
@@ -16,6 +16,7 @@ import { parseManifest, CHECKSUMS_FILE, MANIFEST_FILE } from '../schema/manifest
16
16
  import { validateSectionData, SECTION_JSON_PATHS, SECTION_FILE_PREFIXES, isFileSection } from '../schema/config.js';
17
17
  import { parseMarketItemManifest } from './index-parser.js';
18
18
  import { MAX_MARKET_ZIP_BYTES, BANNED_MARKET_SECTIONS } from './types.js';
19
+ import { marketMsg } from './messages-market.js';
19
20
  // 限额唯一来源 = utils/zip.ts 的 DEFAULT_ZIP_SAFETY_LIMITS(此处曾复制一份字面量,纯冗余):
20
21
  // 不传 defaultLimits → 与默认解析路径、读侧上限逐项一致。
21
22
  const parseZipHardened = createHardenedZipParser();
@@ -36,17 +37,17 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
36
37
  }
37
38
  const manifest = parsedManifest.manifest;
38
39
  if (manifest.id !== itemId) {
39
- return { status: 'invalid', errors: [`清单 id 与请求条目不一致: ${manifest.id} ≠ ${itemId}`], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
40
+ return { status: 'invalid', errors: [marketMsg('market.security.manifestIdMismatch', { manifestId: manifest.id, itemId })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
40
41
  }
41
42
  const buf = Buffer.isBuffer(zipBytes) ? zipBytes : Buffer.from(zipBytes);
42
43
  // 2. 字节体积上限(zip bomb 首道闸)
43
44
  if (buf.length > MAX_MARKET_ZIP_BYTES) {
44
- return { status: 'invalid', errors: [`config.zip 体积 ${buf.length} 超过上限 ${MAX_MARKET_ZIP_BYTES} 字节`], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
45
+ return { status: 'invalid', errors: [marketMsg('market.security.zipTooLarge', { size: buf.length, max: MAX_MARKET_ZIP_BYTES })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
45
46
  }
46
47
  // 3. L2↔L3 一致性:清单声明的 config.zip SHA-256 与实际一致
47
48
  const actualSha = sha256Hex(buf);
48
49
  if (manifest.checksums.zip !== actualSha) {
49
- return { status: 'invalid', errors: [`清单声明的 config.zip SHA-256 与实际不符`], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
50
+ return { status: 'invalid', errors: [marketMsg('market.security.zipShaMismatch')], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
50
51
  }
51
52
  // 4. Zip 加固解包(Zip Slip / 绝对路径 / 恶意条目 / 重复 / symlink;异常 → 拒绝整包)
52
53
  let archive;
@@ -54,21 +55,21 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
54
55
  archive = parseZipHardened(buf);
55
56
  }
56
57
  catch (err) {
57
- return { status: 'invalid', errors: [`config.zip 安全解析失败: ${err instanceof Error ? err.message : String(err)}`], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
58
+ return { status: 'invalid', errors: [marketMsg('market.security.zipParseFailed', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
58
59
  }
59
60
  // 5. 内部 manifest + 分区校验
60
61
  if (!archive.has(MANIFEST_FILE)) {
61
- return { status: 'invalid', errors: ['config.zip 缺少内部 manifest.json'], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
62
+ return { status: 'invalid', errors: [marketMsg('market.security.zipMissingManifest')], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
62
63
  }
63
64
  let internalManifest;
64
65
  try {
65
66
  internalManifest = parseManifest(archive.readEntryText(MANIFEST_FILE));
66
67
  }
67
68
  catch (err) {
68
- return { status: 'invalid', errors: [`config.zip 内部 manifest 无效: ${err instanceof Error ? err.message : String(err)}`], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
69
+ return { status: 'invalid', errors: [marketMsg('market.security.zipManifestInvalid', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
69
70
  }
70
71
  if (internalManifest.security.containsSecrets) {
71
- return { status: 'invalid', errors: ['config.zip 声明 containsSecrets=true,市场通道永不携带秘密,拒绝'], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
72
+ return { status: 'invalid', errors: [marketMsg('market.security.zipContainsSecrets')], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
72
73
  }
73
74
  // 6. 内部 checksum 校验(integrity/checksums.json ↔ 实际条目)
74
75
  let checksumsOk = false;
@@ -89,11 +90,11 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
89
90
  const result = verifyChecksums(entries, table);
90
91
  checksumsOk = result.ok;
91
92
  if (!checksumsOk) {
92
- return { status: 'invalid', errors: [`config.zip 内部完整性校验失败: ${[...result.mismatches, ...result.missing].join(', ')}`], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
93
+ return { status: 'invalid', errors: [marketMsg('market.security.checksumMismatch', { entries: [...result.mismatches, ...result.missing].join(', ') })], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
93
94
  }
94
95
  }
95
96
  catch (err) {
96
- return { status: 'invalid', errors: [`config.zip 内部 checksums 解析失败: ${err instanceof Error ? err.message : String(err)}`], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
97
+ return { status: 'invalid', errors: [marketMsg('market.security.checksumsParseFailed', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
97
98
  }
98
99
  }
99
100
  // 7. 内部分区数据校验(JSON 分区逐一 validateSectionData)
@@ -103,7 +104,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
103
104
  // 市场条目禁止分区(BANNED_MARKET_SECTIONS:sessions 历史会话 / pluginFiles 任意文件 / self 本地环境):
104
105
  // 与 secrets 同级硬约束(产品决策 2026-08-19,guide docs/design/2026-08-19-market-repo-setup-guide.md)。
105
106
  if (BANNED_MARKET_SECTIONS.includes(sectionId)) {
106
- return { status: 'invalid', errors: [`config.zip 包含禁止分区 ${sectionId}(sessions=历史会话 / pluginFiles=任意文件 / self=本地环境),市场条目禁止携带`], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
107
+ return { status: 'invalid', errors: [marketMsg('market.security.bannedSection', { section: sectionId })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
107
108
  }
108
109
  if (isFileSection(sectionId)) {
109
110
  // 文件类分区:以文件形式进入 ZIP,结构校验由 security/safeExtract 在导入期做。
@@ -112,7 +113,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
112
113
  // 与 sync 通道对空分区的语义一致(空分区参与 manifest 标记但不产生任何文件)。
113
114
  // 仅加 warning 提示,不拒绝;JSON 分区仍严格要求文件存在(有内容才可能 enabled)。
114
115
  if (!archive.names().some((n) => n.startsWith(SECTION_FILE_PREFIXES[sectionId]))) {
115
- warnings.push(`config.zip 中分区 ${sectionId} 为空(无文件),导入后不产生任何效果`);
116
+ warnings.push(marketMsg('market.security.emptyFileSection', { section: sectionId }));
116
117
  }
117
118
  continue;
118
119
  }
@@ -120,19 +121,19 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
120
121
  if (jsonPath === undefined)
121
122
  continue;
122
123
  if (!archive.has(jsonPath)) {
123
- return { status: 'invalid', errors: [`config.zip 缺少分区文件 ${jsonPath}(${sectionId})`], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
124
+ return { status: 'invalid', errors: [marketMsg('market.security.missingSectionFile', { path: jsonPath, section: sectionId })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
124
125
  }
125
126
  let data;
126
127
  try {
127
128
  data = archive.readEntryJson(jsonPath);
128
129
  }
129
130
  catch (err) {
130
- return { status: 'invalid', errors: [`分区 ${sectionId} 数据解析失败: ${err instanceof Error ? err.message : String(err)}`], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
131
+ return { status: 'invalid', errors: [marketMsg('market.security.sectionParseFailed', { section: sectionId, reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
131
132
  }
132
133
  const issues = validateSectionData(sectionId, data);
133
134
  const hardErrors = issues.filter((i) => i.severity === 'error');
134
135
  if (hardErrors.length > 0) {
135
- return { status: 'invalid', errors: [`分区 ${sectionId} 数据无效: ${hardErrors.map((e) => e.message).join('; ')}`], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
136
+ return { status: 'invalid', errors: [marketMsg('market.security.sectionInvalid', { section: sectionId, errors: hardErrors.map((e) => e.message).join('; ') })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
136
137
  }
137
138
  // T1 供应链约束:plugins 分区禁止携带本地插件 tarball(localTarballs)。
138
139
  //
@@ -146,7 +147,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
146
147
  if (Array.isArray(tarballs) && tarballs.length > 0) {
147
148
  return {
148
149
  status: 'invalid',
149
- errors: [`config.zip 的 plugins 分区携带 ${tarballs.length} 个本地插件 tarball(localTarballs),市场条目禁止携带内嵌插件代码(供应链防线;本地插件迁移请用自己的备份,不要经公共市场分发)`],
150
+ errors: [marketMsg('market.security.localTarballsRejected', { count: tarballs.length })],
150
151
  warnings, manifest, internalManifest, sections: [], checksumsOk: true,
151
152
  };
152
153
  }
@@ -156,7 +157,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
156
157
  if (Array.isArray(patchFiles) && patchFiles.length > 0) {
157
158
  return {
158
159
  status: 'invalid',
159
- errors: [`config.zip 的 plugins 分区携带 ${patchFiles.length} 个 pnpm patch 文件(patchFiles),市场条目禁止携带会在安装时改写依赖代码的内容(供应链防线;请用自己的备份迁移)`],
160
+ errors: [marketMsg('market.security.patchFilesRejected', { count: patchFiles.length })],
160
161
  warnings, manifest, internalManifest, sections: [], checksumsOk: true,
161
162
  };
162
163
  }
@@ -169,7 +170,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
169
170
  .map(([id]) => id);
170
171
  const intersection = l3Sections.filter((id) => l2Sections.has(id));
171
172
  if (intersection.length === 0) {
172
- return { status: 'invalid', errors: ['清单 sections 与 config.zip 内部 sections 无交集'], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
173
+ return { status: 'invalid', errors: [marketMsg('market.security.sectionsNoIntersection')], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
173
174
  }
174
175
  return { status: 'valid', errors: [], warnings, manifest, internalManifest, sections: intersection, checksumsOk };
175
176
  }
@@ -179,8 +180,8 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
179
180
  */
180
181
  export function generateSupplyChainWarnings(itemId) {
181
182
  return [
182
- `条目 ${itemId} 来自公共网络市场,未经官方审核(供应链警示)`,
183
- '下载内容为不可信输入,导入前请逐项核对',
183
+ marketMsg('market.security.warningFromPublicMarket', { itemId }),
184
+ marketMsg('market.security.warningUntrustedContent'),
184
185
  ];
185
186
  }
186
187
  //# sourceMappingURL=security.js.map
@@ -1,3 +1,4 @@
1
+ import { marketMsg } from './messages-market.js';
1
2
  /** index.json schema 版本(本版恒 1) */
2
3
  export const MARKET_INDEX_SCHEMA_VERSION = 1;
3
4
  /** items/<id>/manifest.json schema 版本(本版恒 1) */
@@ -22,7 +23,7 @@ export const SAFE_ITEM_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
22
23
  /** 校验 itemId 是否合法;不合法抛错(防 items/<id>/ 越界)。 */
23
24
  export function assertSafeItemId(id) {
24
25
  if (typeof id !== 'string' || !SAFE_ITEM_ID_RE.test(id)) {
25
- throw new Error(`非法市场条目 id: ${JSON.stringify(id)}(仅允许字母数字开头,字符限 . _ -)`);
26
+ throw new Error(marketMsg('market.types.itemIdInvalid', { id: JSON.stringify(id) }));
26
27
  }
27
28
  }
28
29
  //# sourceMappingURL=types.js.map
package/lib/market/url.js CHANGED
@@ -7,13 +7,14 @@
7
7
  * 复用 sync 的 validateRepoUrl(拒绝 userinfo / 空白),叠加 scheme 强制。
8
8
  */
9
9
  import { validateRepoUrl } from '../sync/sync-config.js';
10
+ import { marketMsg } from './messages-market.js';
10
11
  /** 校验市场仓库地址;合法返回 null,非法返回错误消息。仅接受 http(s),拒绝 git@/ssh/空白/含 userinfo。 */
11
12
  export function validateMarketRepoUrl(repoUrl) {
12
13
  const err = validateRepoUrl(repoUrl);
13
14
  if (err !== null)
14
15
  return err;
15
16
  if (!/^https?:\/\//i.test(repoUrl.trim())) {
16
- return '市场仓库地址仅支持 http(s)(拒绝 git@/ssh 等形态)';
17
+ return marketMsg('market.url.schemeUnsupported');
17
18
  }
18
19
  return null;
19
20
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { CURRENT_SCHEMA_VERSION, MIN_SUPPORTED_SCHEMA_VERSION, UnsupportedSchemaError } from '../schema/versions.js';
8
8
  import { V1_TO_V2 } from './v1-to-v2.js';
9
+ import { migMsg } from './messages-migrations.js';
9
10
  /**
10
11
  * 迁移注册表:按 from 升序的链式步骤。
11
12
  * 当前只有 v1→v2 占位(CURRENT=1 时不会触发,schema v2 发布后生效)。
@@ -15,7 +16,7 @@ export const MIGRATIONS = [V1_TO_V2];
15
16
  export function registerMigration(step) {
16
17
  const dup = MIGRATIONS.find((s) => s.from === step.from || s.to === step.to);
17
18
  if (dup)
18
- throw new Error(`迁移步骤冲突: ${step.from}→${step.to} 与 ${dup.from}→${dup.to} 重叠`);
19
+ throw new Error(migMsg('migrations.stepConflict', { stepFrom: step.from, stepTo: step.to, dupFrom: dup.from, dupTo: dup.to }));
19
20
  MIGRATIONS.push(step);
20
21
  MIGRATIONS.sort((a, b) => a.from - b.from);
21
22
  }
@@ -31,10 +32,10 @@ export function migrateToCurrent(doc, fromVersion, targetVersion = CURRENT_SCHEM
31
32
  return { doc, migratedFrom: fromVersion, applied: [] };
32
33
  }
33
34
  if (fromVersion > targetVersion) {
34
- throw new UnsupportedSchemaError(fromVersion, `备份 schema v${fromVersion} 高于当前 v${targetVersion},需升级本插件`);
35
+ throw new UnsupportedSchemaError(fromVersion, migMsg('migrations.schemaTooNew', { from: fromVersion, target: targetVersion }));
35
36
  }
36
37
  if (fromVersion < MIN_SUPPORTED_SCHEMA_VERSION) {
37
- throw new UnsupportedSchemaError(fromVersion, `备份 schema v${fromVersion} 低于最低支持 v${MIN_SUPPORTED_SCHEMA_VERSION}`);
38
+ throw new UnsupportedSchemaError(fromVersion, migMsg('migrations.schemaTooOld', { from: fromVersion, min: MIN_SUPPORTED_SCHEMA_VERSION }));
38
39
  }
39
40
  let current = doc;
40
41
  let version = fromVersion;
@@ -42,10 +43,10 @@ export function migrateToCurrent(doc, fromVersion, targetVersion = CURRENT_SCHEM
42
43
  while (version < targetVersion) {
43
44
  const step = MIGRATIONS.find((s) => s.from === version && s.to <= targetVersion);
44
45
  if (!step) {
45
- throw new UnsupportedSchemaError(version, `没有从 schema v${version} 到 v${targetVersion} 的迁移路径`);
46
+ throw new UnsupportedSchemaError(version, migMsg('migrations.noPath', { from: version, to: targetVersion }));
46
47
  }
47
48
  if (step.to <= step.from) {
48
- throw new Error(`迁移步骤 ${step.from}→${step.to} 未前进(注册表损坏)`);
49
+ throw new Error(migMsg('migrations.stepNotAdvanced', { from: step.from, to: step.to }));
49
50
  }
50
51
  current = step.migrate(current);
51
52
  applied.push({ from: step.from, to: step.to });
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 迁移链领域消息目录(zh 源 / en 镜像)。
3
+ *
4
+ * 为什么不放在 src/core/messages-core.ts:架构边界(tests/architecture-boundaries.test.ts)
5
+ * 规定 src/migrations/** 只允许依赖 node 内置 / 自身 / ../schema / ../utils,
6
+ * **不得 import ../core** —— 迁移链必须保持对 core 零耦合(core 消费它,不能反过来)。
7
+ * 因此本目录自带一份零依赖的消息解析器:语言源由 core 侧接线(core → migrations 是允许方向,
8
+ * 见 src/core/messages-core.ts 的 setMigrationLanguageSource),未接线时 = zh
9
+ * (与迁移链改造前的行为逐字一致)。
10
+ *
11
+ * 纪律:
12
+ * - zh 值 = 改造前的中文字面量原样(逐字不变,既有测试断言依赖它);
13
+ * - en 值类型 Record<keyof typeof zh, string>(缺键/多键即编译错误),必须纯英文;
14
+ * - 动态明细(版本号、from/to)一律以 {param} 插值传入。
15
+ */
16
+ export declare const zh: {
17
+ 'migrations.stepConflict': string;
18
+ 'migrations.schemaTooNew': string;
19
+ 'migrations.schemaTooOld': string;
20
+ 'migrations.noPath': string;
21
+ 'migrations.stepNotAdvanced': string;
22
+ };
23
+ export declare const en: Record<keyof typeof zh, string>;
24
+ export type MigrationMessageKey = keyof typeof zh;
25
+ export type MigrationMessageParams = Readonly<Record<string, string | number>>;
26
+ /** core 侧接线:让迁移链文案跟随引擎语言(core → migrations 是允许方向)。 */
27
+ export declare function setMigrationLanguageSource(resolve: () => 'zh' | 'en'): void;
28
+ /**
29
+ * 迁移链的活翻译器:每次调用解析当前语言(默认 zh,绝不抛错),未知键回落键名。
30
+ * 与 src/core/engine-locale.ts 的 engineMsg 同口径,只是不依赖 core。
31
+ */
32
+ export declare function migMsg(key: MigrationMessageKey, params?: MigrationMessageParams): string;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * 迁移链领域消息目录(zh 源 / en 镜像)。
3
+ *
4
+ * 为什么不放在 src/core/messages-core.ts:架构边界(tests/architecture-boundaries.test.ts)
5
+ * 规定 src/migrations/** 只允许依赖 node 内置 / 自身 / ../schema / ../utils,
6
+ * **不得 import ../core** —— 迁移链必须保持对 core 零耦合(core 消费它,不能反过来)。
7
+ * 因此本目录自带一份零依赖的消息解析器:语言源由 core 侧接线(core → migrations 是允许方向,
8
+ * 见 src/core/messages-core.ts 的 setMigrationLanguageSource),未接线时 = zh
9
+ * (与迁移链改造前的行为逐字一致)。
10
+ *
11
+ * 纪律:
12
+ * - zh 值 = 改造前的中文字面量原样(逐字不变,既有测试断言依赖它);
13
+ * - en 值类型 Record<keyof typeof zh, string>(缺键/多键即编译错误),必须纯英文;
14
+ * - 动态明细(版本号、from/to)一律以 {param} 插值传入。
15
+ */
16
+ export const zh = {
17
+ 'migrations.stepConflict': '迁移步骤冲突: {stepFrom}→{stepTo} 与 {dupFrom}→{dupTo} 重叠',
18
+ 'migrations.schemaTooNew': '备份 schema v{from} 高于当前 v{target},需升级本插件',
19
+ 'migrations.schemaTooOld': '备份 schema v{from} 低于最低支持 v{min}',
20
+ 'migrations.noPath': '没有从 schema v{from} 到 v{to} 的迁移路径',
21
+ 'migrations.stepNotAdvanced': '迁移步骤 {from}→{to} 未前进(注册表损坏)',
22
+ };
23
+ export const en = {
24
+ 'migrations.stepConflict': 'Migration step conflict: {stepFrom}→{stepTo} overlaps {dupFrom}→{dupTo}',
25
+ 'migrations.schemaTooNew': 'Backup schema v{from} is newer than the current v{target}; upgrade this plugin',
26
+ 'migrations.schemaTooOld': 'Backup schema v{from} is older than the minimum supported v{min}',
27
+ 'migrations.noPath': 'No migration path from schema v{from} to v{to}',
28
+ 'migrations.stepNotAdvanced': 'Migration step {from}→{to} does not advance (corrupt registry)',
29
+ };
30
+ let languageSource = () => 'zh';
31
+ /** core 侧接线:让迁移链文案跟随引擎语言(core → migrations 是允许方向)。 */
32
+ export function setMigrationLanguageSource(resolve) {
33
+ languageSource = resolve;
34
+ }
35
+ /** 插值:{param} → 形参值;缺参原样保留(与 engine-locale.ts 的 interpolate 同口径)。 */
36
+ function interpolate(template, params) {
37
+ if (params === undefined)
38
+ return template;
39
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
40
+ const value = params[key];
41
+ return value === undefined ? match : String(value);
42
+ });
43
+ }
44
+ /**
45
+ * 迁移链的活翻译器:每次调用解析当前语言(默认 zh,绝不抛错),未知键回落键名。
46
+ * 与 src/core/engine-locale.ts 的 engineMsg 同口径,只是不依赖 core。
47
+ */
48
+ export function migMsg(key, params) {
49
+ let lang = 'zh';
50
+ try {
51
+ lang = languageSource() === 'en' ? 'en' : 'zh';
52
+ }
53
+ catch {
54
+ lang = 'zh';
55
+ }
56
+ return interpolate((lang === 'en' ? en : zh)[key], params);
57
+ }
58
+ //# sourceMappingURL=messages-migrations.js.map