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
@@ -20,6 +20,7 @@ import type { SelectionSection } from '../../ui/selection-model.ts'
20
20
  import {
21
21
  initialSessionPicks, pickedSessionIds, sessionPickerSelection,
22
22
  } from '../../ui/sync-settings-view.ts'
23
+ import type { UiT } from '../../ui/i18n.ts'
23
24
  import { DEFAULT_SYNC_SESSIONS_LIMIT, syncSectionGroups } from './sync-view.ts'
24
25
  import type { SyncSectionOption } from './sync-view.ts'
25
26
  import css from '../config-manager.module.css'
@@ -30,8 +31,10 @@ import css from '../config-manager.module.css'
30
31
  * (与导出选择器一致的弹窗语义),因此底部只有「完成」—— 没有「取消」
31
32
  * (取消会让用户以为改动被丢弃)。
32
33
  */
33
- export function SyncSectionPickerDialog({ open, t, cmT, sectionName, catalog, sections, sessionsLimit, sessionsInclude, sessionNode, sessionPending, sessionFailed, onToggleSection, onSessionsLimit, onSessionsInclude, onClose }: {
34
+ export function SyncSectionPickerDialog({ open, t, cmT, uiT, sectionName, catalog, sections, sessionsLimit, sessionsInclude, sessionNode, sessionPending, sessionFailed, onToggleSection, onSessionsLimit, onSessionsInclude, onClose }: {
34
35
  t: TranslateNS<'config-manager-sync'>
36
+ /** ui 命名空间的翻译器(UiT):分区描述与分组说明的**字典键**由它解析(英文界面零中文) */
37
+ uiT: UiT
35
38
  /** config-manager 命名空间的翻译器:ContentPicker 的搜索/全选/合计文案来自它 */
36
39
  cmT: TranslateNS<'config-manager'>
37
40
  open: boolean
@@ -119,7 +122,7 @@ export function SyncSectionPickerDialog({ open, t, cmT, sectionName, catalog, se
119
122
  /* 分组勾选目录:与「导出备份·自定义模式」同构(分组 Card + 名称/描述/徽章),
120
123
  分区名走 section-labels 单一映射 —— 与导出选择器显示**同一个中文名**。 */
121
124
  <div className={css.groupList}>
122
- {syncSectionGroups(catalog).map((g) => (
125
+ {syncSectionGroups(catalog, uiT).map((g) => (
123
126
  <Card key={g.group} className={css.groupCard}>
124
127
  <div className={css.groupHeader}>
125
128
  <span className={css.groupLabel}>{g.label}</span>
@@ -137,9 +140,15 @@ export function SyncSectionPickerDialog({ open, t, cmT, sectionName, catalog, se
137
140
  <span className={css.categoryName}>{sectionName(s.id)}</span>
138
141
  <span className={css.categoryDesc}>{s.description}</span>
139
142
  {s.portability === 'portable' && <Badge kind="info">{t('mode.sectionPortable')}</Badge>}
143
+ {s.portability === 'platformSpecific' && (
144
+ <Badge kind="warn">{t('mode.sectionPlatformSpecific')}</Badge>
145
+ )}
140
146
  {s.portability === 'deviceSpecific' && (
141
147
  <Badge kind="warn">{t('mode.sectionDeviceSpecific')}</Badge>
142
148
  )}
149
+ {/* issue #78:MCP 的机器相关内容(绝对路径 command / cwd、缺运行时)走 ⓘ,
150
+ 与「机制说明进 InfoHint」的同一条纪律(见 info-hint-guard)。 */}
151
+ {s.id === 'mcp' && <InfoHint text={t('mode.mcpHint')} label={t('common.infoHint')} />}
143
152
  {s.defaultIncluded && <Badge kind="ok">{t('mode.sectionRecommended')}</Badge>}
144
153
  </span>
145
154
  }
@@ -60,6 +60,8 @@ test('t42: 组件按职责物理拆分成独立文件(各自导出 + 主文件
60
60
  ['SecurityOptionsCard', 'SecurityOptionsCard.tsx'],
61
61
  ['DecryptPasswordCard', 'DecryptPasswordCard.tsx'],
62
62
  ['AutosyncCard', 'AutosyncCard.tsx'],
63
+ // issue #79:工作副本卡(含确认弹窗 + 宿主调用 + 回执)自成文件,主文件只装配 —— 行数棘轮因此不必上调
64
+ ['WorkDirCard', 'WorkDirCard.tsx'],
63
65
  ]
64
66
  for (const [name, file] of units) {
65
67
  // 主文件:必须 import 并渲染该单元
@@ -92,7 +94,10 @@ test('t42: 组件按职责物理拆分成独立文件(各自导出 + 主文件
92
94
  // · 云端点表单 12 个字段进出 state(校验 / 组装 / 就绪判定都已下沉 ui 层,这里是 state 通道);
93
95
  // · 多两张通道卡;对话框改为收单个 form 快照后,调用点反而少了几行;
94
96
  // · /sync/status → 四通道回填 + 云端点非密字段回填。
95
- assert.ok(viewLines <= 1580, '主文件行数必须保持在拆分后的量级(实际 ' + viewLines + ' 行)')
97
+ // 1580 → 1585(t3:英文界面零中文)。增量是**装配接线**:分区选择弹窗新增 uiT 注入
98
+ // (分区描述 / 分组说明改为字典键后,弹窗内需要 ui 字典翻译器才能解析;判定逻辑在 ui 层)。
99
+ // 同一轮把 11 处 toast 的「全角冒号 + 详情」拼接改为 t('common.labelSeparator'),行数不变。
100
+ assert.ok(viewLines <= 1585, '主文件行数必须保持在拆分后的量级(实际 ' + viewLines + ' 行)')
96
101
  })
97
102
 
98
103
  test('t42: 通道卡必须是渲染函数(内联组件 = 每次渲染新类型 → 子树重挂、Select 展开态清零)', () => {
@@ -106,6 +111,20 @@ test('t42: 通道卡必须是渲染函数(内联组件 = 每次渲染新类型
106
111
  assert.match(viewSource, /\{renderChannelCard\('webdav'\)\}/, 'webdav 通道卡必须经渲染函数调用')
107
112
  })
108
113
 
114
+ test('工作副本「重建」按钮必须在请求在途时转加载态(用户报告:点了没有任何反馈)', () => {
115
+ // 真机用户报告:点「重新克隆工作副本」后整卡没有任何变化,只有结束时才跳出 toast —— reclone 要删目录 +
116
+ // 重新 clone(可达数十秒),期间用户无从判断是否在执行。守卫钉住三条:在途态由本卡自持、两个出口都转
117
+ // 加载态并互相禁用、失败也必须收掉加载态;任一条回退即变红。
118
+ const src = readSource(fileURLToPath(new URL('./WorkDirCard.tsx', import.meta.url)))
119
+ assert.match(src, /const \[pending, setPending\] = useState<'relink' \| 'reclone' \| null>\(null\)/, '在途态必须是本卡自持的 relink/reclone 判别式状态')
120
+ assert.match(src, /loading=\{pending === 'reclone'\}/, '重新克隆按钮必须在自身请求在途时显示加载态')
121
+ assert.match(src, /loading=\{pending === 'relink'\}/, '重指向按钮同理')
122
+ assert.match(src, /disabled=\{busy \|\| pending !== null\}/, '在途时必须同时禁用两个出口(防重复点击)')
123
+ const start = src.indexOf('const reset = async')
124
+ const body = src.slice(start, start + 900)
125
+ assert.match(body, /finally \{[\s\S]*setPending\(null\)/, '失败也必须收掉加载态(否则按钮会永远转圈)')
126
+ })
127
+
109
128
  test('t42: 断开 git 通道必须同步复位内存登录态(令牌槽位被删 = 已登出,否则登录入口恒隐藏)', () => {
110
129
  // 真机用户报告:点「断开配置」删掉的是 SYNC_CREDENTIAL_REF —— 与 GitHub 登录**同一个槽位**,
111
130
  // 但 githubSignedIn 当时不复位,登录块恒隐藏,用户以为要重启 profile 才能重新登录。
@@ -28,7 +28,7 @@ export function UnreadableSnapshotsBanner({
28
28
  <Banner kind="warn">
29
29
  {t('snapshots.unreadableHint', {
30
30
  n: String(items.length),
31
- files: items.map((u) => baseName(u.file)).join('、'),
31
+ files: items.map((u) => baseName(u.file)).join(t('common.listSeparator')),
32
32
  })}
33
33
  </Banner>
34
34
  )
@@ -0,0 +1,112 @@
1
+ /**
2
+ * 工作副本卡(issue #79;t42 物理拆分:渲染段自成文件,主文件只装配)。
3
+ *
4
+ * 为什么需要它:改仓库地址后同步失败时,App 内此前**没有任何自救路径** —— 只能手工
5
+ * `git remote set-url`,或删掉 `<dataDir>/sync/work` 重来。这里给出工作副本的只读事实
6
+ * (路径 / origin / 与配置地址是否一致)与两条出口:
7
+ * - 「重指向仓库地址」= host 的 relink(非破坏性:只改 git 配置,不碰工作树与本地提交);
8
+ * - 「重新克隆工作副本」= host 的 reclone(**丢弃未上行的本地提交**)→ danger 二次确认。
9
+ *
10
+ * 约定(与 ChannelClearConfirmDialog 同族):确认弹窗、宿主调用与终局回执都在本文件内自持,
11
+ * 调用方只传「渲染所需的数据 + 变更通知」;判定在 `src/ui/sync-settings-view.ts`(可测)。
12
+ */
13
+ import { useState } from 'react'
14
+ import type { TranslateNS } from '../client-types.ts'
15
+ import type { SyncApi, SyncStatusResponse } from './sync-api.ts'
16
+ import { Badge, Button, Card } from '../common/ui.tsx'
17
+ import { InfoHint } from '../common/InfoHint.tsx'
18
+ import { ConfirmDialog } from '../common/ConfirmDialog.tsx'
19
+ import { toast } from '../common/toast-store.ts'
20
+ import { redact } from '../../security/redaction.ts'
21
+ import {
22
+ canRecloneWorkdir, formatSyncUrlPreview, needsWorkdirRelink, workdirStateBadgeKind, workdirStateKey,
23
+ } from '../../ui/sync-settings-view.ts'
24
+ import css from '../config-manager.module.css'
25
+
26
+ export function WorkDirCard({ t, api, workDir, busy, onChanged }: {
27
+ t: TranslateNS<'config-manager-sync'>
28
+ api: SyncApi
29
+ /** GET /sync/status 的 workDir 字段(非 git 通道 / 未配置时 undefined) */
30
+ workDir: SyncStatusResponse['workDir']
31
+ busy: boolean
32
+ /** 重建成功后重拉状态(调用方负责它的状态源) */
33
+ onChanged: () => void
34
+ }) {
35
+ /** 重新克隆的二次确认(破坏性:丢弃未上行的本地提交) */
36
+ const [recloneOpen, setRecloneOpen] = useState(false)
37
+ /** 本次发起的出口(relink / reclone):请求在途时卡片按钮就地转加载态。
38
+ * 为什么必须有:reclone 要删目录 + 重新 clone(真机可达数十秒),此前点了只有 toast 在**结束**时才响,
39
+ * 用户无从判断是否正在执行,容易重复点击或以为没反应。 */
40
+ const [pending, setPending] = useState<'relink' | 'reclone' | null>(null)
41
+ const stateKey = workdirStateKey(workDir)
42
+ const badgeKind = workdirStateBadgeKind(stateKey)
43
+ const facts = workDir !== undefined && 'facts' in workDir ? workDir.facts : null
44
+
45
+ const reset = async (mode: 'relink' | 'reclone'): Promise<void> => {
46
+ setRecloneOpen(false)
47
+ setPending(mode)
48
+ try {
49
+ await api.workdirReset(mode === 'reclone' ? { mode, confirm: true } : { mode })
50
+ toast.ok(t(mode === 'relink' ? 'toast.workdirRelinked' : 'toast.workdirRecloned'))
51
+ onChanged()
52
+ } catch (err) {
53
+ toast.error(t('toast.workdirResetFailed') + t('common.labelSeparator') + redact(err instanceof Error ? err.message : String(err)))
54
+ } finally {
55
+ // 无论成功/失败都要收掉加载态,否则按钮会永远转圈(状态重拉由 onChanged 负责)
56
+ setPending(null)
57
+ }
58
+ }
59
+
60
+ return (
61
+ <Card>
62
+ <span className={css.groupLabel}>{t('workdir.title')} <InfoHint text={t('workdir.hint')} label={t('common.infoHint')} /></span>
63
+ {stateKey !== null && badgeKind !== null && (
64
+ <div className={css.statRow}>
65
+ <Badge kind={badgeKind}>{t(stateKey)}</Badge>
66
+ </div>
67
+ )}
68
+ {workDir === undefined ? (
69
+ <span className={css.categoryDesc}>{t('workdir.unknown')}</span>
70
+ ) : (
71
+ <div className={css.factGrid} style={{ marginTop: 8 }}>
72
+ <div className={css.factCell} style={{ gridColumn: '1 / -1' }}>
73
+ <span className={css.factLabel}>{t('workdir.path')}</span>
74
+ <span className={css.factValue}>
75
+ <span className={css.mono}>{formatSyncUrlPreview(facts?.path ?? t('workdir.unknown'))}</span>
76
+ </span>
77
+ </div>
78
+ <div className={css.factCell} style={{ gridColumn: '1 / -1' }}>
79
+ <span className={css.factLabel}>{t('workdir.origin')}</span>
80
+ <span className={css.factValue}>
81
+ <span className={css.mono}>
82
+ {facts === null || facts.origin === null ? t('workdir.unknown') : formatSyncUrlPreview(facts.origin)}
83
+ </span>
84
+ </span>
85
+ </div>
86
+ </div>
87
+ )}
88
+ <div className={css.actionRowTop}>
89
+ {needsWorkdirRelink(workDir) && (
90
+ <Button disabled={busy || pending !== null} loading={pending === 'relink'} onClick={() => { void reset('relink') }}>
91
+ {t('workdir.relink')}
92
+ </Button>
93
+ )}
94
+ {canRecloneWorkdir(workDir) && (
95
+ <Button variant="danger" disabled={busy || pending !== null} loading={pending === 'reclone'} onClick={() => { setRecloneOpen(true) }}>
96
+ {t('workdir.reclone')}
97
+ </Button>
98
+ )}
99
+ </div>
100
+ <ConfirmDialog
101
+ open={recloneOpen}
102
+ danger
103
+ title={t('workdir.recloneConfirmTitle')}
104
+ message={t('workdir.recloneConfirmMessage')}
105
+ confirmLabel={t('workdir.recloneConfirm')}
106
+ cancelLabel={t('common.cancel')}
107
+ onConfirm={() => reset('reclone')}
108
+ onCancel={() => { setRecloneOpen(false) }}
109
+ />
110
+ </Card>
111
+ )
112
+ }
@@ -14,7 +14,8 @@ import type { SyncHistoryEntry } from './sync-api.ts';
14
14
  import { en as syncEn, zh as syncZh } from './sync-locales.ts';
15
15
  import {
16
16
  autosyncBadgeKind, autosyncStatusLabel, describeSkipReason, directionLabel, formatDateTime,
17
- formatDateTimeFull, midEllipsis, projectAutosyncEntry, projectSyncHistoryEntries, summarizeSyncHistory,
17
+ formatDateTimeFull, midEllipsis, projectAutosyncEntry, projectSyncHistoryEntries, skipReasonKey,
18
+ summarizeSyncHistory,
18
19
  } from './history-model.ts';
19
20
  import type { AutosyncHistoryEntry } from './sync-api.ts';
20
21
 
@@ -79,17 +80,20 @@ test('directionLabel / autosyncStatusLabel:方向与状态映射(走字典
79
80
  assert.equal(autosyncStatusLabel('partial', enT), 'Partial');
80
81
  });
81
82
 
82
- test('describeSkipReason:已知原因走字典;未落字典的三类保持现状(F1 残留,见 findings.md)', () => {
83
+ test('describeSkipReason:七类已知原因全部走字典(F1 收口:不再有硬编码中文)', () => {
83
84
  assert.equal(describeSkipReason('conflict', zhT), syncZh['history.autosyncReasonConflict']);
84
85
  assert.equal(describeSkipReason('no-remote', zhT), syncZh['history.autosyncReasonNoRemote']);
85
86
  assert.equal(describeSkipReason('not-configured', zhT), syncZh['history.autosyncReasonNotConfigured']);
86
87
  assert.equal(describeSkipReason('network', zhT), syncZh['history.autosyncReasonNetwork']);
87
88
  assert.equal(describeSkipReason('conflict', enT), syncEn['history.autosyncReasonConflict']);
88
- // F1 残留(sync-locales.ts 在外部团队在改清单内,暂不能补键)—— 这三类的现状被钉住,
89
- // 一旦补键就必须同时改这里,避免「悄悄漏掉一类」。
90
- assert.equal(describeSkipReason('encrypted', zhT), '远端快照已加密,自动同步跳过(请手动同步)');
89
+ // F1 收口(t3):encrypted / mutation-locked / 未知 三类也已补键,en 界面下不再透出中文
90
+ assert.equal(describeSkipReason('encrypted', zhT), syncZh['history.autosyncReasonEncrypted']);
91
+ assert.equal(describeSkipReason('encrypted', enT), syncEn['history.autosyncReasonEncrypted']);
92
+ assert.equal(describeSkipReason('mutation-locked', zhT), syncZh['history.autosyncReasonMutationLocked']);
93
+ assert.equal(describeSkipReason(undefined, zhT), syncZh['history.autosyncReasonUnknown']);
94
+ assert.equal(describeSkipReason(undefined, enT), syncEn['history.autosyncReasonUnknown']);
95
+ // 未知原因仍原样透出宿主回传的串(不吞信息)
91
96
  assert.equal(describeSkipReason('weird', zhT), 'weird');
92
- assert.equal(describeSkipReason(undefined, zhT), '未知');
93
97
  });
94
98
 
95
99
  // issue #31:宿主统一以 'mutation-locked' 落历史(不细分 LOCKED/STALE)→ 界面不得透出裸 token。
@@ -217,16 +221,45 @@ test('client-F1:en 界面下自动同步行的三个标签必须全是英文
217
221
  assert.equal(row.status, 'Skipped');
218
222
  });
219
223
 
220
- test('client-F1 源码守卫:两个标签函数的函数体不得含中文字面量(文案只许来自字典)', () => {
224
+ test('client-F1 源码守卫:标签函数体不得含中文字面量;跳过原因一律经 skipReasonKey 取字典键', () => {
221
225
  const src = fs.readFileSync(new URL('./history-model.ts', import.meta.url), 'utf8');
222
- for (const fn of ['directionLabel', 'autosyncStatusLabel']) {
226
+ for (const fn of ['directionLabel', 'autosyncStatusLabel', 'describeSkipReason']) {
223
227
  const start = src.indexOf('export function ' + fn);
224
228
  assert.ok(start >= 0, fn + ' 必须存在(守卫需同步更新)');
225
229
  const end = src.indexOf('\n}', start);
226
230
  const body = src.slice(start, end < 0 ? src.length : end);
227
231
  assert.doesNotMatch(body, /[\u4e00-\u9fff]/, fn + ' 函数体内不得含中文字面量');
228
232
  }
233
+ // issue #79:映射抽到 skipReasonKey(历史行与自动同步状态行共用一份),守卫落点随之挪到它,
234
+ // 但硬约束不变:**文案只许来自字典**,且 describeSkipReason 不得再自带第二份 switch。
235
+ const mapStart = src.indexOf('export function skipReasonKey');
236
+ assert.ok(mapStart >= 0, 'skipReasonKey 必须存在(守卫需同步更新)');
237
+ const mapEnd = src.indexOf('\n}', mapStart);
238
+ const mapBody = src.slice(mapStart, mapEnd < 0 ? src.length : mapEnd);
229
239
  for (const key of ['history.autosyncReasonConflict', 'history.autosyncReasonNoRemote', 'history.autosyncReasonNotConfigured', 'history.autosyncReasonNetwork']) {
230
- assert.ok(src.includes("t('" + key + "')"), 'describeSkipReason 必须走字典键 ' + key);
240
+ assert.ok(mapBody.includes("'" + key + "'"), 'skipReasonKey 必须映射字典键 ' + key);
231
241
  }
242
+ const reasonStart = src.indexOf('export function describeSkipReason');
243
+ const reasonEnd = src.indexOf('\n}', reasonStart);
244
+ const reasonBody = src.slice(reasonStart, reasonEnd < 0 ? src.length : reasonEnd);
245
+ assert.ok(reasonBody.includes('skipReasonKey('), 'describeSkipReason 必须经 skipReasonKey 取键');
246
+ });
247
+
248
+ /* ================================================================ issue #79 */
249
+
250
+ test('issue #79:skipReasonKey —— 新旧码都映射到字典键,未知码返回 null(不猜)', () => {
251
+ assert.equal(skipReasonKey('conflict'), 'history.autosyncReasonConflict');
252
+ assert.equal(skipReasonKey('not-configured'), 'history.autosyncReasonNotConfigured');
253
+ // 宿主同一语义两处命名:历史写 'unconfigured',两处必须同一文案
254
+ assert.equal(skipReasonKey('unconfigured'), 'history.autosyncReasonNotConfigured');
255
+ assert.equal(skipReasonKey('upToDate'), 'history.autosyncReasonUpToDate');
256
+ assert.equal(skipReasonKey('unchanged'), 'history.autosyncReasonUnchanged');
257
+ assert.equal(skipReasonKey('mutation-locked'), 'history.autosyncReasonMutationLocked');
258
+ assert.equal(skipReasonKey('future-code-from-newer-host'), null, '未知码不猜');
259
+ assert.equal(skipReasonKey(undefined), null);
260
+ });
261
+
262
+ test('issue #79:describeSkipReason 未知码保留宿主原串(不吞信息)', () => {
263
+ assert.equal(describeSkipReason('brand-new-reason', zhT), 'brand-new-reason');
264
+ assert.equal(describeSkipReason(undefined, zhT), syncZh['history.autosyncReasonUnknown']);
232
265
  });
@@ -6,6 +6,8 @@
6
6
  * + 自动同步 kind=autosync),投影需统一处理两源,并生成自动同步跳过冲突的可读明细。
7
7
  */
8
8
  import type { AutosyncHistoryEntry, SyncHistoryEntry } from './sync-api.ts'
9
+ // 跳过原因码 → 字典键的映射键类型(**type-only**:不把 zh/en 目录按值带进本条 import)
10
+ import type { SyncKey } from './sync-locales.ts'
9
11
  import type { TranslateNS } from '../client-types.ts'
10
12
 
11
13
  /** 同步命名空间翻译器(本文件的标签一律走它 —— 硬编码中文在英文界面下会原样透出)。 */
@@ -84,29 +86,42 @@ export function autosyncStatusLabel(status: AutosyncHistoryEntry['status'], t: S
84
86
  /**
85
87
  * 跳过原因 → 可读描述(host 透传语义;未知原因回退原串)。
86
88
  *
87
- * client-F1(部分修复):conflict / no-remote / not-configured / network 四类走已存在的
88
- * history.autosyncReason* 字典键;encrypted / mutation-locked / 未知 三类**仍为字面中文**
89
- * —— 它们在 sync-locales.ts 里没有对应键,而该文件在外部团队在改清单内(不可写)。
90
- * 补键后这里一并字典化,见 findings.md 的 client-F1 defer 段。
89
+ * client-F1(本次收口):conflict / no-remote / not-configured / network / encrypted /
90
+ * mutation-locked / 未知 **七类全部**走 history.autosyncReason* 字典键 —— 此前后三类是
91
+ * 硬编码中文(sync-locales.ts 当时不可写),英文界面下会原样透出。
91
92
  */
92
- export function describeSkipReason(reason: string | undefined, t: SyncTranslate): string {
93
+ export function skipReasonKey(reason: string | undefined): SyncKey | null {
93
94
  switch (reason) {
94
- case 'conflict': return t('history.autosyncReasonConflict');
95
- case 'no-remote': return t('history.autosyncReasonNoRemote');
96
- case 'not-configured': return t('history.autosyncReasonNotConfigured');
97
- case 'network': return t('history.autosyncReasonNetwork');
98
- // F1 残留(需新增 sync-locales 键 history.autosyncReasonEncrypted)
99
- case 'encrypted': return '远端快照已加密,自动同步跳过(请手动同步)';
95
+ case 'conflict': return 'history.autosyncReasonConflict';
96
+ case 'no-remote': return 'history.autosyncReasonNoRemote';
97
+ case 'not-configured':
98
+ // issue #79:宿主同一语义两处命名(历史写 'unconfigured',旧口径是 'not-configured')→ 同一文案
99
+ case 'unconfigured': return 'history.autosyncReasonNotConfigured';
100
+ case 'network': return 'history.autosyncReasonNetwork';
101
+ case 'encrypted': return 'history.autosyncReasonEncrypted';
102
+ // issue #79:状态行新增两类(无变化 / 已是最新)——此前会落到 default 渲染裸码
103
+ case 'upToDate': return 'history.autosyncReasonUpToDate';
104
+ case 'unchanged': return 'history.autosyncReasonUnchanged';
100
105
  // issue #31:宿主侧的分类锁判定(LOCKED/STALE/UNKNOWN)统一以 'mutation-locked' 落历史,
101
106
  // 客户端拿不到细分 reason → 文案必须同时覆盖「活锁占用」与「残留锁需回收」两种可能,
102
107
  // 并给出可操作方向(否则界面只显示裸 token,用户无从判断要不要处理)。
103
- // F1 残留(需新增 sync-locales 键 history.autosyncReasonMutationLocked)
104
- case 'mutation-locked': return '环境锁被占用(另一项任务进行中,或存在需回收的残留锁)';
105
- // F1 残留(需新增 sync-locales 键 history.autosyncReasonUnknown)
106
- default: return reason ?? '未知';
108
+ case 'mutation-locked': return 'history.autosyncReasonMutationLocked';
109
+ // 未知原因:没有字典键(调用方决定回退策略)
110
+ default: return null;
107
111
  }
108
112
  }
109
113
 
114
+ /**
115
+ * 跳过原因码 → 可读描述(历史行与自动同步状态行**共用同一份映射**)。
116
+ *
117
+ * 参数取「码 → 文案」的最小取词器(只要吃 SyncKey 即可),这样 UiT / 组件 t / 活目录
118
+ * 三种取词器都能直接传入,不必为了类型对齐再造一个包装。
119
+ */
120
+ export function describeSkipReason(reason: string | undefined, t: (key: SyncKey) => string): string {
121
+ const key = skipReasonKey(reason);
122
+ return key === null ? (reason ?? t('history.autosyncReasonUnknown')) : t(key);
123
+ }
124
+
110
125
  /** 把一条自动同步记录投影为展示行(摘要文本 + 可展开的被跳过冲突分区明细)。 */
111
126
  export interface AutosyncHistoryRow {
112
127
  id: string;