dsh-config-manager 0.1.63 → 0.1.65

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 (547) hide show
  1. package/README.md +66 -14
  2. package/README.zh-CN.md +43 -13
  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 +46 -4
  38. package/lib/cli/index.js +170 -34
  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 +464 -173
  42. package/lib/client.js +14414 -8888
  43. package/lib/core/analyzer.d.ts +71 -5
  44. package/lib/core/analyzer.js +409 -112
  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-paths.d.ts +14 -0
  50. package/lib/core/boot-paths.js +28 -0
  51. package/lib/core/boot-rescue.d.ts +21 -0
  52. package/lib/core/boot-rescue.js +57 -1
  53. package/lib/core/boot-safety.d.ts +50 -0
  54. package/lib/core/boot-safety.js +211 -0
  55. package/lib/core/conflict-decisions.d.ts +16 -0
  56. package/lib/core/conflict-decisions.js +21 -0
  57. package/lib/core/crash-report.d.ts +15 -12
  58. package/lib/core/crash-report.js +33 -40
  59. package/lib/core/credential-status.d.ts +15 -0
  60. package/lib/core/credential-status.js +13 -0
  61. package/lib/core/exporter.d.ts +18 -0
  62. package/lib/core/exporter.js +115 -3
  63. package/lib/core/importer.d.ts +15 -2
  64. package/lib/core/importer.js +10 -3
  65. package/lib/core/index.d.ts +2 -0
  66. package/lib/core/index.js +3 -0
  67. package/lib/core/journal.d.ts +74 -5
  68. package/lib/core/journal.js +127 -12
  69. package/lib/core/messages.d.ts +54 -0
  70. package/lib/core/messages.js +110 -0
  71. package/lib/core/migration-consult.js +0 -4
  72. package/lib/core/model-tools.js +6 -9
  73. package/lib/core/path-mapping.d.ts +11 -0
  74. package/lib/core/path-mapping.js +28 -0
  75. package/lib/core/phase3-host.d.ts +79 -6
  76. package/lib/core/phase3-host.js +223 -73
  77. package/lib/core/phase4-crash-child.js +0 -1
  78. package/lib/core/plugin-cli.js +22 -2
  79. package/lib/core/reconcile.d.ts +8 -1
  80. package/lib/core/reconcile.js +12 -4
  81. package/lib/core/recovery-orchestrator.js +1 -1
  82. package/lib/core/restore.js +10 -4
  83. package/lib/core/rollback.js +1 -1
  84. package/lib/core/run-registry.d.ts +63 -3
  85. package/lib/core/run-registry.js +74 -4
  86. package/lib/core/session-meta.d.ts +27 -8
  87. package/lib/core/session-meta.js +59 -32
  88. package/lib/core/session-repair.d.ts +89 -0
  89. package/lib/core/session-repair.js +118 -0
  90. package/lib/core/session-select.d.ts +50 -0
  91. package/lib/core/session-select.js +137 -0
  92. package/lib/core/startup-barrier.d.ts +15 -0
  93. package/lib/core/startup-barrier.js +24 -2
  94. package/lib/core/transaction-coordinator.js +3 -3
  95. package/lib/core/types.d.ts +250 -0
  96. package/lib/core/validator.js +0 -1
  97. package/lib/index.d.ts +368 -6
  98. package/lib/index.js +884 -3365
  99. package/lib/market/index-parser.d.ts +2 -1
  100. package/lib/market/index-parser.js +27 -6
  101. package/lib/market/my-repo.d.ts +0 -1
  102. package/lib/market/my-repo.js +0 -6
  103. package/lib/market/prepare.d.ts +9 -0
  104. package/lib/market/prepare.js +5 -31
  105. package/lib/market/reader.js +0 -1
  106. package/lib/market/security.js +3 -9
  107. package/lib/profiles/dsh-profile-launcher.d.ts +141 -0
  108. package/lib/profiles/dsh-profile-launcher.js +396 -0
  109. package/lib/profiles/dsh-profile-manager.d.ts +49 -15
  110. package/lib/profiles/dsh-profile-manager.js +151 -48
  111. package/lib/profiles/dsh-profile-runtime.d.ts +83 -0
  112. package/lib/profiles/dsh-profile-runtime.js +194 -0
  113. package/lib/profiles/dsh-profile-shared.d.ts +117 -10
  114. package/lib/profiles/dsh-profile-shared.js +12 -0
  115. package/lib/profiles/index.d.ts +10 -3
  116. package/lib/profiles/index.js +10 -3
  117. package/lib/profiles/process-control.d.ts +37 -0
  118. package/lib/profiles/process-control.js +75 -0
  119. package/lib/routes/backup.d.ts +9 -0
  120. package/lib/routes/backup.js +99 -0
  121. package/lib/routes/consult.d.ts +9 -0
  122. package/lib/routes/consult.js +134 -0
  123. package/lib/routes/context.d.ts +9 -0
  124. package/lib/routes/context.js +2 -0
  125. package/lib/routes/history.d.ts +9 -0
  126. package/lib/routes/history.js +53 -0
  127. package/lib/routes/import.d.ts +9 -0
  128. package/lib/routes/import.js +390 -0
  129. package/lib/routes/index.d.ts +10 -0
  130. package/lib/routes/index.js +27 -0
  131. package/lib/routes/kit.d.ts +99 -0
  132. package/lib/routes/kit.js +161 -0
  133. package/lib/routes/market.d.ts +9 -0
  134. package/lib/routes/market.js +256 -0
  135. package/lib/routes/me.d.ts +9 -0
  136. package/lib/routes/me.js +170 -0
  137. package/lib/routes/prefs.d.ts +9 -0
  138. package/lib/routes/prefs.js +103 -0
  139. package/lib/routes/profiles.d.ts +9 -0
  140. package/lib/routes/profiles.js +250 -0
  141. package/lib/routes/recovery.d.ts +9 -0
  142. package/lib/routes/recovery.js +137 -0
  143. package/lib/routes/snapshots.d.ts +9 -0
  144. package/lib/routes/snapshots.js +214 -0
  145. package/lib/routes/sync.d.ts +9 -0
  146. package/lib/routes/sync.js +783 -0
  147. package/lib/schema/config.d.ts +10 -9
  148. package/lib/schema/config.js +78 -96
  149. package/lib/schema/index.d.ts +8 -1
  150. package/lib/schema/index.js +13 -1
  151. package/lib/schema/manifest.d.ts +2 -0
  152. package/lib/schema/manifest.js +1 -0
  153. package/lib/schema/section-registry.d.ts +211 -0
  154. package/lib/schema/section-registry.js +285 -0
  155. package/lib/schema/types.d.ts +13 -3
  156. package/lib/schema/versions.d.ts +11 -6
  157. package/lib/schema/versions.js +27 -1
  158. package/lib/security/secret-scanner.js +1 -1
  159. package/lib/security/zip-security.d.ts +8 -5
  160. package/lib/security/zip-security.js +20 -90
  161. package/lib/sync/ancestor.d.ts +11 -0
  162. package/lib/sync/ancestor.js +20 -0
  163. package/lib/sync/autosync-config.js +9 -18
  164. package/lib/sync/autosync-scheduler.d.ts +7 -2
  165. package/lib/sync/autosync-scheduler.js +35 -17
  166. package/lib/sync/backup-schedule-config.js +0 -1
  167. package/lib/sync/blob-store.d.ts +85 -0
  168. package/lib/sync/blob-store.js +85 -0
  169. package/lib/sync/git/git-transport.d.ts +28 -5
  170. package/lib/sync/git/git-transport.js +170 -24
  171. package/lib/sync/github-auth.d.ts +0 -1
  172. package/lib/sync/github-auth.js +0 -2
  173. package/lib/sync/layout.d.ts +16 -2
  174. package/lib/sync/layout.js +48 -7
  175. package/lib/sync/risk.d.ts +18 -12
  176. package/lib/sync/risk.js +17 -28
  177. package/lib/sync/session-tombstones.d.ts +39 -0
  178. package/lib/sync/session-tombstones.js +125 -0
  179. package/lib/sync/sync-config.d.ts +26 -3
  180. package/lib/sync/sync-config.js +40 -3
  181. package/lib/sync/sync-engine.d.ts +140 -12
  182. package/lib/sync/sync-engine.js +266 -49
  183. package/lib/sync/sync-history.js +0 -1
  184. package/lib/sync/sync-selection.d.ts +18 -0
  185. package/lib/sync/sync-selection.js +50 -24
  186. package/lib/sync/sync-state.d.ts +31 -4
  187. package/lib/sync/sync-state.js +38 -6
  188. package/lib/sync/transport.d.ts +103 -0
  189. package/lib/sync/transport.js +121 -0
  190. package/lib/sync/ui-prefs.d.ts +4 -2
  191. package/lib/sync/ui-prefs.js +6 -4
  192. package/lib/sync/webdav/webdav-transport.d.ts +36 -4
  193. package/lib/sync/webdav/webdav-transport.js +242 -43
  194. package/lib/ui/dsh-profiles-view.d.ts +52 -9
  195. package/lib/ui/dsh-profiles-view.js +80 -13
  196. package/lib/ui/export-flow.d.ts +2 -2
  197. package/lib/ui/export-flow.js +63 -17
  198. package/lib/ui/i18n.d.ts +12 -2
  199. package/lib/ui/i18n.js +25 -4
  200. package/lib/ui/import-log.d.ts +72 -0
  201. package/lib/ui/import-log.js +110 -0
  202. package/lib/ui/import-stepper.d.ts +14 -0
  203. package/lib/ui/import-stepper.js +18 -0
  204. package/lib/ui/import-wizard.d.ts +106 -3
  205. package/lib/ui/import-wizard.js +125 -7
  206. package/lib/ui/mid-ellipsis.d.ts +31 -0
  207. package/lib/ui/mid-ellipsis.js +39 -0
  208. package/lib/ui/my-configs-view.d.ts +136 -0
  209. package/lib/ui/my-configs-view.js +127 -0
  210. package/lib/ui/progress.js +6 -0
  211. package/lib/ui/report.d.ts +26 -0
  212. package/lib/ui/report.js +37 -0
  213. package/lib/ui/runs-view.d.ts +128 -0
  214. package/lib/ui/runs-view.js +123 -0
  215. package/lib/ui/select-model.d.ts +51 -0
  216. package/lib/ui/select-model.js +84 -0
  217. package/lib/ui/selection-model.d.ts +118 -2
  218. package/lib/ui/selection-model.js +291 -4
  219. package/lib/ui/snapshots-view.d.ts +116 -0
  220. package/lib/ui/snapshots-view.js +141 -0
  221. package/lib/ui/sync-settings-view.d.ts +295 -0
  222. package/lib/ui/sync-settings-view.js +280 -0
  223. package/lib/ui/test-helpers.d.ts +7 -1
  224. package/lib/ui/test-helpers.js +4 -1
  225. package/lib/ui/types.d.ts +8 -1
  226. package/lib/utils/env-lock.d.ts +77 -5
  227. package/lib/utils/env-lock.js +171 -57
  228. package/lib/utils/logger.d.ts +17 -13
  229. package/lib/utils/logger.js +79 -41
  230. package/lib/utils/paths.d.ts +1 -1
  231. package/lib/utils/paths.js +2 -2
  232. package/lib/utils/session-log.d.ts +70 -0
  233. package/lib/utils/session-log.js +347 -0
  234. package/lib/utils/zip.d.ts +49 -5
  235. package/lib/utils/zip.js +240 -51
  236. package/lib/utils/zstd-frame.d.ts +44 -0
  237. package/lib/utils/zstd-frame.js +115 -0
  238. package/package.json +4 -2
  239. package/src/adapters/agent-instructions.ts +5 -3
  240. package/src/adapters/agent-presets.ts +5 -3
  241. package/src/adapters/credentials.ts +19 -22
  242. package/src/adapters/file-collection.test.ts +256 -0
  243. package/src/adapters/file-collection.ts +129 -17
  244. package/src/adapters/index.ts +12 -1
  245. package/src/adapters/json-section.ts +73 -0
  246. package/src/adapters/mcp.ts +16 -19
  247. package/src/adapters/plugin-files.ts +11 -14
  248. package/src/adapters/plugins.test.ts +1 -1
  249. package/src/adapters/plugins.ts +39 -42
  250. package/src/adapters/prompts.ts +16 -20
  251. package/src/adapters/providers.ts +22 -25
  252. package/src/adapters/roundtrip.test.ts +44 -0
  253. package/src/adapters/sessions.test.ts +400 -1
  254. package/src/adapters/sessions.ts +435 -16
  255. package/src/adapters/settings.ts +22 -25
  256. package/src/adapters/skills.ts +5 -3
  257. package/src/adapters/test-helpers.ts +8 -0
  258. package/src/adapters/ui.ts +14 -17
  259. package/src/adapters/workspaces.test.ts +226 -2
  260. package/src/adapters/workspaces.ts +247 -16
  261. package/src/cli/index.ts +180 -37
  262. package/src/cli/sessions-repair.ts +229 -0
  263. package/src/client/CLIENT_DEPENDENCIES.md +3 -1
  264. package/src/client/ConfigManagerSection.tsx +50 -46
  265. package/src/client/about/AboutPanel.tsx +22 -8
  266. package/src/client/about/about-view.test.ts +53 -6
  267. package/src/client/about/about-view.ts +26 -2
  268. package/src/client/api.ts +195 -242
  269. package/src/client/client-types.ts +2 -2
  270. package/src/client/common/ConfirmDialog.tsx +3 -2
  271. package/src/client/common/ContentPicker.tsx +53 -24
  272. package/src/client/common/CopyButton.tsx +74 -0
  273. package/src/client/common/ErrorBanner.tsx +3 -2
  274. package/src/client/common/Icon.tsx +116 -0
  275. package/src/client/common/Modal.tsx +29 -8
  276. package/src/client/common/Motion.tsx +70 -0
  277. package/src/client/common/ProgressBar.tsx +18 -9
  278. package/src/client/common/ReportView.tsx +128 -54
  279. package/src/client/common/RunsCenter.tsx +378 -0
  280. package/src/client/common/Select.tsx +223 -0
  281. package/src/client/common/Skeleton.tsx +128 -0
  282. package/src/client/common/client-route-parity.test.ts +171 -0
  283. package/src/client/common/http-usage-guard.test.ts +163 -0
  284. package/src/client/common/http.test.ts +225 -0
  285. package/src/client/common/http.ts +238 -0
  286. package/src/client/common/morph-icons.test.ts +206 -0
  287. package/src/client/common/morph-icons.ts +89 -0
  288. package/src/client/common/plan-text-redaction.test.ts +29 -7
  289. package/src/client/common/progress-view.test.ts +33 -1
  290. package/src/client/common/progress-view.ts +17 -0
  291. package/src/client/common/routes.ts +130 -0
  292. package/src/client/common/runs-center-guard.test.ts +44 -0
  293. package/src/client/common/ui.tsx +48 -5
  294. package/src/client/config-manager.module.css +877 -35
  295. package/src/client/export/ExportView.tsx +27 -3
  296. package/src/client/history/HistoryPanel.tsx +30 -30
  297. package/src/client/history/history-api.ts +34 -61
  298. package/src/client/icon-layer-guard.test.ts +137 -0
  299. package/src/client/import/ImportLogPanel.tsx +136 -0
  300. package/src/client/import/ImportWizardView.tsx +455 -838
  301. package/src/client/import/PathMappingForm.tsx +12 -1
  302. package/src/client/import/import-wizard-split.test.ts +101 -0
  303. package/src/client/import/import-wizard-steps.tsx +352 -0
  304. package/src/client/import/use-import-wizard-controller.ts +476 -0
  305. package/src/client/index.ts +4 -6
  306. package/src/client/locales.ts +258 -123
  307. package/src/client/lucide-icons.d.ts +11 -0
  308. package/src/client/market/MarketPanel.tsx +39 -37
  309. package/src/client/market/MyConfigsInstall.tsx +93 -0
  310. package/src/client/market/MyConfigsList.tsx +121 -0
  311. package/src/client/market/MyConfigsLoginCard.tsx +100 -0
  312. package/src/client/market/MyConfigsView.tsx +148 -476
  313. package/src/client/market/MyConfigsWizard.tsx +252 -0
  314. package/src/client/market/market-api.test.ts +36 -0
  315. package/src/client/market/market-api.ts +13 -70
  316. package/src/client/market/market-view.ts +1 -1
  317. package/src/client/market/my-configs-api.ts +17 -71
  318. package/src/client/market/my-configs-split.test.ts +58 -0
  319. package/src/client/overview/OverviewPanel.tsx +5 -48
  320. package/src/client/profiles/ProfilesPanel.tsx +422 -215
  321. package/src/client/recovery/RecoveryPanel.tsx +121 -5
  322. package/src/client/recovery/incident-api.ts +91 -0
  323. package/src/client/recovery/recovery-api.ts +15 -65
  324. package/src/client/recovery/recovery-locales.ts +56 -0
  325. package/src/client/recovery/recovery-view.ts +71 -0
  326. package/src/client/run-store.test.ts +593 -3
  327. package/src/client/run-store.ts +430 -80
  328. package/src/client/snapshots/BackupScheduleCard.tsx +434 -0
  329. package/src/client/snapshots/RestorePlanView.tsx +8 -5
  330. package/src/client/snapshots/SnapshotsEmptyState.tsx +31 -0
  331. package/src/client/snapshots/SnapshotsListTable.tsx +97 -0
  332. package/src/client/snapshots/SnapshotsPanel.tsx +55 -630
  333. package/src/client/sync/AutosyncCard.tsx +84 -0
  334. package/src/client/sync/ChannelConfigDialog.tsx +266 -0
  335. package/src/client/sync/DecryptPasswordCard.tsx +48 -0
  336. package/src/client/sync/SecurityOptionsCard.tsx +83 -0
  337. package/src/client/sync/SyncChannelEntryCard.tsx +78 -0
  338. package/src/client/sync/SyncConfirmView.tsx +10 -8
  339. package/src/client/sync/SyncHistoryView.tsx +3 -2
  340. package/src/client/sync/SyncSectionPickerDialog.tsx +182 -0
  341. package/src/client/sync/SyncSettingsView.test.ts +85 -0
  342. package/src/client/sync/SyncSettingsView.tsx +247 -667
  343. package/src/client/sync/history-model.ts +4 -10
  344. package/src/client/sync/sync-api.ts +59 -100
  345. package/src/client/sync/sync-locales.ts +16 -2
  346. package/src/client/sync/sync-view-v2.test.ts +30 -4
  347. package/src/client/sync/sync-view.ts +67 -17
  348. package/src/core/analyzer-mapping.test.ts +55 -0
  349. package/src/core/analyzer.ts +458 -111
  350. package/src/core/apply-order.test.ts +182 -0
  351. package/src/core/backup-plan.test.ts +141 -0
  352. package/src/core/backup-plan.ts +16 -8
  353. package/src/core/backup.test.ts +310 -0
  354. package/src/core/backup.ts +174 -23
  355. package/src/core/base-path-rebase.test.ts +97 -0
  356. package/src/core/boot-paths.test.ts +35 -0
  357. package/src/core/boot-paths.ts +29 -0
  358. package/src/core/boot-rescue.test.ts +108 -0
  359. package/src/core/boot-rescue.ts +57 -1
  360. package/src/core/boot-safety.test.ts +138 -0
  361. package/src/core/boot-safety.ts +248 -0
  362. package/src/core/conflict-decisions.ts +34 -0
  363. package/src/core/crash-report.test.ts +25 -41
  364. package/src/core/crash-report.ts +34 -41
  365. package/src/core/credential-status.test.ts +35 -0
  366. package/src/core/credential-status.ts +25 -0
  367. package/src/core/export-coupling.test.ts +255 -0
  368. package/src/core/exporter.ts +110 -3
  369. package/src/core/finalize-order.test.ts +82 -0
  370. package/src/core/import-sessions-visibility.test.ts +93 -0
  371. package/src/core/importer.ts +21 -3
  372. package/src/core/incident-wiring.test.ts +96 -0
  373. package/src/core/index.ts +10 -0
  374. package/src/core/journal.test.ts +59 -2
  375. package/src/core/journal.ts +152 -10
  376. package/src/core/messages.ts +110 -0
  377. package/src/core/migration-consult.ts +0 -5
  378. package/src/core/migration-history.test.ts +3 -5
  379. package/src/core/model-tools.test.ts +0 -14
  380. package/src/core/model-tools.ts +6 -8
  381. package/src/core/path-mapping.ts +33 -0
  382. package/src/core/phase3-consistency.test.ts +1 -1
  383. package/src/core/phase3-host.ts +248 -70
  384. package/src/core/phase3-p2b-fingerprint.test.ts +0 -1
  385. package/src/core/phase3-production-integration.test.ts +1 -1
  386. package/src/core/phase4-crash-child.ts +0 -1
  387. package/src/core/phase4-crash-injection.test.ts +1 -3
  388. package/src/core/plugin-cli.fs.test.ts +54 -2
  389. package/src/core/plugin-cli.ts +21 -2
  390. package/src/core/reconcile.test.ts +62 -0
  391. package/src/core/reconcile.ts +20 -5
  392. package/src/core/recovery-orchestrator.test.ts +13 -1
  393. package/src/core/recovery-orchestrator.ts +1 -1
  394. package/src/core/restore.ts +10 -4
  395. package/src/core/rollback.ts +1 -1
  396. package/src/core/run-registry-runs.test.ts +71 -0
  397. package/src/core/run-registry.test.ts +58 -7
  398. package/src/core/run-registry.ts +96 -5
  399. package/src/core/session-meta.test.ts +59 -1
  400. package/src/core/session-meta.ts +62 -29
  401. package/src/core/session-repair.test.ts +91 -0
  402. package/src/core/session-repair.ts +203 -0
  403. package/src/core/session-select.test.ts +73 -0
  404. package/src/core/session-select.ts +131 -0
  405. package/src/core/smoke.test.ts +5 -3
  406. package/src/core/startup-barrier.ts +29 -2
  407. package/src/core/transaction-coordinator.ts +3 -3
  408. package/src/core/types.ts +253 -0
  409. package/src/core/validator.ts +0 -1
  410. package/src/index.sync.test.ts +8 -0
  411. package/src/index.ts +611 -2922
  412. package/src/market/git-file-writer.test.ts +1 -1
  413. package/src/market/index-parser.ts +29 -6
  414. package/src/market/market.test.ts +146 -1
  415. package/src/market/my-repo.test.ts +57 -4
  416. package/src/market/my-repo.ts +0 -7
  417. package/src/market/prepare.ts +12 -32
  418. package/src/market/reader.ts +0 -1
  419. package/src/market/security.ts +3 -11
  420. package/src/profiles/dsh-profile-launcher.test.ts +359 -0
  421. package/src/profiles/dsh-profile-launcher.ts +473 -0
  422. package/src/profiles/dsh-profile-manager.test.ts +123 -59
  423. package/src/profiles/dsh-profile-manager.ts +148 -51
  424. package/src/profiles/dsh-profile-runtime.test.ts +196 -0
  425. package/src/profiles/dsh-profile-runtime.ts +228 -0
  426. package/src/profiles/dsh-profile-shared.ts +126 -9
  427. package/src/profiles/index.ts +30 -4
  428. package/src/profiles/process-control.ts +99 -0
  429. package/src/routes/backup.ts +103 -0
  430. package/src/routes/consult.ts +139 -0
  431. package/src/routes/context.ts +10 -0
  432. package/src/routes/history.ts +56 -0
  433. package/src/routes/import.ts +399 -0
  434. package/src/routes/index.ts +36 -0
  435. package/src/routes/kit.ts +223 -0
  436. package/src/routes/market.ts +275 -0
  437. package/src/routes/me.ts +168 -0
  438. package/src/routes/prefs.ts +104 -0
  439. package/src/routes/profiles.ts +252 -0
  440. package/src/routes/recovery.ts +113 -0
  441. package/src/routes/snapshots.ts +219 -0
  442. package/src/routes/sync.ts +810 -0
  443. package/src/schema/config.ts +96 -91
  444. package/src/schema/index.ts +33 -1
  445. package/src/schema/manifest.ts +3 -0
  446. package/src/schema/registry.test.ts +293 -0
  447. package/src/schema/section-registry.ts +463 -0
  448. package/src/schema/types.ts +13 -3
  449. package/src/schema/versions.ts +31 -1
  450. package/src/security/secret-scanner.ts +1 -1
  451. package/src/security/security.test.ts +145 -2
  452. package/src/security/zip-security.ts +20 -92
  453. package/src/sync/ancestor.test.ts +0 -1
  454. package/src/sync/ancestor.ts +23 -0
  455. package/src/sync/autosync-config.ts +9 -18
  456. package/src/sync/autosync-scheduler.test.ts +60 -3
  457. package/src/sync/autosync-scheduler.ts +45 -19
  458. package/src/sync/backup-schedule-config.ts +0 -1
  459. package/src/sync/backup-scheduler.test.ts +15 -2
  460. package/src/sync/blob-store.test.ts +191 -0
  461. package/src/sync/blob-store.ts +139 -0
  462. package/src/sync/git/git-transport.test.ts +203 -1
  463. package/src/sync/git/git-transport.ts +173 -24
  464. package/src/sync/github-auth.ts +0 -2
  465. package/src/sync/layout.ts +62 -8
  466. package/src/sync/risk.test.ts +32 -1
  467. package/src/sync/risk.ts +26 -31
  468. package/src/sync/session-tombstones.test.ts +87 -0
  469. package/src/sync/session-tombstones.ts +123 -0
  470. package/src/sync/sync-config.test.ts +132 -0
  471. package/src/sync/sync-config.ts +47 -6
  472. package/src/sync/sync-credentials.test.ts +77 -0
  473. package/src/sync/sync-engine.test.ts +799 -8
  474. package/src/sync/sync-engine.ts +322 -51
  475. package/src/sync/sync-history.ts +0 -1
  476. package/src/sync/sync-selection.test.ts +43 -7
  477. package/src/sync/sync-selection.ts +56 -24
  478. package/src/sync/sync-state.test.ts +83 -0
  479. package/src/sync/sync-state.ts +58 -7
  480. package/src/sync/transport.ts +191 -0
  481. package/src/sync/ui-prefs.ts +10 -6
  482. package/src/sync/webdav/webdav-transport.test.ts +122 -2
  483. package/src/sync/webdav/webdav-transport.ts +256 -37
  484. package/src/ui/dsh-profiles-view.test.ts +87 -12
  485. package/src/ui/dsh-profiles-view.ts +106 -17
  486. package/src/ui/export-flow.ts +65 -18
  487. package/src/ui/i18n.test.ts +147 -0
  488. package/src/ui/i18n.ts +25 -4
  489. package/src/ui/import-log.test.ts +106 -0
  490. package/src/ui/import-log.ts +158 -0
  491. package/src/ui/import-stepper.test.ts +25 -1
  492. package/src/ui/import-stepper.ts +18 -0
  493. package/src/ui/import-wizard.test.ts +168 -2
  494. package/src/ui/import-wizard.ts +175 -8
  495. package/src/ui/market-import.test.ts +0 -1
  496. package/src/ui/mid-ellipsis.test.ts +160 -0
  497. package/src/ui/mid-ellipsis.ts +37 -0
  498. package/src/ui/my-configs-view.test.ts +222 -0
  499. package/src/ui/my-configs-view.ts +206 -0
  500. package/src/ui/progress.ts +6 -0
  501. package/src/ui/report.test.ts +32 -2
  502. package/src/ui/report.ts +52 -0
  503. package/src/ui/runs-view.test.ts +152 -0
  504. package/src/ui/runs-view.ts +207 -0
  505. package/src/ui/select-model.test.ts +75 -0
  506. package/src/ui/select-model.ts +92 -0
  507. package/src/ui/selection-model.test.ts +315 -12
  508. package/src/ui/selection-model.ts +327 -4
  509. package/src/ui/snapshots-view.test.ts +228 -0
  510. package/src/ui/snapshots-view.ts +233 -0
  511. package/src/ui/sync-settings-view.test.ts +348 -0
  512. package/src/ui/sync-settings-view.ts +516 -0
  513. package/src/ui/test-helpers.ts +8 -1
  514. package/src/ui/types.ts +10 -1
  515. package/src/utils/env-lock.test.ts +290 -107
  516. package/src/utils/env-lock.ts +209 -54
  517. package/src/utils/logger.test.ts +178 -1
  518. package/src/utils/logger.ts +81 -44
  519. package/src/utils/paths.ts +2 -2
  520. package/src/utils/session-log.test.ts +136 -0
  521. package/src/utils/session-log.ts +344 -0
  522. package/src/utils/zip.test.ts +284 -0
  523. package/src/utils/zip.ts +226 -52
  524. package/src/utils/zstd-frame.test.ts +80 -0
  525. package/src/utils/zstd-frame.ts +131 -0
  526. package/lib/core/config-lifecycle.d.ts +0 -180
  527. package/lib/core/config-lifecycle.js +0 -481
  528. package/lib/core/config-snapshot.d.ts +0 -135
  529. package/lib/core/config-snapshot.js +0 -424
  530. package/lib/core/config-state.d.ts +0 -70
  531. package/lib/core/config-state.js +0 -177
  532. package/lib/core/undo.d.ts +0 -73
  533. package/lib/core/undo.js +0 -87
  534. package/lib/core/watcher.d.ts +0 -101
  535. package/lib/core/watcher.js +0 -219
  536. package/src/client/lifecycle/LifecyclePanel.tsx +0 -525
  537. package/src/client/lifecycle/lifecycle-api.ts +0 -249
  538. package/src/core/config-lifecycle.test.ts +0 -727
  539. package/src/core/config-lifecycle.ts +0 -573
  540. package/src/core/config-snapshot.test.ts +0 -369
  541. package/src/core/config-snapshot.ts +0 -522
  542. package/src/core/config-state.test.ts +0 -381
  543. package/src/core/config-state.ts +0 -207
  544. package/src/core/phase1-wiring.test.ts +0 -139
  545. package/src/core/undo.ts +0 -116
  546. package/src/core/watcher.test.ts +0 -326
  547. package/src/core/watcher.ts +0 -263
@@ -0,0 +1,128 @@
1
+ /**
2
+ * 运行中心(Run Center)纯渲染模型:把宿主 /runs 的 RunState 列表变成「一张卡片 + 一次决策」。
3
+ *
4
+ * 为什么单独一层:卡片要区分「在跑 / 在等人 / 已结束」,决策框要算「已应用多少 / 剩下多少」
5
+ * 并给出默认选项 —— 这些判据都必须可单测(UI 分层铁律:逻辑在 src/ui,React 壳只装配)。
6
+ *
7
+ * 三条设计约束(读码得出,不是偏好):
8
+ * 1. **可终止 ≠ 所有 run**:只有导入引擎实现了「计划项边界协作式取消」(/runs/cancel);
9
+ * 其余 run 类型(同步/恢复/备份/自动同步)没有安全点,UI 必须如实禁用而不是给个假按钮。
10
+ * 2. **待决策态优先于一切**:pendingDecision 非空时 run 并没有卡住,它在等人 —— 卡片必须显性提示,
11
+ * 否则用户会以为任务死了(这正是「终止需弹窗」的功能入口)。
12
+ * 3. **默认选项跟随既有偏好**:用户此前勾过「失败不回滚」(rollbackOnError=false)时,决策框默认落在
13
+ * 「保留已应用项」;没勾过则默认「回滚」。不推翻用户已有的心智,也不替他做更危险的选择。
14
+ */
15
+ import type { RunKind, RunState } from '../core/run-registry.ts';
16
+ import type { ImportLogCounts } from './import-log.ts';
17
+ /** 终止决策(与宿主 /runs/cancel/decision 的取值一一对应)。 */
18
+ export type CancelDecision = 'rollback' | 'keep';
19
+ /** 一张运行卡片的纯数据模型(文案一律给 key,由壳层翻译)。 */
20
+ export interface RunCardModel {
21
+ runId: string;
22
+ kind: RunKind;
23
+ /** 卡片标题 key(`runs.kind.<kind>`)。 */
24
+ kindLabelKey: string;
25
+ status: 'running' | 'done' | 'failed';
26
+ /** 状态徽章 key(`runs.status.<status>`;待决策时另由 awaitingDecision 覆盖显示)。 */
27
+ statusLabelKey: string;
28
+ /** 已到安全点、等用户选「回滚 / 保留」。 */
29
+ awaitingDecision: boolean;
30
+ /** 可手动终止(只有导入支持计划项边界协作式取消;已请求过就不再显示按钮)。 */
31
+ canCancel: boolean;
32
+ /**
33
+ * 已请求终止、正等当前计划项结束(null = 未请求)。
34
+ * `stuck` = 等待超过阈值 → 界面必须从「请稍候」升级为「给出路」。
35
+ */
36
+ cancelWait: {
37
+ minutes: number;
38
+ stuck: boolean;
39
+ } | null;
40
+ /** 可「跳过当前插件」(仅导入的插件安装阶段有意义)。 */
41
+ canSkip: boolean;
42
+ /** 进度(item/itemTotal 优先;导出用 section 计数)。 */
43
+ progress: {
44
+ labelKey: string;
45
+ section: string | null;
46
+ item: number | null;
47
+ itemTotal: number | null;
48
+ detail: string | null;
49
+ percent: number | null;
50
+ };
51
+ /** 日志聚合计数(无日志时为 null)。 */
52
+ counts: ImportLogCounts | null;
53
+ /** 需要用户关注的项数(警告 + 失败)。 */
54
+ problems: number;
55
+ /** 最近日志行(已截断;供折叠查看,不含敏感内容:宿主侧落账前已 redact)。 */
56
+ logTail: string[];
57
+ updatedAt: number;
58
+ /**
59
+ * 「最近一次更新」的相对时间(复用 overview.time.* 字典)。
60
+ * 为什么需要:没有它,「进行中」和「三小时前就结束的僵尸卡片」在界面上长得一模一样,
61
+ * 用户只能得出「一直在加载」的结论(真机报告)。
62
+ */
63
+ time: {
64
+ key: string;
65
+ params?: {
66
+ n: string;
67
+ };
68
+ };
69
+ }
70
+ /** 日志尾部保留行数(卡片里只给「最近发生了什么」,完整日志在所属页面)。 */
71
+ export declare const RUN_CARD_LOG_TAIL = 8;
72
+ /**
73
+ * 终止请求等多久算「可能卡住」(2 分钟)。
74
+ * 为什么要有阈值:协作式取消只能等到**项边界**,而插件安装可以跑几十分钟。
75
+ * 2 分钟内大概率是在装插件(正常),超过就该把出路摆到用户面前(跳过当前项 / 重启),
76
+ * 否则用户只能一遍遍点「终止…」——而那个按钮此刻已经没有任何作用了。
77
+ */
78
+ export declare const CANCEL_STUCK_AFTER_MS: number;
79
+ /**
80
+ * 相对时间(纯函数,now 显式传入 → 可单测):刚刚 / N 分钟前 / N 小时前 / N 天前。
81
+ * 键复用 config-manager 字典里既有的 overview.time.*(同字典,不新造文案)。
82
+ */
83
+ export declare function relativeTimeLabel(updatedAt: number, now: number): {
84
+ key: string;
85
+ params?: {
86
+ n: string;
87
+ };
88
+ };
89
+ /** RunState[] → 卡片模型(按 updatedAt 倒序;终态与进行中同列,由 status 区分)。 */
90
+ export declare function runCards(runs: readonly RunState[], opts?: {
91
+ logTail?: number;
92
+ now?: number;
93
+ }): RunCardModel[];
94
+ /** 状态栏 / 抽屉徽章用的计数。 */
95
+ export declare function runSummary(runs: readonly RunState[]): {
96
+ running: number;
97
+ awaiting: number;
98
+ total: number;
99
+ };
100
+ /** 终止决策框模型。 */
101
+ export interface CancelDialogModel {
102
+ runId: string;
103
+ kindLabelKey: string;
104
+ /** 已经应用的计划项数(引擎回传的 executed 长度不可得,故用进度计数近似并如实标注)。 */
105
+ appliedCount: number | null;
106
+ /** 计划项总数(null = 宿主未上报)。 */
107
+ totalCount: number | null;
108
+ /** 未执行项数(total - applied;无法计算时 null)。 */
109
+ pendingCount: number | null;
110
+ defaultDecision: CancelDecision;
111
+ options: {
112
+ id: CancelDecision;
113
+ labelKey: string;
114
+ descKey: string;
115
+ }[];
116
+ }
117
+ /**
118
+ * 终止决策框模型。默认选项跟随用户既有的 rollbackOnError 偏好:
119
+ * 勾过「失败不回滚」→ 默认保留(与他的心智一致);否则默认回滚(安全侧)。
120
+ */
121
+ export declare function cancelDialogModel(run: Pick<RunState, 'runId' | 'kind' | 'item' | 'itemTotal'>, opts: {
122
+ defaultRollbackOnError: boolean;
123
+ }): CancelDialogModel;
124
+ /** 「已应用/未执行」的用户可读摘要参数(null → 壳层显示「未知」而不是编个数字)。 */
125
+ export declare function cancelCostParams(model: CancelDialogModel): {
126
+ applied: string;
127
+ pending: string;
128
+ };
@@ -0,0 +1,123 @@
1
+ import { buildImportLogModel } from './import-log.js';
2
+ import { isSkippablePluginInstall } from './import-wizard.js';
3
+ /** 日志尾部保留行数(卡片里只给「最近发生了什么」,完整日志在所属页面)。 */
4
+ export const RUN_CARD_LOG_TAIL = 8;
5
+ /**
6
+ * 终止请求等多久算「可能卡住」(2 分钟)。
7
+ * 为什么要有阈值:协作式取消只能等到**项边界**,而插件安装可以跑几十分钟。
8
+ * 2 分钟内大概率是在装插件(正常),超过就该把出路摆到用户面前(跳过当前项 / 重启),
9
+ * 否则用户只能一遍遍点「终止…」——而那个按钮此刻已经没有任何作用了。
10
+ */
11
+ export const CANCEL_STUCK_AFTER_MS = 2 * 60 * 1000;
12
+ /**
13
+ * 相对时间(纯函数,now 显式传入 → 可单测):刚刚 / N 分钟前 / N 小时前 / N 天前。
14
+ * 键复用 config-manager 字典里既有的 overview.time.*(同字典,不新造文案)。
15
+ */
16
+ export function relativeTimeLabel(updatedAt, now) {
17
+ const diffMs = Math.max(0, now - updatedAt);
18
+ const minutes = Math.floor(diffMs / 60_000);
19
+ if (minutes < 1)
20
+ return { key: 'overview.time.now' };
21
+ if (minutes < 60)
22
+ return { key: 'overview.time.min', params: { n: String(minutes) } };
23
+ const hours = Math.floor(minutes / 60);
24
+ if (hours < 24)
25
+ return { key: 'overview.time.hour', params: { n: String(hours) } };
26
+ return { key: 'overview.time.day', params: { n: String(Math.floor(hours / 24)) } };
27
+ }
28
+ function percentOf(item, itemTotal) {
29
+ if (item === null || itemTotal === null || itemTotal <= 0)
30
+ return null;
31
+ return Math.min(100, Math.max(0, Math.round((item / itemTotal) * 100)));
32
+ }
33
+ /** RunState[] → 卡片模型(按 updatedAt 倒序;终态与进行中同列,由 status 区分)。 */
34
+ export function runCards(runs, opts = {}) {
35
+ const tail = opts.logTail ?? RUN_CARD_LOG_TAIL;
36
+ const now = opts.now ?? Date.now();
37
+ return [...runs]
38
+ .sort((a, b) => b.updatedAt - a.updatedAt)
39
+ .map((run) => {
40
+ const log = run.log ?? [];
41
+ const model = log.length > 0 ? buildImportLogModel(log) : null;
42
+ const running = run.status === 'running';
43
+ const awaitingDecision = running && run.pendingDecision === 'cancel';
44
+ // 终止请求已到达、但还没走到安全点:把「等了多久」算出来给界面用(见 CANCEL_STUCK_AFTER_MS)
45
+ const requestedAt = typeof run.cancelRequestedAt === 'number' ? run.cancelRequestedAt : null;
46
+ const waitedMs = requestedAt === null ? 0 : Math.max(0, now - requestedAt);
47
+ const cancelWait = requestedAt !== null && running && !awaitingDecision
48
+ ? { minutes: Math.floor(waitedMs / 60_000), stuck: waitedMs >= CANCEL_STUCK_AFTER_MS }
49
+ : null;
50
+ return {
51
+ runId: run.runId,
52
+ kind: run.kind,
53
+ kindLabelKey: `runs.kind.${run.kind}`,
54
+ status: run.status,
55
+ statusLabelKey: `runs.status.${run.status}`,
56
+ awaitingDecision,
57
+ // 只有导入实现了协作式取消;其余类型给禁用态(不给假按钮)。
58
+ // 已请求过终止就不再给按钮:再点也不会有新效果(run 级信号是幂等的),
59
+ // 留着一个亮着的主按钮只会让用户以为「多点几次就能好」。
60
+ canCancel: running && run.kind === 'import' && !awaitingDecision && cancelWait === null,
61
+ cancelWait,
62
+ // 「跳过当前插件」复用向导的**同一判据**(isSkippablePluginInstall:detail 带 plugin: 前缀),
63
+ // 绝不在这里另写一份「是不是插件安装项」的判断 —— 两份判据漂移会让两个页面对同一个 run
64
+ // 给出不同的可操作性。skipRequested 由运行中心自己的在途状态抑制(见 RunsCenter)。
65
+ canSkip: running && run.kind === 'import' && run.pendingDecision !== 'cancel'
66
+ && isSkippablePluginInstall(run.detail ?? undefined, running, false),
67
+ progress: {
68
+ labelKey: `runs.progress.${running ? 'running' : run.status === 'done' ? 'done' : 'failed'}`,
69
+ section: run.section,
70
+ item: run.item,
71
+ itemTotal: run.itemTotal,
72
+ detail: run.detail,
73
+ percent: percentOf(run.item, run.itemTotal),
74
+ },
75
+ counts: model === null ? null : model.counts,
76
+ problems: model === null ? 0 : model.problems,
77
+ logTail: log.slice(-Math.max(0, tail)),
78
+ updatedAt: run.updatedAt,
79
+ time: relativeTimeLabel(run.updatedAt, now),
80
+ };
81
+ });
82
+ }
83
+ /** 状态栏 / 抽屉徽章用的计数。 */
84
+ export function runSummary(runs) {
85
+ let running = 0;
86
+ let awaiting = 0;
87
+ for (const run of runs) {
88
+ if (run.status !== 'running')
89
+ continue;
90
+ running += 1;
91
+ if (run.pendingDecision === 'cancel')
92
+ awaiting += 1;
93
+ }
94
+ return { running, awaiting, total: runs.length };
95
+ }
96
+ /**
97
+ * 终止决策框模型。默认选项跟随用户既有的 rollbackOnError 偏好:
98
+ * 勾过「失败不回滚」→ 默认保留(与他的心智一致);否则默认回滚(安全侧)。
99
+ */
100
+ export function cancelDialogModel(run, opts) {
101
+ const applied = run.item;
102
+ const total = run.itemTotal;
103
+ return {
104
+ runId: run.runId,
105
+ kindLabelKey: `runs.kind.${run.kind}`,
106
+ appliedCount: applied,
107
+ totalCount: total,
108
+ pendingCount: applied !== null && total !== null ? Math.max(0, total - applied) : null,
109
+ defaultDecision: opts.defaultRollbackOnError ? 'rollback' : 'keep',
110
+ options: [
111
+ { id: 'rollback', labelKey: 'runs.cancel.rollback', descKey: 'runs.cancel.rollbackDesc' },
112
+ { id: 'keep', labelKey: 'runs.cancel.keep', descKey: 'runs.cancel.keepDesc' },
113
+ ],
114
+ };
115
+ }
116
+ /** 「已应用/未执行」的用户可读摘要参数(null → 壳层显示「未知」而不是编个数字)。 */
117
+ export function cancelCostParams(model) {
118
+ return {
119
+ applied: model.appliedCount === null ? '' : String(model.appliedCount),
120
+ pending: model.pendingCount === null ? '' : String(model.pendingCount),
121
+ };
122
+ }
123
+ //# sourceMappingURL=runs-view.js.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 自定义下拉(Select)的纯交互模型 —— src/client/common/Select.tsx 只做装配。
3
+ *
4
+ * 为什么需要它:原生 <select> 的外观由浏览器渲染(无法跟随 `--dsw-*` token,
5
+ * 深色主题下是白底系统控件、与工作台其余控件格格不入),故改为 div/button 自绘。
6
+ * 自绘换来的是**无障碍与键盘语义要自己扛**(ARIA combobox + listbox 模式),
7
+ * 所以可测的那部分(高亮项推导 / 上下移动 / 首尾跳转)全部落在这里,node 可测。
8
+ *
9
+ * 分层铁律:本文件零依赖(不 import react / 浏览器 API),client 与 node 单测都能用。
10
+ */
11
+ /** 下拉选项(value 一律字符串:调用点的数字枚举在边界处 String/Number 转换)。 */
12
+ export interface SelectOption {
13
+ /** 回传给 onChange 的值(唯一键) */
14
+ value: string;
15
+ /** 展示文本(已本地化;不在本层做 i18n) */
16
+ label: string;
17
+ /** 禁用项:键盘移动跳过、不可提交(灰显) */
18
+ disabled?: boolean;
19
+ }
20
+ /** 选中值对应的选项;值不在清单里(如远端快照已被清理)→ undefined。 */
21
+ export declare function selectedOption(options: SelectOption[], value: string): SelectOption | undefined;
22
+ /**
23
+ * 触发器上的显示文本。
24
+ *
25
+ * 值不在清单里时**不显示空**:先用 fallback(调用点给的占位文案),
26
+ * 仍为空则显示原值本身 —— 显示「一个不存在的 id」也比显示空白强
27
+ * (用户至少能看出当前选中的是什么;空白会被读成「没选中」)。
28
+ */
29
+ export declare function selectDisplayLabel(options: SelectOption[], value: string, fallback?: string): string;
30
+ /**
31
+ * 打开菜单时的初始高亮项:
32
+ * 选中项优先(它是用户下次最可能改动的锚点);选中项不存在或已禁用 → 第一个可用项。
33
+ * 全部禁用 / 空清单 → -1(表示没有高亮项)。
34
+ */
35
+ export declare function initialActiveIndex(options: SelectOption[], value: string): number;
36
+ /** 第一个可用项下标;没有 → -1。 */
37
+ export declare function firstEnabledIndex(options: SelectOption[]): number;
38
+ /** 最后一个可用项下标;没有 → -1。 */
39
+ export declare function lastEnabledIndex(options: SelectOption[]): number;
40
+ /**
41
+ * 从 from 出发按 delta(±1)移动高亮项。
42
+ *
43
+ * 三条语义(与原生 <select> 的键盘行为对齐):
44
+ * ① 禁用项跳过,不停留;
45
+ * ② 到达末尾**环绕**(原生 select 也环绕),环绕一整圈回到原位;
46
+ * ③ 当前无高亮(from < 0)时:delta > 0 → 第一个可用项,delta < 0 → 最后一个可用项。
47
+ * 全部项禁用 / 空清单 → -1。
48
+ */
49
+ export declare function stepActiveIndex(options: SelectOption[], from: number, delta: number): number;
50
+ /** Home/End:跳到第一个/最后一个可用项。 */
51
+ export declare function edgeActiveIndex(options: SelectOption[], edge: 'first' | 'last'): number;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 自定义下拉(Select)的纯交互模型 —— src/client/common/Select.tsx 只做装配。
3
+ *
4
+ * 为什么需要它:原生 <select> 的外观由浏览器渲染(无法跟随 `--dsw-*` token,
5
+ * 深色主题下是白底系统控件、与工作台其余控件格格不入),故改为 div/button 自绘。
6
+ * 自绘换来的是**无障碍与键盘语义要自己扛**(ARIA combobox + listbox 模式),
7
+ * 所以可测的那部分(高亮项推导 / 上下移动 / 首尾跳转)全部落在这里,node 可测。
8
+ *
9
+ * 分层铁律:本文件零依赖(不 import react / 浏览器 API),client 与 node 单测都能用。
10
+ */
11
+ /** 选中值对应的选项;值不在清单里(如远端快照已被清理)→ undefined。 */
12
+ export function selectedOption(options, value) {
13
+ for (const option of options) {
14
+ if (option.value === value)
15
+ return option;
16
+ }
17
+ return undefined;
18
+ }
19
+ /**
20
+ * 触发器上的显示文本。
21
+ *
22
+ * 值不在清单里时**不显示空**:先用 fallback(调用点给的占位文案),
23
+ * 仍为空则显示原值本身 —— 显示「一个不存在的 id」也比显示空白强
24
+ * (用户至少能看出当前选中的是什么;空白会被读成「没选中」)。
25
+ */
26
+ export function selectDisplayLabel(options, value, fallback = '') {
27
+ const hit = selectedOption(options, value);
28
+ if (hit !== undefined)
29
+ return hit.label;
30
+ return fallback !== '' ? fallback : value;
31
+ }
32
+ /**
33
+ * 打开菜单时的初始高亮项:
34
+ * 选中项优先(它是用户下次最可能改动的锚点);选中项不存在或已禁用 → 第一个可用项。
35
+ * 全部禁用 / 空清单 → -1(表示没有高亮项)。
36
+ */
37
+ export function initialActiveIndex(options, value) {
38
+ const selected = options.findIndex((option) => option.value === value);
39
+ if (selected >= 0 && options[selected]?.disabled !== true)
40
+ return selected;
41
+ return firstEnabledIndex(options);
42
+ }
43
+ /** 第一个可用项下标;没有 → -1。 */
44
+ export function firstEnabledIndex(options) {
45
+ return options.findIndex((option) => option.disabled !== true);
46
+ }
47
+ /** 最后一个可用项下标;没有 → -1。 */
48
+ export function lastEnabledIndex(options) {
49
+ for (let i = options.length - 1; i >= 0; i--) {
50
+ if (options[i]?.disabled !== true)
51
+ return i;
52
+ }
53
+ return -1;
54
+ }
55
+ /**
56
+ * 从 from 出发按 delta(±1)移动高亮项。
57
+ *
58
+ * 三条语义(与原生 <select> 的键盘行为对齐):
59
+ * ① 禁用项跳过,不停留;
60
+ * ② 到达末尾**环绕**(原生 select 也环绕),环绕一整圈回到原位;
61
+ * ③ 当前无高亮(from < 0)时:delta > 0 → 第一个可用项,delta < 0 → 最后一个可用项。
62
+ * 全部项禁用 / 空清单 → -1。
63
+ */
64
+ export function stepActiveIndex(options, from, delta) {
65
+ const total = options.length;
66
+ if (total === 0)
67
+ return -1;
68
+ if (firstEnabledIndex(options) < 0)
69
+ return -1;
70
+ if (from < 0)
71
+ return delta >= 0 ? firstEnabledIndex(options) : lastEnabledIndex(options);
72
+ let index = from;
73
+ for (let hop = 0; hop < total; hop++) {
74
+ index = (index + delta + total) % total;
75
+ if (options[index]?.disabled !== true)
76
+ return index;
77
+ }
78
+ return from;
79
+ }
80
+ /** Home/End:跳到第一个/最后一个可用项。 */
81
+ export function edgeActiveIndex(options, edge) {
82
+ return edge === 'first' ? firstEnabledIndex(options) : lastEnabledIndex(options);
83
+ }
84
+ //# sourceMappingURL=select-model.js.map
@@ -32,6 +32,30 @@ export interface SelectionUnit {
32
32
  * 与既有分区(skills / pluginFiles / …)的表现完全一致。纯展示层,不参与勾选契约。
33
33
  */
34
34
  group?: string;
35
+ /**
36
+ * 本单元拥有的会话 id(工作区单元专用;issue #45)。
37
+ *
38
+ * 用途:勾选联动 —— 勾了会话就自动勾上它所属的工作区,取消工作区就取消它的会话。
39
+ * 取值 = 工作区记录 sessionIds **原样**(DSH 一律写 `session-<uuid>`);与会话单元 id 的末段(目录名,
40
+ * 可能是 `session-<uuid>` 也可能是裸 `<uuid>`)用 `sessionIdKey()` 归一化后配对 —— 两种形态直接做
41
+ * 字符串比较会失配(真机实测 546/570 会话因此联不上工作区)。
42
+ */
43
+ sessionIds?: string[];
44
+ /**
45
+ * 本单元 **path 的 cwd 目录键**(工作区单元专用;issue #45 勾选联动的第二判据)。
46
+ *
47
+ * 为什么需要它:DSH 注册表 `sessionIds` 覆盖率很低(真机实测一次可选择的 570 条会话里只有 23 条在
48
+ * 里面),而 DSH 自己按「会话 cwd 的目录键 == 工作区 path 的目录键」会话显示在工作区下。勾选联动若
49
+ * 只认 sessionIds,就会出现「界面明明把对话挂在某工作区下、勾它却不联动」。
50
+ */
51
+ projectKey?: string;
52
+ /**
53
+ * 父对话的会话裸键(**只有子代理会话才有值**;宿主在 /export-preview 里按 DSH 的会话存储关系下发)。
54
+ *
55
+ * 用途:「父 ↔ 子代理会话」勾选联动(勾父自动勾上它的子代理会话、勾子自动带上父)。没有这个字段
56
+ * 时联动整段失效 —— 界面勾了什么与包里装了什么就会不一致(真机事故:勾 2 条、包里 41 条)。
57
+ */
58
+ parentSessionId?: string;
35
59
  }
36
60
  /** 一个分区及其可勾选单元;units 为空 = 本分区不可细分(UI 只给整体开关)。 */
37
61
  export interface SelectionSection {
@@ -85,7 +109,7 @@ export declare function visibleUnits(units: readonly SelectionUnit[], showAll: b
85
109
  * 纯函数(node 可测):只改显示文本,不改数据;完整值由调用方放进 `title`。
86
110
  * 与 CSS `text-overflow: ellipsis` 叠加使用(CSS 兜底实际容器宽度)。
87
111
  */
88
- export declare function middleEllipsis(text: string, max?: number): string;
112
+ export declare function tailWeightedEllipsis(text: string, max?: number): string;
89
113
  /** 缺省:全选(excluded 为空 = 没有任何例外)。 */
90
114
  export declare function defaultSelection(sections: SectionId[]): Selection;
91
115
  /** 把 /export-preview 的分区响应映射为选择模型的分区(宿主类型 → UI 模型,显式转换一次)。 */
@@ -110,11 +134,27 @@ export interface UnitGroup {
110
134
  * - 无分组的单元单独成一组,排在**最前**(它们通常是该分区的公共条目)。
111
135
  */
112
136
  export declare function groupUnits(units: readonly SelectionUnit[]): UnitGroup[];
137
+ /**
138
+ * 分组内已勾选单元数。
139
+ *
140
+ * **必须先看所属分区**(与 sectionPickState / selectedUnitCount 同一口径):稀疏表示下
141
+ * 「不在 excluded 里」只说明「没有显式排除」,分区整体没勾时同样成立 —— 只看 excluded 会渲染出
142
+ * 「分区 已选 0/13、13 个子项却全部打勾」的自相矛盾(用户实测:点「全不选」后子项仍然勾着)。
143
+ */
144
+ export declare function groupSelectedCount(sel: Selection, group: UnitGroup, section: SectionId): number;
113
145
  /** 二级分组三态(与分区三态同一套口径;分组只是分区内的视图)。 */
114
- export declare function groupPickState(sel: Selection, group: UnitGroup): SectionPickState;
146
+ export declare function groupPickState(sel: Selection, group: UnitGroup, section: SectionId): SectionPickState;
115
147
  /** 分组级勾选:整组一起选/取消(并保证其所属分区处于勾选状态)。 */
116
148
  export declare function toggleUnitGroup(sel: Selection, node: SelectionSection, group: UnitGroup, checked: boolean): Selection;
117
149
  export declare function isUnitSelected(sel: Selection, unitId: string): boolean;
150
+ /**
151
+ * 单元勾选态(**渲染层唯一口径**)。
152
+ *
153
+ * 与 `isUnitSelected` 的差别:后者是稀疏表示下的「未显式排除」,分区整体没勾时它依然为 true,
154
+ * 直接拿它渲染勾选框会出现「分区计数 0/13 + 13 个子项全打勾」的自相矛盾(用户实测:
155
+ * 点「全不选」后分区标题变成未勾选,子项却还是勾的)。渲染必须先看分区,再看排除集。
156
+ */
157
+ export declare function unitChecked(sel: Selection, section: SectionId, unitId: string): boolean;
118
158
  /** 分区三态:不可细分分区退回整体开关。 */
119
159
  export declare function sectionPickState(sel: Selection, node: SelectionSection): SectionPickState;
120
160
  /** 已勾选条目数(不可细分分区用宿主的 count 兜底)。 */
@@ -155,6 +195,71 @@ export interface ExportRequestPlan {
155
195
  */
156
196
  export declare function buildExportRequest(sel: Selection, nodes: SelectionSection[]): ExportRequestPlan;
157
197
  /** 搜索过滤:按分区名/单元名/副标题匹配;命中的分区保留其全部单元(便于继续勾选同分区其它项)。 */
198
+ /** 会话单元 id(sessions:<projectKey>/<sessionId>)→ sessionId;非会话单元 → undefined。 */
199
+ /**
200
+ * 会话单元 id(`sessions:<项目键>/<目录名>`)→ **cwd 目录键**(项目键);非会话单元 / 形状不符 → undefined。
201
+ *
202
+ * 用途:勾选联动的第二判据 —— 与工作区单元的 `projectKey` 比较,口径与 DSH「把会话显示在工作区下」一致。
203
+ */
204
+ /**
205
+ * 工作区单元的 cwd 目录键:宿主直出的 `projectKey` 优先,缺省时用 `detail`(= 工作区绝对路径)现算。
206
+ *
207
+ * 为什么要有兜底:`projectKey` 是后加的宿主字段,**旧宿主**(未重启的 DSH 进程)不会回传它,而工作区单元的
208
+ * `detail` 一直就是绝对路径。只在 detail 形如绝对路径时才现算 —— 计划项的 detail 是描述文案
209
+ * (如 `current={...} imported={...}`),绝不会被误当成路径。
210
+ */
211
+ export declare function workspaceProjectKeyOf(unit: {
212
+ projectKey?: string;
213
+ detail?: string;
214
+ }): string | undefined;
215
+ export declare function sessionProjectKeyOfUnit(unitId: string): string | undefined;
216
+ export declare function sessionIdOfUnit(unitId: string): string | undefined;
217
+ /** 本次勾选动作(哪个单元、点完后是勾还是取消)—— 父/子两条规则必须按动作方向定夺。 */
218
+ export interface SessionParentChange {
219
+ unitId: string;
220
+ checked: boolean;
221
+ }
222
+ /**
223
+ * 父对话 ↔ 子代理会话联动。三件事,全部由**用户勾选的父对话**这一侧驱动:
224
+ * - **勾父** → 连带勾上它的子代理会话(传递:子会话自己还有子会话也一起带);
225
+ * - **勾子** → 连带勾上它的父对话(父链向上传递)—— 与导出的「向上补父对话」同口径,
226
+ * 否则界面勾了子、包里却多出父,勾选与包内容不一致;
227
+ * - **取消父** → 连带取消它的子代理会话;**取消子** → 只取消这一条(父对话留着)。
228
+ *
229
+ * 为什么必须按动作方向(而不是无脑闭包):两条规则会互相抵消 —— 取消父之后若还跑
230
+ * 「已勾选的子 ⇒ 勾上父」,父立刻被勾回来(与工作区联动完全同一个坑,见 focus 的注释)。
231
+ *
232
+ * @param change 本次点击的单元与状态;缺省(分区/分组/全选等批量动作、清单到货后补跑)
233
+ * 只做**正向闭包**:已勾选的父把子补齐、已勾选的子把父补齐,绝不取消任何东西。
234
+ */
235
+ export declare function applySessionParentCoupling(sel: Selection, nodes: SelectionSection[], change?: SessionParentChange): Selection;
236
+ /**
237
+ * 会话 ↔ 工作区联动(issue #45):① 勾了会话 → 自动勾上「拥有它的工作区」;② 取消工作区 → 它的会话也取消。
238
+ *
239
+ * 为什么写在选择模型层:导出页与导入向导共用同一个 ContentPicker,规则写一次两端行为一致
240
+ * (「同样的勾选框不一样的行为」是本仓库明确要消灭的状态)。只按 sessionIds 匹配、不翻译路径:
241
+ * 跨机时工作区路径与会话 cwd 都是源机形态,按路径匹配不可靠。无 sessionIds 的单元一律不参与联动(不猜)。
242
+ */
243
+ /**
244
+ * 本次勾选动作的方向:① 只勾会话 → 带上其工作区;② 只动工作区 → 撤销未勾工作区的会话;both = 两条都跑。
245
+ *
246
+ * 为什么必须有方向:两条规则在「会话勾着、它的工作区却被取消」这种状态下会互相抵消(谁后跑谁翻盘)。
247
+ * 用动作方向定夺,行为才是确定的:取消工作区一定连带取消它的会话,勾上会话一定带上它的工作区。
248
+ */
249
+ export type SessionWorkspaceCouplingFocus = 'sessions' | 'workspaces' | 'both';
250
+ /**
251
+ * 会话↔工作区联动需要的**伙伴分区**(导出页的清单是逐分区惰性拉的)。
252
+ *
253
+ * 联动的唯一数据源是**工作区单元上的 `sessionIds`**:勾了会话但工作区清单还没读出来 → 没有 sessionIds 可读 →
254
+ * 联动**静默失效**(用户真机复验:取消工作区后再勾一个对话,对应工作区不会回来)。所以只要勾了会话,就
255
+ * 必须把工作区清单一起读出来;工作区记录是纯元数据(每条几十字节),这点代价可以忽略。
256
+ *
257
+ * 反向刻意不加(勾着工作区、会话清单没读):那种状态下会话单元根本不在选择器里,没有需要联动的会话。
258
+ */
259
+ export declare function couplingInventorySections(sections: readonly SectionId[]): SectionId[];
260
+ /** 两份选择是否等价(按集合;用于「清单到货后补一次联动」避免无谓写库 / 自激渲染)。 */
261
+ export declare function sameSelection(a: Selection, b: Selection): boolean;
262
+ export declare function applySessionWorkspaceCoupling(sel: Selection, nodes: SelectionSection[], focus?: SessionWorkspaceCouplingFocus): Selection;
158
263
  export declare function filterSections(nodes: SelectionSection[], query: string): SelectionSection[];
159
264
  /**
160
265
  * 高风险分区(严格分层信任的默认:须用户显式勾选)。
@@ -190,6 +295,17 @@ export declare function defaultSelectionFromPlan(plan: ImportPlan, opts?: {
190
295
  export declare function isPlanItemExcluded(item: PlanItem, selection: Selection): boolean;
191
296
  /** 被用户排除的计划项(供报告如实展示「N 项未导入」)。 */
192
297
  export declare function excludedPlanItems(plan: ImportPlan, selection: Selection): PlanItem[];
298
+ /**
299
+ * 本次导入**确实会写盘**的分区集合。
300
+ *
301
+ * 用途:路径映射阶段的过滤依据 —— PathIssue 只说明「这条路径来自哪个分区」,
302
+ * 还需要「这个分区这次会不会写」才能判断该不该问用户(工作区全为 Skip 或整个分区被取消时,
303
+ * 为它填映射是纯粹的无效劳动)。
304
+ *
305
+ * 入参必须是**已裁剪**的子计划(buildSelectedPlan 的产物):本函数只按 kind 判定,
306
+ * 不再重复做选择语义,避免出现第二套「哪些项会被导入」的判定。
307
+ */
308
+ export declare function planWriteSections(plan: ImportPlan): Set<SectionId>;
193
309
  /**
194
310
  * 选择是否**留下了**计划中的任何一项(UI-05「全不选」守卫)。
195
311
  *