dsh-config-manager 0.1.63 → 0.1.64

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 (467) hide show
  1. package/README.md +35 -8
  2. package/README.zh-CN.md +12 -7
  3. package/lib/adapters/agent-instructions.d.ts +3 -3
  4. package/lib/adapters/agent-instructions.js +5 -3
  5. package/lib/adapters/agent-presets.d.ts +3 -3
  6. package/lib/adapters/agent-presets.js +5 -3
  7. package/lib/adapters/credentials.d.ts +3 -3
  8. package/lib/adapters/credentials.js +20 -23
  9. package/lib/adapters/file-collection.d.ts +43 -0
  10. package/lib/adapters/file-collection.js +130 -18
  11. package/lib/adapters/index.d.ts +1 -0
  12. package/lib/adapters/index.js +11 -0
  13. package/lib/adapters/json-section.d.ts +32 -0
  14. package/lib/adapters/json-section.js +61 -0
  15. package/lib/adapters/mcp.d.ts +3 -3
  16. package/lib/adapters/mcp.js +16 -19
  17. package/lib/adapters/plugin-files.d.ts +3 -3
  18. package/lib/adapters/plugin-files.js +11 -14
  19. package/lib/adapters/plugins.d.ts +3 -3
  20. package/lib/adapters/plugins.js +39 -42
  21. package/lib/adapters/prompts.d.ts +3 -3
  22. package/lib/adapters/prompts.js +16 -20
  23. package/lib/adapters/providers.d.ts +3 -3
  24. package/lib/adapters/providers.js +23 -26
  25. package/lib/adapters/sessions.d.ts +97 -1
  26. package/lib/adapters/sessions.js +455 -15
  27. package/lib/adapters/settings.d.ts +3 -3
  28. package/lib/adapters/settings.js +23 -26
  29. package/lib/adapters/skills.d.ts +3 -3
  30. package/lib/adapters/skills.js +5 -3
  31. package/lib/adapters/test-helpers.d.ts +8 -0
  32. package/lib/adapters/test-helpers.js +8 -0
  33. package/lib/adapters/ui.d.ts +3 -3
  34. package/lib/adapters/ui.js +14 -17
  35. package/lib/adapters/workspaces.d.ts +22 -0
  36. package/lib/adapters/workspaces.js +237 -16
  37. package/lib/cli/index.d.ts +35 -4
  38. package/lib/cli/index.js +147 -25
  39. package/lib/cli/sessions-repair.d.ts +28 -0
  40. package/lib/cli/sessions-repair.js +206 -0
  41. package/lib/client.d.ts +260 -19
  42. package/lib/client.js +9257 -5984
  43. package/lib/core/analyzer.d.ts +67 -5
  44. package/lib/core/analyzer.js +402 -91
  45. package/lib/core/backup-plan.d.ts +7 -1
  46. package/lib/core/backup-plan.js +15 -8
  47. package/lib/core/backup.d.ts +53 -2
  48. package/lib/core/backup.js +120 -24
  49. package/lib/core/boot-safety.d.ts +50 -0
  50. package/lib/core/boot-safety.js +211 -0
  51. package/lib/core/config-lifecycle.d.ts +0 -1
  52. package/lib/core/config-lifecycle.js +0 -2
  53. package/lib/core/config-snapshot.d.ts +3 -1
  54. package/lib/core/config-snapshot.js +25 -6
  55. package/lib/core/credential-status.d.ts +15 -0
  56. package/lib/core/credential-status.js +13 -0
  57. package/lib/core/exporter.d.ts +18 -0
  58. package/lib/core/exporter.js +115 -3
  59. package/lib/core/importer.d.ts +15 -2
  60. package/lib/core/importer.js +10 -3
  61. package/lib/core/index.d.ts +2 -0
  62. package/lib/core/index.js +3 -0
  63. package/lib/core/journal.d.ts +74 -5
  64. package/lib/core/journal.js +127 -12
  65. package/lib/core/messages.d.ts +54 -0
  66. package/lib/core/messages.js +110 -0
  67. package/lib/core/migration-consult.js +0 -4
  68. package/lib/core/model-tools.js +6 -9
  69. package/lib/core/path-mapping.d.ts +11 -0
  70. package/lib/core/path-mapping.js +28 -0
  71. package/lib/core/phase3-host.d.ts +79 -6
  72. package/lib/core/phase3-host.js +223 -73
  73. package/lib/core/phase4-crash-child.js +0 -1
  74. package/lib/core/plugin-cli.js +1 -1
  75. package/lib/core/reconcile.d.ts +8 -1
  76. package/lib/core/reconcile.js +12 -4
  77. package/lib/core/recovery-orchestrator.js +1 -1
  78. package/lib/core/restore.js +10 -4
  79. package/lib/core/rollback.js +1 -1
  80. package/lib/core/run-registry.d.ts +63 -3
  81. package/lib/core/run-registry.js +74 -4
  82. package/lib/core/session-meta.d.ts +27 -8
  83. package/lib/core/session-meta.js +59 -32
  84. package/lib/core/session-repair.d.ts +89 -0
  85. package/lib/core/session-repair.js +118 -0
  86. package/lib/core/session-select.d.ts +50 -0
  87. package/lib/core/session-select.js +137 -0
  88. package/lib/core/startup-barrier.d.ts +15 -0
  89. package/lib/core/startup-barrier.js +24 -2
  90. package/lib/core/transaction-coordinator.js +3 -3
  91. package/lib/core/types.d.ts +241 -0
  92. package/lib/core/validator.js +0 -1
  93. package/lib/index.d.ts +367 -6
  94. package/lib/index.js +909 -3278
  95. package/lib/market/index-parser.d.ts +2 -1
  96. package/lib/market/index-parser.js +27 -6
  97. package/lib/market/my-repo.d.ts +0 -1
  98. package/lib/market/my-repo.js +0 -6
  99. package/lib/market/prepare.d.ts +9 -0
  100. package/lib/market/prepare.js +5 -31
  101. package/lib/market/reader.js +0 -1
  102. package/lib/market/security.js +3 -9
  103. package/lib/routes/backup.d.ts +9 -0
  104. package/lib/routes/backup.js +99 -0
  105. package/lib/routes/consult.d.ts +9 -0
  106. package/lib/routes/consult.js +134 -0
  107. package/lib/routes/context.d.ts +9 -0
  108. package/lib/routes/context.js +2 -0
  109. package/lib/routes/history.d.ts +9 -0
  110. package/lib/routes/history.js +53 -0
  111. package/lib/routes/import.d.ts +9 -0
  112. package/lib/routes/import.js +390 -0
  113. package/lib/routes/index.d.ts +10 -0
  114. package/lib/routes/index.js +27 -0
  115. package/lib/routes/kit.d.ts +98 -0
  116. package/lib/routes/kit.js +161 -0
  117. package/lib/routes/market.d.ts +9 -0
  118. package/lib/routes/market.js +256 -0
  119. package/lib/routes/me.d.ts +9 -0
  120. package/lib/routes/me.js +170 -0
  121. package/lib/routes/prefs.d.ts +9 -0
  122. package/lib/routes/prefs.js +103 -0
  123. package/lib/routes/profiles.d.ts +9 -0
  124. package/lib/routes/profiles.js +130 -0
  125. package/lib/routes/recovery.d.ts +9 -0
  126. package/lib/routes/recovery.js +137 -0
  127. package/lib/routes/snapshots.d.ts +9 -0
  128. package/lib/routes/snapshots.js +214 -0
  129. package/lib/routes/sync.d.ts +9 -0
  130. package/lib/routes/sync.js +783 -0
  131. package/lib/schema/config.d.ts +10 -9
  132. package/lib/schema/config.js +78 -96
  133. package/lib/schema/index.d.ts +8 -1
  134. package/lib/schema/index.js +13 -1
  135. package/lib/schema/manifest.d.ts +2 -0
  136. package/lib/schema/manifest.js +1 -0
  137. package/lib/schema/section-registry.d.ts +189 -0
  138. package/lib/schema/section-registry.js +258 -0
  139. package/lib/schema/types.d.ts +13 -3
  140. package/lib/schema/versions.d.ts +11 -6
  141. package/lib/schema/versions.js +27 -1
  142. package/lib/security/secret-scanner.js +1 -1
  143. package/lib/security/zip-security.d.ts +8 -5
  144. package/lib/security/zip-security.js +20 -90
  145. package/lib/sync/ancestor.d.ts +11 -0
  146. package/lib/sync/ancestor.js +20 -0
  147. package/lib/sync/autosync-config.js +9 -18
  148. package/lib/sync/autosync-scheduler.d.ts +7 -2
  149. package/lib/sync/autosync-scheduler.js +35 -17
  150. package/lib/sync/backup-schedule-config.js +0 -1
  151. package/lib/sync/blob-store.d.ts +85 -0
  152. package/lib/sync/blob-store.js +85 -0
  153. package/lib/sync/git/git-transport.d.ts +28 -5
  154. package/lib/sync/git/git-transport.js +170 -24
  155. package/lib/sync/github-auth.d.ts +0 -1
  156. package/lib/sync/github-auth.js +0 -2
  157. package/lib/sync/layout.d.ts +16 -2
  158. package/lib/sync/layout.js +48 -7
  159. package/lib/sync/risk.d.ts +18 -12
  160. package/lib/sync/risk.js +17 -28
  161. package/lib/sync/session-tombstones.d.ts +39 -0
  162. package/lib/sync/session-tombstones.js +125 -0
  163. package/lib/sync/sync-config.d.ts +26 -3
  164. package/lib/sync/sync-config.js +40 -3
  165. package/lib/sync/sync-engine.d.ts +140 -12
  166. package/lib/sync/sync-engine.js +266 -49
  167. package/lib/sync/sync-history.js +0 -1
  168. package/lib/sync/sync-selection.d.ts +18 -0
  169. package/lib/sync/sync-selection.js +50 -24
  170. package/lib/sync/sync-state.d.ts +31 -4
  171. package/lib/sync/sync-state.js +38 -6
  172. package/lib/sync/transport.d.ts +103 -0
  173. package/lib/sync/transport.js +121 -0
  174. package/lib/sync/ui-prefs.d.ts +4 -2
  175. package/lib/sync/ui-prefs.js +6 -4
  176. package/lib/sync/webdav/webdav-transport.d.ts +36 -4
  177. package/lib/sync/webdav/webdav-transport.js +242 -43
  178. package/lib/ui/export-flow.d.ts +2 -2
  179. package/lib/ui/export-flow.js +63 -17
  180. package/lib/ui/i18n.d.ts +12 -1
  181. package/lib/ui/i18n.js +25 -2
  182. package/lib/ui/import-log.d.ts +72 -0
  183. package/lib/ui/import-log.js +110 -0
  184. package/lib/ui/import-stepper.d.ts +14 -0
  185. package/lib/ui/import-stepper.js +18 -0
  186. package/lib/ui/import-wizard.d.ts +76 -0
  187. package/lib/ui/import-wizard.js +96 -2
  188. package/lib/ui/mid-ellipsis.d.ts +31 -0
  189. package/lib/ui/mid-ellipsis.js +39 -0
  190. package/lib/ui/my-configs-view.d.ts +136 -0
  191. package/lib/ui/my-configs-view.js +127 -0
  192. package/lib/ui/progress.js +6 -0
  193. package/lib/ui/report.d.ts +26 -0
  194. package/lib/ui/report.js +37 -0
  195. package/lib/ui/runs-view.d.ts +128 -0
  196. package/lib/ui/runs-view.js +123 -0
  197. package/lib/ui/selection-model.d.ts +90 -1
  198. package/lib/ui/selection-model.js +243 -1
  199. package/lib/ui/snapshots-view.d.ts +116 -0
  200. package/lib/ui/snapshots-view.js +141 -0
  201. package/lib/ui/sync-settings-view.d.ts +295 -0
  202. package/lib/ui/sync-settings-view.js +280 -0
  203. package/lib/ui/test-helpers.d.ts +7 -1
  204. package/lib/ui/test-helpers.js +4 -1
  205. package/lib/ui/types.d.ts +8 -1
  206. package/lib/utils/env-lock.d.ts +38 -5
  207. package/lib/utils/env-lock.js +55 -37
  208. package/lib/utils/logger.d.ts +17 -13
  209. package/lib/utils/logger.js +79 -41
  210. package/lib/utils/paths.d.ts +1 -1
  211. package/lib/utils/paths.js +2 -2
  212. package/lib/utils/session-log.d.ts +70 -0
  213. package/lib/utils/session-log.js +347 -0
  214. package/lib/utils/zip.d.ts +49 -5
  215. package/lib/utils/zip.js +240 -51
  216. package/lib/utils/zstd-frame.d.ts +44 -0
  217. package/lib/utils/zstd-frame.js +115 -0
  218. package/package.json +2 -2
  219. package/src/adapters/agent-instructions.ts +5 -3
  220. package/src/adapters/agent-presets.ts +5 -3
  221. package/src/adapters/credentials.ts +19 -22
  222. package/src/adapters/file-collection.test.ts +256 -0
  223. package/src/adapters/file-collection.ts +129 -17
  224. package/src/adapters/index.ts +12 -1
  225. package/src/adapters/json-section.ts +73 -0
  226. package/src/adapters/mcp.ts +16 -19
  227. package/src/adapters/plugin-files.ts +11 -14
  228. package/src/adapters/plugins.test.ts +1 -1
  229. package/src/adapters/plugins.ts +39 -42
  230. package/src/adapters/prompts.ts +16 -20
  231. package/src/adapters/providers.ts +22 -25
  232. package/src/adapters/roundtrip.test.ts +44 -0
  233. package/src/adapters/sessions.test.ts +400 -1
  234. package/src/adapters/sessions.ts +435 -16
  235. package/src/adapters/settings.ts +22 -25
  236. package/src/adapters/skills.ts +5 -3
  237. package/src/adapters/test-helpers.ts +8 -0
  238. package/src/adapters/ui.ts +14 -17
  239. package/src/adapters/workspaces.test.ts +226 -2
  240. package/src/adapters/workspaces.ts +247 -16
  241. package/src/cli/index.ts +154 -28
  242. package/src/cli/sessions-repair.ts +229 -0
  243. package/src/client/CLIENT_DEPENDENCIES.md +3 -1
  244. package/src/client/ConfigManagerSection.tsx +38 -7
  245. package/src/client/api.ts +160 -237
  246. package/src/client/common/ContentPicker.tsx +33 -8
  247. package/src/client/common/ProgressBar.tsx +18 -9
  248. package/src/client/common/ReportView.tsx +127 -53
  249. package/src/client/common/RunsCenter.tsx +377 -0
  250. package/src/client/common/client-route-parity.test.ts +171 -0
  251. package/src/client/common/http-usage-guard.test.ts +163 -0
  252. package/src/client/common/http.test.ts +225 -0
  253. package/src/client/common/http.ts +238 -0
  254. package/src/client/common/plan-text-redaction.test.ts +29 -7
  255. package/src/client/common/progress-view.test.ts +33 -1
  256. package/src/client/common/progress-view.ts +17 -0
  257. package/src/client/common/routes.ts +132 -0
  258. package/src/client/common/runs-center-guard.test.ts +44 -0
  259. package/src/client/config-manager.module.css +332 -1
  260. package/src/client/export/ExportView.tsx +27 -3
  261. package/src/client/history/HistoryPanel.tsx +0 -1
  262. package/src/client/history/history-api.ts +34 -61
  263. package/src/client/import/ImportLogPanel.tsx +136 -0
  264. package/src/client/import/ImportWizardView.tsx +442 -836
  265. package/src/client/import/PathMappingForm.tsx +12 -1
  266. package/src/client/import/import-wizard-split.test.ts +101 -0
  267. package/src/client/import/import-wizard-steps.tsx +352 -0
  268. package/src/client/import/use-import-wizard-controller.ts +459 -0
  269. package/src/client/index.ts +0 -2
  270. package/src/client/lifecycle/lifecycle-api.ts +17 -88
  271. package/src/client/locales.ts +140 -0
  272. package/src/client/market/MyConfigsInstall.tsx +92 -0
  273. package/src/client/market/MyConfigsList.tsx +120 -0
  274. package/src/client/market/MyConfigsLoginCard.tsx +99 -0
  275. package/src/client/market/MyConfigsView.tsx +148 -476
  276. package/src/client/market/MyConfigsWizard.tsx +252 -0
  277. package/src/client/market/market-api.test.ts +36 -0
  278. package/src/client/market/market-api.ts +13 -70
  279. package/src/client/market/market-view.ts +1 -1
  280. package/src/client/market/my-configs-api.ts +17 -71
  281. package/src/client/market/my-configs-split.test.ts +58 -0
  282. package/src/client/overview/OverviewPanel.tsx +1 -9
  283. package/src/client/recovery/RecoveryPanel.tsx +1 -2
  284. package/src/client/recovery/recovery-api.ts +15 -65
  285. package/src/client/run-store.test.ts +421 -3
  286. package/src/client/run-store.ts +284 -53
  287. package/src/client/snapshots/BackupScheduleCard.tsx +437 -0
  288. package/src/client/snapshots/SnapshotsEmptyState.tsx +31 -0
  289. package/src/client/snapshots/SnapshotsListTable.tsx +97 -0
  290. package/src/client/snapshots/SnapshotsPanel.tsx +42 -626
  291. package/src/client/sync/AutosyncCard.tsx +88 -0
  292. package/src/client/sync/ChannelConfigDialog.tsx +268 -0
  293. package/src/client/sync/DecryptPasswordCard.tsx +48 -0
  294. package/src/client/sync/SecurityOptionsCard.tsx +83 -0
  295. package/src/client/sync/SyncChannelEntryCard.tsx +78 -0
  296. package/src/client/sync/SyncConfirmView.tsx +10 -8
  297. package/src/client/sync/SyncSectionPickerDialog.tsx +182 -0
  298. package/src/client/sync/SyncSettingsView.test.ts +85 -0
  299. package/src/client/sync/SyncSettingsView.tsx +230 -649
  300. package/src/client/sync/history-model.ts +4 -10
  301. package/src/client/sync/sync-api.ts +59 -100
  302. package/src/client/sync/sync-locales.ts +16 -2
  303. package/src/client/sync/sync-view-v2.test.ts +30 -4
  304. package/src/client/sync/sync-view.ts +67 -17
  305. package/src/core/analyzer-mapping.test.ts +55 -0
  306. package/src/core/analyzer.ts +451 -91
  307. package/src/core/apply-order.test.ts +182 -0
  308. package/src/core/backup-plan.test.ts +141 -0
  309. package/src/core/backup-plan.ts +16 -8
  310. package/src/core/backup.test.ts +310 -0
  311. package/src/core/backup.ts +174 -23
  312. package/src/core/base-path-rebase.test.ts +97 -0
  313. package/src/core/boot-rescue.test.ts +26 -0
  314. package/src/core/boot-safety.test.ts +138 -0
  315. package/src/core/boot-safety.ts +248 -0
  316. package/src/core/config-lifecycle.test.ts +255 -2
  317. package/src/core/config-lifecycle.ts +0 -2
  318. package/src/core/config-snapshot.test.ts +193 -3
  319. package/src/core/config-snapshot.ts +24 -8
  320. package/src/core/credential-status.test.ts +35 -0
  321. package/src/core/credential-status.ts +25 -0
  322. package/src/core/export-coupling.test.ts +255 -0
  323. package/src/core/exporter.ts +110 -3
  324. package/src/core/finalize-order.test.ts +82 -0
  325. package/src/core/import-sessions-visibility.test.ts +93 -0
  326. package/src/core/importer.ts +21 -3
  327. package/src/core/index.ts +10 -0
  328. package/src/core/journal.test.ts +59 -2
  329. package/src/core/journal.ts +152 -10
  330. package/src/core/messages.ts +110 -0
  331. package/src/core/migration-consult.ts +0 -5
  332. package/src/core/migration-history.test.ts +3 -5
  333. package/src/core/model-tools.test.ts +0 -14
  334. package/src/core/model-tools.ts +6 -8
  335. package/src/core/path-mapping.ts +33 -0
  336. package/src/core/phase1-wiring.test.ts +12 -3
  337. package/src/core/phase3-consistency.test.ts +1 -1
  338. package/src/core/phase3-host.ts +248 -70
  339. package/src/core/phase3-p2b-fingerprint.test.ts +0 -1
  340. package/src/core/phase3-production-integration.test.ts +1 -1
  341. package/src/core/phase4-crash-child.ts +0 -1
  342. package/src/core/phase4-crash-injection.test.ts +1 -3
  343. package/src/core/plugin-cli.fs.test.ts +2 -2
  344. package/src/core/plugin-cli.ts +1 -1
  345. package/src/core/reconcile.test.ts +62 -0
  346. package/src/core/reconcile.ts +20 -5
  347. package/src/core/recovery-orchestrator.test.ts +13 -1
  348. package/src/core/recovery-orchestrator.ts +1 -1
  349. package/src/core/restore.ts +10 -4
  350. package/src/core/rollback.ts +1 -1
  351. package/src/core/run-registry-runs.test.ts +71 -0
  352. package/src/core/run-registry.test.ts +58 -7
  353. package/src/core/run-registry.ts +96 -5
  354. package/src/core/session-meta.test.ts +59 -1
  355. package/src/core/session-meta.ts +62 -29
  356. package/src/core/session-repair.test.ts +91 -0
  357. package/src/core/session-repair.ts +203 -0
  358. package/src/core/session-select.test.ts +73 -0
  359. package/src/core/session-select.ts +131 -0
  360. package/src/core/smoke.test.ts +1 -2
  361. package/src/core/startup-barrier.ts +29 -2
  362. package/src/core/transaction-coordinator.ts +3 -3
  363. package/src/core/types.ts +244 -0
  364. package/src/core/validator.ts +0 -1
  365. package/src/index.sync.test.ts +8 -0
  366. package/src/index.ts +530 -2757
  367. package/src/market/git-file-writer.test.ts +1 -1
  368. package/src/market/index-parser.ts +29 -6
  369. package/src/market/market.test.ts +146 -1
  370. package/src/market/my-repo.test.ts +57 -4
  371. package/src/market/my-repo.ts +0 -7
  372. package/src/market/prepare.ts +12 -32
  373. package/src/market/reader.ts +0 -1
  374. package/src/market/security.ts +3 -11
  375. package/src/routes/backup.ts +103 -0
  376. package/src/routes/consult.ts +139 -0
  377. package/src/routes/context.ts +10 -0
  378. package/src/routes/history.ts +56 -0
  379. package/src/routes/import.ts +399 -0
  380. package/src/routes/index.ts +36 -0
  381. package/src/routes/kit.ts +222 -0
  382. package/src/routes/market.ts +275 -0
  383. package/src/routes/me.ts +168 -0
  384. package/src/routes/prefs.ts +104 -0
  385. package/src/routes/profiles.ts +131 -0
  386. package/src/routes/recovery.ts +113 -0
  387. package/src/routes/snapshots.ts +219 -0
  388. package/src/routes/sync.ts +810 -0
  389. package/src/schema/config.ts +96 -91
  390. package/src/schema/index.ts +33 -1
  391. package/src/schema/manifest.ts +3 -0
  392. package/src/schema/registry.test.ts +257 -0
  393. package/src/schema/section-registry.ts +423 -0
  394. package/src/schema/types.ts +13 -3
  395. package/src/schema/versions.ts +31 -1
  396. package/src/security/secret-scanner.ts +1 -1
  397. package/src/security/security.test.ts +145 -2
  398. package/src/security/zip-security.ts +20 -92
  399. package/src/sync/ancestor.test.ts +0 -1
  400. package/src/sync/ancestor.ts +23 -0
  401. package/src/sync/autosync-config.ts +9 -18
  402. package/src/sync/autosync-scheduler.test.ts +60 -3
  403. package/src/sync/autosync-scheduler.ts +45 -19
  404. package/src/sync/backup-schedule-config.ts +0 -1
  405. package/src/sync/backup-scheduler.test.ts +15 -2
  406. package/src/sync/blob-store.test.ts +191 -0
  407. package/src/sync/blob-store.ts +139 -0
  408. package/src/sync/git/git-transport.test.ts +203 -1
  409. package/src/sync/git/git-transport.ts +173 -24
  410. package/src/sync/github-auth.ts +0 -2
  411. package/src/sync/layout.ts +62 -8
  412. package/src/sync/risk.test.ts +32 -1
  413. package/src/sync/risk.ts +26 -31
  414. package/src/sync/session-tombstones.test.ts +87 -0
  415. package/src/sync/session-tombstones.ts +123 -0
  416. package/src/sync/sync-config.test.ts +132 -0
  417. package/src/sync/sync-config.ts +47 -6
  418. package/src/sync/sync-credentials.test.ts +77 -0
  419. package/src/sync/sync-engine.test.ts +799 -8
  420. package/src/sync/sync-engine.ts +322 -51
  421. package/src/sync/sync-history.ts +0 -1
  422. package/src/sync/sync-selection.test.ts +43 -7
  423. package/src/sync/sync-selection.ts +56 -24
  424. package/src/sync/sync-state.test.ts +83 -0
  425. package/src/sync/sync-state.ts +58 -7
  426. package/src/sync/transport.ts +191 -0
  427. package/src/sync/ui-prefs.ts +10 -6
  428. package/src/sync/webdav/webdav-transport.test.ts +122 -2
  429. package/src/sync/webdav/webdav-transport.ts +256 -37
  430. package/src/ui/export-flow.ts +65 -18
  431. package/src/ui/i18n.test.ts +149 -0
  432. package/src/ui/i18n.ts +25 -2
  433. package/src/ui/import-log.test.ts +106 -0
  434. package/src/ui/import-log.ts +158 -0
  435. package/src/ui/import-stepper.test.ts +25 -1
  436. package/src/ui/import-stepper.ts +18 -0
  437. package/src/ui/import-wizard.test.ts +130 -2
  438. package/src/ui/import-wizard.ts +135 -2
  439. package/src/ui/market-import.test.ts +0 -1
  440. package/src/ui/mid-ellipsis.test.ts +160 -0
  441. package/src/ui/mid-ellipsis.ts +37 -0
  442. package/src/ui/my-configs-view.test.ts +222 -0
  443. package/src/ui/my-configs-view.ts +206 -0
  444. package/src/ui/progress.ts +6 -0
  445. package/src/ui/report.test.ts +32 -2
  446. package/src/ui/report.ts +52 -0
  447. package/src/ui/runs-view.test.ts +152 -0
  448. package/src/ui/runs-view.ts +207 -0
  449. package/src/ui/selection-model.test.ts +268 -7
  450. package/src/ui/selection-model.ts +277 -1
  451. package/src/ui/snapshots-view.test.ts +228 -0
  452. package/src/ui/snapshots-view.ts +233 -0
  453. package/src/ui/sync-settings-view.test.ts +348 -0
  454. package/src/ui/sync-settings-view.ts +516 -0
  455. package/src/ui/test-helpers.ts +8 -1
  456. package/src/ui/types.ts +10 -1
  457. package/src/utils/env-lock.test.ts +170 -107
  458. package/src/utils/env-lock.ts +71 -35
  459. package/src/utils/logger.test.ts +178 -1
  460. package/src/utils/logger.ts +81 -44
  461. package/src/utils/paths.ts +2 -2
  462. package/src/utils/session-log.test.ts +136 -0
  463. package/src/utils/session-log.ts +344 -0
  464. package/src/utils/zip.test.ts +284 -0
  465. package/src/utils/zip.ts +226 -52
  466. package/src/utils/zstd-frame.test.ts +80 -0
  467. package/src/utils/zstd-frame.ts +131 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * JSON 分区 adapter 的共享骨架(t31,批次 4)。
3
+ *
4
+ * 为什么存在:改动前 9 个「有 JSON 载荷」的分区的 `validate()` 都以**同一段样板**开头 ——
5
+ * 先是 object 守卫,再是 `data.version !== 1` 守卫,最后才是各分区自己的载荷形状检查;
6
+ * 这段样板在每个文件里逐字抄了一遍(审计 sync#F-08 / infra#C1)。本模块把它收敛为一份,
7
+ * 并让「期望版本」直接取自注册表 `dataVersion`(不再各写 `!== 1` 字面量)。
8
+ *
9
+ * 与注册表的分工:结构校验的**描述符**(zipPath / 载荷形态 / dataVersion)在
10
+ * `src/schema/section-registry.ts`;本模块只提供「按该描述符执行校验」的通用算法,
11
+ * 各 adapter 仍负责自己特有的形状检查(用 `shape` 回调注入)。
12
+ *
13
+ * 分层:只 import schema 与 core 的类型 —— 不 import node 内置模块(client bundle 自包含铁律)。
14
+ */
15
+ import { sectionMeta } from '../schema/section-registry.js';
16
+ /**
17
+ * JSON 分区通用结构校验骨架。
18
+ *
19
+ * 执行顺序(与改造前逐字一致,保证行为等价):
20
+ * 1. object 守卫:非对象 → **直接返回**单条 `objectMessageKey` 错误(缺省 `adapter.validate.object`;后续检查不再执行);
21
+ * 2. version 守卫:`version !== sectionMeta(sectionId).dataVersion` → push 一条 `version` 错误,
22
+ * **不提前返回**(继续做形状检查,与历史实现一致);
23
+ * 3. `shape` 回调:分区特有的载荷形状检查(可选)。
24
+ *
25
+ * @param sectionId 分区 id —— 同时是 object 错误的默认 subject 与 dataVersion 的查表键
26
+ * @param data 反序列化后的分区载荷(来自不可信备份 → 必须按 unknown 处理)
27
+ * @param msg 文案翻译器(adapter 的 `validate(data, msg = zhMsg)` 原样传入)
28
+ * @param shape 分区特有的形状检查(在 version 守卫之后调用)
29
+ * @param subject object 错误里的显示名;缺省 = sectionId。历史上 credentials 用的是
30
+ * `'credentials'`(而非分区 id `credentialsStatus`),此参数为保留该原文案而存在 ——
31
+ * 改文案属独立的行为变更,不在本任务射程内。
32
+ * @param objectMessageKey object 错误的**消息键**(t11 新增,可选);缺省 `adapter.validate.object`
33
+ * (`'{subject} 数据必须是对象'`)。为什么需要它:文件类分区历史上用的是专用键
34
+ * `adapter.validate.fileSection`(`'文件分区数据必须是对象'`,**不含** `{subject}` 占位符),
35
+ * 那是**键**级差异 —— `subject` 参数补不了它(把 subject 拼成"文件分区"后中文模板仍多一个空格),
36
+ * 而硬换键会让 skills / agentPresets / agentInstructions / sessions / self 五个分区的用户可见
37
+ * 报错文案一起变。有了这个可选键,那处也能并入骨架而文案逐字不变。
38
+ * 键必须存在于 `src/core/messages.ts` 目录(`MsgFunc` 形参是 string,编译器只能校验到键名集合)。
39
+ */
40
+ export function validateJsonSection(sectionId, data, msg, shape, subject = sectionId, objectMessageKey = 'adapter.validate.object') {
41
+ if (data === null || typeof data !== 'object') {
42
+ return {
43
+ valid: false,
44
+ issues: [{ path: '$', message: msg(objectMessageKey, { subject }), severity: 'error' }],
45
+ };
46
+ }
47
+ const section = data;
48
+ const issues = [];
49
+ const expectedVersion = sectionMeta(sectionId).dataVersion;
50
+ if (section.version !== expectedVersion) {
51
+ issues.push({
52
+ path: 'version',
53
+ message: msg('adapter.validate.version', { value: String(section.version) }),
54
+ severity: 'error',
55
+ });
56
+ }
57
+ if (shape !== undefined)
58
+ shape(section, issues);
59
+ return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
60
+ }
61
+ //# sourceMappingURL=json-section.js.map
@@ -18,9 +18,9 @@ export declare function extractMcpServers(lines: {
18
18
  export declare function buildMcpPatchLine(lineId: string, server: McpServerEntry): Record<string, unknown>;
19
19
  export declare class McpAdapter implements ConfigAdapter<McpExportSection> {
20
20
  readonly id: 'mcp';
21
- readonly displayName = "MCP Servers";
22
- readonly defaultIncluded = true;
23
- readonly portability: 'platformSpecific';
21
+ readonly displayName: string;
22
+ readonly defaultIncluded: boolean;
23
+ readonly portability: import("../schema/section-registry.ts").SectionPortability;
24
24
  export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<McpExportSection>>;
25
25
  analyzeImport(data: McpExportSection, ctx: ImportContext): Promise<PlanItem[]>;
26
26
  applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
@@ -8,6 +8,8 @@
8
8
  */
9
9
  import { isDeepStrictEqual } from 'node:util';
10
10
  import { msgOf, zhMsg } from '../core/messages.js';
11
+ import { sectionMeta } from '../schema/section-registry.js';
12
+ import { validateJsonSection } from './json-section.js';
11
13
  import { USER_PATCH_FILE } from './plugins.js';
12
14
  /** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
13
15
  export function extractMcpServers(lines) {
@@ -77,9 +79,10 @@ function newLineId(serverName) {
77
79
  }
78
80
  export class McpAdapter {
79
81
  id = 'mcp';
80
- displayName = 'MCP Servers';
81
- defaultIncluded = true;
82
- portability = 'platformSpecific';
82
+ // 元数据唯一来源 = 注册表(t31):不再与 ui/export-flow.ts 的导出目录各写一份
83
+ displayName = sectionMeta('mcp').displayName;
84
+ defaultIncluded = sectionMeta('mcp').defaultIncluded;
85
+ portability = sectionMeta('mcp').portability;
83
86
  async export(ctx, _options) {
84
87
  const warnings = [];
85
88
  let lines = [];
@@ -155,24 +158,18 @@ export class McpAdapter {
155
158
  return { ok: true, needsRestart: true, message: msg('adapter.mcpWritten', { serverName }) };
156
159
  }
157
160
  async validate(data, msg = zhMsg) {
158
- const issues = [];
159
- if (data === null || typeof data !== 'object') {
160
- return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'mcp' }), severity: 'error' }] };
161
- }
162
- if (data.version !== 1) {
163
- issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
164
- }
165
- if (!Array.isArray(data.servers)) {
166
- issues.push({ path: 'servers', message: msg('adapter.validate.array', { subject: 'servers' }), severity: 'error' });
167
- }
168
- else {
169
- for (const s of data.servers) {
170
- if (s === null || typeof s !== 'object' || typeof s.serverName !== 'string' || s.serverName === '') {
171
- issues.push({ path: 'servers[]', message: msg('adapter.validate.serverName'), severity: 'error' });
161
+ return validateJsonSection('mcp', data, msg, (section, issues) => {
162
+ if (!Array.isArray(section.servers)) {
163
+ issues.push({ path: 'servers', message: msg('adapter.validate.array', { subject: 'servers' }), severity: 'error' });
164
+ }
165
+ else {
166
+ for (const s of section.servers) {
167
+ if (s === null || typeof s !== 'object' || typeof s.serverName !== 'string' || s.serverName === '') {
168
+ issues.push({ path: 'servers[]', message: msg('adapter.validate.serverName'), severity: 'error' });
169
+ }
172
170
  }
173
171
  }
174
- }
175
- return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
172
+ });
176
173
  }
177
174
  }
178
175
  //# sourceMappingURL=mcp.js.map
@@ -4,9 +4,9 @@ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, ExportUn
4
4
  export declare const DEFAULT_PLUGIN_FILE_WHITELIST: readonly string[];
5
5
  export declare class PluginFilesAdapter implements ConfigAdapter<FilesSection> {
6
6
  readonly id: 'pluginFiles';
7
- readonly displayName = "Plugin Files";
8
- readonly defaultIncluded = false;
9
- readonly portability: 'deviceSpecific';
7
+ readonly displayName: string;
8
+ readonly defaultIncluded: boolean;
9
+ readonly portability: import("../schema/section-registry.ts").SectionPortability;
10
10
  private readonly whitelist;
11
11
  /** 约定配置目录(相对 ~/.dsh 根,如 'plugin-config');递归收集其下所有文件。undefined = 不收集。 */
12
12
  private readonly collectDir?;
@@ -11,12 +11,15 @@ import { msgOf, zhMsg } from '../core/messages.js';
11
11
  import { linkWarnings, listFilesDetailed } from './link-report.js';
12
12
  import { isPathSafe, isReservedInternalRel } from '../utils/paths.js';
13
13
  import { unitAllowed, unitsFromFiles } from './units.js';
14
+ import { sectionMeta } from '../schema/section-registry.js';
15
+ import { validateJsonSection } from './json-section.js';
14
16
  export const DEFAULT_PLUGIN_FILE_WHITELIST = ['dsh-ssh.json', 'pet.json'];
15
17
  export class PluginFilesAdapter {
16
18
  id = 'pluginFiles';
17
- displayName = 'Plugin Files';
18
- defaultIncluded = false;
19
- portability = 'deviceSpecific';
19
+ // 元数据唯一来源 = 注册表(t31):不再与 ui/export-flow.ts 的导出目录各写一份
20
+ displayName = sectionMeta('pluginFiles').displayName;
21
+ defaultIncluded = sectionMeta('pluginFiles').defaultIncluded;
22
+ portability = sectionMeta('pluginFiles').portability;
20
23
  whitelist;
21
24
  /** 约定配置目录(相对 ~/.dsh 根,如 'plugin-config');递归收集其下所有文件。undefined = 不收集。 */
22
25
  collectDir;
@@ -138,17 +141,11 @@ export class PluginFilesAdapter {
138
141
  return { ok: true };
139
142
  }
140
143
  async validate(data, msg = zhMsg) {
141
- const issues = [];
142
- if (data === null || typeof data !== 'object') {
143
- return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'pluginFiles' }), severity: 'error' }] };
144
- }
145
- if (data.version !== 1) {
146
- issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
147
- }
148
- if (!Array.isArray(data.files)) {
149
- issues.push({ path: 'files', message: msg('adapter.validate.array', { subject: 'files' }), severity: 'error' });
150
- }
151
- return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
144
+ return validateJsonSection('pluginFiles', data, msg, (section, issues) => {
145
+ if (!Array.isArray(section.files)) {
146
+ issues.push({ path: 'files', message: msg('adapter.validate.array', { subject: 'files' }), severity: 'error' });
147
+ }
148
+ });
152
149
  }
153
150
  }
154
151
  //# sourceMappingURL=plugin-files.js.map
@@ -20,9 +20,9 @@ export declare const PNPM_WORKSPACE_REL: (profile: string | undefined) => string
20
20
  export declare const MAX_PATCH_FILE_BYTES: number;
21
21
  export declare class PluginsAdapter implements ConfigAdapter<PluginsSection> {
22
22
  readonly id: 'plugins';
23
- readonly displayName = "Plugins";
24
- readonly defaultIncluded = true;
25
- readonly portability: 'portable';
23
+ readonly displayName: string;
24
+ readonly defaultIncluded: boolean;
25
+ readonly portability: import("../schema/section-registry.ts").SectionPortability;
26
26
  /** 插件自身包名:导出 plugins 分区时不列自己(避免备份里出现「当前正在生成备份的插件」的自引用条目) */
27
27
  private readonly selfName;
28
28
  /** T1:本地源(link:/file:)插件打包钩子;未注入 = 不打包(保持改造前行为) */
@@ -13,11 +13,13 @@
13
13
  import { isDeepStrictEqual } from 'node:util';
14
14
  import { sha256Hex } from '../utils/hashing.js';
15
15
  import { installSpecFor, resolveProfileNameFromArgv } from '../core/plugin-cli.js';
16
- import { isLocalPluginSpec, isPackedLocalSpec, LOCAL_PLUGIN_DIR } from '../core/local-plugin-pack.js';
16
+ import { LOCAL_PLUGIN_DIR } from '../core/local-plugin-pack.js';
17
17
  import { msgOf, zhMsg } from '../core/messages.js';
18
18
  import { isPathSafe, normalizePath } from '../utils/paths.js';
19
19
  import { PLUGIN_PATCH_REF_PREFIX } from '../core/backup.js';
20
20
  import { parsePnpmPatchedDependencies, sanitizePnpmWorkspacePatches } from './pnpm-workspace.js';
21
+ import { sectionMeta } from '../schema/section-registry.js';
22
+ import { validateJsonSection } from './json-section.js';
21
23
  export const USER_PATCH_FILE = 'cordis.patch.yml';
22
24
  /** tarball 字节 → base64(零依赖,避免 Buffer 在浏览器侧类型问题) */
23
25
  function bytesToBase64(data) {
@@ -108,9 +110,10 @@ function entriesOfRaw(raw) {
108
110
  }
109
111
  export class PluginsAdapter {
110
112
  id = 'plugins';
111
- displayName = 'Plugins';
112
- defaultIncluded = true;
113
- portability = 'portable';
113
+ // 元数据唯一来源 = 注册表(t31):不再与 ui/export-flow.ts 的导出目录各写一份
114
+ displayName = sectionMeta('plugins').displayName;
115
+ defaultIncluded = sectionMeta('plugins').defaultIncluded;
116
+ portability = sectionMeta('plugins').portability;
114
117
  /** 插件自身包名:导出 plugins 分区时不列自己(避免备份里出现「当前正在生成备份的插件」的自引用条目) */
115
118
  selfName;
116
119
  /** T1:本地源(link:/file:)插件打包钩子;未注入 = 不打包(保持改造前行为) */
@@ -629,47 +632,41 @@ export class PluginsAdapter {
629
632
  return { ok: true, needsRestart: true, message: msg('adapter.patchWritten', { ref }) };
630
633
  }
631
634
  async validate(data, msg = zhMsg) {
632
- const issues = [];
633
- if (data === null || typeof data !== 'object') {
634
- return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'plugins' }), severity: 'error' }] };
635
- }
636
- if (data.version !== 1) {
637
- issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
638
- }
639
- if (!Array.isArray(data.plugins)) {
640
- issues.push({ path: 'plugins', message: msg('adapter.validate.array', { subject: 'plugins' }), severity: 'error' });
641
- }
642
- if (data.patch !== undefined && !Array.isArray(data.patch)) {
643
- issues.push({ path: 'patch', message: msg('adapter.validate.array', { subject: 'patch' }), severity: 'error' });
644
- }
645
- if (data.pnpmWorkspace !== undefined && data.pnpmWorkspace !== null && typeof data.pnpmWorkspace !== 'string') {
646
- issues.push({ path: 'pnpmWorkspace', message: msg('adapter.validate.string', { subject: 'pnpmWorkspace' }), severity: 'error' });
647
- }
648
- // T1:本地插件 tarball 载荷(缺省合法;存在时逐条校验形状与路径安全)
649
- if (data.localTarballs !== undefined) {
650
- if (!Array.isArray(data.localTarballs)) {
651
- issues.push({ path: 'localTarballs', message: msg('adapter.validate.array', { subject: 'localTarballs' }), severity: 'error' });
635
+ return validateJsonSection('plugins', data, msg, (section, issues) => {
636
+ if (!Array.isArray(section.plugins)) {
637
+ issues.push({ path: 'plugins', message: msg('adapter.validate.array', { subject: 'plugins' }), severity: 'error' });
652
638
  }
653
- else {
654
- data.localTarballs.forEach((t, i) => {
655
- if (t === null || typeof t !== 'object') {
656
- issues.push({ path: `localTarballs[${i}]`, message: msg('adapter.validate.object', { subject: 'localTarballs[]' }), severity: 'error' });
657
- return;
658
- }
659
- for (const field of ['packageName', 'version', 'relativePath', 'base64']) {
660
- if (typeof t[field] !== 'string' || t[field] === '') {
661
- issues.push({ path: `localTarballs[${i}].${field}`, message: msg('adapter.validate.string', { subject: field }), severity: 'error' });
639
+ if (section.patch !== undefined && !Array.isArray(section.patch)) {
640
+ issues.push({ path: 'patch', message: msg('adapter.validate.array', { subject: 'patch' }), severity: 'error' });
641
+ }
642
+ if (section.pnpmWorkspace !== undefined && section.pnpmWorkspace !== null && typeof section.pnpmWorkspace !== 'string') {
643
+ issues.push({ path: 'pnpmWorkspace', message: msg('adapter.validate.string', { subject: 'pnpmWorkspace' }), severity: 'error' });
644
+ }
645
+ // T1:本地插件 tarball 载荷(缺省合法;存在时逐条校验形状与路径安全)
646
+ if (section.localTarballs !== undefined) {
647
+ if (!Array.isArray(section.localTarballs)) {
648
+ issues.push({ path: 'localTarballs', message: msg('adapter.validate.array', { subject: 'localTarballs' }), severity: 'error' });
649
+ }
650
+ else {
651
+ section.localTarballs.forEach((t, i) => {
652
+ if (t === null || typeof t !== 'object') {
653
+ issues.push({ path: `localTarballs[${i}]`, message: msg('adapter.validate.object', { subject: 'localTarballs[]' }), severity: 'error' });
654
+ return;
662
655
  }
663
- }
664
- // 相对路径必须落在 LOCAL_PLUGIN_DIR 之下且不含穿越段(防写 homeDir 外)
665
- const rel = typeof t.relativePath === 'string' ? t.relativePath.replace(/\\/g, '/') : '';
666
- if (rel !== '' && (!rel.startsWith(`${LOCAL_PLUGIN_DIR}/`) || rel.includes('..'))) {
667
- issues.push({ path: `localTarballs[${i}].relativePath`, message: msg('adapter.validate.localTarballPath', { path: rel }), severity: 'error' });
668
- }
669
- });
656
+ for (const field of ['packageName', 'version', 'relativePath', 'base64']) {
657
+ if (typeof t[field] !== 'string' || t[field] === '') {
658
+ issues.push({ path: `localTarballs[${i}].${field}`, message: msg('adapter.validate.string', { subject: field }), severity: 'error' });
659
+ }
660
+ }
661
+ // 相对路径必须落在 LOCAL_PLUGIN_DIR 之下且不含穿越段(防写 homeDir 外)
662
+ const rel = typeof t.relativePath === 'string' ? t.relativePath.replace(/\\/g, '/') : '';
663
+ if (rel !== '' && (!rel.startsWith(`${LOCAL_PLUGIN_DIR}/`) || rel.includes('..'))) {
664
+ issues.push({ path: `localTarballs[${i}].relativePath`, message: msg('adapter.validate.localTarballPath', { path: rel }), severity: 'error' });
665
+ }
666
+ });
667
+ }
670
668
  }
671
- }
672
- return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
669
+ });
673
670
  }
674
671
  }
675
672
  //# sourceMappingURL=plugins.js.map
@@ -20,9 +20,9 @@ export declare function mergePromptIntoLine(raw: unknown, prompt: PromptExportEn
20
20
  export declare function buildPromptLine(lineId: string, prompt: PromptExportEntry): Record<string, unknown>;
21
21
  export declare class PromptsAdapter implements ConfigAdapter<PromptsExportSection> {
22
22
  readonly id: 'prompts';
23
- readonly displayName = "Prompts";
24
- readonly defaultIncluded = true;
25
- readonly portability: 'portable';
23
+ readonly displayName: string;
24
+ readonly defaultIncluded: boolean;
25
+ readonly portability: import("../schema/section-registry.ts").SectionPortability;
26
26
  export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<PromptsExportSection>>;
27
27
  analyzeImport(data: PromptsExportSection, ctx: ImportContext): Promise<PlanItem[]>;
28
28
  applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
@@ -9,9 +9,10 @@
9
9
  * - 均不存在 → Warning(不编造行名自动创建,提示手动配置)。
10
10
  * rules/commands 无独立存储(研究报告 §2.2),不实现。
11
11
  */
12
- import { isDeepStrictEqual } from 'node:util';
13
12
  import { msgOf, zhMsg } from '../core/messages.js';
14
13
  import { USER_PATCH_FILE } from './plugins.js';
14
+ import { sectionMeta } from '../schema/section-registry.js';
15
+ import { validateJsonSection } from './json-section.js';
15
16
  /** 从 patch 行 config 中提取 prompt 条目(systemPrompt.persona / planMode.sections[].text) */
16
17
  export function extractPrompts(lines) {
17
18
  const prompts = [];
@@ -101,9 +102,10 @@ export function buildPromptLine(lineId, prompt) {
101
102
  }
102
103
  export class PromptsAdapter {
103
104
  id = 'prompts';
104
- displayName = 'Prompts';
105
- defaultIncluded = true;
106
- portability = 'portable';
105
+ // 元数据唯一来源 = 注册表(t31):不再与 ui/export-flow.ts 的导出目录各写一份
106
+ displayName = sectionMeta('prompts').displayName;
107
+ defaultIncluded = sectionMeta('prompts').defaultIncluded;
108
+ portability = sectionMeta('prompts').portability;
107
109
  async export(ctx, _options) {
108
110
  const warnings = [];
109
111
  let lines = [];
@@ -208,24 +210,18 @@ export class PromptsAdapter {
208
210
  return { ok: true, needsRestart: true, message: msg('adapter.promptWritten', { name: prompt.name, ref }) };
209
211
  }
210
212
  async validate(data, msg = zhMsg) {
211
- const issues = [];
212
- if (data === null || typeof data !== 'object') {
213
- return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'prompts' }), severity: 'error' }] };
214
- }
215
- if (data.version !== 1) {
216
- issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
217
- }
218
- if (!Array.isArray(data.prompts)) {
219
- issues.push({ path: 'prompts', message: msg('adapter.validate.array', { subject: 'prompts' }), severity: 'error' });
220
- }
221
- else {
222
- for (const p of data.prompts) {
223
- if (p === null || typeof p !== 'object' || typeof p.name !== 'string' || typeof p.text !== 'string') {
224
- issues.push({ path: 'prompts[]', message: msg('adapter.validate.promptIdentity'), severity: 'error' });
213
+ return validateJsonSection('prompts', data, msg, (section, issues) => {
214
+ if (!Array.isArray(section.prompts)) {
215
+ issues.push({ path: 'prompts', message: msg('adapter.validate.array', { subject: 'prompts' }), severity: 'error' });
216
+ }
217
+ else {
218
+ for (const p of section.prompts) {
219
+ if (p === null || typeof p !== 'object' || typeof p.name !== 'string' || typeof p.text !== 'string') {
220
+ issues.push({ path: 'prompts[]', message: msg('adapter.validate.promptIdentity'), severity: 'error' });
221
+ }
225
222
  }
226
223
  }
227
- }
228
- return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
224
+ });
229
225
  }
230
226
  }
231
227
  //# sourceMappingURL=prompts.js.map
@@ -22,9 +22,9 @@ export interface ProviderExportSection {
22
22
  export declare const DEFAULT_PROVIDER_NAMESPACES: readonly string[];
23
23
  export declare class ProvidersAdapter implements ConfigAdapter<ProviderExportSection> {
24
24
  readonly id: 'providers';
25
- readonly displayName = "Providers & Models";
26
- readonly defaultIncluded = true;
27
- readonly portability: 'portable';
25
+ readonly displayName: string;
26
+ readonly defaultIncluded: boolean;
27
+ readonly portability: import("../schema/section-registry.ts").SectionPortability;
28
28
  private readonly namespaces;
29
29
  constructor(namespaces?: string[] | NamespaceProvider);
30
30
  export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<ProviderExportSection>>;
@@ -8,6 +8,8 @@
8
8
  import { isDeepStrictEqual } from 'node:util';
9
9
  import { msgOf, zhMsg } from '../core/messages.js';
10
10
  import { resolveNamespaces, isEmptyValue } from './settings.js';
11
+ import { sectionMeta } from '../schema/section-registry.js';
12
+ import { validateJsonSection } from './json-section.js';
11
13
  export const DEFAULT_PROVIDER_NAMESPACES = ['llm-deepseek', 'llm-pi-ai'];
12
14
  /** 无 raw 时的兜底重建:只取 ProviderEntry 已知字段 */
13
15
  function stripEntry(entry) {
@@ -20,9 +22,10 @@ function stripEntry(entry) {
20
22
  }
21
23
  export class ProvidersAdapter {
22
24
  id = 'providers';
23
- displayName = 'Providers & Models';
24
- defaultIncluded = true;
25
- portability = 'portable';
25
+ // 元数据唯一来源 = 注册表(t31):不再与 ui/export-flow.ts 的导出目录各写一份
26
+ displayName = sectionMeta('providers').displayName;
27
+ defaultIncluded = sectionMeta('providers').defaultIncluded;
28
+ portability = sectionMeta('providers').portability;
26
29
  namespaces;
27
30
  constructor(namespaces = DEFAULT_PROVIDER_NAMESPACES) {
28
31
  this.namespaces = namespaces;
@@ -125,31 +128,25 @@ export class ProvidersAdapter {
125
128
  return { ok: true };
126
129
  }
127
130
  async validate(data, msg = zhMsg) {
128
- const issues = [];
129
- if (data === null || typeof data !== 'object') {
130
- return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'providers' }), severity: 'error' }] };
131
- }
132
- if (data.version !== 1) {
133
- issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
134
- }
135
- if (data.providers === null || typeof data.providers !== 'object') {
136
- issues.push({ path: 'providers', message: msg('adapter.validate.missingObject', { subject: 'providers' }), severity: 'error' });
137
- }
138
- else {
139
- for (const [route, entry] of Object.entries(data.providers)) {
140
- if (entry === null || typeof entry !== 'object') {
141
- issues.push({ path: `providers.${route}`, message: msg('adapter.validate.recordObject', { subject: 'provider' }), severity: 'error' });
142
- continue;
143
- }
144
- if (typeof entry.namespace !== 'string' || entry.namespace === '') {
145
- issues.push({ path: `providers.${route}.namespace`, message: msg('adapter.validate.sourceNamespace'), severity: 'error' });
146
- }
147
- if (typeof entry.revision !== 'number') {
148
- issues.push({ path: `providers.${route}.revision`, message: msg('adapter.validate.number', { subject: 'revision' }), severity: 'error' });
131
+ return validateJsonSection('providers', data, msg, (section, issues) => {
132
+ if (section.providers === null || typeof section.providers !== 'object') {
133
+ issues.push({ path: 'providers', message: msg('adapter.validate.missingObject', { subject: 'providers' }), severity: 'error' });
134
+ }
135
+ else {
136
+ for (const [route, entry] of Object.entries(section.providers)) {
137
+ if (entry === null || typeof entry !== 'object') {
138
+ issues.push({ path: `providers.${route}`, message: msg('adapter.validate.recordObject', { subject: 'provider' }), severity: 'error' });
139
+ continue;
140
+ }
141
+ if (typeof entry.namespace !== 'string' || entry.namespace === '') {
142
+ issues.push({ path: `providers.${route}.namespace`, message: msg('adapter.validate.sourceNamespace'), severity: 'error' });
143
+ }
144
+ if (typeof entry.revision !== 'number') {
145
+ issues.push({ path: `providers.${route}.revision`, message: msg('adapter.validate.number', { subject: 'revision' }), severity: 'error' });
146
+ }
149
147
  }
150
148
  }
151
- }
152
- return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
149
+ });
153
150
  }
154
151
  }
155
152
  //# sourceMappingURL=providers.js.map
@@ -8,7 +8,8 @@
8
8
  * core/session-select.ts(单位 = 会话目录、文件名判据不写死、按日志最新 mtime 倒序)。
9
9
  */
10
10
  import { FileCollectionAdapter } from './file-collection.ts';
11
- import type { ExportOptions, HostContext } from '../core/types.ts';
11
+ import type { ApplyResult, ExportOptions, ExportSection, HostContext, ImportContext } from '../core/types.ts';
12
+ import type { FilesSection } from '../schema/types.ts';
12
13
  export declare class SessionsAdapter extends FileCollectionAdapter {
13
14
  readonly id: 'sessions';
14
15
  readonly displayName = "Sessions";
@@ -35,4 +36,99 @@ export declare class SessionsAdapter extends FileCollectionAdapter {
35
36
  keep: Set<string> | null;
36
37
  warnings: string[];
37
38
  } | null>;
39
+ /**
40
+ * 本机**全部**会话单元 id(P1-5 会话删除墓碑的「本机现存集合」)。
41
+ *
42
+ * 只做「列目录 + 按单元分组」,**不 stat 时间**(时间只用于排序,这里只要集合)——比
43
+ * restrictUnits 少一轮 stat,扫描千级会话库的代价可接受。目录不可读 → 空数组:调用方
44
+ * 据此**跳过**本轮删除检测(把「读不到」当成「全删了」会误标一大片墓碑)。
45
+ */
46
+ listAllUnitIds(ctx: HostContext): Promise<string[]>;
47
+ /**
48
+ * 会话日志文件 → mtime(毫秒;读不到 = null)。批量并发 stat,避免一次性开满 Promise 打爆 libuv 线程池。
49
+ *
50
+ * 两个调用方共用同一实现(口径不许漂移):
51
+ * - `restrictUnits`:「最新 N 个会话」预筛选(issue #39 Feature 1);
52
+ * - `unitActivityTimes`:导出选择器「历史对话按最新到最旧排序」的时间来源。
53
+ *
54
+ * @param rels 调用方的键(这里一律是 baseDir 相对路径);@param homeRelOf 把它们映射到
55
+ * FileSystemFacade 认的 homeDir 相对路径(缺省 = 键本身就是 homeDir 相对路径)。
56
+ */
57
+ private logMtimes;
58
+ /**
59
+ * 导出选择器的排序时间源(宿主 `/export-preview` 调用):会话裸键 → 该会话**最新一份**日志的 mtime。
60
+ *
61
+ * 为什么需要它:会话标题与活跃时间来自 DSH 的 `storages/session_projcache.json`,而那份缓存只覆盖
62
+ * **一部分**会话(真机实测:同一项目 731 个会话目录里 347 个不在缓存内)。这些会话拿不到时间,
63
+ * 只能按 uuid 字典序排在组尾 —— 用户看到的就是「历史对话没有按最新到最旧排序」。会话日志 mtime
64
+ * 对每个会话都存在,且与「最新 N 个会话」预筛选(restrictUnits)**同一口径**(同一门面、同一批大小)。
65
+ *
66
+ * 返回键 = `sessionIdKey(目录名)`(`session-<uuid>` 与裸 `<uuid>` 归一化成同一个会话,取较大者);
67
+ * 时间读不到的会话**不进 Map**(调用方按「时间未知」处理,绝不把未知当成 0 —— 那会把它排到最旧)。
68
+ * 宿主不提供 `mtimeMs` 门面 → 返回空 Map(调用方退回元数据缓存时间,行为与改造前一致)。
69
+ */
70
+ unitActivityTimes(ctx: HostContext, section: ExportSection<FilesSection>): Promise<Map<string, number>>;
71
+ /**
72
+ * 分区收尾(issue #45):把用户填的路径映射**同时**作用到会话日志首帧 cwd 与目录位置。
73
+ *
74
+ * 为什么必须做:DSH 判定工作区成员要求 realpath(会话首帧 cwd) == workspace.path 且 id 在
75
+ * sessionIds 里;跨机恢复时 header 还是源机路径,attachSession 必然被拒 —— 这正是
76
+ * 「会话数据恢复了却显示不出来」。workspace.path 侧已由 analyzer 应用了同一份映射,
77
+ * 这里补上会话侧,两边共用同一个 applyPathMapping(口径不许漂移)。
78
+ *
79
+ * 顺序与一致性(DSH 启动时校验「日志位置 == projectKeyOf(header.cwd)/id」,不一致直接拒绝启动):
80
+ * ① 先改写首帧(全部 generation)→ ② 再按映射后的 cwd 归位目录 → ③ 目录搬不动就回滚首帧。
81
+ * 任何一步失败都如实报告,绝不留「header 与位置不一致」的半套状态;回滚也失败时按硬失败上报
82
+ * (宁可让 rollbackOnError 把这次导入整体回滚,也不要悄悄留下坏状态)。
83
+ *
84
+ * 没有命中映射时退回原有护栏:只校验「目录段 == projectKeyOf(首帧 cwd)」,不一致就归位。
85
+ * 读不出 cwd(非日志 / 非 zstd / 首帧不完整)→ 跳过该单元:不猜、不动、也不谎报通过。
86
+ */
87
+ /**
88
+ * 导出收尾:选中子代理会话时**连带它的父对话**(真机事故:导入全部成功,工作区里却看不见)。
89
+ *
90
+ * 为什么(DSH 客户端源码 dsh-client-ui-workspace 的 sessionVisible):
91
+ * session.origin !== "subagent" && ...
92
+ * —— 工作区列表**只显示** origin !== 'subagent' 的会话;子代理会话只在**父对话之下**出现。
93
+ * 于是「只导出子会话、不带父对话」的包在导入侧一切成功(文件落盘、workspace.json 也登记了),
94
+ * 用户在 DSH 工作区里却一条都看不到(真机:用户勾了 4 条子代理会话导出再导入,全无踪影)。
95
+ * 所以导出侧按父链补齐(父对话本身也可能是子代理会话 → 继续往上追),并在报告里如实说明;
96
+ * 追不到(本机没有 / 超出分区上限)也绝不静默。
97
+ */
98
+ export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<FilesSection>>;
99
+ /** 一个会话单元的 header(取该单元第一份日志;读不出 → undefined,不猜)。 */
100
+ private firstUnitHeader;
101
+ /**
102
+ * 按「父会话 id」在本机找它的会话目录(两种命名形态 + 子会话所在项目键优先)。
103
+ * 只认「目录里真有会话日志」的候选:空目录 / 同名目录不算命中(不猜)。
104
+ */
105
+ private findSessionDir;
106
+ /**
107
+ * 目标机上是否已有该父对话。
108
+ *
109
+ * 探测面 = 两种目录命名形态(`session-<uuid>` / 裸 `<uuid>`)× 目录本身 + 三种已知日志文件名:
110
+ * 目录级探测在真实宿主上就够,文件级探测则让内存 fs(单测)与「日志名不按常见形态」的机器也能命中。
111
+ * 探针异常按「不知道」处理 —— 宁可多告警一条(可解释),也不静默放过。
112
+ */
113
+ private parentExistsLocally;
114
+ /**
115
+ * 导出侧会话族补齐:**只向上补父对话**(选中子代理会话 → 连带它的父对话,否则 DSH 工作区列表里
116
+ * 一条都看不见)。追不到(本机没有 / 超出分区上限)一律进报告,绝不静默。
117
+ *
118
+ * **引擎为什么不再向下展开**(历史上这里还有一条「勾父带子」):条目级白名单是**用户意图的唯一事实**,
119
+ * 而引擎看不到「界面为什么没勾这条子会话」—— 一旦自己向下补,「用户在界面上单独取消某条子代理会话」
120
+ * 就会被无声地加回包里(真机事故:只勾 2 条,导出 41 个会话目录)。所以「勾父带子」改由**界面**在
121
+ * 勾选时联动完成(`src/ui/selection-model.ts` 的 `applySessionParentCoupling`:勾父自动勾上它的子代理
122
+ * 会话、勾子自动带上父、取消父连带取消子),引擎只保留这条「不让用户吃亏」的**向上**兜底
123
+ * (父对话缺席 = 导入后在工作区里完全看不见)。
124
+ */
125
+ private coupleSessionParents;
126
+ /**
127
+ * 导入侧:子代理会话的父对话**既不在本包内、本机也没有** → 一条 warning。
128
+ *
129
+ * 为什么要探测「本机有没有」:目标机本来就有该父对话时(例如把旧包导回同机),子会话会正常
130
+ * 挂在本地父对话之下 —— 无脑告警会把正常情况说成「看不到」,比不告警更糟。
131
+ */
132
+ private orphanSubagentWarnings;
133
+ finalizeApply(ctx: ImportContext): Promise<ApplyResult[]>;
38
134
  }