dsh-config-manager 0.1.69 → 0.1.71

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 (719) hide show
  1. package/README.md +59 -18
  2. package/README.zh-CN.md +29 -9
  3. package/lib/adapters/credentials.js +6 -3
  4. package/lib/adapters/file-collection.d.ts +21 -1
  5. package/lib/adapters/file-collection.js +32 -8
  6. package/lib/adapters/mcp.d.ts +12 -2
  7. package/lib/adapters/mcp.js +30 -13
  8. package/lib/adapters/plugin-files.d.ts +8 -0
  9. package/lib/adapters/plugin-files.js +36 -4
  10. package/lib/adapters/plugins.d.ts +28 -2
  11. package/lib/adapters/plugins.js +204 -55
  12. package/lib/adapters/prompts.d.ts +12 -2
  13. package/lib/adapters/prompts.js +34 -19
  14. package/lib/adapters/providers.js +5 -4
  15. package/lib/adapters/self.d.ts +20 -0
  16. package/lib/adapters/self.js +73 -0
  17. package/lib/adapters/sessions.d.ts +8 -0
  18. package/lib/adapters/sessions.js +47 -1
  19. package/lib/adapters/settings.d.ts +21 -3
  20. package/lib/adapters/settings.js +44 -7
  21. package/lib/adapters/skills.d.ts +36 -4
  22. package/lib/adapters/skills.js +222 -3
  23. package/lib/adapters/test-helpers.d.ts +31 -2
  24. package/lib/adapters/test-helpers.js +42 -5
  25. package/lib/adapters/workspaces.js +97 -5
  26. package/lib/cli/actions.d.ts +50 -3
  27. package/lib/cli/actions.js +103 -11
  28. package/lib/cli/help.d.ts +65 -0
  29. package/lib/cli/help.js +517 -0
  30. package/lib/cli/import-source.d.ts +32 -0
  31. package/lib/cli/import-source.js +178 -0
  32. package/lib/cli/index.d.ts +18 -2
  33. package/lib/cli/index.js +118 -90
  34. package/lib/cli/web/client-script.d.ts +8 -0
  35. package/lib/cli/web/client-script.js +11 -0
  36. package/lib/cli/web/http.d.ts +5 -0
  37. package/lib/cli/web/http.js +2 -1
  38. package/lib/cli/web/page.d.ts +16 -6
  39. package/lib/cli/web/page.js +102 -25
  40. package/lib/cli/web/routes.js +24 -5
  41. package/lib/cli/web/server.js +23 -2
  42. package/lib/client.d.ts +1019 -36
  43. package/lib/client.js +5522 -1564
  44. package/lib/core/analyzer.d.ts +15 -1
  45. package/lib/core/analyzer.js +114 -13
  46. package/lib/core/backup-plan.js +17 -1
  47. package/lib/core/backup.d.ts +8 -0
  48. package/lib/core/backup.js +28 -6
  49. package/lib/core/cache-cleaner.d.ts +10 -0
  50. package/lib/core/cache-cleaner.js +63 -22
  51. package/lib/core/checkpoint.d.ts +362 -0
  52. package/lib/core/checkpoint.js +1309 -0
  53. package/lib/core/journal.d.ts +37 -0
  54. package/lib/core/journal.js +69 -0
  55. package/lib/core/messages.d.ts +13 -0
  56. package/lib/core/messages.js +26 -0
  57. package/lib/core/model-tools.js +3 -0
  58. package/lib/core/patch-layers.d.ts +83 -0
  59. package/lib/core/patch-layers.js +110 -0
  60. package/lib/core/restore.d.ts +21 -0
  61. package/lib/core/restore.js +37 -3
  62. package/lib/core/rollback.js +6 -3
  63. package/lib/core/self-update.d.ts +52 -0
  64. package/lib/core/self-update.js +70 -0
  65. package/lib/core/session-health.d.ts +29 -5
  66. package/lib/core/session-health.js +28 -3
  67. package/lib/core/types.d.ts +126 -0
  68. package/lib/foreign/antigravity.d.ts +28 -0
  69. package/lib/foreign/antigravity.js +157 -0
  70. package/lib/foreign/bundle.d.ts +25 -0
  71. package/lib/foreign/bundle.js +79 -0
  72. package/lib/foreign/chatgpt.d.ts +6 -0
  73. package/lib/foreign/chatgpt.js +25 -0
  74. package/lib/foreign/claude-code.d.ts +3 -0
  75. package/lib/foreign/claude-code.js +85 -0
  76. package/lib/foreign/claude-sessions.d.ts +108 -0
  77. package/lib/foreign/claude-sessions.js +299 -0
  78. package/lib/foreign/cline.d.ts +6 -0
  79. package/lib/foreign/cline.js +27 -0
  80. package/lib/foreign/codex.d.ts +83 -0
  81. package/lib/foreign/codex.js +348 -0
  82. package/lib/foreign/continue.d.ts +9 -0
  83. package/lib/foreign/continue.js +65 -0
  84. package/lib/foreign/copilot.d.ts +39 -0
  85. package/lib/foreign/copilot.js +95 -0
  86. package/lib/foreign/crush.d.ts +11 -0
  87. package/lib/foreign/crush.js +57 -0
  88. package/lib/foreign/cursor.d.ts +87 -0
  89. package/lib/foreign/cursor.js +171 -0
  90. package/lib/foreign/dsh.d.ts +30 -0
  91. package/lib/foreign/dsh.js +101 -0
  92. package/lib/foreign/dsh4.d.ts +3 -0
  93. package/lib/foreign/dsh4.js +13 -0
  94. package/lib/foreign/gemini.d.ts +7 -0
  95. package/lib/foreign/gemini.js +44 -0
  96. package/lib/foreign/goose.d.ts +11 -0
  97. package/lib/foreign/goose.js +53 -0
  98. package/lib/foreign/grokbuild.d.ts +8 -0
  99. package/lib/foreign/grokbuild.js +25 -0
  100. package/lib/foreign/hermes.d.ts +2 -0
  101. package/lib/foreign/hermes.js +78 -0
  102. package/lib/foreign/kernel.d.ts +132 -0
  103. package/lib/foreign/kernel.js +569 -0
  104. package/lib/foreign/kilocode.d.ts +11 -0
  105. package/lib/foreign/kilocode.js +51 -0
  106. package/lib/foreign/kimi.d.ts +7 -0
  107. package/lib/foreign/kimi.js +47 -0
  108. package/lib/foreign/mimocode.d.ts +11 -0
  109. package/lib/foreign/mimocode.js +51 -0
  110. package/lib/foreign/openclaw.d.ts +7 -0
  111. package/lib/foreign/openclaw.js +46 -0
  112. package/lib/foreign/opencode.d.ts +11 -0
  113. package/lib/foreign/opencode.js +52 -0
  114. package/lib/foreign/pi.d.ts +7 -0
  115. package/lib/foreign/pi.js +48 -0
  116. package/lib/foreign/platform-paths.d.ts +66 -0
  117. package/lib/foreign/platform-paths.js +141 -0
  118. package/lib/foreign/qoder.d.ts +10 -0
  119. package/lib/foreign/qoder.js +49 -0
  120. package/lib/foreign/qwen.d.ts +7 -0
  121. package/lib/foreign/qwen.js +45 -0
  122. package/lib/foreign/read-antigravity.d.ts +57 -0
  123. package/lib/foreign/read-antigravity.js +504 -0
  124. package/lib/foreign/read-chatgpt.d.ts +20 -0
  125. package/lib/foreign/read-chatgpt.js +509 -0
  126. package/lib/foreign/read-claude-code.d.ts +24 -0
  127. package/lib/foreign/read-claude-code.js +234 -0
  128. package/lib/foreign/read-cline.d.ts +53 -0
  129. package/lib/foreign/read-cline.js +479 -0
  130. package/lib/foreign/read-codex.d.ts +75 -0
  131. package/lib/foreign/read-codex.js +842 -0
  132. package/lib/foreign/read-continue.d.ts +87 -0
  133. package/lib/foreign/read-continue.js +489 -0
  134. package/lib/foreign/read-copilot.d.ts +42 -0
  135. package/lib/foreign/read-copilot.js +267 -0
  136. package/lib/foreign/read-crush.d.ts +54 -0
  137. package/lib/foreign/read-crush.js +478 -0
  138. package/lib/foreign/read-cursor.d.ts +141 -0
  139. package/lib/foreign/read-cursor.js +705 -0
  140. package/lib/foreign/read-dsh.d.ts +43 -0
  141. package/lib/foreign/read-dsh.js +205 -0
  142. package/lib/foreign/read-dsh4.d.ts +14 -0
  143. package/lib/foreign/read-dsh4.js +27 -0
  144. package/lib/foreign/read-gemini.d.ts +55 -0
  145. package/lib/foreign/read-gemini.js +273 -0
  146. package/lib/foreign/read-goose.d.ts +25 -0
  147. package/lib/foreign/read-goose.js +322 -0
  148. package/lib/foreign/read-grokbuild.d.ts +34 -0
  149. package/lib/foreign/read-grokbuild.js +455 -0
  150. package/lib/foreign/read-hermes.d.ts +74 -0
  151. package/lib/foreign/read-hermes.js +690 -0
  152. package/lib/foreign/read-kilocode.d.ts +17 -0
  153. package/lib/foreign/read-kilocode.js +46 -0
  154. package/lib/foreign/read-kimi.d.ts +63 -0
  155. package/lib/foreign/read-kimi.js +719 -0
  156. package/lib/foreign/read-mimocode.d.ts +12 -0
  157. package/lib/foreign/read-mimocode.js +56 -0
  158. package/lib/foreign/read-openclaw.d.ts +51 -0
  159. package/lib/foreign/read-openclaw.js +445 -0
  160. package/lib/foreign/read-opencode.d.ts +150 -0
  161. package/lib/foreign/read-opencode.js +896 -0
  162. package/lib/foreign/read-pi.d.ts +56 -0
  163. package/lib/foreign/read-pi.js +411 -0
  164. package/lib/foreign/read-qoder.d.ts +54 -0
  165. package/lib/foreign/read-qoder.js +317 -0
  166. package/lib/foreign/read-qwen.d.ts +40 -0
  167. package/lib/foreign/read-qwen.js +326 -0
  168. package/lib/foreign/read-reasonix.d.ts +66 -0
  169. package/lib/foreign/read-reasonix.js +537 -0
  170. package/lib/foreign/read-teleagent.d.ts +15 -0
  171. package/lib/foreign/read-teleagent.js +68 -0
  172. package/lib/foreign/read-trae.d.ts +67 -0
  173. package/lib/foreign/read-trae.js +492 -0
  174. package/lib/foreign/read-vibe.d.ts +28 -0
  175. package/lib/foreign/read-vibe.js +364 -0
  176. package/lib/foreign/read-workbuddy.d.ts +49 -0
  177. package/lib/foreign/read-workbuddy.js +346 -0
  178. package/lib/foreign/read-zcode.d.ts +12 -0
  179. package/lib/foreign/read-zcode.js +38 -0
  180. package/lib/foreign/read-zed.d.ts +30 -0
  181. package/lib/foreign/read-zed.js +473 -0
  182. package/lib/foreign/reasonix.d.ts +6 -0
  183. package/lib/foreign/reasonix.js +20 -0
  184. package/lib/foreign/registry.d.ts +196 -0
  185. package/lib/foreign/registry.js +573 -0
  186. package/lib/foreign/session-ir.d.ts +186 -0
  187. package/lib/foreign/session-ir.js +427 -0
  188. package/lib/foreign/session-read.d.ts +102 -0
  189. package/lib/foreign/session-read.js +254 -0
  190. package/lib/foreign/session-source.d.ts +216 -0
  191. package/lib/foreign/session-source.js +453 -0
  192. package/lib/foreign/source-modules.d.ts +45 -0
  193. package/lib/foreign/source-modules.js +52 -0
  194. package/lib/foreign/sqlite.d.ts +61 -0
  195. package/lib/foreign/sqlite.js +404 -0
  196. package/lib/foreign/teleagent.d.ts +11 -0
  197. package/lib/foreign/teleagent.js +53 -0
  198. package/lib/foreign/trae.d.ts +6 -0
  199. package/lib/foreign/trae.js +20 -0
  200. package/lib/foreign/truth-table.d.ts +62 -0
  201. package/lib/foreign/truth-table.js +333 -0
  202. package/lib/foreign/types.d.ts +272 -0
  203. package/lib/foreign/types.js +2 -0
  204. package/lib/foreign/vibe.d.ts +6 -0
  205. package/lib/foreign/vibe.js +23 -0
  206. package/lib/foreign/workbuddy.d.ts +7 -0
  207. package/lib/foreign/workbuddy.js +45 -0
  208. package/lib/foreign/zcode.d.ts +11 -0
  209. package/lib/foreign/zcode.js +51 -0
  210. package/lib/foreign/zed.d.ts +11 -0
  211. package/lib/foreign/zed.js +53 -0
  212. package/lib/index.d.ts +154 -13
  213. package/lib/index.js +661 -84
  214. package/lib/market/github-repos.d.ts +28 -0
  215. package/lib/market/github-repos.js +43 -1
  216. package/lib/market/types.d.ts +1 -3
  217. package/lib/profiles/dsh-profile-manager.d.ts +54 -2
  218. package/lib/profiles/dsh-profile-manager.js +182 -11
  219. package/lib/profiles/dsh-profile-shared.d.ts +47 -0
  220. package/lib/profiles/dsh-profile-shared.js +15 -1
  221. package/lib/profiles/index.d.ts +1 -1
  222. package/lib/profiles/index.js +1 -1
  223. package/lib/routes/consult.js +51 -5
  224. package/lib/routes/foreign-context.d.ts +43 -0
  225. package/lib/routes/foreign-context.js +65 -0
  226. package/lib/routes/foreign.d.ts +11 -0
  227. package/lib/routes/foreign.js +208 -0
  228. package/lib/routes/import.d.ts +8 -0
  229. package/lib/routes/import.js +22 -1
  230. package/lib/routes/index.d.ts +2 -2
  231. package/lib/routes/index.js +4 -0
  232. package/lib/routes/kit.d.ts +4 -3
  233. package/lib/routes/market.js +0 -1
  234. package/lib/routes/prefs.d.ts +2 -1
  235. package/lib/routes/prefs.js +14 -2
  236. package/lib/routes/recovery.js +363 -3
  237. package/lib/routes/session-export.d.ts +117 -0
  238. package/lib/routes/session-export.js +270 -0
  239. package/lib/routes/snapshots.js +12 -2
  240. package/lib/routes/sync.js +82 -10
  241. package/lib/security/redaction.d.ts +14 -0
  242. package/lib/security/redaction.js +24 -9
  243. package/lib/security/secret-scanner.d.ts +27 -0
  244. package/lib/security/secret-scanner.js +54 -1
  245. package/lib/sync/autosync-scheduler.d.ts +13 -1
  246. package/lib/sync/autosync-scheduler.js +49 -26
  247. package/lib/sync/backup-files.js +6 -0
  248. package/lib/sync/gist/gist-transport.d.ts +91 -0
  249. package/lib/sync/gist/gist-transport.js +406 -0
  250. package/lib/sync/gist/messages.d.ts +22 -0
  251. package/lib/sync/gist/messages.js +46 -0
  252. package/lib/sync/s3/messages.d.ts +53 -0
  253. package/lib/sync/s3/messages.js +83 -0
  254. package/lib/sync/s3/s3-providers.d.ts +78 -0
  255. package/lib/sync/s3/s3-providers.js +156 -0
  256. package/lib/sync/s3/s3-transport.d.ts +126 -0
  257. package/lib/sync/s3/s3-transport.js +551 -0
  258. package/lib/sync/s3/sigv4.d.ts +107 -0
  259. package/lib/sync/s3/sigv4.js +196 -0
  260. package/lib/sync/sync-config.d.ts +250 -4
  261. package/lib/sync/sync-config.js +658 -57
  262. package/lib/sync/sync-engine.d.ts +1 -0
  263. package/lib/sync/sync-engine.js +4 -2
  264. package/lib/sync/sync-history.d.ts +1 -1
  265. package/lib/sync/webdav/webdav-transport.d.ts +31 -3
  266. package/lib/sync/webdav/webdav-transport.js +102 -18
  267. package/lib/ui/commands.d.ts +4 -3
  268. package/lib/ui/commands.js +41 -9
  269. package/lib/ui/disk-usage-view.d.ts +44 -0
  270. package/lib/ui/disk-usage-view.js +55 -1
  271. package/lib/ui/dsh-profiles-view.d.ts +53 -0
  272. package/lib/ui/dsh-profiles-view.js +56 -0
  273. package/lib/ui/foreign-view.d.ts +162 -0
  274. package/lib/ui/foreign-view.js +225 -0
  275. package/lib/ui/i18n.d.ts +77 -0
  276. package/lib/ui/i18n.js +164 -0
  277. package/lib/ui/session-inventory-view.d.ts +106 -0
  278. package/lib/ui/session-inventory-view.js +58 -0
  279. package/lib/ui/session-layout-view.d.ts +265 -0
  280. package/lib/ui/session-layout-view.js +271 -0
  281. package/lib/ui/sync-repository-picker.d.ts +98 -0
  282. package/lib/ui/sync-repository-picker.js +145 -0
  283. package/lib/ui/sync-settings-view.d.ts +143 -4
  284. package/lib/ui/sync-settings-view.js +231 -7
  285. package/lib/ui/types.d.ts +47 -0
  286. package/lib/utils/asar-read.d.ts +113 -0
  287. package/lib/utils/asar-read.js +523 -0
  288. package/lib/utils/env-lock.js +7 -2
  289. package/lib/utils/patch-yaml.d.ts +25 -0
  290. package/lib/utils/patch-yaml.js +62 -0
  291. package/lib/utils/paths.d.ts +10 -1
  292. package/lib/utils/paths.js +24 -3
  293. package/lib/utils/recursive-walk.js +12 -13
  294. package/lib/utils/session-format.d.ts +35 -2
  295. package/lib/utils/session-format.js +131 -5
  296. package/lib/utils/session-health-scan.d.ts +7 -1
  297. package/lib/utils/session-health-scan.js +369 -12
  298. package/lib/utils/session-layout-repair-service.d.ts +98 -0
  299. package/lib/utils/session-layout-repair-service.js +457 -0
  300. package/lib/utils/session-log-repair.d.ts +27 -0
  301. package/lib/utils/session-log-repair.js +233 -62
  302. package/lib/utils/session-repair-service.d.ts +32 -3
  303. package/lib/utils/session-repair-service.js +148 -9
  304. package/lib/utils/session-row-facts.d.ts +73 -0
  305. package/lib/utils/session-row-facts.js +182 -0
  306. package/lib/utils/session-verify.d.ts +118 -0
  307. package/lib/utils/session-verify.js +705 -0
  308. package/lib/utils/shared-constants.d.ts +59 -0
  309. package/lib/utils/shared-constants.js +71 -0
  310. package/package.json +1 -1
  311. package/src/adapters/credentials.test.ts +27 -0
  312. package/src/adapters/credentials.ts +6 -3
  313. package/src/adapters/file-collection.ts +33 -8
  314. package/src/adapters/files.test.ts +222 -0
  315. package/src/adapters/mcp.test.ts +43 -0
  316. package/src/adapters/mcp.ts +37 -14
  317. package/src/adapters/plugin-files.ts +34 -4
  318. package/src/adapters/plugins.test.ts +241 -0
  319. package/src/adapters/plugins.ts +201 -53
  320. package/src/adapters/prompts.test.ts +49 -0
  321. package/src/adapters/prompts.ts +41 -20
  322. package/src/adapters/providers.test.ts +16 -0
  323. package/src/adapters/providers.ts +4 -3
  324. package/src/adapters/self.test.ts +77 -0
  325. package/src/adapters/self.ts +75 -0
  326. package/src/adapters/sessions.test.ts +77 -0
  327. package/src/adapters/sessions.ts +47 -1
  328. package/src/adapters/settings.test.ts +78 -0
  329. package/src/adapters/settings.ts +42 -6
  330. package/src/adapters/skills.ts +221 -3
  331. package/src/adapters/test-helpers.ts +43 -5
  332. package/src/adapters/workspaces-dup-id.test.ts +144 -0
  333. package/src/adapters/workspaces.test.ts +10 -0
  334. package/src/adapters/workspaces.ts +97 -5
  335. package/src/cli/actions.test.ts +117 -2
  336. package/src/cli/actions.ts +132 -12
  337. package/src/cli/help.ts +570 -0
  338. package/src/cli/import-source.test.ts +193 -0
  339. package/src/cli/import-source.ts +229 -0
  340. package/src/cli/index.ts +129 -94
  341. package/src/cli/web/client-script.ts +12 -0
  342. package/src/cli/web/console-fixes.test.ts +36 -0
  343. package/src/cli/web/http.ts +7 -1
  344. package/src/cli/web/page.ts +108 -25
  345. package/src/cli/web/routes.ts +23 -4
  346. package/src/cli/web/script-anchor.test.ts +33 -0
  347. package/src/cli/web/server.ts +28 -2
  348. package/src/cli/web/web.test.ts +447 -4
  349. package/src/client/ConfigManagerSection.tsx +118 -63
  350. package/src/client/about/AboutPanel.tsx +42 -3
  351. package/src/client/about/PluginUpdateDialog.tsx +185 -0
  352. package/src/client/about/ReleaseBody.tsx +144 -0
  353. package/src/client/about/ReleaseNotesDialog.tsx +3 -130
  354. package/src/client/about/about-view.ts +7 -5
  355. package/src/client/about/plugin-update-view.test.ts +25 -0
  356. package/src/client/about/plugin-update-view.ts +26 -0
  357. package/src/client/about/release-notes-view.test.ts +33 -0
  358. package/src/client/about/release-notes-view.ts +34 -0
  359. package/src/client/api.ts +145 -1
  360. package/src/client/common/Icon.tsx +3 -0
  361. package/src/client/common/RunsCenter.tsx +8 -2
  362. package/src/client/common/Select.tsx +9 -2
  363. package/src/client/common/info-hint-guard.test.ts +63 -18
  364. package/src/client/common/plan-text-redaction.test.ts +193 -8
  365. package/src/client/common/routes.ts +15 -0
  366. package/src/client/config-manager.module.css +506 -354
  367. package/src/client/css-scale-guard.test.ts +122 -0
  368. package/src/client/css-token-guard.test.ts +82 -0
  369. package/src/client/environment/EnvironmentPanel.tsx +45 -16
  370. package/src/client/{snapshots → environment/maintenance}/DiskUsageCard.tsx +21 -17
  371. package/src/client/environment/profiles-incomplete-ui.test.ts +103 -0
  372. package/src/client/export/ExportView.tsx +9 -2
  373. package/src/client/{snapshots → home}/BackupScheduleCard.tsx +13 -4
  374. package/src/client/home/HomePanel.tsx +69 -157
  375. package/src/client/icon-layer-guard.test.ts +1 -1
  376. package/src/client/import/ForeignImportView.tsx +318 -0
  377. package/src/client/import/ImportWizardView.tsx +3 -1
  378. package/src/client/import/foreign-import-view-guard.test.ts +195 -0
  379. package/src/client/import/import-file-select.test.ts +6 -6
  380. package/src/client/import/import-wizard-steps.tsx +17 -1
  381. package/src/client/import/use-import-wizard-controller.ts +3 -3
  382. package/src/client/index.ts +1 -1
  383. package/src/client/library/LibraryActions.tsx +31 -34
  384. package/src/client/library/LibraryPanel.tsx +4 -1
  385. package/src/client/{snapshots → library}/RestorePlanView.tsx +1 -1
  386. package/src/client/{snapshots → library}/files-layout.test.ts +2 -2
  387. package/src/client/{snapshots → library}/report-dismiss.test.ts +26 -3
  388. package/src/client/library/task-panel-visibility.test.ts +25 -17
  389. package/src/client/locales.ts +278 -36
  390. package/src/client/lucide-icons.d.ts +1 -0
  391. package/src/client/market/MarketPanel.tsx +11 -2
  392. package/src/client/recovery/IncompleteCopiesSection.tsx +132 -0
  393. package/src/client/recovery/RecoveryPanel.tsx +486 -59
  394. package/src/client/recovery/recovery-api.ts +30 -0
  395. package/src/client/recovery/recovery-incomplete-copies.test.ts +151 -0
  396. package/src/client/recovery/recovery-locales.test.ts +88 -0
  397. package/src/client/recovery/recovery-locales.ts +321 -6
  398. package/src/client/recovery/recovery-panel-layout.test.ts +52 -0
  399. package/src/client/recovery/recovery-view.ts +92 -2
  400. package/src/client/run-store.test.ts +201 -89
  401. package/src/client/run-store.ts +114 -143
  402. package/src/client/session-export/session-export-view.test.ts +103 -0
  403. package/src/client/session-export/session-export-view.ts +118 -0
  404. package/src/client/sync/ChannelConfigDialog.tsx +219 -14
  405. package/src/client/sync/SyncChannelEntryCard.tsx +52 -4
  406. package/src/client/sync/SyncLogList.tsx +1 -1
  407. package/src/client/sync/SyncPanel.tsx +166 -102
  408. package/src/client/sync/SyncRepositoryPicker.tsx +216 -0
  409. package/src/client/sync/SyncSettingsView.test.ts +6 -1
  410. package/src/client/sync/history-model.test.ts +94 -57
  411. package/src/client/sync/history-model.ts +44 -25
  412. package/src/client/sync/sync-api.test.ts +56 -0
  413. package/src/client/sync/sync-api.ts +92 -0
  414. package/src/client/sync/sync-locales.ts +162 -2
  415. package/src/client/sync/sync-view.test.ts +3 -8
  416. package/src/client/sync/sync-view.ts +111 -21
  417. package/src/core/analyzer-cache.test.ts +47 -0
  418. package/src/core/analyzer.ts +115 -14
  419. package/src/core/backup-plan.test.ts +35 -0
  420. package/src/core/backup-plan.ts +19 -1
  421. package/src/core/backup.test.ts +37 -1
  422. package/src/core/backup.ts +27 -6
  423. package/src/core/cache-cleaner.test.ts +120 -1
  424. package/src/core/cache-cleaner.ts +59 -22
  425. package/src/core/checkpoint.test.ts +665 -0
  426. package/src/core/checkpoint.ts +1614 -0
  427. package/src/core/credential-plan-consistency.test.ts +93 -0
  428. package/src/core/incident-wiring.test.ts +6 -2
  429. package/src/core/journal.test.ts +70 -0
  430. package/src/core/journal.ts +68 -0
  431. package/src/core/messages.ts +26 -0
  432. package/src/core/model-tools.ts +3 -0
  433. package/src/core/patch-layers.test.ts +94 -0
  434. package/src/core/patch-layers.ts +150 -0
  435. package/src/core/restore.ts +59 -3
  436. package/src/core/rollback.ts +6 -3
  437. package/src/core/section-content-consistency.test.ts +185 -0
  438. package/src/core/self-update.test.ts +110 -0
  439. package/src/core/self-update.ts +118 -0
  440. package/src/core/session-health.test.ts +91 -1
  441. package/src/core/session-health.ts +56 -9
  442. package/src/core/types.ts +127 -1
  443. package/src/foreign/antigravity.test.ts +271 -0
  444. package/src/foreign/antigravity.ts +188 -0
  445. package/src/foreign/bundle.ts +108 -0
  446. package/src/foreign/chatgpt.test.ts +279 -0
  447. package/src/foreign/chatgpt.ts +30 -0
  448. package/src/foreign/claude-code.test.ts +234 -0
  449. package/src/foreign/claude-code.ts +106 -0
  450. package/src/foreign/claude-sessions-bytes.test.ts +227 -0
  451. package/src/foreign/claude-sessions.test.ts +180 -0
  452. package/src/foreign/claude-sessions.ts +461 -0
  453. package/src/foreign/cline.test.ts +274 -0
  454. package/src/foreign/cline.ts +32 -0
  455. package/src/foreign/codex.test.ts +585 -0
  456. package/src/foreign/codex.ts +409 -0
  457. package/src/foreign/continue.test.ts +365 -0
  458. package/src/foreign/continue.ts +73 -0
  459. package/src/foreign/copilot.test.ts +273 -0
  460. package/src/foreign/copilot.ts +133 -0
  461. package/src/foreign/crush.test.ts +234 -0
  462. package/src/foreign/crush.ts +66 -0
  463. package/src/foreign/cursor.test.ts +509 -0
  464. package/src/foreign/cursor.ts +259 -0
  465. package/src/foreign/dsh.test.ts +198 -0
  466. package/src/foreign/dsh.ts +117 -0
  467. package/src/foreign/dsh4.test.ts +127 -0
  468. package/src/foreign/dsh4.ts +15 -0
  469. package/src/foreign/file-budget.test.ts +256 -0
  470. package/src/foreign/fixtures/antigravity/README.md +18 -0
  471. package/src/foreign/fixtures/antigravity/basic/gemini/antigravity/mcp_config.json +13 -0
  472. package/src/foreign/fixtures/antigravity/basic/gemini/config/mcp_config.json +24 -0
  473. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_config.json +0 -0
  474. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_oauth_tokens.json +0 -0
  475. package/src/foreign/fixtures/antigravity/empty/gemini/config/mcp_config.json +0 -0
  476. package/src/foreign/fixtures/codex/README.md +31 -0
  477. package/src/foreign/fixtures/codex/basic/.agents/skills/hello/SKILL.md +6 -0
  478. package/src/foreign/fixtures/codex/basic/.agents/skills/wrapper/nested/SKILL.md +6 -0
  479. package/src/foreign/fixtures/codex/basic/.codex/AGENTS.md +4 -0
  480. package/src/foreign/fixtures/codex/basic/.codex/config.toml +28 -0
  481. package/src/foreign/fixtures/codex/malformed/.codex/config.toml +3 -0
  482. package/src/foreign/fixtures/codex/override/.codex/AGENTS.md +1 -0
  483. package/src/foreign/fixtures/codex/override/.codex/AGENTS.override.md +3 -0
  484. package/src/foreign/fixtures/codex/sessions/.codex/archived_sessions/rollout-2026-10-05T09-00-00-22222222-2222-4222-8222-222222222222.jsonl +2 -0
  485. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T10-00-00-11111111-1111-4111-8111-111111111111.jsonl +15 -0
  486. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-00-00-33333333-3333-4333-8333-333333333333.jsonl +2 -0
  487. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-30-00-55555555-5555-4555-8555-555555555555.jsonl +2 -0
  488. package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T12-00-00-44444444-4444-4444-8444-444444444444.jsonl +2 -0
  489. package/src/foreign/fixtures/codex/sessions/.codex/sessions/notes.jsonl +1 -0
  490. package/src/foreign/fixtures/copilot/README.md +22 -0
  491. package/src/foreign/fixtures/copilot/basic/copilot-instructions.md +3 -0
  492. package/src/foreign/fixtures/copilot/basic/instructions/empty.instructions.md +1 -0
  493. package/src/foreign/fixtures/copilot/basic/instructions/repo.instructions.md +5 -0
  494. package/src/foreign/fixtures/copilot/basic/mcp-config.json +24 -0
  495. package/src/foreign/fixtures/copilot/basic/skills/hello/SKILL.md +6 -0
  496. package/src/foreign/fixtures/copilot/overridden/mcp-config.json +10 -0
  497. package/src/foreign/fixtures/cursor/README.md +17 -0
  498. package/src/foreign/fixtures/cursor/alt/.cursor/mcp.json +5 -0
  499. package/src/foreign/fixtures/cursor/basic/.cursor/mcp.json +18 -0
  500. package/src/foreign/fixtures/cursor/basic/.cursor/rules/agent.mdc +5 -0
  501. package/src/foreign/fixtures/cursor/basic/.cursor/rules/always.mdc +6 -0
  502. package/src/foreign/fixtures/cursor/basic/.cursor/rules/auto.mdc +7 -0
  503. package/src/foreign/fixtures/cursor/basic/.cursor/rules/manual.mdc +3 -0
  504. package/src/foreign/fixtures/cursor/basic/.cursor/skills/.hidden/SKILL.md +6 -0
  505. package/src/foreign/fixtures/cursor/basic/.cursor/skills/hello/SKILL.md +6 -0
  506. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/SKILL.md +6 -0
  507. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/notes.md +1 -0
  508. package/src/foreign/fixtures/cursor/basic/.cursorrules +1 -0
  509. package/src/foreign/fixtures/cursor/basic/project/.cursor/mcp.json +9 -0
  510. package/src/foreign/fixtures/cursor/basic/project/.cursor/rules/project-always.mdc +5 -0
  511. package/src/foreign/fixtures/cursor/basic/project/.cursor/skills/hello/SKILL.md +6 -0
  512. package/src/foreign/fixtures/cursor/basic/project/.cursorrules +1 -0
  513. package/src/foreign/fixtures/cursor/empty/.cursor/mcp.json +0 -0
  514. package/src/foreign/fixtures/cursor/malformed/.cursor/mcp.json +3 -0
  515. package/src/foreign/fixtures/cursor/malformed/.cursor/rules/broken.mdc +6 -0
  516. package/src/foreign/fixtures/cursor/shape/.cursor/mcp.json +3 -0
  517. package/src/foreign/fixtures/hermes/README.md +14 -0
  518. package/src/foreign/fixtures/hermes/basic/SOUL.md +3 -0
  519. package/src/foreign/fixtures/hermes/basic/config.yaml +48 -0
  520. package/src/foreign/fixtures/hermes/basic/dotenv.fixture +1 -0
  521. package/src/foreign/fixtures/hermes/basic/memories/MEMORY.md +1 -0
  522. package/src/foreign/fixtures/hermes/basic/memories/USER.md +1 -0
  523. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/SKILL.md +6 -0
  524. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/examples/usage.md +1 -0
  525. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/SKILL.md +6 -0
  526. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/notes.md +1 -0
  527. package/src/foreign/fixtures/hermes/basic/state.db +1 -0
  528. package/src/foreign/fixtures/hermes/empty/config.yaml +0 -0
  529. package/src/foreign/gemini.test.ts +291 -0
  530. package/src/foreign/gemini.ts +52 -0
  531. package/src/foreign/goose.test.ts +196 -0
  532. package/src/foreign/goose.ts +62 -0
  533. package/src/foreign/grokbuild.test.ts +246 -0
  534. package/src/foreign/grokbuild.ts +30 -0
  535. package/src/foreign/hermes.test.ts +545 -0
  536. package/src/foreign/hermes.ts +86 -0
  537. package/src/foreign/kernel-credentials.test.ts +88 -0
  538. package/src/foreign/kernel-workspaces.test.ts +88 -0
  539. package/src/foreign/kernel.ts +584 -0
  540. package/src/foreign/kilocode.test.ts +186 -0
  541. package/src/foreign/kilocode.ts +60 -0
  542. package/src/foreign/kimi.test.ts +259 -0
  543. package/src/foreign/kimi.ts +55 -0
  544. package/src/foreign/mcp-value-shape.test.ts +286 -0
  545. package/src/foreign/mimocode.test.ts +178 -0
  546. package/src/foreign/mimocode.ts +60 -0
  547. package/src/foreign/openclaw.test.ts +248 -0
  548. package/src/foreign/openclaw.ts +54 -0
  549. package/src/foreign/opencode.test.ts +376 -0
  550. package/src/foreign/opencode.ts +61 -0
  551. package/src/foreign/pi.test.ts +275 -0
  552. package/src/foreign/pi.ts +56 -0
  553. package/src/foreign/platform-paths.ts +156 -0
  554. package/src/foreign/qoder.test.ts +266 -0
  555. package/src/foreign/qoder.ts +57 -0
  556. package/src/foreign/qwen.test.ts +203 -0
  557. package/src/foreign/qwen.ts +53 -0
  558. package/src/foreign/read-antigravity.ts +569 -0
  559. package/src/foreign/read-chatgpt.ts +506 -0
  560. package/src/foreign/read-claude-code.ts +264 -0
  561. package/src/foreign/read-cline.ts +517 -0
  562. package/src/foreign/read-codex.ts +823 -0
  563. package/src/foreign/read-continue.ts +557 -0
  564. package/src/foreign/read-copilot.ts +304 -0
  565. package/src/foreign/read-crush.ts +503 -0
  566. package/src/foreign/read-cursor.ts +778 -0
  567. package/src/foreign/read-dsh.ts +215 -0
  568. package/src/foreign/read-dsh4.ts +35 -0
  569. package/src/foreign/read-gemini.ts +306 -0
  570. package/src/foreign/read-goose.ts +333 -0
  571. package/src/foreign/read-grokbuild.ts +475 -0
  572. package/src/foreign/read-hermes.ts +724 -0
  573. package/src/foreign/read-kilocode.ts +55 -0
  574. package/src/foreign/read-kimi.ts +768 -0
  575. package/src/foreign/read-mimocode.ts +66 -0
  576. package/src/foreign/read-openclaw.ts +505 -0
  577. package/src/foreign/read-opencode.ts +994 -0
  578. package/src/foreign/read-pi.ts +459 -0
  579. package/src/foreign/read-qoder.ts +351 -0
  580. package/src/foreign/read-qwen.ts +342 -0
  581. package/src/foreign/read-reasonix.ts +590 -0
  582. package/src/foreign/read-teleagent.ts +82 -0
  583. package/src/foreign/read-trae.ts +488 -0
  584. package/src/foreign/read-vibe.ts +353 -0
  585. package/src/foreign/read-workbuddy.ts +374 -0
  586. package/src/foreign/read-zcode.ts +48 -0
  587. package/src/foreign/read-zed.ts +479 -0
  588. package/src/foreign/reasonix.test.ts +336 -0
  589. package/src/foreign/reasonix.ts +25 -0
  590. package/src/foreign/registry.test.ts +377 -0
  591. package/src/foreign/registry.ts +750 -0
  592. package/src/foreign/session-ir.test.ts +189 -0
  593. package/src/foreign/session-ir.ts +614 -0
  594. package/src/foreign/session-read.ts +295 -0
  595. package/src/foreign/session-source.ts +625 -0
  596. package/src/foreign/skill-file-caps.test.ts +171 -0
  597. package/src/foreign/source-modules.ts +86 -0
  598. package/src/foreign/source-registry.test.ts +190 -0
  599. package/src/foreign/sqlite.test.ts +313 -0
  600. package/src/foreign/sqlite.ts +446 -0
  601. package/src/foreign/teleagent.test.ts +147 -0
  602. package/src/foreign/teleagent.ts +62 -0
  603. package/src/foreign/trae.test.ts +316 -0
  604. package/src/foreign/trae.ts +25 -0
  605. package/src/foreign/truncation-report.test.ts +238 -0
  606. package/src/foreign/truth-table.ts +453 -0
  607. package/src/foreign/types.ts +312 -0
  608. package/src/foreign/vibe.test.ts +209 -0
  609. package/src/foreign/vibe.ts +28 -0
  610. package/src/foreign/workbuddy.test.ts +207 -0
  611. package/src/foreign/workbuddy.ts +53 -0
  612. package/src/foreign/zcode.test.ts +224 -0
  613. package/src/foreign/zcode.ts +60 -0
  614. package/src/foreign/zed.test.ts +240 -0
  615. package/src/foreign/zed.ts +62 -0
  616. package/src/index.facade.test.ts +254 -16
  617. package/src/index.ts +704 -82
  618. package/src/market/github-repos.test.ts +98 -1
  619. package/src/market/github-repos.ts +63 -1
  620. package/src/market/types.ts +3 -3
  621. package/src/profiles/dsh-profile-incomplete-copy.test.ts +177 -0
  622. package/src/profiles/dsh-profile-manager.test.ts +37 -0
  623. package/src/profiles/dsh-profile-manager.ts +188 -13
  624. package/src/profiles/dsh-profile-shared.ts +55 -1
  625. package/src/profiles/index.ts +2 -1
  626. package/src/routes/consult.ts +53 -4
  627. package/src/routes/foreign-context.ts +89 -0
  628. package/src/routes/foreign.test.ts +443 -0
  629. package/src/routes/foreign.ts +226 -0
  630. package/src/routes/import.ts +23 -1
  631. package/src/routes/index.ts +6 -2
  632. package/src/routes/kit.ts +4 -3
  633. package/src/routes/market.ts +0 -1
  634. package/src/routes/prefs.ts +15 -1
  635. package/src/routes/recovery.test.ts +167 -0
  636. package/src/routes/recovery.ts +331 -1
  637. package/src/routes/session-export.ts +308 -0
  638. package/src/routes/snapshots.ts +12 -2
  639. package/src/routes/sync.ts +91 -7
  640. package/src/security/redaction.test.ts +149 -0
  641. package/src/security/redaction.ts +25 -9
  642. package/src/security/secret-scanner.test.ts +186 -0
  643. package/src/security/secret-scanner.ts +57 -1
  644. package/src/sync/autosync-risk-tier.test.ts +61 -0
  645. package/src/sync/autosync-scheduler.test.ts +48 -1
  646. package/src/sync/autosync-scheduler.ts +50 -26
  647. package/src/sync/backup-files.ts +5 -0
  648. package/src/sync/gist/gist-msgkeys-catalog.test.ts +33 -0
  649. package/src/sync/gist/gist-msgkeys.test.ts +71 -0
  650. package/src/sync/gist/gist-transport.test.ts +308 -0
  651. package/src/sync/gist/gist-transport.ts +550 -0
  652. package/src/sync/gist/messages.ts +49 -0
  653. package/src/sync/retention-prune-all-off.test.ts +50 -0
  654. package/src/sync/s3/cloud-config.test.ts +283 -0
  655. package/src/sync/s3/messages.ts +102 -0
  656. package/src/sync/s3/s3-delete-index-404.test.ts +170 -0
  657. package/src/sync/s3/s3-delete-index-unreadable.test.ts +141 -0
  658. package/src/sync/s3/s3-msgkeys.test.ts +101 -0
  659. package/src/sync/s3/s3-providers.ts +213 -0
  660. package/src/sync/s3/s3-transport.test.ts +394 -0
  661. package/src/sync/s3/s3-transport.ts +651 -0
  662. package/src/sync/s3/s3-upload-index-404.test.ts +171 -0
  663. package/src/sync/s3/sigv4.test.ts +219 -0
  664. package/src/sync/s3/sigv4.ts +283 -0
  665. package/src/sync/sync-config.test.ts +237 -5
  666. package/src/sync/sync-config.ts +841 -52
  667. package/src/sync/sync-engine-download-tombstone.test.ts +73 -0
  668. package/src/sync/sync-engine.ts +5 -3
  669. package/src/sync/sync-history.ts +1 -1
  670. package/src/sync/webdav/webdav-delete-index-404.test.ts +178 -0
  671. package/src/sync/webdav/webdav-delete-index-unreadable.test.ts +143 -0
  672. package/src/sync/webdav/webdav-msgkeys.test.ts +53 -0
  673. package/src/sync/webdav/webdav-transport.test.ts +9 -5
  674. package/src/sync/webdav/webdav-transport.ts +99 -17
  675. package/src/sync/webdav/webdav-upload-index-404.test.ts +177 -0
  676. package/src/ui/commands.test.ts +4 -1
  677. package/src/ui/commands.ts +46 -12
  678. package/src/ui/disk-usage-view.test.ts +63 -1
  679. package/src/ui/disk-usage-view.ts +93 -1
  680. package/src/ui/dsh-profiles-incomplete-view.test.ts +102 -0
  681. package/src/ui/dsh-profiles-view.ts +94 -0
  682. package/src/ui/foreign-view.test.ts +241 -0
  683. package/src/ui/foreign-view.ts +362 -0
  684. package/src/ui/i18n.ts +164 -0
  685. package/src/ui/mid-ellipsis.test.ts +5 -10
  686. package/src/ui/session-inventory-view.test.ts +142 -2
  687. package/src/ui/session-inventory-view.ts +127 -0
  688. package/src/ui/session-layout-view.test.ts +361 -0
  689. package/src/ui/session-layout-view.ts +444 -0
  690. package/src/ui/sync-repository-picker.test.ts +130 -0
  691. package/src/ui/sync-repository-picker.ts +180 -0
  692. package/src/ui/sync-settings-view.test.ts +166 -0
  693. package/src/ui/sync-settings-view.ts +319 -9
  694. package/src/ui/types.ts +42 -0
  695. package/src/utils/asar-read.test.ts +330 -0
  696. package/src/utils/asar-read.ts +606 -0
  697. package/src/utils/env-lock.test.ts +55 -0
  698. package/src/utils/env-lock.ts +6 -1
  699. package/src/utils/patch-yaml.test.ts +112 -0
  700. package/src/utils/patch-yaml.ts +72 -0
  701. package/src/utils/paths.test.ts +63 -0
  702. package/src/utils/paths.ts +25 -3
  703. package/src/utils/recursive-walk.test.ts +20 -0
  704. package/src/utils/recursive-walk.ts +12 -13
  705. package/src/utils/session-format.test.ts +113 -2
  706. package/src/utils/session-format.ts +127 -5
  707. package/src/utils/session-health-scan.test.ts +364 -1
  708. package/src/utils/session-health-scan.ts +434 -15
  709. package/src/utils/session-layout-repair-service.test.ts +362 -0
  710. package/src/utils/session-layout-repair-service.ts +568 -0
  711. package/src/utils/session-log-repair.test.ts +260 -1
  712. package/src/utils/session-log-repair.ts +294 -55
  713. package/src/utils/session-repair-service.test.ts +148 -1
  714. package/src/utils/session-repair-service.ts +165 -10
  715. package/src/utils/session-row-facts.test.ts +159 -0
  716. package/src/utils/session-row-facts.ts +187 -0
  717. package/src/utils/session-verify.test.ts +762 -0
  718. package/src/utils/session-verify.ts +784 -0
  719. package/src/utils/shared-constants.ts +82 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * ReleaseBody —— GitHub Release 正文的结构化安全渲染(**共享原语**)。
3
+ *
4
+ * 为什么抽出来:「更新内容」浏览弹窗(ReleaseNotesDialog)与新版本弹窗(PluginUpdateDialog)
5
+ * 都要渲染 release 正文 —— 渲染规则必须只有一份:纯文本分块解析 + 行内格式
6
+ * (`code` / **bold** / [link](url)),**零 dangerouslySetInnerHTML**(杜绝 XSS)。
7
+ * 本组件从 ReleaseNotesDialog 原样搬出(含内联样式),视觉与行为逐字不变。
8
+ */
9
+ import type { TranslateNS } from '../client-types.ts'
10
+ import { parseMarkdownBlocks, type MarkdownBlock } from './release-notes-view.ts'
11
+ import css from '../config-manager.module.css'
12
+
13
+ /**
14
+ * 纯 React 安全渲染 Markdown 文本行中的行内格式(粗体、行内代码、链接)。
15
+ */
16
+ function renderInlineMarkdown(text: string) {
17
+ // 简单行内解析:`code`, **bold**, [link](url)
18
+ // 分割正则
19
+ const regex = /(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/g
20
+ const parts = text.split(regex)
21
+
22
+ return parts.map((part, idx) => {
23
+ if (!part) return null
24
+ // 行内代码 `...`
25
+ if (part.startsWith('`') && part.endsWith('`') && part.length >= 2) {
26
+ return (
27
+ <code key={idx} className={css.cliName}>
28
+ {part.slice(1, -1)}
29
+ </code>
30
+ )
31
+ }
32
+ // 粗体 **...**
33
+ if (part.startsWith('**') && part.endsWith('**') && part.length >= 4) {
34
+ return <strong key={idx}>{part.slice(2, -2)}</strong>
35
+ }
36
+ // 链接 [text](url)
37
+ const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/)
38
+ if (linkMatch && linkMatch[1] && linkMatch[2]) {
39
+ return (
40
+ <a
41
+ key={idx}
42
+ href={linkMatch[2]}
43
+ target="_blank"
44
+ rel="noreferrer"
45
+ className={css.aboutAuthor}
46
+ >
47
+ {linkMatch[1]}
48
+ </a>
49
+ )
50
+ }
51
+ return <span key={idx}>{part}</span>
52
+ })
53
+ }
54
+
55
+ /**
56
+ * 渲染单个结构化 Markdown 块。
57
+ */
58
+ function MarkdownBlockView({ block }: { block: MarkdownBlock }) {
59
+ switch (block.type) {
60
+ case 'heading': {
61
+ const headingStyle = {
62
+ fontWeight: 600,
63
+ color: 'var(--dsw-alias-label-primary)',
64
+ marginTop: block.level === 1 ? '12px' : '8px',
65
+ marginBottom: '4px',
66
+ fontSize: block.level === 1 ? '15px' : block.level === 2 ? '14px' : '13px',
67
+ }
68
+ return <div style={headingStyle}>{block.text}</div>
69
+ }
70
+ case 'list-item': {
71
+ return (
72
+ <div style={{ display: 'flex', gap: '6px', alignItems: 'flex-start', paddingLeft: '4px' }}>
73
+ <span style={{ color: 'var(--dsw-alias-label-tertiary)', userSelect: 'none' }}>
74
+ {block.ordered ? (block.index ?? 1) + '.' : '•'}
75
+ </span>
76
+ <div style={{ flex: 1 }}>{renderInlineMarkdown(block.text)}</div>
77
+ </div>
78
+ )
79
+ }
80
+ case 'quote': {
81
+ return (
82
+ <div
83
+ style={{
84
+ borderLeft: '3px solid var(--dsw-alias-border-l1)',
85
+ paddingLeft: '8px',
86
+ color: 'var(--dsw-alias-label-secondary)',
87
+ fontStyle: 'italic',
88
+ }}
89
+ >
90
+ {renderInlineMarkdown(block.text)}
91
+ </div>
92
+ )
93
+ }
94
+ case 'code-block': {
95
+ return (
96
+ <pre
97
+ className={css.cliCommand}
98
+ style={{ margin: '4px 0', padding: '8px', fontSize: '12px' }}
99
+ >
100
+ {block.code}
101
+ </pre>
102
+ )
103
+ }
104
+ case 'hr': {
105
+ return (
106
+ <div
107
+ style={{
108
+ borderBottom: '1px solid var(--dsw-alias-border-l2)',
109
+ margin: '8px 0',
110
+ }}
111
+ />
112
+ )
113
+ }
114
+ case 'paragraph':
115
+ default: {
116
+ return <div>{renderInlineMarkdown(block.text)}</div>
117
+ }
118
+ }
119
+ }
120
+
121
+ /** release 正文:结构化块渲染;空正文显示「暂无详细说明」。 */
122
+ export function ReleaseBody({ body, t }: { body: string; t: TranslateNS<'config-manager'> }) {
123
+ const blocks = parseMarkdownBlocks(body)
124
+ return (
125
+ <div
126
+ style={{
127
+ display: 'flex',
128
+ flexDirection: 'column',
129
+ gap: '6px',
130
+ fontSize: '13px',
131
+ lineHeight: '1.6',
132
+ color: 'var(--dsw-alias-label-primary)',
133
+ }}
134
+ >
135
+ {blocks.length > 0 ? (
136
+ blocks.map((block, idx) => <MarkdownBlockView key={idx} block={block} />)
137
+ ) : (
138
+ <div style={{ color: 'var(--dsw-alias-label-tertiary)', fontStyle: 'italic' }}>
139
+ {t('about.releaseNotes.noBody')}
140
+ </div>
141
+ )}
142
+ </div>
143
+ )
144
+ }
@@ -17,10 +17,9 @@ import { ABOUT_META } from './about-view.ts'
17
17
  import {
18
18
  deriveReleasesUrl,
19
19
  fetchReleases,
20
- parseMarkdownBlocks,
21
20
  type FormattedRelease,
22
- type MarkdownBlock,
23
21
  } from './release-notes-view.ts'
22
+ import { ReleaseBody } from './ReleaseBody.tsx'
24
23
  import css from '../config-manager.module.css'
25
24
 
26
25
  export interface ReleaseNotesDialogProps {
@@ -34,113 +33,6 @@ export interface ReleaseNotesDialogProps {
34
33
 
35
34
  const PAGE_SIZE = 5
36
35
 
37
- /**
38
- * 纯 React 安全渲染 Markdown 文本行中的行内格式(粗体、行内代码、链接)。
39
- */
40
- function renderInlineMarkdown(text: string) {
41
- // 简单行内解析:`code`, **bold**, [link](url)
42
- // 分割正则
43
- const regex = /(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/g
44
- const parts = text.split(regex)
45
-
46
- return parts.map((part, idx) => {
47
- if (!part) return null
48
- // 行内代码 `...`
49
- if (part.startsWith('`') && part.endsWith('`') && part.length >= 2) {
50
- return (
51
- <code key={idx} className={css.cliName}>
52
- {part.slice(1, -1)}
53
- </code>
54
- )
55
- }
56
- // 粗体 **...**
57
- if (part.startsWith('**') && part.endsWith('**') && part.length >= 4) {
58
- return <strong key={idx}>{part.slice(2, -2)}</strong>
59
- }
60
- // 链接 [text](url)
61
- const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/)
62
- if (linkMatch && linkMatch[1] && linkMatch[2]) {
63
- return (
64
- <a
65
- key={idx}
66
- href={linkMatch[2]}
67
- target="_blank"
68
- rel="noreferrer"
69
- className={css.aboutAuthor}
70
- >
71
- {linkMatch[1]}
72
- </a>
73
- )
74
- }
75
- return <span key={idx}>{part}</span>
76
- })
77
- }
78
-
79
- /**
80
- * 渲染单个结构化 Markdown 块。
81
- */
82
- function MarkdownBlockView({ block }: { block: MarkdownBlock }) {
83
- switch (block.type) {
84
- case 'heading': {
85
- const headingStyle = {
86
- fontWeight: 600,
87
- color: 'var(--dsw-alias-label-primary)',
88
- marginTop: block.level === 1 ? '12px' : '8px',
89
- marginBottom: '4px',
90
- fontSize: block.level === 1 ? '15px' : block.level === 2 ? '14px' : '13px',
91
- }
92
- return <div style={headingStyle}>{block.text}</div>
93
- }
94
- case 'list-item': {
95
- return (
96
- <div style={{ display: 'flex', gap: '6px', alignItems: 'flex-start', paddingLeft: '4px' }}>
97
- <span style={{ color: 'var(--dsw-alias-label-tertiary)', userSelect: 'none' }}>
98
- {block.ordered ? `${block.index ?? 1}.` : '•'}
99
- </span>
100
- <div style={{ flex: 1 }}>{renderInlineMarkdown(block.text)}</div>
101
- </div>
102
- )
103
- }
104
- case 'quote': {
105
- return (
106
- <div
107
- style={{
108
- borderLeft: '3px solid var(--dsw-alias-border-l1)',
109
- paddingLeft: '8px',
110
- color: 'var(--dsw-alias-label-secondary)',
111
- fontStyle: 'italic',
112
- }}
113
- >
114
- {renderInlineMarkdown(block.text)}
115
- </div>
116
- )
117
- }
118
- case 'code-block': {
119
- return (
120
- <pre
121
- className={css.cliCommand}
122
- style={{ margin: '4px 0', padding: '8px', fontSize: '12px' }}
123
- >
124
- {block.code}
125
- </pre>
126
- )
127
- }
128
- case 'hr': {
129
- return (
130
- <div
131
- style={{
132
- borderBottom: '1px solid var(--dsw-alias-border-l2)',
133
- margin: '8px 0',
134
- }}
135
- />
136
- )
137
- }
138
- case 'paragraph':
139
- default: {
140
- return <div>{renderInlineMarkdown(block.text)}</div>
141
- }
142
- }
143
- }
144
36
 
145
37
  /**
146
38
  * 单个版本的卡片视图。
@@ -154,8 +46,6 @@ function ReleaseCardView({
154
46
  isFirst: boolean
155
47
  t: TranslateNS<'config-manager'>
156
48
  }) {
157
- const blocks = parseMarkdownBlocks(release.body)
158
-
159
49
  return (
160
50
  <div
161
51
  style={{
@@ -215,25 +105,8 @@ function ReleaseCardView({
215
105
  </Button>
216
106
  </div>
217
107
 
218
- {/* 正文:结构化 Markdown 块渲染 */}
219
- <div
220
- style={{
221
- display: 'flex',
222
- flexDirection: 'column',
223
- gap: '6px',
224
- fontSize: '13px',
225
- lineHeight: '1.6',
226
- color: 'var(--dsw-alias-label-primary)',
227
- }}
228
- >
229
- {blocks.length > 0 ? (
230
- blocks.map((block, idx) => <MarkdownBlockView key={idx} block={block} />)
231
- ) : (
232
- <div style={{ color: 'var(--dsw-alias-label-tertiary)', fontStyle: 'italic' }}>
233
- {t('about.releaseNotes.noBody')}
234
- </div>
235
- )}
236
- </div>
108
+ {/* 正文:结构化 Markdown 块渲染(共享原语 ReleaseBody,渲染规则只有一份) */}
109
+ <ReleaseBody body={release.body} t={t} />
237
110
  </div>
238
111
  )
239
112
  }
@@ -15,6 +15,7 @@
15
15
  * 判定(是否真的有新版本、给不给终端命令)全在这里,面板只映射 kind → 文案与 Badge。
16
16
  */
17
17
  import type { PluginUpdateCheckResult } from '../../ui/types.ts';
18
+ import type { ConfigManagerKey } from '../locales.ts';
18
19
 
19
20
  /** 插件的公开元数据(静态常量,来源见设计文档 §3 信息表) */
20
21
  export interface AboutMeta {
@@ -183,15 +184,16 @@ export const ABOUT_LINKS: AboutLinks = deriveAboutLinks(ABOUT_META.repoUrl);
183
184
  * DSH 挂了也能用;文案与命令见 README.md「CLI — the first line of defense」。 */
184
185
  export const ABOUT_CLI: {
185
186
  installCommand: string;
186
- commands: { command: string; description: string }[];
187
+ /** client-F5:说明文案走字典键(此前硬编码中文,英文界面恒为中文) */
188
+ commands: { command: string; descriptionKey: ConfigManagerKey }[];
187
189
  docsUrl: string;
188
190
  } = {
189
191
  installCommand: 'npm install -g dsh-config-manager@latest --omit=peer',
190
192
  commands: [
191
- { command: 'dsh-config-manager help', description: '列出全部 CLI 命令与用法(离线可用)' },
192
- { command: 'dsh-config-manager snapshots', description: '列出本机回滚快照(无需 DSH 运行)' },
193
- { command: 'dsh-config-manager restore [--id <id>] [--dry-run]', description: '恢复到导入前状态(离线)' },
194
- { command: 'dsh-config-manager reinstall [--yes] [--wipe-config]', description: '一键重装 DSH(救援)' },
193
+ { command: 'dsh-config-manager help', descriptionKey: 'about.cli.help' },
194
+ { command: 'dsh-config-manager snapshots', descriptionKey: 'about.cli.snapshots' },
195
+ { command: 'dsh-config-manager restore [--id <id>] [--dry-run]', descriptionKey: 'about.cli.restore' },
196
+ { command: 'dsh-config-manager reinstall [--yes] [--wipe-config]', descriptionKey: 'about.cli.reinstall' },
195
197
  ],
196
198
  docsUrl: 'https://github.com/xiajiajun516/dsh-config-manager#-cli--the-first-line-of-defense-when-dsh-is-broken',
197
199
  };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * plugin-update-view 测试:失败码 → 文案键的映射必须覆盖共享失败码清单,
3
+ * 且这些键在 zh/en 两本字典里都存在(穷尽映射的运行时兜底 —— 编译期由 Record 类型保证)。
4
+ */
5
+ import test from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { UPDATE_FAILURE_KEYS, updateFailureKey } from './plugin-update-view.ts';
8
+ import { SELF_UPDATE_FAILURE_CODES } from '../../utils/shared-constants.ts';
9
+ import { en, zh } from '../locales.ts';
10
+
11
+ test('每个失败码都有专属文案键,且 zh/en 字典都有该键', () => {
12
+ for (const code of SELF_UPDATE_FAILURE_CODES) {
13
+ const key = UPDATE_FAILURE_KEYS[code];
14
+ assert.ok(typeof key === 'string' && key.length > 0, '缺少映射: ' + code);
15
+ assert.equal(typeof zh[key], 'string', 'zh 缺键: ' + key);
16
+ assert.equal(typeof en[key], 'string', 'en 缺键: ' + key);
17
+ }
18
+ });
19
+
20
+ test('updateFailureKey:已知码查表,未知码回 null(调用方回落宿主原始文本)', () => {
21
+ assert.equal(updateFailureKey('install-failed'), 'about.update.code.installFailed');
22
+ assert.equal(updateFailureKey('non-registry-install'), 'about.update.code.nonRegistryInstall');
23
+ assert.equal(updateFailureKey('boom'), null);
24
+ assert.equal(updateFailureKey(''), null);
25
+ });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 插件自更新弹窗的纯函数模型(node 可测,无 React / 无 DOM)。
3
+ *
4
+ * 只有一件事:把宿主返回的**失败码**映射到本地化文案键。
5
+ *
6
+ * 为什么不让组件直接渲染 host 的 error:那是英文技术细节(还可能带路径),界面必须给用户
7
+ * 可读的中文/英文文案;只有**未知码**才回落到宿主原始文本(此时至少不隐藏真实原因)。
8
+ */
9
+ import type { ConfigManagerKey } from '../locales.ts';
10
+ import type { SelfUpdateFailureCode } from '../../utils/shared-constants.ts';
11
+
12
+ /** 失败码 → 文案键(穷尽映射:共享常量新增码时这里编译报错,逼着同时补 zh/en 字典)。 */
13
+ export const UPDATE_FAILURE_KEYS: Readonly<Record<SelfUpdateFailureCode, ConfigManagerKey>> = {
14
+ 'invalid-version': 'about.update.code.invalidVersion',
15
+ 'profile-unknown': 'about.update.code.profileUnknown',
16
+ 'unsupported-profile': 'about.update.code.unsupportedProfile',
17
+ 'non-registry-install': 'about.update.code.nonRegistryInstall',
18
+ 'not-newer': 'about.update.code.notNewer',
19
+ 'install-failed': 'about.update.code.installFailed',
20
+ };
21
+
22
+ /** 失败码 → 文案键;未知码回 null(调用方回落宿主原始文本)。 */
23
+ export function updateFailureKey(code: string): ConfigManagerKey | null {
24
+ const table: Record<string, ConfigManagerKey | undefined> = UPDATE_FAILURE_KEYS;
25
+ return table[code] ?? null;
26
+ }
@@ -12,6 +12,8 @@ import {
12
12
  parseMarkdownBlocks,
13
13
  parseReleaseItem,
14
14
  parseReleasesResponse,
15
+ findReleaseForVersion,
16
+ normalizeReleaseVersion,
15
17
  } from './release-notes-view.ts';
16
18
 
17
19
  test('parseGitHubRepo: 正确解析 GitHub 仓库 URL', () => {
@@ -172,3 +174,34 @@ test('parseMarkdownBlocks: 解析标题、列表、引用、代码块与段落',
172
174
  assert.equal((blocks[8] as { code: string }).code, 'const a = 1;');
173
175
  assert.equal(blocks[9]!.type, 'paragraph');
174
176
  });
177
+
178
+ test('normalizeReleaseVersion: 去空白与前导 v/V', () => {
179
+ assert.equal(normalizeReleaseVersion('v0.1.70'), '0.1.70');
180
+ assert.equal(normalizeReleaseVersion('V0.1.70'), '0.1.70');
181
+ assert.equal(normalizeReleaseVersion(' 0.1.70 '), '0.1.70');
182
+ assert.equal(normalizeReleaseVersion(''), '');
183
+ });
184
+
185
+ test('findReleaseForVersion: 按归一化 tag 精确匹配(v 前缀 / 大小写 / 空格)', () => {
186
+ const releases = [
187
+ parseReleaseItem({ id: 2, tag_name: 'v0.1.70', body: 'new' })!,
188
+ parseReleaseItem({ id: 1, tag_name: 'v0.1.69', body: 'old' })!,
189
+ ];
190
+ assert.equal(findReleaseForVersion(releases, '0.1.70').release?.tag, 'v0.1.70');
191
+ assert.equal(findReleaseForVersion(releases, '0.1.70').exact, true);
192
+ assert.equal(findReleaseForVersion(releases, 'V0.1.69').release?.tag, 'v0.1.69');
193
+ assert.equal(findReleaseForVersion(releases, ' 0.1.70 ').exact, true);
194
+ });
195
+
196
+ test('findReleaseForVersion: 匹配不到时回退第一条并标记 exact=false(不假装是目标版本)', () => {
197
+ const releases = [parseReleaseItem({ id: 2, tag_name: 'v0.2.0', body: 'newest' })!];
198
+ const miss = findReleaseForVersion(releases, '0.3.0');
199
+ assert.equal(miss.release?.tag, 'v0.2.0');
200
+ assert.equal(miss.exact, false);
201
+ });
202
+
203
+ test('findReleaseForVersion: 空列表 / 空版本不抛错', () => {
204
+ assert.deepEqual(findReleaseForVersion([], '0.1.70'), { release: null, exact: false });
205
+ const releases = [parseReleaseItem({ id: 1, tag_name: 'v0.1.70', body: '' })!];
206
+ assert.equal(findReleaseForVersion(releases, '').exact, false);
207
+ });
@@ -308,3 +308,37 @@ export function parseMarkdownBlocks(text: string): MarkdownBlock[] {
308
308
 
309
309
  return blocks;
310
310
  }
311
+
312
+ /* ---------------- 版本匹配(「新版本」弹窗用) ---------------- */
313
+
314
+ /**
315
+ * 版本 / tag 归一化:去掉空白与前导 v/V。
316
+ *
317
+ * 为什么需要:npm 版本是 `0.1.70`,而 GitHub release tag 是 `v0.1.70`(本仓打 tag 即触发发布),
318
+ * 两边直接比较永远不相等。
319
+ */
320
+ export function normalizeReleaseVersion(value: string): string {
321
+ return value.trim().replace(/^[vV]/, '');
322
+ }
323
+
324
+ /** 目标版本 → release 的匹配结果:exact=false 表示回退到了最新一条(tag 没对上)。 */
325
+ export interface ReleaseMatch {
326
+ release: FormattedRelease | null;
327
+ exact: boolean;
328
+ }
329
+
330
+ /**
331
+ * 找目标版本对应的 release:先按归一化 tag 精确匹配;匹配不到时**回退到第一条**并标记
332
+ * exact=false —— npm 已发布但 GitHub release 尚未生成时界面仍要显示一段说明,同时如实标注
333
+ * 这是「最新一条发布内容」,绝不假装它就是目标版本。
334
+ */
335
+ export function findReleaseForVersion(releases: readonly FormattedRelease[], version: string): ReleaseMatch {
336
+ const want = normalizeReleaseVersion(version);
337
+ const first = releases.length > 0 ? releases[0] : undefined;
338
+ const fallback = first === undefined ? null : first;
339
+ if (want === '') return { release: fallback, exact: false };
340
+ for (const release of releases) {
341
+ if (normalizeReleaseVersion(release.tag) === want) return { release, exact: true };
342
+ }
343
+ return { release: fallback, exact: false };
344
+ }
package/src/client/api.ts CHANGED
@@ -37,7 +37,10 @@ import type { ConsultReport } from '../core/migration-consult.ts';
37
37
  import type { Manifest, SectionId } from '../schema/types.ts';
38
38
  import type { BackupScheduleStatus, BackupRunResult, BackupScheduleDraft } from '../ui/backup-schedule.ts';
39
39
  import type { BackupFileMeta } from '../sync/backup-files.ts';
40
- import type { DiskUsageCleanupResult, PluginUpdateCheckResult } from '../ui/types.ts';
40
+ import type { DiskUsageCleanupResult, PluginUpdateApplyResult, PluginUpdateCheckResult } from '../ui/types.ts';
41
+ import type { SessionExportProbe } from './session-export/session-export-view.ts';
42
+ import type { ForeignSkip } from '../foreign/types.ts';
43
+ import type { ForeignSourcesResponse as ForeignSourcesViewInput } from '../ui/foreign-view.ts';
41
44
  import type { DiskUsageReport } from '../core/disk-usage.ts';
42
45
  import { zhUiT, type UiT } from '../ui/i18n.ts';
43
46
  import { failedSectionsFromResponse } from '../ui/export-flow.ts';
@@ -75,6 +78,42 @@ export interface ServiceStatus {
75
78
  bundles?: string[];
76
79
  }
77
80
 
81
+ /**
82
+ * 外部 agent 来源发现响应(`GET /foreign-sources`)。
83
+ *
84
+ * 复用 `src/ui/foreign-view.ts` 的输入类型 —— 响应形状的**唯一权威**在那边(视图模型只读它)。
85
+ * 路径刻意在本文件重导出,避免组件同时 import 两处(同 `UploadResponse` 的做法)。
86
+ */
87
+ export type ForeignSourcesResponse = ForeignSourcesViewInput
88
+
89
+ /**
90
+ * 外部来源 → bundle 的响应(`POST /foreign-import`)。
91
+ *
92
+ * `zipPath`:受控临时目录里的标准 bundle v1 ZIP,直接喂给既有导入向导的 `analyzeImport`。
93
+ * `skipped` / `credentialRefs` **绝不含凭据值**(值在转换层已被剥离)。
94
+ */
95
+ export interface ForeignImportResponse {
96
+ zipPath: string
97
+ name: string
98
+ /** 来源 id(六来源之一) */
99
+ source: string
100
+ /** 来源显示名的字典键(如 foreign.source.hermes) */
101
+ labelKey: string
102
+ /** 产物字节数(读不到时为 0,界面按「未知」处理) */
103
+ sizeBytes: number
104
+ /** 本次产出包内的分区(空分区不会被写入,也不出现在这里) */
105
+ sections: SectionId[]
106
+ /** 各分区的条目计数(机器可读;键形如 `mcp.servers`) */
107
+ counts: Record<string, number>
108
+ /** 未迁移项(只有机器码 + 位置名 + 条数) */
109
+ skipped: ForeignSkip[]
110
+ /** 凭据**引用名**(形如 `mcp:gitnexus:GITHUB_TOKEN`);导入后由用户补录值 */
111
+ credentialRefs: string[]
112
+ /** 检测到的本机位置(相对 home;绝不回传绝对路径) */
113
+ detectedPaths: string[]
114
+ /** 冲突策略(用户决策:同 id 不覆盖、跳过并报码) */
115
+ conflictPolicy: string
116
+ }
78
117
  /** export 端点响应(对齐 ExportFlow 的 ExportRunResult 前半部分;runId 为 m1 run 注册表标识) */
79
118
  export interface ExportResponse {
80
119
  zipPath: string;
@@ -248,6 +287,9 @@ export { ConfigManagerApiError } from './common/http.ts';
248
287
  /** 长操作请求选项(5 分钟;超时文案走默认键 `error.requestTimeout`,导出族另有专属键)。 */
249
288
  const LONG_OPTS: RequestOptions = { timeoutMs: LONG_REQUEST_TIMEOUT_MS };
250
289
 
290
+ /** 插件自更新请求的超时:宿主侧安装上限 15 分钟,客户端留出余量(16 分钟)避免误报「请求超时」。 */
291
+ const SELF_UPDATE_OPTS: RequestOptions = { timeoutMs: 16 * 60 * 1000 };
292
+
251
293
  /** query-string 辅助(跳过 undefined/空串,与 dsh-ssh 一致) */
252
294
  function query(params: Record<string, string | number | undefined>): string {
253
295
  const search = new URLSearchParams();
@@ -778,6 +820,16 @@ export class ConfigManagerApi {
778
820
  return getJson<PluginUpdateCheckResult>(url, this.t);
779
821
  }
780
822
 
823
+ /**
824
+ * 执行插件自更新(POST /update-apply)—— **只在用户显式点「立即更新」后**调用。
825
+ *
826
+ * 宿主经官方 dsh plugin 通道安装钉住的精确版本(见 core/self-update.ts);成功后必须
827
+ * 重启 DSH 才生效。安装可能较慢(慢网络下分钟级),故用独立的长超时档位。
828
+ */
829
+ async applyUpdate(version: string): Promise<PluginUpdateApplyResult> {
830
+ return postJson<PluginUpdateApplyResult>(CONFIG_MANAGER_API.updateApply, { version }, this.t, SELF_UPDATE_OPTS);
831
+ }
832
+
781
833
  // ------------------------------------------------- Phase 7 迁移前咨询
782
834
  /** 迁移前咨询(只读健康评分 + 建议):对 4 种可迁移源生成统一咨询报告。 */
783
835
  async consult(input: { type: 'export-zip' | 'local-snapshot' | 'remote-snapshot' | 'profile'; id: string; snapshotId?: string }): Promise<ConsultReport> {
@@ -785,6 +837,98 @@ export class ConfigManagerApi {
785
837
  return postJson<ConsultReport>(CONFIG_MANAGER_API.consult, input, this.t, LONG_OPTS);
786
838
  }
787
839
 
840
+ // ------------------------------------------------- 外部 agent 来源(t17)
841
+ /**
842
+ * GET /foreign-sources:六来源的本机检测结果(**只读**,零写入)。
843
+ *
844
+ * 未安装是正常状态(found=false),不是错误 —— 界面照常列出全部六条,
845
+ * 用户才知道「哪些查过了、本机确实没有」。
846
+ */
847
+ async foreignSources(opts: { projectDir?: string } = {}): Promise<ForeignSourcesResponse> {
848
+ const q = opts.projectDir !== undefined && opts.projectDir !== ''
849
+ ? query({ projectDir: opts.projectDir })
850
+ : ''
851
+ return getJson<ForeignSourcesResponse>(CONFIG_MANAGER_API.foreignSources + q, this.t, LONG_OPTS);
852
+ }
853
+
854
+ /**
855
+ * POST /foreign-import:把本机某个外部 agent 的配置翻译成标准 bundle v1 ZIP,
856
+ * 落到宿主受控临时目录并返回 zipPath —— 之后**完全复用既有导入向导**
857
+ * (analyzeImport → createImportPlan → executeImportPlan),不新建第二套向导。
858
+ *
859
+ * 为什么必须由宿主产包:转换要读本机文件系统并写 ZIP,浏览器半做不了
860
+ * (此前该能力只在 CLI 里,GUI 会在「点导入」之后断链)。
861
+ *
862
+ * `projectDir`:项目级真值位置(Cursor/Codex 的 <项目>/.cursor/**)。**缺省不传 = 不扫项目级** ——
863
+ * 宿主进程的 cwd 不是用户的项目,绝不猜。
864
+ *
865
+ * 响应**绝不含凭据值**:只有 skipped 机器码与凭据**引用名**(值在转换层已被剥离)。
866
+ */
867
+ async foreignImport(source: string, opts: { projectDir?: string } = {}): Promise<ForeignImportResponse> {
868
+ return postJson<ForeignImportResponse>(
869
+ CONFIG_MANAGER_API.foreignImport,
870
+ {
871
+ source,
872
+ ...(opts.projectDir !== undefined && opts.projectDir !== '' ? { projectDir: opts.projectDir } : {}),
873
+ },
874
+ this.t,
875
+ LONG_OPTS,
876
+ );
877
+ }
878
+
879
+ // ------------------------------------------------- F-2 官方 session.export 通道
880
+ /**
881
+ * GET /session-export(**只读**):探测 DSH 官方 session.export 端的可用性。
882
+ *
883
+ * 为什么由宿主代探而不是浏览器直接打 DSH:见 src/routes/session-export.ts 的文件头 ——
884
+ * 该端点挂在带浏览器认证的 `/api` 前缀下,浏览器侧「401 / 404 / 连不上」分不开,
885
+ * 而「路由没注册」与「持久化后端缺服务」的处理方式**完全不同**(前者保守显示、后者隐藏入口)。
886
+ *
887
+ * 返回三态(**绝不二值化**):`available` 显示 / `unavailable` 禁用并说明原因 / `unknown` 保守显示。
888
+ */
889
+ async sessionExportProbe(): Promise<SessionExportProbe> {
890
+ const body = await getJson<{ ok: boolean } & SessionExportProbe>(CONFIG_MANAGER_API.sessionExport, this.t);
891
+ return {
892
+ availability: body.availability,
893
+ ...(body.reason === undefined ? {} : { reason: body.reason }),
894
+ status: body.status,
895
+ path: body.path,
896
+ checkedAt: body.checkedAt,
897
+ };
898
+ }
899
+
900
+ /** F-2:原始日志 ZIP 的代理下载地址(探测与下载同一条路由,query 决定形态)。 */
901
+ sessionExportUrl(sessionId: string, opts: { includeDescendants?: boolean } = {}): string {
902
+ const descendants = opts.includeDescendants !== false ? 'true' : 'false';
903
+ return `${CONFIG_MANAGER_API.sessionExport}?sessionId=${encodeURIComponent(sessionId)}&includeDescendants=${descendants}`;
904
+ }
905
+
906
+ /**
907
+ * F-2:把某条会话的原始日志 ZIP 下载到本机(由 DSH 自己打包,含子会话与附件)。
908
+ *
909
+ * 复用**既有**的流式下载链路(openStream + saveDownloaded,与导出产物同一条):
910
+ * - 大 ZIP 不整本驻留内存(流式写优先,不可用时才 Blob 兜底);
911
+ * - 正文阶段是**空闲超时**(每读一块重置)—— 宿主卡死时明确失败而非永久转圈;
912
+ * - 失败时把上游(DSH)的状态码与正文如实抛出(代理路由原样透传,见 src/routes/session-export.ts)。
913
+ *
914
+ * 为什么走本插件的代理而不是直连 DSH 的 `/api/session.export`:`openStream` 认得本插件的
915
+ * 错误信封;直连 DSH 时 401/404 只是一次没有解释的失败(而 404 也可能是「拼错路径」)。
916
+ */
917
+ async downloadSessionExport(
918
+ sessionId: string,
919
+ opts?: DownloadOptions,
920
+ onProgress?: (received: number, total: number) => void,
921
+ ): Promise<DownloadResult> {
922
+ const stream = await openStream(this.sessionExportUrl(sessionId), this.t);
923
+ try {
924
+ return await this.saveDownloaded(stream, 'dsh-session-' + sessionId + '.zip', opts, onProgress);
925
+ } catch (err) {
926
+ throw stream.mapTimeout(err);
927
+ } finally {
928
+ stream.close();
929
+ }
930
+ }
931
+
788
932
  // ------------------------------------------------- P1-⑦/P2-⑬ 备份内容查看 / 差异对比
789
933
  /** 「查看备份内容」:对 exports 目录内的备份文件做只读分析(reuse /analyze,零写入)。
790
934
  * 返回分析 + 与当前配置的差异计划摘要("装/导这个备份会动你什么")。 */
@@ -31,6 +31,7 @@ import Download from 'lucide-react/dist/esm/icons/download.mjs'
31
31
  import Eye from 'lucide-react/dist/esm/icons/eye.mjs'
32
32
  import FolderInput from 'lucide-react/dist/esm/icons/folder-input.mjs'
33
33
  import HardDriveDownload from 'lucide-react/dist/esm/icons/hard-drive-download.mjs'
34
+ import History from 'lucide-react/dist/esm/icons/history.mjs'
34
35
  import Info from 'lucide-react/dist/esm/icons/info.mjs'
35
36
  import Lock from 'lucide-react/dist/esm/icons/lock.mjs'
36
37
  import MessageSquare from 'lucide-react/dist/esm/icons/message-square.mjs'
@@ -57,6 +58,7 @@ const ICONS = {
57
58
  refresh: RotateCw,
58
59
  activity: Clock,
59
60
  clock: Clock,
61
+ history: History,
60
62
  about: Info,
61
63
  info: Info,
62
64
  lock: Lock,
@@ -226,6 +228,7 @@ export const SyncIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name=
226
228
  export const RefreshIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="refresh" {...p} />
227
229
  export const ActivityIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="activity" {...p} />
228
230
  export const ClockIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="clock" {...p} />
231
+ export const HistoryIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="history" {...p} />
229
232
  export const AboutIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="about" {...p} />
230
233
  export const MessageIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="message" {...p} />
231
234
  export const PencilIcon = (p: Omit<IconProps, 'name'>): JSX.Element => <Icon name="pencil" {...p} />