dsh-config-manager 0.1.71 → 0.1.72

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 (548) hide show
  1. package/README.md +111 -96
  2. package/README.zh-CN.md +207 -86
  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 +1 -1
  6. package/lib/adapters/index.js +1 -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 +18 -0
  10. package/lib/adapters/messages.js +50 -0
  11. package/lib/adapters/plugin-files.js +3 -2
  12. package/lib/adapters/plugins.js +4 -3
  13. package/lib/adapters/prompts.d.ts +1 -0
  14. package/lib/adapters/prompts.js +7 -1
  15. package/lib/adapters/self.d.ts +7 -1
  16. package/lib/adapters/self.js +1 -1
  17. package/lib/adapters/sessions.d.ts +1 -0
  18. package/lib/adapters/sessions.js +4 -2
  19. package/lib/adapters/ui.d.ts +2 -2
  20. package/lib/adapters/ui.js +25 -25
  21. package/lib/adapters/workspaces.d.ts +1 -0
  22. package/lib/adapters/workspaces.js +7 -3
  23. package/lib/cli/actions.d.ts +9 -1
  24. package/lib/cli/actions.js +5 -2
  25. package/lib/cli/help.js +23 -10
  26. package/lib/cli/index.d.ts +23 -0
  27. package/lib/cli/index.js +90 -32
  28. package/lib/cli/sessions-inspect.js +3 -1
  29. package/lib/cli/sessions-repair.js +25 -6
  30. package/lib/cli/web/page.d.ts +12 -0
  31. package/lib/cli/web/page.js +21 -12
  32. package/lib/cli/web/routes.js +2 -2
  33. package/lib/cli/web/server.js +4 -4
  34. package/lib/client.d.ts +1349 -490
  35. package/lib/client.js +43047 -36738
  36. package/lib/core/backup-plan.js +30 -17
  37. package/lib/core/backup-verify.js +24 -23
  38. package/lib/core/backup.d.ts +5 -0
  39. package/lib/core/backup.js +51 -25
  40. package/lib/core/boot-rescue.js +4 -3
  41. package/lib/core/boot-safety.js +2 -1
  42. package/lib/core/command-rollback.d.ts +178 -0
  43. package/lib/core/command-rollback.js +289 -0
  44. package/lib/core/consult-source.js +7 -6
  45. package/lib/core/disk-usage.d.ts +74 -10
  46. package/lib/core/disk-usage.js +67 -4
  47. package/lib/core/engine-locale.d.ts +87 -0
  48. package/lib/core/engine-locale.js +125 -0
  49. package/lib/core/exporter.js +3 -3
  50. package/lib/core/fork-reconcile.d.ts +128 -0
  51. package/lib/core/fork-reconcile.js +150 -0
  52. package/lib/core/journal.js +6 -5
  53. package/lib/core/local-plugin-host.js +3 -2
  54. package/lib/core/local-plugin-pack.js +11 -6
  55. package/lib/core/messages-core.d.ts +292 -0
  56. package/lib/core/messages-core.js +615 -0
  57. package/lib/core/messages.d.ts +60 -2
  58. package/lib/core/messages.js +116 -13
  59. package/lib/core/migration-consult.js +21 -20
  60. package/lib/core/migration-history.js +19 -5
  61. package/lib/core/model-tools.d.ts +0 -22
  62. package/lib/core/model-tools.js +50 -27
  63. package/lib/core/phase3-host.js +30 -5
  64. package/lib/core/plugin-cli.js +18 -19
  65. package/lib/core/reconcile.d.ts +2 -14
  66. package/lib/core/reconcile.js +87 -17
  67. package/lib/core/recovery-orchestrator.js +19 -18
  68. package/lib/core/reinstall.d.ts +4 -0
  69. package/lib/core/reinstall.js +26 -21
  70. package/lib/core/restore.js +20 -17
  71. package/lib/core/rollback-ignore-rules.d.ts +31 -0
  72. package/lib/core/rollback-ignore-rules.js +140 -0
  73. package/lib/core/rollback-point.d.ts +1208 -0
  74. package/lib/core/rollback-point.js +2993 -0
  75. package/lib/core/run-registry.js +6 -5
  76. package/lib/core/session-health.d.ts +15 -0
  77. package/lib/core/session-meta.d.ts +42 -2
  78. package/lib/core/session-meta.js +140 -1
  79. package/lib/core/session-title.d.ts +28 -0
  80. package/lib/core/session-title.js +185 -0
  81. package/lib/core/startup-barrier.d.ts +24 -0
  82. package/lib/core/startup-barrier.js +22 -0
  83. package/lib/core/transaction-coordinator.js +4 -3
  84. package/lib/core/types.d.ts +18 -1
  85. package/lib/core/validator.js +5 -4
  86. package/lib/core/verify-recovery.js +31 -30
  87. package/lib/foreign/bundle.js +3 -2
  88. package/lib/foreign/continue.js +2 -1
  89. package/lib/foreign/crush.js +2 -1
  90. package/lib/foreign/gemini.js +2 -1
  91. package/lib/foreign/goose.js +2 -1
  92. package/lib/foreign/kilocode.js +2 -1
  93. package/lib/foreign/kimi.js +2 -1
  94. package/lib/foreign/messages.d.ts +15 -0
  95. package/lib/foreign/messages.js +40 -0
  96. package/lib/foreign/mimocode.js +2 -1
  97. package/lib/foreign/openclaw.js +2 -1
  98. package/lib/foreign/opencode.js +2 -1
  99. package/lib/foreign/pi.js +2 -1
  100. package/lib/foreign/qoder.js +2 -1
  101. package/lib/foreign/qwen.js +2 -1
  102. package/lib/foreign/registry.js +7 -6
  103. package/lib/foreign/teleagent.js +2 -1
  104. package/lib/foreign/workbuddy.js +2 -1
  105. package/lib/foreign/zcode.js +2 -1
  106. package/lib/foreign/zed.js +2 -1
  107. package/lib/index.d.ts +223 -7
  108. package/lib/index.js +729 -54
  109. package/lib/market/git-file-writer.js +20 -19
  110. package/lib/market/github-repos.js +35 -19
  111. package/lib/market/index-parser.js +31 -30
  112. package/lib/market/messages-market.d.ts +138 -0
  113. package/lib/market/messages-market.js +305 -0
  114. package/lib/market/my-repo.d.ts +2 -2
  115. package/lib/market/my-repo.js +32 -29
  116. package/lib/market/prepare.js +18 -15
  117. package/lib/market/security.js +20 -19
  118. package/lib/market/types.js +2 -1
  119. package/lib/market/url.js +2 -1
  120. package/lib/migrations/index.js +6 -5
  121. package/lib/migrations/messages-migrations.d.ts +32 -0
  122. package/lib/migrations/messages-migrations.js +58 -0
  123. package/lib/profiles/dsh-profile-launcher.js +2 -1
  124. package/lib/profiles/dsh-profile-runtime.js +2 -1
  125. package/lib/profiles/messages.d.ts +7 -0
  126. package/lib/profiles/messages.js +27 -0
  127. package/lib/routes/backup.js +18 -2
  128. package/lib/routes/import.js +3 -3
  129. package/lib/routes/index.d.ts +1 -1
  130. package/lib/routes/kit.d.ts +3 -2
  131. package/lib/routes/kit.js +6 -1
  132. package/lib/routes/market.js +1 -1
  133. package/lib/routes/messages.d.ts +42 -0
  134. package/lib/routes/messages.js +100 -0
  135. package/lib/routes/profiles.js +12 -6
  136. package/lib/routes/recovery.js +378 -114
  137. package/lib/routes/snapshots.js +51 -4
  138. package/lib/routes/sync.js +68 -16
  139. package/lib/schema/config.d.ts +11 -1
  140. package/lib/schema/config.js +52 -20
  141. package/lib/schema/index.d.ts +8 -6
  142. package/lib/schema/index.js +8 -6
  143. package/lib/schema/manifest.js +19 -18
  144. package/lib/schema/messages.d.ts +47 -0
  145. package/lib/schema/messages.js +110 -0
  146. package/lib/schema/section-registry.d.ts +41 -14
  147. package/lib/schema/section-registry.js +35 -29
  148. package/lib/schema/versions.js +6 -5
  149. package/lib/security/encryption.js +16 -15
  150. package/lib/security/integrity.js +8 -7
  151. package/lib/security/messages.d.ts +30 -0
  152. package/lib/security/messages.js +77 -0
  153. package/lib/security/redaction.js +12 -0
  154. package/lib/security/secret-scanner.js +16 -13
  155. package/lib/security/vault.js +2 -1
  156. package/lib/security/zip-security.js +4 -3
  157. package/lib/sync/ancestor.js +2 -1
  158. package/lib/sync/autosync-config.d.ts +11 -0
  159. package/lib/sync/autosync-config.js +12 -0
  160. package/lib/sync/autosync-scheduler.d.ts +11 -1
  161. package/lib/sync/autosync-scheduler.js +115 -76
  162. package/lib/sync/backup-files.js +3 -2
  163. package/lib/sync/backup-scheduler.js +6 -5
  164. package/lib/sync/blob-store.js +2 -1
  165. package/lib/sync/git/git-transport.d.ts +55 -0
  166. package/lib/sync/git/git-transport.js +152 -2
  167. package/lib/sync/github-auth.js +10 -9
  168. package/lib/sync/layout.js +13 -12
  169. package/lib/sync/messages.d.ts +63 -0
  170. package/lib/sync/messages.js +149 -0
  171. package/lib/sync/review-queue.js +6 -5
  172. package/lib/sync/s3/messages.d.ts +7 -0
  173. package/lib/sync/s3/messages.js +16 -0
  174. package/lib/sync/s3/s3-providers.d.ts +4 -1
  175. package/lib/sync/s3/s3-providers.js +5 -5
  176. package/lib/sync/s3/s3-transport.js +1 -1
  177. package/lib/sync/s3/sigv4.js +2 -1
  178. package/lib/sync/snapshot-crypto.js +6 -5
  179. package/lib/sync/snapshot-json.js +3 -2
  180. package/lib/sync/sync-engine.d.ts +37 -15
  181. package/lib/sync/sync-engine.js +67 -37
  182. package/lib/sync/sync-history.js +7 -6
  183. package/lib/sync/sync-selection.d.ts +15 -4
  184. package/lib/sync/sync-selection.js +17 -5
  185. package/lib/sync/webdav/webdav-transport.d.ts +1 -0
  186. package/lib/sync/webdav/webdav-transport.js +4 -1
  187. package/lib/ui/artifact-view.js +4 -3
  188. package/lib/ui/commands.d.ts +1 -1
  189. package/lib/ui/commands.js +10 -0
  190. package/lib/ui/disk-usage-view.d.ts +33 -4
  191. package/lib/ui/disk-usage-view.js +29 -1
  192. package/lib/ui/errors.d.ts +1 -1
  193. package/lib/ui/errors.js +4 -3
  194. package/lib/ui/export-flow.js +24 -20
  195. package/lib/ui/foreign-view.d.ts +5 -1
  196. package/lib/ui/foreign-view.js +26 -2
  197. package/lib/ui/i18n.d.ts +65 -0
  198. package/lib/ui/i18n.js +114 -0
  199. package/lib/ui/import-wizard.d.ts +7 -0
  200. package/lib/ui/import-wizard.js +13 -10
  201. package/lib/ui/progress.d.ts +2 -2
  202. package/lib/ui/progress.js +1 -2
  203. package/lib/ui/report.d.ts +5 -5
  204. package/lib/ui/report.js +6 -6
  205. package/lib/ui/rollback-view.d.ts +756 -0
  206. package/lib/ui/rollback-view.js +724 -0
  207. package/lib/ui/selection-model.d.ts +41 -0
  208. package/lib/ui/selection-model.js +30 -0
  209. package/lib/ui/session-inventory-view.d.ts +97 -3
  210. package/lib/ui/session-inventory-view.js +76 -2
  211. package/lib/ui/session-issue-labels.d.ts +43 -0
  212. package/lib/ui/session-issue-labels.js +48 -0
  213. package/lib/ui/sync-settings-view.d.ts +33 -0
  214. package/lib/ui/sync-settings-view.js +40 -0
  215. package/lib/ui/types.d.ts +8 -3
  216. package/lib/ui/types.js +1 -1
  217. package/lib/utils/atomic-write.js +6 -5
  218. package/lib/utils/bundle-scan.d.ts +11 -1
  219. package/lib/utils/bundle-scan.js +12 -1
  220. package/lib/utils/env-lock.d.ts +10 -0
  221. package/lib/utils/env-lock.js +73 -46
  222. package/lib/utils/json.js +6 -5
  223. package/lib/utils/messages-utils.d.ts +96 -0
  224. package/lib/utils/messages-utils.js +216 -0
  225. package/lib/utils/proxy.js +18 -5
  226. package/lib/utils/recursive-walk.d.ts +22 -0
  227. package/lib/utils/recursive-walk.js +75 -1
  228. package/lib/utils/session-log-repair.js +10 -9
  229. package/lib/utils/zip.js +27 -26
  230. package/package.json +12 -2
  231. package/src/adapters/agent-instructions.ts +1 -1
  232. package/src/adapters/file-collection.test.ts +1 -0
  233. package/src/adapters/file-collection.ts +1 -1
  234. package/src/adapters/files.test.ts +27 -0
  235. package/src/adapters/index.ts +1 -1
  236. package/src/adapters/link-report.test.ts +22 -2
  237. package/src/adapters/link-report.ts +29 -4
  238. package/src/adapters/messages.ts +54 -0
  239. package/src/adapters/plugin-files.ts +3 -2
  240. package/src/adapters/plugins.ts +4 -3
  241. package/src/adapters/prompts.ts +7 -1
  242. package/src/adapters/self.test.ts +26 -0
  243. package/src/adapters/self.ts +8 -2
  244. package/src/adapters/sessions.ts +4 -2
  245. package/src/adapters/settings.test.ts +38 -1
  246. package/src/adapters/ui.test.ts +2 -2
  247. package/src/adapters/ui.ts +27 -26
  248. package/src/adapters/workspaces.ts +7 -3
  249. package/src/cli/actions.test.ts +28 -0
  250. package/src/cli/actions.ts +11 -2
  251. package/src/cli/help.ts +23 -10
  252. package/src/cli/index-guards.test.ts +193 -0
  253. package/src/cli/index.ts +106 -31
  254. package/src/cli/sessions-inspect.test.ts +15 -1
  255. package/src/cli/sessions-inspect.ts +3 -1
  256. package/src/cli/sessions-repair.test.ts +84 -0
  257. package/src/cli/sessions-repair.ts +33 -7
  258. package/src/cli/web/console-route-parity.test.ts +115 -0
  259. package/src/cli/web/page.ts +23 -12
  260. package/src/cli/web/routes.ts +2 -1
  261. package/src/cli/web/server.ts +4 -4
  262. package/src/cli/web/web.test.ts +2 -2
  263. package/src/client/ConfigManagerSection.tsx +30 -2
  264. package/src/client/about/AboutPanel.tsx +2 -2
  265. package/src/client/about/PluginUpdateDialog.tsx +1 -1
  266. package/src/client/about/ReleaseNotesDialog.tsx +2 -2
  267. package/src/client/about/about-view.test.ts +13 -3
  268. package/src/client/about/about-view.ts +10 -6
  269. package/src/client/about/release-notes-view.test.ts +14 -4
  270. package/src/client/about/release-notes-view.ts +4 -2
  271. package/src/client/api.ts +18 -6
  272. package/src/client/chat/ChatEntryView.tsx +391 -0
  273. package/src/client/chat/RollbackDiffTab.tsx +61 -0
  274. package/src/client/chat/RollbackFileIcon.tsx +96 -0
  275. package/src/client/chat/RollbackReviewDialog.tsx +591 -0
  276. package/src/client/chat/chat-entry-view.test.ts +1174 -0
  277. package/src/client/chat/chat-entry-view.ts +758 -0
  278. package/src/client/chat/chat-locales.ts +152 -0
  279. package/src/client/chat/chat-node-registration.ts +64 -0
  280. package/src/client/chat/chat-point-window.ts +173 -0
  281. package/src/client/chat/chat-slots.ts +107 -0
  282. package/src/client/chat/rollback-diff-tab.test.ts +105 -0
  283. package/src/client/chat/rollback-diff-tab.ts +164 -0
  284. package/src/client/chat/rollback-entry-view.test.ts +1469 -0
  285. package/src/client/chat/rollback-entry-view.ts +941 -0
  286. package/src/client/chat/sidebar-slots.ts +37 -0
  287. package/src/client/common/ConfirmDialog.tsx +7 -3
  288. package/src/client/common/ContentPicker.tsx +48 -10
  289. package/src/client/common/DiffTable.tsx +97 -0
  290. package/src/client/common/ErrorBanner.tsx +4 -4
  291. package/src/client/common/Icon.tsx +2 -0
  292. package/src/client/common/Modal.tsx +8 -1
  293. package/src/client/common/ProgressBar.tsx +8 -2
  294. package/src/client/common/ReportView.tsx +4 -4
  295. package/src/client/common/RunsCenter.tsx +1 -0
  296. package/src/client/common/client-route-parity.test.ts +78 -0
  297. package/src/client/common/info-hint-guard.test.ts +46 -13
  298. package/src/client/common/picker-lazy-units.test.ts +42 -0
  299. package/src/client/common/plan-text-redaction.test.ts +17 -6
  300. package/src/client/common/progress-view-edge.test.ts +8 -7
  301. package/src/client/common/progress-view.test.ts +9 -9
  302. package/src/client/common/progress-view.ts +2 -2
  303. package/src/client/common/routes.ts +28 -0
  304. package/src/client/common/ui.tsx +73 -10
  305. package/src/client/config-manager.module.css +996 -21
  306. package/src/client/css-scale-guard.test.ts +30 -0
  307. package/src/client/environment/EnvironmentPanel.tsx +11 -4
  308. package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
  309. package/src/client/export/ExportView.tsx +13 -8
  310. package/src/client/history/HistoryPanel.tsx +4 -3
  311. package/src/client/history/history-api.ts +2 -2
  312. package/src/client/history/history-locales.ts +5 -1
  313. package/src/client/home/HomePanel.tsx +29 -8
  314. package/src/client/import/ForeignImportView.tsx +4 -5
  315. package/src/client/import/ImportWizardView.tsx +5 -3
  316. package/src/client/import/import-wizard-split.test.ts +3 -1
  317. package/src/client/import/import-wizard-steps.tsx +9 -9
  318. package/src/client/index.ts +186 -3
  319. package/src/client/library/LibraryActions.tsx +32 -7
  320. package/src/client/library/LibraryPanel.tsx +23 -3
  321. package/src/client/library/RestorePlanView.tsx +9 -66
  322. package/src/client/locales.ts +54 -5
  323. package/src/client/lucide-icons.d.ts +1 -0
  324. package/src/client/market/MarketPanel.tsx +13 -2
  325. package/src/client/market/MyConfigsView.tsx +5 -4
  326. package/src/client/market/MyConfigsWizard.tsx +10 -8
  327. package/src/client/market/market-api.test.ts +9 -8
  328. package/src/client/market/market-api.ts +2 -2
  329. package/src/client/market/market-locales.ts +3 -0
  330. package/src/client/market/market-view.test.ts +6 -6
  331. package/src/client/market/market-view.ts +3 -3
  332. package/src/client/market/my-configs-api.ts +2 -2
  333. package/src/client/market/my-configs-view.test.ts +23 -22
  334. package/src/client/market/my-configs-view.ts +8 -8
  335. package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
  336. package/src/client/recovery/RecoveryPanel.tsx +442 -283
  337. package/src/client/recovery/RollbackCard.tsx +665 -0
  338. package/src/client/recovery/RollbackDiffPane.tsx +58 -0
  339. package/src/client/recovery/RollbackFileChanges.tsx +163 -0
  340. package/src/client/recovery/incident-api.ts +2 -2
  341. package/src/client/recovery/recovery-api.ts +116 -6
  342. package/src/client/recovery/recovery-locales.test.ts +299 -0
  343. package/src/client/recovery/recovery-locales.ts +488 -149
  344. package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
  345. package/src/client/recovery/rollback-file-changes.test.ts +73 -0
  346. package/src/client/recovery/session-dialog-mount.test.ts +54 -0
  347. package/src/client/run-store.ts +84 -21
  348. package/src/client/sync/AutosyncCard.tsx +25 -1
  349. package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
  350. package/src/client/sync/ChannelConfigDialog.tsx +10 -10
  351. package/src/client/sync/DecryptPasswordCard.tsx +2 -2
  352. package/src/client/sync/SecurityOptionsCard.tsx +27 -17
  353. package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
  354. package/src/client/sync/SyncConfirmView.tsx +46 -8
  355. package/src/client/sync/SyncLogList.tsx +1 -1
  356. package/src/client/sync/SyncPanel.tsx +31 -23
  357. package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
  358. package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
  359. package/src/client/sync/SyncSettingsView.test.ts +20 -1
  360. package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
  361. package/src/client/sync/WorkDirCard.tsx +112 -0
  362. package/src/client/sync/history-model.test.ts +42 -9
  363. package/src/client/sync/history-model.ts +30 -15
  364. package/src/client/sync/sync-api.test.ts +57 -33
  365. package/src/client/sync/sync-api.ts +80 -6
  366. package/src/client/sync/sync-locales.ts +104 -3
  367. package/src/client/sync/sync-view-v2.test.ts +65 -13
  368. package/src/client/sync/sync-view.test.ts +107 -52
  369. package/src/client/sync/sync-view.ts +124 -41
  370. package/src/core/backup-plan.test.ts +28 -0
  371. package/src/core/backup-plan.ts +29 -17
  372. package/src/core/backup-verify.ts +24 -23
  373. package/src/core/backup.test.ts +76 -0
  374. package/src/core/backup.ts +55 -25
  375. package/src/core/boot-rescue.ts +4 -3
  376. package/src/core/boot-safety.ts +2 -1
  377. package/src/core/command-rollback.test.ts +536 -0
  378. package/src/core/command-rollback.ts +402 -0
  379. package/src/core/consult-source.ts +7 -6
  380. package/src/core/disk-usage.test.ts +257 -1
  381. package/src/core/disk-usage.ts +134 -14
  382. package/src/core/engine-locale.test.ts +593 -0
  383. package/src/core/engine-locale.ts +175 -0
  384. package/src/core/exporter.ts +3 -3
  385. package/src/core/fork-reconcile.test.ts +279 -0
  386. package/src/core/fork-reconcile.ts +234 -0
  387. package/src/core/journal.ts +6 -5
  388. package/src/core/local-plugin-host.ts +3 -2
  389. package/src/core/local-plugin-pack.ts +11 -6
  390. package/src/core/messages-core.ts +657 -0
  391. package/src/core/messages.ts +123 -17
  392. package/src/core/migration-consult.ts +21 -20
  393. package/src/core/migration-history.ts +19 -5
  394. package/src/core/model-tools.ts +28 -27
  395. package/src/core/phase3-host.ts +21 -5
  396. package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
  397. package/src/core/plugin-cli.ts +18 -19
  398. package/src/core/reconcile.test.ts +117 -0
  399. package/src/core/reconcile.ts +85 -17
  400. package/src/core/recovery-orchestrator.ts +19 -18
  401. package/src/core/reinstall.ts +26 -21
  402. package/src/core/restore.ts +20 -17
  403. package/src/core/rollback-file-diff.test.ts +159 -0
  404. package/src/core/rollback-ignore-rules.ts +126 -0
  405. package/src/core/rollback-point.test.ts +2603 -0
  406. package/src/core/rollback-point.ts +3755 -0
  407. package/src/core/run-registry.ts +6 -5
  408. package/src/core/session-health.ts +15 -0
  409. package/src/core/session-meta.test.ts +117 -1
  410. package/src/core/session-meta.ts +149 -2
  411. package/src/core/session-title.test.ts +127 -0
  412. package/src/core/session-title.ts +189 -0
  413. package/src/core/startup-barrier.test.ts +69 -0
  414. package/src/core/startup-barrier.ts +27 -0
  415. package/src/core/transaction-coordinator.ts +4 -3
  416. package/src/core/types.ts +18 -1
  417. package/src/core/validator.ts +5 -4
  418. package/src/core/verify-recovery.ts +31 -30
  419. package/src/foreign/bundle.ts +3 -2
  420. package/src/foreign/continue.ts +2 -1
  421. package/src/foreign/crush.ts +2 -1
  422. package/src/foreign/file-budget.test.ts +2 -0
  423. package/src/foreign/gemini.ts +2 -1
  424. package/src/foreign/goose.ts +2 -1
  425. package/src/foreign/kilocode.ts +2 -1
  426. package/src/foreign/kimi.ts +2 -1
  427. package/src/foreign/messages.ts +44 -0
  428. package/src/foreign/mimocode.ts +2 -1
  429. package/src/foreign/openclaw.ts +2 -1
  430. package/src/foreign/opencode.ts +2 -1
  431. package/src/foreign/pi.ts +2 -1
  432. package/src/foreign/qoder.ts +2 -1
  433. package/src/foreign/qwen.ts +2 -1
  434. package/src/foreign/registry.ts +7 -6
  435. package/src/foreign/teleagent.ts +2 -1
  436. package/src/foreign/workbuddy.ts +2 -1
  437. package/src/foreign/zcode.ts +2 -1
  438. package/src/foreign/zed.ts +2 -1
  439. package/src/index.facade.test.ts +340 -1
  440. package/src/index.ts +813 -57
  441. package/src/market/git-file-writer.ts +20 -19
  442. package/src/market/github-repos.test.ts +23 -0
  443. package/src/market/github-repos.ts +37 -19
  444. package/src/market/index-parser.ts +31 -30
  445. package/src/market/messages-market.ts +325 -0
  446. package/src/market/my-repo.ts +32 -29
  447. package/src/market/prepare.ts +18 -15
  448. package/src/market/security.ts +20 -19
  449. package/src/market/types.ts +2 -1
  450. package/src/market/url.ts +2 -1
  451. package/src/migrations/index.ts +6 -5
  452. package/src/migrations/messages-migrations.ts +59 -0
  453. package/src/profiles/dsh-profile-launcher.ts +2 -1
  454. package/src/profiles/dsh-profile-runtime.ts +2 -1
  455. package/src/profiles/messages.ts +31 -0
  456. package/src/routes/backup.ts +18 -2
  457. package/src/routes/import.ts +3 -3
  458. package/src/routes/index.ts +1 -1
  459. package/src/routes/kit.ts +9 -3
  460. package/src/routes/market.ts +1 -1
  461. package/src/routes/messages.ts +104 -0
  462. package/src/routes/profiles.ts +12 -6
  463. package/src/routes/recovery.test.ts +405 -1
  464. package/src/routes/recovery.ts +377 -109
  465. package/src/routes/snapshots.ts +51 -4
  466. package/src/routes/sync.ts +67 -16
  467. package/src/schema/config.ts +53 -20
  468. package/src/schema/index.ts +7 -5
  469. package/src/schema/manifest.ts +19 -18
  470. package/src/schema/messages.ts +120 -0
  471. package/src/schema/registry.test.ts +19 -5
  472. package/src/schema/section-registry.ts +64 -39
  473. package/src/schema/versions.ts +6 -5
  474. package/src/security/encryption.ts +16 -15
  475. package/src/security/integrity.ts +8 -7
  476. package/src/security/messages.ts +87 -0
  477. package/src/security/redaction.test.ts +17 -1
  478. package/src/security/redaction.ts +13 -0
  479. package/src/security/secret-scanner.ts +19 -12
  480. package/src/security/security.test.ts +47 -0
  481. package/src/security/vault.ts +2 -1
  482. package/src/security/zip-security.ts +4 -3
  483. package/src/sync/ancestor.ts +2 -1
  484. package/src/sync/autosync-config.ts +21 -0
  485. package/src/sync/autosync-scheduler.test.ts +63 -7
  486. package/src/sync/autosync-scheduler.ts +118 -78
  487. package/src/sync/backup-files.ts +3 -2
  488. package/src/sync/backup-scheduler.ts +6 -5
  489. package/src/sync/blob-store.ts +2 -1
  490. package/src/sync/git/git-transport.test.ts +170 -8
  491. package/src/sync/git/git-transport.ts +176 -2
  492. package/src/sync/github-auth.ts +10 -9
  493. package/src/sync/layout.ts +13 -12
  494. package/src/sync/messages.ts +153 -0
  495. package/src/sync/review-queue.ts +6 -5
  496. package/src/sync/s3/messages.ts +16 -0
  497. package/src/sync/s3/s3-providers.ts +9 -6
  498. package/src/sync/s3/s3-transport.ts +1 -1
  499. package/src/sync/s3/sigv4.ts +3 -1
  500. package/src/sync/snapshot-crypto.ts +6 -5
  501. package/src/sync/snapshot-json.ts +3 -2
  502. package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
  503. package/src/sync/sync-engine.test.ts +226 -8
  504. package/src/sync/sync-engine.ts +103 -41
  505. package/src/sync/sync-history.ts +7 -6
  506. package/src/sync/sync-selection.test.ts +12 -2
  507. package/src/sync/sync-selection.ts +18 -5
  508. package/src/sync/webdav/webdav-transport.ts +4 -1
  509. package/src/ui/artifact-view.ts +4 -3
  510. package/src/ui/commands.test.ts +1 -0
  511. package/src/ui/commands.ts +11 -1
  512. package/src/ui/disk-usage-view.test.ts +113 -6
  513. package/src/ui/disk-usage-view.ts +63 -7
  514. package/src/ui/errors.ts +4 -3
  515. package/src/ui/export-flow.ts +24 -20
  516. package/src/ui/foreign-view.test.ts +43 -0
  517. package/src/ui/foreign-view.ts +30 -3
  518. package/src/ui/i18n.ts +125 -0
  519. package/src/ui/import-wizard.ts +18 -10
  520. package/src/ui/progress.ts +2 -2
  521. package/src/ui/report.ts +7 -6
  522. package/src/ui/rollback-view.test.ts +819 -0
  523. package/src/ui/rollback-view.ts +1339 -0
  524. package/src/ui/selection-model.test.ts +33 -1
  525. package/src/ui/selection-model.ts +53 -0
  526. package/src/ui/session-inventory-view.test.ts +109 -0
  527. package/src/ui/session-inventory-view.ts +138 -3
  528. package/src/ui/session-issue-labels.test.ts +51 -0
  529. package/src/ui/session-issue-labels.ts +70 -0
  530. package/src/ui/sync-settings-view.test.ts +36 -0
  531. package/src/ui/sync-settings-view.ts +49 -0
  532. package/src/ui/types.ts +10 -4
  533. package/src/utils/atomic-write.ts +6 -5
  534. package/src/utils/bundle-scan.test.ts +2 -2
  535. package/src/utils/bundle-scan.ts +12 -1
  536. package/src/utils/env-lock.ts +83 -49
  537. package/src/utils/json.ts +7 -5
  538. package/src/utils/messages-utils.ts +232 -0
  539. package/src/utils/proxy.test.ts +25 -0
  540. package/src/utils/proxy.ts +19 -5
  541. package/src/utils/recursive-walk.test.ts +43 -0
  542. package/src/utils/recursive-walk.ts +75 -1
  543. package/src/utils/session-log-repair.ts +10 -9
  544. package/src/utils/zip.ts +27 -26
  545. package/lib/core/checkpoint.d.ts +0 -362
  546. package/lib/core/checkpoint.js +0 -1309
  547. package/src/core/checkpoint.test.ts +0 -665
  548. package/src/core/checkpoint.ts +0 -1614
@@ -7,14 +7,18 @@ import assert from 'node:assert/strict'
7
7
 
8
8
  import type { PullChange, SyncPullReport, SyncPushReport } from '../../sync/sync-engine.ts'
9
9
  import type { SectionId } from '../../schema/types.ts'
10
- import type { GithubPollResponse, SyncSectionInfo, SyncStatusResponse } from './sync-api.ts'
10
+ import type { AutosyncStatusResponse, GithubPollResponse, SyncSectionInfo, SyncStatusResponse } from './sync-api.ts'
11
11
  import {
12
12
  autosyncIntervalMs, computeAutosyncCountdown, computeGithubLoginView, computeSyncButtons, computeSyncStatus,
13
13
  DEFAULT_SYNC_SESSIONS_LIMIT, defaultChannelSyncState, formatDateTime, formatIntervalDuration, formatLastSync,
14
+ autosyncSkipDetail,
14
15
  githubPollMessage, initialSyncSections, kindLabel, normalizeSessionsLimit, privateRepoHint,
15
16
  pullReportView, pushReportView, presetById, presetIdForUrl, readStoredChannel, recommendedSyncSections,
16
17
  severityLabel, summarizePullChanges, syncSectionGroups, syncSectionOptions, WEBDAV_PRESETS, writeStoredChannel,
17
18
  } from './sync-view.ts'
19
+ import type { SyncTextFn } from './sync-view.ts'
20
+ import { en as syncEn, zh as syncZh } from './sync-locales.ts'
21
+ import { zhUiT } from '../../ui/i18n.ts'
18
22
 
19
23
  /* ---------------------------------------------------------------- sessions 可选分区 */
20
24
 
@@ -50,7 +54,7 @@ test('sync-view: initialSyncSections —— 首次打开预勾选推荐分区;
50
54
  /* ---------------------------------------------------------------- 私有仓库提示 */
51
55
 
52
56
  test('sync-view: 私有仓库强制提示文案存在且强调私有', () => {
53
- const hint = privateRepoHint()
57
+ const hint = privateRepoHint(zhUiT)
54
58
  assert.match(hint, /私有/)
55
59
  assert.match(hint, /public/)
56
60
  assert.match(hint, /token/)
@@ -66,7 +70,7 @@ const SYNC_CATALOG: SyncSectionInfo[] = [
66
70
  ];
67
71
 
68
72
  test('sync-view: syncSectionOptions 把 host 目录投影为勾选项(保 id 顺序 + 导出目录分组/描述补充)', () => {
69
- const opts = syncSectionOptions(SYNC_CATALOG)
73
+ const opts = syncSectionOptions(SYNC_CATALOG, zhUiT)
70
74
  assert.deepEqual(opts.map((o) => o.id), ['settings', 'providers', 'plugins', 'skills'])
71
75
  assert.equal(opts[0]?.label, 'Settings')
72
76
  assert.equal(opts[0]?.defaultIncluded, true)
@@ -82,14 +86,14 @@ test('sync-view: syncSectionOptions 把 host 目录投影为勾选项(保 id
82
86
  test('sync-view: syncSectionOptions 未知分区 id → 兜底(description 空、group=general)', () => {
83
87
  const opts = syncSectionOptions([
84
88
  { id: 'nope' as SectionId, displayName: 'Nope', portability: 'portable', defaultIncluded: true },
85
- ])
89
+ ], zhUiT)
86
90
  assert.equal(opts[0]?.label, 'Nope')
87
91
  assert.equal(opts[0]?.description, '')
88
92
  assert.equal(opts[0]?.group, 'general')
89
93
  });
90
94
 
91
95
  test('sync-view: syncSectionGroups 按导出分组投影(与「导出备份·自定义模式」同构,空组省略)', () => {
92
- const groups = syncSectionGroups(syncSectionOptions(SYNC_CATALOG))
96
+ const groups = syncSectionGroups(syncSectionOptions(SYNC_CATALOG, zhUiT), zhUiT)
93
97
  // settings(general) + providers(ai) + plugins(extensions) + skills(customization)
94
98
  assert.deepEqual(groups.map((g) => g.group), ['general', 'ai', 'extensions', 'customization'])
95
99
  assert.equal(groups[0]?.label, 'General')
@@ -114,13 +118,13 @@ test('sync-view: recommendedSyncSections 排除非 portable / 非默认包含分
114
118
 
115
119
  test('sync-view: 空/缺省目录 → 推荐分区为空数组(UI 显示「至少选一个」场景)', () => {
116
120
  assert.deepEqual(recommendedSyncSections([]), [])
117
- assert.deepEqual(syncSectionOptions([]), [])
121
+ assert.deepEqual(syncSectionOptions([], zhUiT), [])
118
122
  });
119
123
 
120
124
  /* ---------------------------------------------------------------- 按钮状态 */
121
125
 
122
126
  test('sync-view: 空闲 + 活动通道地址未就绪 → 两个按钮都禁用', () => {
123
- const b = computeSyncButtons(null, false)
127
+ const b = computeSyncButtons(null, false, zhUiT)
124
128
  assert.equal(b.canPush, false)
125
129
  assert.equal(b.canPull, false)
126
130
  assert.equal(b.pushLabel, '推送到远端')
@@ -128,13 +132,13 @@ test('sync-view: 空闲 + 活动通道地址未就绪 → 两个按钮都禁用'
128
132
  })
129
133
 
130
134
  test('sync-view: 空闲 + 活动通道地址就绪 → 两个按钮可用', () => {
131
- const b = computeSyncButtons(null, true)
135
+ const b = computeSyncButtons(null, true, zhUiT)
132
136
  assert.equal(b.canPush, true)
133
137
  assert.equal(b.canPull, true)
134
138
  })
135
139
 
136
140
  test('sync-view: push 进行中 → 按钮禁用且文案切换为正在推送(防并发)', () => {
137
- const b = computeSyncButtons('push', true)
141
+ const b = computeSyncButtons('push', true, zhUiT)
138
142
  assert.equal(b.canPush, false)
139
143
  assert.equal(b.canPull, false)
140
144
  assert.equal(b.pushLabel, '正在推送…')
@@ -142,7 +146,7 @@ test('sync-view: push 进行中 → 按钮禁用且文案切换为正在推送
142
146
  })
143
147
 
144
148
  test('sync-view: pull 进行中 → 两个按钮都禁用,pull 文案切换', () => {
145
- const b = computeSyncButtons('pull', true)
149
+ const b = computeSyncButtons('pull', true, zhUiT)
146
150
  assert.equal(b.canPush, false)
147
151
  assert.equal(b.canPull, false)
148
152
  assert.equal(b.pullLabel, '正在拉取…')
@@ -191,19 +195,19 @@ test('sync-view: summarizePullChanges 空数组 → total 0 且不需决策', ()
191
195
  })
192
196
 
193
197
  test('sync-view: kindLabel / severityLabel 覆盖关键类型', () => {
194
- assert.equal(kindLabel('Conflict'), '冲突')
195
- assert.equal(kindLabel('Install'), '安装')
196
- assert.equal(kindLabel('MissingSecret'), '缺密钥')
197
- assert.equal(severityLabel('error'), '错误')
198
- assert.equal(severityLabel('warning'), '警告')
199
- assert.equal(severityLabel('info'), '信息')
198
+ assert.equal(kindLabel('Conflict', zhUiT), '冲突')
199
+ assert.equal(kindLabel('Install', zhUiT), '安装')
200
+ assert.equal(kindLabel('MissingSecret', zhUiT), '缺密钥')
201
+ assert.equal(severityLabel('error', zhUiT), '错误')
202
+ assert.equal(severityLabel('warning', zhUiT), '警告')
203
+ assert.equal(severityLabel('info', zhUiT), '信息')
200
204
  })
201
205
 
202
206
  /* ---------------------------------------------------------------- push 报告渲染 */
203
207
 
204
208
  test('sync-view: push 成功报告 → ok 头部含快照 id + 分区透传', () => {
205
209
  const report: SyncPushReport = { ok: true, snapshotId: 'sync-1', sections: ['settings', 'plugins'], warnings: [] }
206
- const view = pushReportView(report)
210
+ const view = pushReportView(report, zhUiT)
207
211
  assert.notEqual(view, null)
208
212
  assert.equal(view?.kind, 'ok')
209
213
  assert.match(view?.headline ?? '', /sync-1/)
@@ -212,21 +216,21 @@ test('sync-view: push 成功报告 → ok 头部含快照 id + 分区透传', ()
212
216
 
213
217
  test('sync-view: push 失败报告 → error 显示引擎 message', () => {
214
218
  const report: SyncPushReport = { ok: false, snapshotId: '', sections: [], warnings: [], message: '全部导出失败' }
215
- const view = pushReportView(report)
219
+ const view = pushReportView(report, zhUiT)
216
220
  assert.equal(view?.kind, 'error')
217
221
  assert.equal(view?.headline, '全部导出失败')
218
222
  })
219
223
 
220
224
  test('sync-view: null 报告 → null(不渲染卡片)', () => {
221
- assert.equal(pushReportView(null), null)
222
- assert.equal(pullReportView(null), null)
225
+ assert.equal(pushReportView(null, zhUiT), null)
226
+ assert.equal(pullReportView(null, zhUiT), null)
223
227
  })
224
228
 
225
229
  /* ---------------------------------------------------------------- pull 报告渲染 */
226
230
 
227
231
  test('sync-view: pull 无变更 → empty 渲染(无差异列表)', () => {
228
232
  const report: SyncPullReport = { ok: true, snapshotId: 'sync-1', changes: [], needsReview: false }
229
- const view = pullReportView(report)
233
+ const view = pullReportView(report, zhUiT)
230
234
  assert.equal(view?.kind, 'empty')
231
235
  assert.equal(view?.summary, null)
232
236
  })
@@ -241,7 +245,7 @@ test('sync-view: pull 差异报告 → ok + 摘要计数 + needsReview + 只读
241
245
  ],
242
246
  needsReview: true,
243
247
  }
244
- const view = pullReportView(report)
248
+ const view = pullReportView(report, zhUiT)
245
249
  assert.equal(view?.kind, 'ok')
246
250
  assert.match(view?.headline ?? '', /sync-9/)
247
251
  assert.equal(view?.summary?.total, 2)
@@ -254,7 +258,7 @@ test('sync-view: pull 差异报告 → ok + 摘要计数 + needsReview + 只读
254
258
 
255
259
  test('sync-view: pull 失败 → error 渲染', () => {
256
260
  const report: SyncPullReport = { ok: false, snapshotId: '', changes: [], needsReview: false, message: '认证失败' }
257
- const view = pullReportView(report)
261
+ const view = pullReportView(report, zhUiT)
258
262
  assert.equal(view?.kind, 'error')
259
263
  assert.equal(view?.headline, '认证失败')
260
264
  })
@@ -262,18 +266,18 @@ test('sync-view: pull 失败 → error 渲染', () => {
262
266
  /* ---------------------------------------------------------------- 状态行 */
263
267
 
264
268
  test('sync-view: 加载中 → loading 状态', () => {
265
- const s = computeSyncStatus(null, true, null)
269
+ const s = computeSyncStatus(null, true, null, zhUiT)
266
270
  assert.equal(s.kind, 'loading')
267
271
  })
268
272
 
269
273
  test('sync-view: 加载失败 → error 状态带消息', () => {
270
- const s = computeSyncStatus(null, false, '网络错误')
274
+ const s = computeSyncStatus(null, false, '网络错误', zhUiT)
271
275
  assert.equal(s.kind, 'error')
272
276
  assert.equal(s.text, '网络错误')
273
277
  })
274
278
 
275
279
  test('sync-view: 未配置仓库 → unconfigured 提示', () => {
276
- const s = computeSyncStatus(null, false, null)
280
+ const s = computeSyncStatus(null, false, null, zhUiT)
277
281
  assert.equal(s.kind, 'unconfigured')
278
282
  assert.match(s.text, /尚未配置/)
279
283
  })
@@ -283,7 +287,7 @@ test('sync-view: 已配置但缺凭据 → ready 文案提示未配置凭据', (
283
287
  ok: true, configured: true, repoUrl: 'https://github.com/u/r.git',
284
288
  credentialConfigured: false, credentialWritable: true, lastSyncAt: undefined, sectionCount: 0,
285
289
  }
286
- const s = computeSyncStatus(info, false, null)
290
+ const s = computeSyncStatus(info, false, null, zhUiT)
287
291
  assert.equal(s.kind, 'ready')
288
292
  assert.match(s.text, /未配置凭据/)
289
293
  })
@@ -295,7 +299,7 @@ test('sync-view: 已配置 + 凭据就绪 + 上次同步 → ready 文案含日
295
299
  lastSyncAt: '2026-08-16T10:30:00.000Z', sectionCount: 3,
296
300
  transport: { type: 'git', ref: 'main' },
297
301
  }
298
- const s = computeSyncStatus(info, false, null)
302
+ const s = computeSyncStatus(info, false, null, zhUiT)
299
303
  assert.equal(s.kind, 'ready')
300
304
  assert.match(s.text, /凭据已配置/)
301
305
  assert.match(s.text, /上次同步/)
@@ -310,7 +314,7 @@ test('sync-view: webdav 通道 → ready 文案回显通道类型(webdav + 服
310
314
  lastSyncAt: '2026-08-16T10:30:00.000Z', sectionCount: 2,
311
315
  transport: { type: 'webdav', ref: 'https://dav.example.com/dav/config' },
312
316
  }
313
- const s = computeSyncStatus(info, false, null)
317
+ const s = computeSyncStatus(info, false, null, zhUiT)
314
318
  assert.equal(s.kind, 'ready')
315
319
  assert.match(s.text, /凭据已配置/)
316
320
  assert.match(s.text, /webdav/)
@@ -320,12 +324,12 @@ test('sync-view: webdav 通道 → ready 文案回显通道类型(webdav + 服
320
324
  /* ---------------------------------------------------------------- 时间格式化 */
321
325
 
322
326
  test('sync-view: formatLastSync 空值 → 从未同步', () => {
323
- assert.equal(formatLastSync(undefined), '从未同步')
324
- assert.equal(formatLastSync(''), '从未同步')
327
+ assert.equal(formatLastSync(undefined, zhUiT), '从未同步')
328
+ assert.equal(formatLastSync('', zhUiT), '从未同步')
325
329
  })
326
330
 
327
331
  test('sync-view: formatLastSync / formatDateTime 合法 ISO → 本地可读格式', () => {
328
- const text = formatLastSync('2026-08-16T10:30:00.000Z')
332
+ const text = formatLastSync('2026-08-16T10:30:00.000Z', zhUiT)
329
333
  assert.match(text, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/)
330
334
  assert.equal(formatDateTime('not-a-date'), 'not-a-date')
331
335
  })
@@ -333,7 +337,7 @@ test('sync-view: formatLastSync / formatDateTime 合法 ISO → 本地可读格
333
337
  /* ------------------------------------------------ GitHub 登录视图模型 */
334
338
 
335
339
  test('sync-view: github 登录 idle → 可开始、不展示代码区块、无错误', () => {
336
- const v = computeGithubLoginView('idle', '', '', null)
340
+ const v = computeGithubLoginView('idle', '', '', null, zhUiT)
337
341
  assert.equal(v.phase, 'idle')
338
342
  assert.equal(v.canStart, true)
339
343
  assert.equal(v.canCancel, false)
@@ -342,14 +346,14 @@ test('sync-view: github 登录 idle → 可开始、不展示代码区块、无
342
346
  })
343
347
 
344
348
  test('sync-view: github 登录 starting → 不可重复发起、显示进行中文案', () => {
345
- const v = computeGithubLoginView('starting', '', '', null)
349
+ const v = computeGithubLoginView('starting', '', '', null, zhUiT)
346
350
  assert.equal(v.canStart, false)
347
351
  assert.equal(v.canCancel, true)
348
352
  assert.match(v.statusText, /发起/)
349
353
  })
350
354
 
351
355
  test('sync-view: github 登录 waiting → 展示设备码与授权链接、可取消', () => {
352
- const v = computeGithubLoginView('waiting', 'ABCD-EFGH', 'https://github.com/login/device', null)
356
+ const v = computeGithubLoginView('waiting', 'ABCD-EFGH', 'https://github.com/login/device', null, zhUiT)
353
357
  assert.equal(v.showCode, true)
354
358
  assert.equal(v.canCancel, true)
355
359
  assert.equal(v.userCode, 'ABCD-EFGH')
@@ -358,7 +362,7 @@ test('sync-view: github 登录 waiting → 展示设备码与授权链接、可
358
362
  })
359
363
 
360
364
  test('sync-view: github 登录 polling → 仍展示代码区块且可取消', () => {
361
- const v = computeGithubLoginView('polling', 'ABCD-EFGH', 'https://github.com/login/device', null)
365
+ const v = computeGithubLoginView('polling', 'ABCD-EFGH', 'https://github.com/login/device', null, zhUiT)
362
366
  assert.equal(v.showCode, true)
363
367
  assert.equal(v.canCancel, true)
364
368
  assert.equal(v.canStart, false)
@@ -366,14 +370,14 @@ test('sync-view: github 登录 polling → 仍展示代码区块且可取消', (
366
370
  })
367
371
 
368
372
  test('sync-view: github 登录 success → 成功文案、代码区块隐藏', () => {
369
- const v = computeGithubLoginView('success', 'ABCD-EFGH', 'https://github.com/login/device', null)
373
+ const v = computeGithubLoginView('success', 'ABCD-EFGH', 'https://github.com/login/device', null, zhUiT)
370
374
  assert.equal(v.showCode, false)
371
375
  assert.equal(v.canStart, false)
372
376
  assert.match(v.statusText, /已安全写入/)
373
377
  })
374
378
 
375
379
  test('sync-view: github 登录 error → 展示错误 + 重新登录入口', () => {
376
- const v = computeGithubLoginView('error', '', '', '授权被拒绝')
380
+ const v = computeGithubLoginView('error', '', '', '授权被拒绝', zhUiT)
377
381
  assert.equal(v.canStart, true)
378
382
  assert.equal(v.canCancel, false)
379
383
  assert.equal(v.error, '授权被拒绝')
@@ -383,15 +387,15 @@ test('sync-view: github 登录 error → 展示错误 + 重新登录入口', ()
383
387
 
384
388
  test('sync-view: githubPollMessage 映射轮询终止态(denied/expired/error/success)', () => {
385
389
  const denied: GithubPollResponse = { status: 'denied' }
386
- assert.match(githubPollMessage(denied), /拒绝/)
390
+ assert.match(githubPollMessage(denied, zhUiT), /拒绝/)
387
391
  const expired: GithubPollResponse = { status: 'expired' }
388
- assert.match(githubPollMessage(expired), /过期/)
392
+ assert.match(githubPollMessage(expired, zhUiT), /过期/)
389
393
  const error: GithubPollResponse = { status: 'error', errorCode: 'incorrect_device_code', message: '设备码不匹配' }
390
- assert.match(githubPollMessage(error), /设备码不匹配/)
394
+ assert.match(githubPollMessage(error, zhUiT), /设备码不匹配/)
391
395
  const success: GithubPollResponse = { status: 'success', credentialConfigured: true }
392
- assert.match(githubPollMessage(success), /已安全写入/)
396
+ assert.match(githubPollMessage(success, zhUiT), /已安全写入/)
393
397
  const pending: GithubPollResponse = { status: 'pending', pollDelayMs: 5000 }
394
- assert.equal(githubPollMessage(pending), '', 'pending 不是终止态,不应产生消息')
398
+ assert.equal(githubPollMessage(pending, zhUiT), '', 'pending 不是终止态,不应产生消息')
395
399
  })
396
400
 
397
401
  /* ---------------------------------------------------------------- 自动同步倒计时 */
@@ -416,19 +420,19 @@ test('sync-view: computeAutosyncCountdown 已到期 → 0;未到 → interval
416
420
 
417
421
  test('sync-view: formatIntervalDuration 输出真实剩余时长(不再把 <1 小时一律显示成 30 分钟)', () => {
418
422
  // 回归:旧实现 `if (minutes < 60) return '30 分钟'` 导致 5m 间隔永远显示「约 30 分钟后」
419
- assert.equal(formatIntervalDuration(4 * 60 * 1000), '4 分钟')
420
- assert.equal(formatIntervalDuration(5 * 60 * 1000), '5 分钟')
421
- assert.equal(formatIntervalDuration(30 * 60 * 1000), '30 分钟')
423
+ assert.equal(formatIntervalDuration(4 * 60 * 1000, zhUiT), '4 分钟')
424
+ assert.equal(formatIntervalDuration(5 * 60 * 1000, zhUiT), '5 分钟')
425
+ assert.equal(formatIntervalDuration(30 * 60 * 1000, zhUiT), '30 分钟')
422
426
  // 整 60 分钟进位为 1 小时
423
- assert.equal(formatIntervalDuration(60 * 60 * 1000), '1 小时')
427
+ assert.equal(formatIntervalDuration(60 * 60 * 1000, zhUiT), '1 小时')
424
428
  // 向上取整:90 分钟 → 2 小时;6 小时整 → 6 小时
425
- assert.equal(formatIntervalDuration(90 * 60 * 1000), '2 小时')
426
- assert.equal(formatIntervalDuration(6 * 60 * 60 * 1000), '6 小时')
429
+ assert.equal(formatIntervalDuration(90 * 60 * 1000, zhUiT), '2 小时')
430
+ assert.equal(formatIntervalDuration(6 * 60 * 60 * 1000, zhUiT), '6 小时')
427
431
  // 跨天:24 小时 → 1 天;48 小时 → 2 天
428
- assert.equal(formatIntervalDuration(24 * 60 * 60 * 1000), '1 天')
429
- assert.equal(formatIntervalDuration(48 * 60 * 60 * 1000), '2 天')
432
+ assert.equal(formatIntervalDuration(24 * 60 * 60 * 1000, zhUiT), '1 天')
433
+ assert.equal(formatIntervalDuration(48 * 60 * 60 * 1000, zhUiT), '2 天')
430
434
  // 已到期/异常值兜底为 1 分钟,不出现 0 分钟
431
- assert.equal(formatIntervalDuration(0), '1 分钟')
435
+ assert.equal(formatIntervalDuration(0, zhUiT), '1 分钟')
432
436
  })
433
437
 
434
438
  /* ---------------------------------------------------------------- WebDAV 预设 */
@@ -505,3 +509,54 @@ test('sync-view: defaultChannelSyncState 每通道独立缺省值', () => {
505
509
  assert.equal(git.syncMode, 'advanced', '修改一个通道缺省不影响另一个')
506
510
  assert.deepEqual(git.syncSections, [], '另一个通道的勾选不受影响')
507
511
  })
512
+
513
+ /* ---------------------------------------------------------------- issue #79 */
514
+
515
+ /** 同步字典取词器(支持 {n} 插值;缺键回退键本身,与运行时同口径)。 */
516
+ function mkSyncText(dict: Record<string, string>): SyncTextFn {
517
+ return ((key: string, params?: Record<string, unknown>): string => {
518
+ const raw = dict[key] ?? key
519
+ return params === undefined
520
+ ? raw
521
+ : raw.replace(/\{(\w+)\}/g, (m, k: string) => (k in params ? String(params[k]) : m))
522
+ }) as unknown as SyncTextFn
523
+ }
524
+
525
+ const syncZhText = mkSyncText(syncZh)
526
+ const syncEnText = mkSyncText(syncEn)
527
+
528
+ function autosyncStatus(overrides: Partial<AutosyncStatusResponse> = {}): AutosyncStatusResponse {
529
+ return { enabled: true, interval: '30m', consecutiveFailures: 0, elapsedMs: 0, ...overrides }
530
+ }
531
+
532
+ test('issue #79:autosyncSkipDetail —— 冲突带「另有 N 个分区待确认」,其余码只给原因', () => {
533
+ assert.equal(
534
+ autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'conflict', lastRunSkipCount: 3 }), syncZhText),
535
+ '冲突(远端另有 3 个分区待确认,可在同步页手动处理)',
536
+ )
537
+ assert.equal(autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'conflict' }), syncZhText), '冲突', '无计数时只给原因,不编数字')
538
+ assert.equal(
539
+ autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'upToDate' }), syncZhText),
540
+ syncZh['history.autosyncReasonUpToDate'],
541
+ )
542
+ assert.equal(
543
+ autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'unconfigured' }), syncZhText),
544
+ syncZh['history.autosyncReasonNotConfigured'],
545
+ '宿主两处命名同义 → 同一文案',
546
+ )
547
+ })
548
+
549
+ test('issue #79:autosyncSkipDetail —— 未跳过时无明细;未知码给兜底而**不渲染裸码**', () => {
550
+ assert.equal(autosyncSkipDetail(autosyncStatus(), syncZhText), null)
551
+ assert.equal(autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: '' }), syncZhText), null)
552
+ const unknown = autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'brand-new-code' }), syncZhText)
553
+ assert.equal(unknown, syncZh['history.autosyncReasonUnknown'])
554
+ assert.ok(!String(unknown).includes('brand-new-code'), '绝不把裸码当文案渲染')
555
+ })
556
+
557
+ test('issue #79:autosyncSkipDetail —— en 界面下不得出现中文(文案只许来自字典)', () => {
558
+ const en = autosyncSkipDetail(autosyncStatus({ lastRunSkipReason: 'conflict', lastRunSkipCount: 2 }), syncEnText)
559
+ assert.doesNotMatch(String(en), /[\u4e00-\u9fff]/, 'en 界面不得出现中文:' + String(en))
560
+ assert.match(String(en), /2/)
561
+ })
562
+