dsh-config-manager 0.1.68 → 0.1.70

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 (900) hide show
  1. package/README.md +111 -17
  2. package/README.zh-CN.md +79 -8
  3. package/icon.svg +12 -7
  4. package/lib/adapters/agent-instructions.d.ts +28 -3
  5. package/lib/adapters/agent-instructions.js +26 -22
  6. package/lib/adapters/credentials.js +6 -3
  7. package/lib/adapters/file-collection.d.ts +30 -1
  8. package/lib/adapters/file-collection.js +59 -12
  9. package/lib/adapters/mcp.d.ts +12 -2
  10. package/lib/adapters/mcp.js +30 -13
  11. package/lib/adapters/plugin-files.d.ts +8 -0
  12. package/lib/adapters/plugin-files.js +36 -4
  13. package/lib/adapters/plugins.d.ts +31 -3
  14. package/lib/adapters/plugins.js +204 -55
  15. package/lib/adapters/prompts.d.ts +12 -2
  16. package/lib/adapters/prompts.js +34 -19
  17. package/lib/adapters/providers.js +5 -4
  18. package/lib/adapters/self.d.ts +71 -5
  19. package/lib/adapters/self.js +107 -49
  20. package/lib/adapters/sessions.d.ts +15 -2
  21. package/lib/adapters/sessions.js +87 -3
  22. package/lib/adapters/settings.d.ts +21 -3
  23. package/lib/adapters/settings.js +44 -7
  24. package/lib/adapters/skills.d.ts +36 -4
  25. package/lib/adapters/skills.js +222 -3
  26. package/lib/adapters/test-helpers.d.ts +31 -2
  27. package/lib/adapters/test-helpers.js +42 -5
  28. package/lib/adapters/workspaces.js +97 -5
  29. package/lib/cli/actions.d.ts +560 -0
  30. package/lib/cli/actions.js +1166 -0
  31. package/lib/cli/help.d.ts +65 -0
  32. package/lib/cli/help.js +517 -0
  33. package/lib/cli/import-source.d.ts +32 -0
  34. package/lib/cli/import-source.js +178 -0
  35. package/lib/cli/index.d.ts +24 -2
  36. package/lib/cli/index.js +256 -110
  37. package/lib/cli/sessions-inspect.d.ts +17 -0
  38. package/lib/cli/sessions-inspect.js +104 -0
  39. package/lib/cli/sessions-repair.js +12 -1
  40. package/lib/cli/web/browser.d.ts +1 -0
  41. package/lib/cli/web/browser.js +21 -0
  42. package/lib/cli/web/client-script.d.ts +12 -0
  43. package/lib/cli/web/client-script.js +256 -0
  44. package/lib/cli/web/exec.d.ts +1 -0
  45. package/lib/cli/web/exec.js +22 -0
  46. package/lib/cli/web/http.d.ts +24 -0
  47. package/lib/cli/web/http.js +14 -0
  48. package/lib/cli/web/page.d.ts +147 -0
  49. package/lib/cli/web/page.js +1020 -0
  50. package/lib/cli/web/routes.d.ts +15 -0
  51. package/lib/cli/web/routes.js +426 -0
  52. package/lib/cli/web/server.d.ts +37 -0
  53. package/lib/cli/web/server.js +210 -0
  54. package/lib/client.d.ts +2011 -142
  55. package/lib/client.js +30204 -21198
  56. package/lib/core/analyzer.d.ts +39 -2
  57. package/lib/core/analyzer.js +260 -16
  58. package/lib/core/backup-plan.js +17 -1
  59. package/lib/core/backup.d.ts +8 -0
  60. package/lib/core/backup.js +28 -6
  61. package/lib/core/cache-cleaner.d.ts +38 -1
  62. package/lib/core/cache-cleaner.js +199 -93
  63. package/lib/core/checkpoint.d.ts +362 -0
  64. package/lib/core/checkpoint.js +1309 -0
  65. package/lib/core/disk-usage.d.ts +113 -0
  66. package/lib/core/disk-usage.js +246 -0
  67. package/lib/core/importer.d.ts +6 -1
  68. package/lib/core/journal.d.ts +37 -0
  69. package/lib/core/journal.js +69 -0
  70. package/lib/core/local-plugin-host.d.ts +1 -1
  71. package/lib/core/local-plugin-host.js +17 -2
  72. package/lib/core/local-plugin-pack.d.ts +28 -0
  73. package/lib/core/local-plugin-pack.js +56 -1
  74. package/lib/core/messages.d.ts +20 -0
  75. package/lib/core/messages.js +41 -0
  76. package/lib/core/model-tools.js +3 -0
  77. package/lib/core/patch-layers.d.ts +83 -0
  78. package/lib/core/patch-layers.js +110 -0
  79. package/lib/core/recovery-orchestrator.d.ts +19 -1
  80. package/lib/core/recovery-orchestrator.js +68 -7
  81. package/lib/core/restore.d.ts +28 -1
  82. package/lib/core/restore.js +49 -5
  83. package/lib/core/rollback.js +6 -3
  84. package/lib/core/self-update.d.ts +52 -0
  85. package/lib/core/self-update.js +70 -0
  86. package/lib/core/session-health.d.ts +188 -0
  87. package/lib/core/session-health.js +189 -0
  88. package/lib/core/types.d.ts +311 -1
  89. package/lib/core/update-check.d.ts +81 -0
  90. package/lib/core/update-check.js +145 -0
  91. package/lib/core/validator.d.ts +22 -2
  92. package/lib/core/validator.js +52 -19
  93. package/lib/foreign/antigravity.d.ts +28 -0
  94. package/lib/foreign/antigravity.js +157 -0
  95. package/lib/foreign/bundle.d.ts +25 -0
  96. package/lib/foreign/bundle.js +79 -0
  97. package/lib/foreign/chatgpt.d.ts +6 -0
  98. package/lib/foreign/chatgpt.js +25 -0
  99. package/lib/foreign/claude-code.d.ts +3 -0
  100. package/lib/foreign/claude-code.js +85 -0
  101. package/lib/foreign/claude-sessions.d.ts +100 -0
  102. package/lib/foreign/claude-sessions.js +291 -0
  103. package/lib/foreign/cline.d.ts +6 -0
  104. package/lib/foreign/cline.js +27 -0
  105. package/lib/foreign/codex.d.ts +83 -0
  106. package/lib/foreign/codex.js +348 -0
  107. package/lib/foreign/continue.d.ts +9 -0
  108. package/lib/foreign/continue.js +65 -0
  109. package/lib/foreign/copilot.d.ts +39 -0
  110. package/lib/foreign/copilot.js +95 -0
  111. package/lib/foreign/crush.d.ts +11 -0
  112. package/lib/foreign/crush.js +57 -0
  113. package/lib/foreign/cursor.d.ts +87 -0
  114. package/lib/foreign/cursor.js +171 -0
  115. package/lib/foreign/dsh.d.ts +30 -0
  116. package/lib/foreign/dsh.js +101 -0
  117. package/lib/foreign/dsh4.d.ts +3 -0
  118. package/lib/foreign/dsh4.js +13 -0
  119. package/lib/foreign/gemini.d.ts +7 -0
  120. package/lib/foreign/gemini.js +44 -0
  121. package/lib/foreign/goose.d.ts +11 -0
  122. package/lib/foreign/goose.js +53 -0
  123. package/lib/foreign/grokbuild.d.ts +8 -0
  124. package/lib/foreign/grokbuild.js +25 -0
  125. package/lib/foreign/hermes.d.ts +2 -0
  126. package/lib/foreign/hermes.js +78 -0
  127. package/lib/foreign/kernel.d.ts +132 -0
  128. package/lib/foreign/kernel.js +569 -0
  129. package/lib/foreign/kilocode.d.ts +11 -0
  130. package/lib/foreign/kilocode.js +51 -0
  131. package/lib/foreign/kimi.d.ts +7 -0
  132. package/lib/foreign/kimi.js +47 -0
  133. package/lib/foreign/mimocode.d.ts +11 -0
  134. package/lib/foreign/mimocode.js +51 -0
  135. package/lib/foreign/openclaw.d.ts +7 -0
  136. package/lib/foreign/openclaw.js +46 -0
  137. package/lib/foreign/opencode.d.ts +11 -0
  138. package/lib/foreign/opencode.js +52 -0
  139. package/lib/foreign/pi.d.ts +7 -0
  140. package/lib/foreign/pi.js +48 -0
  141. package/lib/foreign/platform-paths.d.ts +66 -0
  142. package/lib/foreign/platform-paths.js +141 -0
  143. package/lib/foreign/qoder.d.ts +10 -0
  144. package/lib/foreign/qoder.js +49 -0
  145. package/lib/foreign/qwen.d.ts +7 -0
  146. package/lib/foreign/qwen.js +45 -0
  147. package/lib/foreign/read-antigravity.d.ts +57 -0
  148. package/lib/foreign/read-antigravity.js +504 -0
  149. package/lib/foreign/read-chatgpt.d.ts +20 -0
  150. package/lib/foreign/read-chatgpt.js +509 -0
  151. package/lib/foreign/read-claude-code.d.ts +24 -0
  152. package/lib/foreign/read-claude-code.js +234 -0
  153. package/lib/foreign/read-cline.d.ts +53 -0
  154. package/lib/foreign/read-cline.js +479 -0
  155. package/lib/foreign/read-codex.d.ts +75 -0
  156. package/lib/foreign/read-codex.js +842 -0
  157. package/lib/foreign/read-continue.d.ts +87 -0
  158. package/lib/foreign/read-continue.js +489 -0
  159. package/lib/foreign/read-copilot.d.ts +42 -0
  160. package/lib/foreign/read-copilot.js +267 -0
  161. package/lib/foreign/read-crush.d.ts +54 -0
  162. package/lib/foreign/read-crush.js +478 -0
  163. package/lib/foreign/read-cursor.d.ts +141 -0
  164. package/lib/foreign/read-cursor.js +705 -0
  165. package/lib/foreign/read-dsh.d.ts +43 -0
  166. package/lib/foreign/read-dsh.js +205 -0
  167. package/lib/foreign/read-dsh4.d.ts +14 -0
  168. package/lib/foreign/read-dsh4.js +27 -0
  169. package/lib/foreign/read-gemini.d.ts +55 -0
  170. package/lib/foreign/read-gemini.js +273 -0
  171. package/lib/foreign/read-goose.d.ts +25 -0
  172. package/lib/foreign/read-goose.js +322 -0
  173. package/lib/foreign/read-grokbuild.d.ts +34 -0
  174. package/lib/foreign/read-grokbuild.js +455 -0
  175. package/lib/foreign/read-hermes.d.ts +74 -0
  176. package/lib/foreign/read-hermes.js +690 -0
  177. package/lib/foreign/read-kilocode.d.ts +17 -0
  178. package/lib/foreign/read-kilocode.js +46 -0
  179. package/lib/foreign/read-kimi.d.ts +63 -0
  180. package/lib/foreign/read-kimi.js +719 -0
  181. package/lib/foreign/read-mimocode.d.ts +12 -0
  182. package/lib/foreign/read-mimocode.js +56 -0
  183. package/lib/foreign/read-openclaw.d.ts +51 -0
  184. package/lib/foreign/read-openclaw.js +445 -0
  185. package/lib/foreign/read-opencode.d.ts +150 -0
  186. package/lib/foreign/read-opencode.js +896 -0
  187. package/lib/foreign/read-pi.d.ts +56 -0
  188. package/lib/foreign/read-pi.js +411 -0
  189. package/lib/foreign/read-qoder.d.ts +54 -0
  190. package/lib/foreign/read-qoder.js +317 -0
  191. package/lib/foreign/read-qwen.d.ts +40 -0
  192. package/lib/foreign/read-qwen.js +326 -0
  193. package/lib/foreign/read-reasonix.d.ts +66 -0
  194. package/lib/foreign/read-reasonix.js +537 -0
  195. package/lib/foreign/read-teleagent.d.ts +15 -0
  196. package/lib/foreign/read-teleagent.js +68 -0
  197. package/lib/foreign/read-trae.d.ts +67 -0
  198. package/lib/foreign/read-trae.js +492 -0
  199. package/lib/foreign/read-vibe.d.ts +28 -0
  200. package/lib/foreign/read-vibe.js +364 -0
  201. package/lib/foreign/read-workbuddy.d.ts +49 -0
  202. package/lib/foreign/read-workbuddy.js +346 -0
  203. package/lib/foreign/read-zcode.d.ts +12 -0
  204. package/lib/foreign/read-zcode.js +38 -0
  205. package/lib/foreign/read-zed.d.ts +30 -0
  206. package/lib/foreign/read-zed.js +473 -0
  207. package/lib/foreign/reasonix.d.ts +6 -0
  208. package/lib/foreign/reasonix.js +20 -0
  209. package/lib/foreign/registry.d.ts +196 -0
  210. package/lib/foreign/registry.js +573 -0
  211. package/lib/foreign/session-ir.d.ts +186 -0
  212. package/lib/foreign/session-ir.js +391 -0
  213. package/lib/foreign/session-read.d.ts +102 -0
  214. package/lib/foreign/session-read.js +254 -0
  215. package/lib/foreign/session-source.d.ts +216 -0
  216. package/lib/foreign/session-source.js +453 -0
  217. package/lib/foreign/source-modules.d.ts +45 -0
  218. package/lib/foreign/source-modules.js +52 -0
  219. package/lib/foreign/sqlite.d.ts +61 -0
  220. package/lib/foreign/sqlite.js +404 -0
  221. package/lib/foreign/teleagent.d.ts +11 -0
  222. package/lib/foreign/teleagent.js +53 -0
  223. package/lib/foreign/trae.d.ts +6 -0
  224. package/lib/foreign/trae.js +20 -0
  225. package/lib/foreign/truth-table.d.ts +62 -0
  226. package/lib/foreign/truth-table.js +333 -0
  227. package/lib/foreign/types.d.ts +272 -0
  228. package/lib/foreign/types.js +2 -0
  229. package/lib/foreign/vibe.d.ts +6 -0
  230. package/lib/foreign/vibe.js +23 -0
  231. package/lib/foreign/workbuddy.d.ts +7 -0
  232. package/lib/foreign/workbuddy.js +45 -0
  233. package/lib/foreign/zcode.d.ts +11 -0
  234. package/lib/foreign/zcode.js +51 -0
  235. package/lib/foreign/zed.d.ts +11 -0
  236. package/lib/foreign/zed.js +53 -0
  237. package/lib/index.d.ts +210 -15
  238. package/lib/index.js +857 -86
  239. package/lib/market/github-repos.d.ts +28 -0
  240. package/lib/market/github-repos.js +43 -1
  241. package/lib/market/types.d.ts +1 -3
  242. package/lib/profiles/dsh-profile-manager.d.ts +56 -2
  243. package/lib/profiles/dsh-profile-manager.js +198 -8
  244. package/lib/profiles/dsh-profile-shared.d.ts +66 -0
  245. package/lib/profiles/dsh-profile-shared.js +15 -1
  246. package/lib/profiles/index.d.ts +1 -1
  247. package/lib/profiles/index.js +1 -1
  248. package/lib/routes/backup.js +155 -1
  249. package/lib/routes/consult.js +61 -7
  250. package/lib/routes/foreign-context.d.ts +43 -0
  251. package/lib/routes/foreign-context.js +65 -0
  252. package/lib/routes/foreign.d.ts +11 -0
  253. package/lib/routes/foreign.js +208 -0
  254. package/lib/routes/import.d.ts +8 -0
  255. package/lib/routes/import.js +43 -11
  256. package/lib/routes/index.d.ts +2 -2
  257. package/lib/routes/index.js +4 -0
  258. package/lib/routes/kit.d.ts +12 -3
  259. package/lib/routes/kit.js +8 -0
  260. package/lib/routes/market.js +0 -1
  261. package/lib/routes/prefs.d.ts +3 -1
  262. package/lib/routes/prefs.js +45 -2
  263. package/lib/routes/profiles.js +2 -0
  264. package/lib/routes/recovery.js +528 -2
  265. package/lib/routes/session-export.d.ts +117 -0
  266. package/lib/routes/session-export.js +270 -0
  267. package/lib/routes/session-format.d.ts +25 -0
  268. package/lib/routes/session-format.js +54 -0
  269. package/lib/routes/snapshots.js +12 -2
  270. package/lib/routes/sync.js +218 -13
  271. package/lib/security/container-kind.d.ts +15 -0
  272. package/lib/security/container-kind.js +48 -0
  273. package/lib/security/index.d.ts +2 -1
  274. package/lib/security/index.js +2 -1
  275. package/lib/security/redaction.d.ts +14 -0
  276. package/lib/security/redaction.js +46 -10
  277. package/lib/security/secret-scanner.d.ts +27 -0
  278. package/lib/security/secret-scanner.js +54 -1
  279. package/lib/sync/autosync-scheduler.d.ts +13 -1
  280. package/lib/sync/autosync-scheduler.js +49 -26
  281. package/lib/sync/backup-files.d.ts +28 -2
  282. package/lib/sync/backup-files.js +25 -5
  283. package/lib/sync/gist/gist-transport.d.ts +91 -0
  284. package/lib/sync/gist/gist-transport.js +406 -0
  285. package/lib/sync/gist/messages.d.ts +22 -0
  286. package/lib/sync/gist/messages.js +46 -0
  287. package/lib/sync/git/git-transport.d.ts +27 -2
  288. package/lib/sync/git/git-transport.js +49 -7
  289. package/lib/sync/s3/messages.d.ts +53 -0
  290. package/lib/sync/s3/messages.js +83 -0
  291. package/lib/sync/s3/s3-providers.d.ts +78 -0
  292. package/lib/sync/s3/s3-providers.js +156 -0
  293. package/lib/sync/s3/s3-transport.d.ts +126 -0
  294. package/lib/sync/s3/s3-transport.js +551 -0
  295. package/lib/sync/s3/sigv4.d.ts +107 -0
  296. package/lib/sync/s3/sigv4.js +196 -0
  297. package/lib/sync/snapshot-json.d.ts +8 -1
  298. package/lib/sync/snapshot-json.js +10 -3
  299. package/lib/sync/sync-config.d.ts +275 -4
  300. package/lib/sync/sync-config.js +677 -30
  301. package/lib/sync/sync-engine.d.ts +55 -0
  302. package/lib/sync/sync-engine.js +77 -7
  303. package/lib/sync/sync-history.d.ts +1 -1
  304. package/lib/sync/transport.js +4 -1
  305. package/lib/sync/ui-prefs.d.ts +10 -0
  306. package/lib/sync/ui-prefs.js +6 -0
  307. package/lib/sync/webdav/webdav-transport.d.ts +43 -3
  308. package/lib/sync/webdav/webdav-transport.js +124 -20
  309. package/lib/ui/artifact-view.d.ts +150 -0
  310. package/lib/ui/artifact-view.js +264 -0
  311. package/lib/ui/commands.d.ts +73 -0
  312. package/lib/ui/commands.js +185 -0
  313. package/lib/ui/disk-usage-view.d.ts +121 -0
  314. package/lib/ui/disk-usage-view.js +158 -0
  315. package/lib/ui/dsh-profiles-view.d.ts +70 -0
  316. package/lib/ui/dsh-profiles-view.js +73 -0
  317. package/lib/ui/foreign-view.d.ts +162 -0
  318. package/lib/ui/foreign-view.js +225 -0
  319. package/lib/ui/history-model.d.ts +1 -1
  320. package/lib/ui/history-model.js +1 -1
  321. package/lib/ui/i18n.d.ts +193 -0
  322. package/lib/ui/i18n.js +398 -0
  323. package/lib/ui/import-wizard.d.ts +25 -1
  324. package/lib/ui/import-wizard.js +69 -1
  325. package/lib/ui/menu-placement.d.ts +56 -0
  326. package/lib/ui/menu-placement.js +63 -0
  327. package/lib/ui/mid-ellipsis.d.ts +3 -2
  328. package/lib/ui/mid-ellipsis.js +3 -2
  329. package/lib/ui/nav-model.d.ts +34 -0
  330. package/lib/ui/nav-model.js +70 -0
  331. package/lib/ui/next-steps.d.ts +23 -1
  332. package/lib/ui/next-steps.js +19 -2
  333. package/lib/ui/overview-view.d.ts +1 -1
  334. package/lib/ui/overview-view.js +1 -1
  335. package/lib/ui/runs-view.d.ts +1 -1
  336. package/lib/ui/runs-view.js +1 -1
  337. package/lib/ui/session-format-disposition.d.ts +108 -0
  338. package/lib/ui/session-format-disposition.js +122 -0
  339. package/lib/ui/session-inventory-view.d.ts +361 -0
  340. package/lib/ui/session-inventory-view.js +239 -0
  341. package/lib/ui/session-layout-view.d.ts +265 -0
  342. package/lib/ui/session-layout-view.js +271 -0
  343. package/lib/ui/sync-repository-picker.d.ts +98 -0
  344. package/lib/ui/sync-repository-picker.js +145 -0
  345. package/lib/ui/sync-settings-view.d.ts +143 -4
  346. package/lib/ui/sync-settings-view.js +231 -7
  347. package/lib/ui/test-helpers.d.ts +6 -2
  348. package/lib/ui/test-helpers.js +1 -1
  349. package/lib/ui/types.d.ts +161 -1
  350. package/lib/utils/asar-read.d.ts +113 -0
  351. package/lib/utils/asar-read.js +523 -0
  352. package/lib/utils/env-lock.js +7 -2
  353. package/lib/utils/json.d.ts +13 -0
  354. package/lib/utils/json.js +13 -0
  355. package/lib/utils/patch-yaml.d.ts +25 -0
  356. package/lib/utils/patch-yaml.js +62 -0
  357. package/lib/utils/paths.d.ts +10 -1
  358. package/lib/utils/paths.js +24 -3
  359. package/lib/utils/recursive-walk.js +12 -13
  360. package/lib/utils/session-format.d.ts +65 -0
  361. package/lib/utils/session-format.js +233 -0
  362. package/lib/utils/session-health-scan.d.ts +48 -0
  363. package/lib/utils/session-health-scan.js +676 -0
  364. package/lib/utils/session-layout-repair-service.d.ts +98 -0
  365. package/lib/utils/session-layout-repair-service.js +457 -0
  366. package/lib/utils/session-log-repair.d.ts +154 -0
  367. package/lib/utils/session-log-repair.js +655 -0
  368. package/lib/utils/session-log.d.ts +28 -0
  369. package/lib/utils/session-log.js +65 -8
  370. package/lib/utils/session-repair-service.d.ts +175 -0
  371. package/lib/utils/session-repair-service.js +492 -0
  372. package/lib/utils/session-row-facts.d.ts +126 -0
  373. package/lib/utils/session-row-facts.js +290 -0
  374. package/lib/utils/session-verify.d.ts +118 -0
  375. package/lib/utils/session-verify.js +705 -0
  376. package/lib/utils/shared-constants.d.ts +82 -0
  377. package/lib/utils/shared-constants.js +95 -0
  378. package/lib/utils/ttl-cache.d.ts +40 -0
  379. package/lib/utils/ttl-cache.js +66 -0
  380. package/package.json +7 -5
  381. package/src/adapters/agent-instructions.test.ts +113 -0
  382. package/src/adapters/agent-instructions.ts +28 -23
  383. package/src/adapters/credentials.test.ts +27 -0
  384. package/src/adapters/credentials.ts +6 -3
  385. package/src/adapters/file-collection.test.ts +71 -0
  386. package/src/adapters/file-collection.ts +63 -11
  387. package/src/adapters/files.test.ts +222 -0
  388. package/src/adapters/mcp.test.ts +43 -0
  389. package/src/adapters/mcp.ts +37 -14
  390. package/src/adapters/plugin-files.ts +34 -4
  391. package/src/adapters/plugins.test.ts +244 -1
  392. package/src/adapters/plugins.ts +203 -54
  393. package/src/adapters/prompts.test.ts +49 -0
  394. package/src/adapters/prompts.ts +41 -20
  395. package/src/adapters/providers.test.ts +16 -0
  396. package/src/adapters/providers.ts +4 -3
  397. package/src/adapters/self.test.ts +125 -0
  398. package/src/adapters/self.ts +120 -29
  399. package/src/adapters/sessions.test.ts +189 -0
  400. package/src/adapters/sessions.ts +95 -4
  401. package/src/adapters/settings.test.ts +78 -0
  402. package/src/adapters/settings.ts +42 -6
  403. package/src/adapters/skills.ts +221 -3
  404. package/src/adapters/test-helpers.ts +43 -5
  405. package/src/adapters/workspaces-dup-id.test.ts +144 -0
  406. package/src/adapters/workspaces.test.ts +10 -0
  407. package/src/adapters/workspaces.ts +97 -5
  408. package/src/cli/actions.test.ts +548 -0
  409. package/src/cli/actions.ts +1560 -0
  410. package/src/cli/help.ts +570 -0
  411. package/src/cli/import-source.test.ts +193 -0
  412. package/src/cli/import-source.ts +229 -0
  413. package/src/cli/index.ts +284 -114
  414. package/src/cli/sessions-inspect.test.ts +90 -0
  415. package/src/cli/sessions-inspect.ts +114 -0
  416. package/src/cli/sessions-repair.ts +11 -1
  417. package/src/cli/web/browser.ts +20 -0
  418. package/src/cli/web/client-script.ts +259 -0
  419. package/src/cli/web/console-fixes.test.ts +36 -0
  420. package/src/cli/web/exec.ts +23 -0
  421. package/src/cli/web/http.ts +36 -0
  422. package/src/cli/web/page.ts +1203 -0
  423. package/src/cli/web/routes.ts +521 -0
  424. package/src/cli/web/script-anchor.test.ts +33 -0
  425. package/src/cli/web/server.ts +260 -0
  426. package/src/cli/web/web.test.ts +1446 -0
  427. package/src/client/CLIENT_DEPENDENCIES.md +10 -1
  428. package/src/client/ConfigManagerSection.tsx +437 -171
  429. package/src/client/about/AboutPanel.tsx +123 -9
  430. package/src/client/about/PluginUpdateDialog.tsx +185 -0
  431. package/src/client/about/ReleaseBody.tsx +144 -0
  432. package/src/client/about/ReleaseNotesDialog.tsx +3 -130
  433. package/src/client/about/about-view.test.ts +42 -2
  434. package/src/client/about/about-view.ts +64 -6
  435. package/src/client/about/plugin-update-view.test.ts +25 -0
  436. package/src/client/about/plugin-update-view.ts +26 -0
  437. package/src/client/about/release-notes-view.test.ts +33 -0
  438. package/src/client/about/release-notes-view.ts +34 -0
  439. package/src/client/api.ts +197 -4
  440. package/src/client/client-types.ts +5 -0
  441. package/src/client/common/CommandPalette.tsx +173 -0
  442. package/src/client/common/ContentPicker.tsx +4 -1
  443. package/src/client/common/CopyButton.tsx +9 -12
  444. package/src/client/common/Icon.tsx +7 -0
  445. package/src/client/common/InfoHint.tsx +280 -0
  446. package/src/client/common/Modal.tsx +14 -2
  447. package/src/client/common/MoreMenu.tsx +195 -0
  448. package/src/client/common/RunsCenter.tsx +20 -12
  449. package/src/client/common/Select.tsx +192 -42
  450. package/src/client/common/ToastViewport.tsx +1 -1
  451. package/src/client/common/client-route-parity.test.ts +10 -0
  452. package/src/client/common/clipboard.test.ts +42 -0
  453. package/src/client/common/clipboard.ts +19 -0
  454. package/src/client/common/compat-label.test.ts +43 -0
  455. package/src/client/common/compat-label.ts +28 -0
  456. package/src/client/common/floating-bounds.ts +48 -0
  457. package/src/client/common/floating-units.test.ts +71 -0
  458. package/src/client/common/http.test.ts +24 -0
  459. package/src/client/common/http.ts +19 -3
  460. package/src/client/common/info-hint-guard.test.ts +881 -0
  461. package/src/client/common/menu-align.ts +29 -0
  462. package/src/client/common/menu-scroll-guard.test.ts +61 -0
  463. package/src/client/common/plan-text-redaction.test.ts +200 -14
  464. package/src/client/common/routes.ts +33 -0
  465. package/src/client/common/ui.tsx +1 -1
  466. package/src/client/config-manager.module.css +1308 -473
  467. package/src/client/css-scale-guard.test.ts +122 -0
  468. package/src/client/css-token-guard.test.ts +82 -0
  469. package/src/client/{profiles/ProfilesPanel.tsx → environment/EnvironmentPanel.tsx} +133 -34
  470. package/src/client/environment/maintenance/DiskUsageCard.tsx +257 -0
  471. package/src/client/environment/profiles-incomplete-ui.test.ts +103 -0
  472. package/src/client/export/ExportView.tsx +27 -12
  473. package/src/client/history/HistoryPanel.tsx +2 -2
  474. package/src/client/{snapshots → home}/BackupScheduleCard.tsx +95 -117
  475. package/src/client/{overview/OverviewPanel.tsx → home/HomePanel.tsx} +161 -176
  476. package/src/client/icon-layer-guard.test.ts +1 -1
  477. package/src/client/import/ForeignImportView.tsx +318 -0
  478. package/src/client/import/ImportWizardView.tsx +113 -17
  479. package/src/client/import/foreign-import-view-guard.test.ts +195 -0
  480. package/src/client/import/import-file-select.test.ts +53 -0
  481. package/src/client/import/import-wizard-steps.tsx +118 -2
  482. package/src/client/import/use-import-wizard-controller.ts +116 -8
  483. package/src/client/index.ts +3 -3
  484. package/src/client/library/ArtifactDetail.tsx +34 -0
  485. package/src/client/library/ArtifactRow.tsx +128 -0
  486. package/src/client/library/BackupInspectView.tsx +96 -0
  487. package/src/client/library/LibraryActions.tsx +635 -0
  488. package/src/client/library/LibraryPanel.tsx +253 -0
  489. package/src/client/{snapshots → library}/RestorePlanView.tsx +1 -1
  490. package/src/client/library/files-layout.test.ts +90 -0
  491. package/src/client/library/report-dismiss.test.ts +86 -0
  492. package/src/client/library/task-panel-visibility.test.ts +50 -0
  493. package/src/client/locales.ts +483 -36
  494. package/src/client/lucide-icons.d.ts +2 -0
  495. package/src/client/market/MarketImportReview.tsx +8 -2
  496. package/src/client/market/MarketPanel.tsx +62 -5
  497. package/src/client/market/MyConfigsLoginCard.tsx +2 -1
  498. package/src/client/market/MyConfigsWizard.tsx +4 -2
  499. package/src/client/market/market-locales.ts +5 -0
  500. package/src/client/recovery/IncompleteCopiesSection.tsx +132 -0
  501. package/src/client/recovery/RecoveryPanel.tsx +1048 -13
  502. package/src/client/recovery/recovery-api.ts +81 -1
  503. package/src/client/recovery/recovery-incomplete-copies.test.ts +151 -0
  504. package/src/client/recovery/recovery-locales.test.ts +88 -0
  505. package/src/client/recovery/recovery-locales.ts +501 -0
  506. package/src/client/recovery/recovery-panel-layout.test.ts +52 -0
  507. package/src/client/recovery/recovery-view.test.ts +38 -0
  508. package/src/client/recovery/recovery-view.ts +104 -2
  509. package/src/client/recovery/session-dialog-mount.test.ts +58 -0
  510. package/src/client/run-store.test.ts +262 -116
  511. package/src/client/run-store.ts +231 -153
  512. package/src/client/session-export/session-export-view.test.ts +103 -0
  513. package/src/client/session-export/session-export-view.ts +118 -0
  514. package/src/client/sync/AutosyncCard.tsx +5 -6
  515. package/src/client/sync/ChannelClearConfirmDialog.tsx +58 -0
  516. package/src/client/sync/ChannelConfigDialog.tsx +246 -55
  517. package/src/client/sync/DecryptPasswordCard.tsx +2 -2
  518. package/src/client/sync/SecurityOptionsCard.tsx +2 -2
  519. package/src/client/sync/SyncChannelEntryCard.tsx +72 -8
  520. package/src/client/sync/SyncConfirmView.tsx +85 -26
  521. package/src/client/sync/{SyncHistoryView.tsx → SyncLogList.tsx} +84 -103
  522. package/src/client/sync/{SyncSettingsView.tsx → SyncPanel.tsx} +464 -261
  523. package/src/client/sync/SyncRepositoryPicker.tsx +216 -0
  524. package/src/client/sync/SyncSectionPickerDialog.tsx +20 -9
  525. package/src/client/sync/SyncSettingsView.test.ts +45 -5
  526. package/src/client/sync/UnreadableSnapshotsBanner.tsx +35 -0
  527. package/src/client/sync/history-model.test.ts +94 -57
  528. package/src/client/sync/history-model.ts +46 -27
  529. package/src/client/sync/sync-api.test.ts +56 -0
  530. package/src/client/sync/sync-api.ts +158 -1
  531. package/src/client/sync/sync-locales.ts +214 -2
  532. package/src/client/sync/sync-view-v2.test.ts +54 -2
  533. package/src/client/sync/sync-view.test.ts +3 -21
  534. package/src/client/sync/sync-view.ts +178 -44
  535. package/src/client/task/TaskShell.tsx +83 -0
  536. package/src/core/analyzer-cache.test.ts +47 -0
  537. package/src/core/analyzer.ts +278 -19
  538. package/src/core/backup-plan.test.ts +35 -0
  539. package/src/core/backup-plan.ts +19 -1
  540. package/src/core/backup.test.ts +37 -1
  541. package/src/core/backup.ts +27 -6
  542. package/src/core/cache-cleaner.test.ts +186 -1
  543. package/src/core/cache-cleaner.ts +155 -35
  544. package/src/core/checkpoint.test.ts +665 -0
  545. package/src/core/checkpoint.ts +1614 -0
  546. package/src/core/compatibility-reasons.test.ts +100 -0
  547. package/src/core/credential-plan-consistency.test.ts +93 -0
  548. package/src/core/disk-usage.test.ts +179 -0
  549. package/src/core/disk-usage.ts +360 -0
  550. package/src/core/import-sessions-visibility.test.ts +183 -7
  551. package/src/core/importer.ts +3 -1
  552. package/src/core/incident-wiring.test.ts +10 -1
  553. package/src/core/journal.test.ts +70 -0
  554. package/src/core/journal.ts +68 -0
  555. package/src/core/local-plugin-host.ts +17 -3
  556. package/src/core/local-plugin-pack.test.ts +101 -0
  557. package/src/core/local-plugin-pack.ts +70 -1
  558. package/src/core/messages.ts +41 -0
  559. package/src/core/model-tools.ts +3 -0
  560. package/src/core/patch-layers.test.ts +94 -0
  561. package/src/core/patch-layers.ts +150 -0
  562. package/src/core/recovery-orchestrator.ts +84 -9
  563. package/src/core/restore.ts +74 -5
  564. package/src/core/rollback.ts +6 -3
  565. package/src/core/section-content-consistency.test.ts +185 -0
  566. package/src/core/self-update.test.ts +110 -0
  567. package/src/core/self-update.ts +118 -0
  568. package/src/core/session-health.test.ts +314 -0
  569. package/src/core/session-health.ts +352 -0
  570. package/src/core/smoke.test.ts +14 -0
  571. package/src/core/types.ts +290 -2
  572. package/src/core/update-check.test.ts +149 -0
  573. package/src/core/update-check.ts +170 -0
  574. package/src/core/validator.ts +53 -21
  575. package/src/foreign/antigravity.test.ts +271 -0
  576. package/src/foreign/antigravity.ts +188 -0
  577. package/src/foreign/bundle.ts +108 -0
  578. package/src/foreign/chatgpt.test.ts +279 -0
  579. package/src/foreign/chatgpt.ts +30 -0
  580. package/src/foreign/claude-code.test.ts +234 -0
  581. package/src/foreign/claude-code.ts +106 -0
  582. package/src/foreign/claude-sessions-bytes.test.ts +214 -0
  583. package/src/foreign/claude-sessions.test.ts +155 -0
  584. package/src/foreign/claude-sessions.ts +453 -0
  585. package/src/foreign/cline.test.ts +274 -0
  586. package/src/foreign/cline.ts +32 -0
  587. package/src/foreign/codex.test.ts +585 -0
  588. package/src/foreign/codex.ts +409 -0
  589. package/src/foreign/continue.test.ts +365 -0
  590. package/src/foreign/continue.ts +73 -0
  591. package/src/foreign/copilot.test.ts +273 -0
  592. package/src/foreign/copilot.ts +133 -0
  593. package/src/foreign/crush.test.ts +234 -0
  594. package/src/foreign/crush.ts +66 -0
  595. package/src/foreign/cursor.test.ts +509 -0
  596. package/src/foreign/cursor.ts +259 -0
  597. package/src/foreign/dsh.test.ts +198 -0
  598. package/src/foreign/dsh.ts +117 -0
  599. package/src/foreign/dsh4.test.ts +127 -0
  600. package/src/foreign/dsh4.ts +15 -0
  601. package/src/foreign/file-budget.test.ts +256 -0
  602. package/src/foreign/fixtures/antigravity/README.md +18 -0
  603. package/src/foreign/fixtures/antigravity/basic/gemini/antigravity/mcp_config.json +13 -0
  604. package/src/foreign/fixtures/antigravity/basic/gemini/config/mcp_config.json +24 -0
  605. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_config.json +0 -0
  606. package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_oauth_tokens.json +0 -0
  607. package/src/foreign/fixtures/antigravity/empty/gemini/config/mcp_config.json +0 -0
  608. package/src/foreign/fixtures/codex/README.md +31 -0
  609. package/src/foreign/fixtures/codex/basic/.agents/skills/hello/SKILL.md +6 -0
  610. package/src/foreign/fixtures/codex/basic/.agents/skills/wrapper/nested/SKILL.md +6 -0
  611. package/src/foreign/fixtures/codex/basic/.codex/AGENTS.md +4 -0
  612. package/src/foreign/fixtures/codex/basic/.codex/config.toml +28 -0
  613. package/src/foreign/fixtures/codex/malformed/.codex/config.toml +3 -0
  614. package/src/foreign/fixtures/codex/override/.codex/AGENTS.md +1 -0
  615. package/src/foreign/fixtures/codex/override/.codex/AGENTS.override.md +3 -0
  616. package/src/foreign/fixtures/codex/sessions/.codex/archived_sessions/rollout-2026-10-05T09-00-00-22222222-2222-4222-8222-222222222222.jsonl +2 -0
  617. 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
  618. 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
  619. 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
  620. 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
  621. package/src/foreign/fixtures/codex/sessions/.codex/sessions/notes.jsonl +1 -0
  622. package/src/foreign/fixtures/copilot/README.md +22 -0
  623. package/src/foreign/fixtures/copilot/basic/copilot-instructions.md +3 -0
  624. package/src/foreign/fixtures/copilot/basic/instructions/empty.instructions.md +1 -0
  625. package/src/foreign/fixtures/copilot/basic/instructions/repo.instructions.md +5 -0
  626. package/src/foreign/fixtures/copilot/basic/mcp-config.json +24 -0
  627. package/src/foreign/fixtures/copilot/basic/skills/hello/SKILL.md +6 -0
  628. package/src/foreign/fixtures/copilot/overridden/mcp-config.json +10 -0
  629. package/src/foreign/fixtures/cursor/README.md +17 -0
  630. package/src/foreign/fixtures/cursor/alt/.cursor/mcp.json +5 -0
  631. package/src/foreign/fixtures/cursor/basic/.cursor/mcp.json +18 -0
  632. package/src/foreign/fixtures/cursor/basic/.cursor/rules/agent.mdc +5 -0
  633. package/src/foreign/fixtures/cursor/basic/.cursor/rules/always.mdc +6 -0
  634. package/src/foreign/fixtures/cursor/basic/.cursor/rules/auto.mdc +7 -0
  635. package/src/foreign/fixtures/cursor/basic/.cursor/rules/manual.mdc +3 -0
  636. package/src/foreign/fixtures/cursor/basic/.cursor/skills/.hidden/SKILL.md +6 -0
  637. package/src/foreign/fixtures/cursor/basic/.cursor/skills/hello/SKILL.md +6 -0
  638. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/SKILL.md +6 -0
  639. package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/notes.md +1 -0
  640. package/src/foreign/fixtures/cursor/basic/.cursorrules +1 -0
  641. package/src/foreign/fixtures/cursor/basic/project/.cursor/mcp.json +9 -0
  642. package/src/foreign/fixtures/cursor/basic/project/.cursor/rules/project-always.mdc +5 -0
  643. package/src/foreign/fixtures/cursor/basic/project/.cursor/skills/hello/SKILL.md +6 -0
  644. package/src/foreign/fixtures/cursor/basic/project/.cursorrules +1 -0
  645. package/src/foreign/fixtures/cursor/empty/.cursor/mcp.json +0 -0
  646. package/src/foreign/fixtures/cursor/malformed/.cursor/mcp.json +3 -0
  647. package/src/foreign/fixtures/cursor/malformed/.cursor/rules/broken.mdc +6 -0
  648. package/src/foreign/fixtures/cursor/shape/.cursor/mcp.json +3 -0
  649. package/src/foreign/fixtures/hermes/README.md +14 -0
  650. package/src/foreign/fixtures/hermes/basic/SOUL.md +3 -0
  651. package/src/foreign/fixtures/hermes/basic/config.yaml +48 -0
  652. package/src/foreign/fixtures/hermes/basic/dotenv.fixture +1 -0
  653. package/src/foreign/fixtures/hermes/basic/memories/MEMORY.md +1 -0
  654. package/src/foreign/fixtures/hermes/basic/memories/USER.md +1 -0
  655. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/SKILL.md +6 -0
  656. package/src/foreign/fixtures/hermes/basic/skills/se-team-design/examples/usage.md +1 -0
  657. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/SKILL.md +6 -0
  658. package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/notes.md +1 -0
  659. package/src/foreign/fixtures/hermes/basic/state.db +1 -0
  660. package/src/foreign/fixtures/hermes/empty/config.yaml +0 -0
  661. package/src/foreign/gemini.test.ts +291 -0
  662. package/src/foreign/gemini.ts +52 -0
  663. package/src/foreign/goose.test.ts +196 -0
  664. package/src/foreign/goose.ts +62 -0
  665. package/src/foreign/grokbuild.test.ts +246 -0
  666. package/src/foreign/grokbuild.ts +30 -0
  667. package/src/foreign/hermes.test.ts +545 -0
  668. package/src/foreign/hermes.ts +86 -0
  669. package/src/foreign/kernel-credentials.test.ts +88 -0
  670. package/src/foreign/kernel-workspaces.test.ts +88 -0
  671. package/src/foreign/kernel.ts +584 -0
  672. package/src/foreign/kilocode.test.ts +186 -0
  673. package/src/foreign/kilocode.ts +60 -0
  674. package/src/foreign/kimi.test.ts +259 -0
  675. package/src/foreign/kimi.ts +55 -0
  676. package/src/foreign/mcp-value-shape.test.ts +286 -0
  677. package/src/foreign/mimocode.test.ts +178 -0
  678. package/src/foreign/mimocode.ts +60 -0
  679. package/src/foreign/openclaw.test.ts +248 -0
  680. package/src/foreign/openclaw.ts +54 -0
  681. package/src/foreign/opencode.test.ts +376 -0
  682. package/src/foreign/opencode.ts +61 -0
  683. package/src/foreign/pi.test.ts +275 -0
  684. package/src/foreign/pi.ts +56 -0
  685. package/src/foreign/platform-paths.ts +156 -0
  686. package/src/foreign/qoder.test.ts +266 -0
  687. package/src/foreign/qoder.ts +57 -0
  688. package/src/foreign/qwen.test.ts +203 -0
  689. package/src/foreign/qwen.ts +53 -0
  690. package/src/foreign/read-antigravity.ts +569 -0
  691. package/src/foreign/read-chatgpt.ts +506 -0
  692. package/src/foreign/read-claude-code.ts +264 -0
  693. package/src/foreign/read-cline.ts +517 -0
  694. package/src/foreign/read-codex.ts +823 -0
  695. package/src/foreign/read-continue.ts +557 -0
  696. package/src/foreign/read-copilot.ts +304 -0
  697. package/src/foreign/read-crush.ts +503 -0
  698. package/src/foreign/read-cursor.ts +778 -0
  699. package/src/foreign/read-dsh.ts +215 -0
  700. package/src/foreign/read-dsh4.ts +35 -0
  701. package/src/foreign/read-gemini.ts +306 -0
  702. package/src/foreign/read-goose.ts +333 -0
  703. package/src/foreign/read-grokbuild.ts +475 -0
  704. package/src/foreign/read-hermes.ts +724 -0
  705. package/src/foreign/read-kilocode.ts +55 -0
  706. package/src/foreign/read-kimi.ts +768 -0
  707. package/src/foreign/read-mimocode.ts +66 -0
  708. package/src/foreign/read-openclaw.ts +505 -0
  709. package/src/foreign/read-opencode.ts +994 -0
  710. package/src/foreign/read-pi.ts +459 -0
  711. package/src/foreign/read-qoder.ts +351 -0
  712. package/src/foreign/read-qwen.ts +342 -0
  713. package/src/foreign/read-reasonix.ts +590 -0
  714. package/src/foreign/read-teleagent.ts +82 -0
  715. package/src/foreign/read-trae.ts +488 -0
  716. package/src/foreign/read-vibe.ts +353 -0
  717. package/src/foreign/read-workbuddy.ts +374 -0
  718. package/src/foreign/read-zcode.ts +48 -0
  719. package/src/foreign/read-zed.ts +479 -0
  720. package/src/foreign/reasonix.test.ts +334 -0
  721. package/src/foreign/reasonix.ts +25 -0
  722. package/src/foreign/registry.test.ts +377 -0
  723. package/src/foreign/registry.ts +750 -0
  724. package/src/foreign/session-ir.test.ts +136 -0
  725. package/src/foreign/session-ir.ts +579 -0
  726. package/src/foreign/session-read.ts +295 -0
  727. package/src/foreign/session-source.ts +625 -0
  728. package/src/foreign/skill-file-caps.test.ts +171 -0
  729. package/src/foreign/source-modules.ts +86 -0
  730. package/src/foreign/source-registry.test.ts +190 -0
  731. package/src/foreign/sqlite.test.ts +313 -0
  732. package/src/foreign/sqlite.ts +446 -0
  733. package/src/foreign/teleagent.test.ts +147 -0
  734. package/src/foreign/teleagent.ts +62 -0
  735. package/src/foreign/trae.test.ts +316 -0
  736. package/src/foreign/trae.ts +25 -0
  737. package/src/foreign/truncation-report.test.ts +238 -0
  738. package/src/foreign/truth-table.ts +453 -0
  739. package/src/foreign/types.ts +312 -0
  740. package/src/foreign/vibe.test.ts +209 -0
  741. package/src/foreign/vibe.ts +28 -0
  742. package/src/foreign/workbuddy.test.ts +207 -0
  743. package/src/foreign/workbuddy.ts +53 -0
  744. package/src/foreign/zcode.test.ts +224 -0
  745. package/src/foreign/zcode.ts +60 -0
  746. package/src/foreign/zed.test.ts +240 -0
  747. package/src/foreign/zed.ts +62 -0
  748. package/src/index.facade.test.ts +254 -16
  749. package/src/index.ts +907 -84
  750. package/src/market/github-repos.test.ts +98 -1
  751. package/src/market/github-repos.ts +63 -1
  752. package/src/market/types.ts +3 -3
  753. package/src/profiles/dsh-profile-incomplete-copy.test.ts +177 -0
  754. package/src/profiles/dsh-profile-manager.test.ts +63 -0
  755. package/src/profiles/dsh-profile-manager.ts +203 -10
  756. package/src/profiles/dsh-profile-shared.ts +74 -1
  757. package/src/profiles/index.ts +2 -1
  758. package/src/routes/backup.ts +167 -1
  759. package/src/routes/consult.ts +63 -6
  760. package/src/routes/foreign-context.ts +89 -0
  761. package/src/routes/foreign.test.ts +385 -0
  762. package/src/routes/foreign.ts +226 -0
  763. package/src/routes/import.ts +44 -9
  764. package/src/routes/index.ts +6 -2
  765. package/src/routes/kit.ts +17 -3
  766. package/src/routes/market.ts +0 -1
  767. package/src/routes/prefs.ts +45 -1
  768. package/src/routes/profiles.ts +2 -0
  769. package/src/routes/recovery.test.ts +167 -0
  770. package/src/routes/recovery.ts +482 -1
  771. package/src/routes/session-export.ts +308 -0
  772. package/src/routes/session-format.test.ts +59 -0
  773. package/src/routes/session-format.ts +74 -0
  774. package/src/routes/snapshots.ts +12 -2
  775. package/src/routes/sync.ts +229 -9
  776. package/src/security/container-kind.test.ts +92 -0
  777. package/src/security/container-kind.ts +51 -0
  778. package/src/security/index.ts +2 -1
  779. package/src/security/redaction.test.ts +149 -0
  780. package/src/security/redaction.ts +45 -10
  781. package/src/security/secret-scanner.test.ts +186 -0
  782. package/src/security/secret-scanner.ts +57 -1
  783. package/src/security/security.test.ts +9 -0
  784. package/src/sync/autosync-risk-tier.test.ts +61 -0
  785. package/src/sync/autosync-scheduler.test.ts +48 -1
  786. package/src/sync/autosync-scheduler.ts +50 -26
  787. package/src/sync/backup-files.test.ts +34 -0
  788. package/src/sync/backup-files.ts +44 -5
  789. package/src/sync/gist/gist-msgkeys-catalog.test.ts +33 -0
  790. package/src/sync/gist/gist-msgkeys.test.ts +71 -0
  791. package/src/sync/gist/gist-transport.test.ts +308 -0
  792. package/src/sync/gist/gist-transport.ts +550 -0
  793. package/src/sync/gist/messages.ts +49 -0
  794. package/src/sync/git/git-transport.test.ts +56 -0
  795. package/src/sync/git/git-transport.ts +49 -11
  796. package/src/sync/retention-prune-all-off.test.ts +50 -0
  797. package/src/sync/s3/cloud-config.test.ts +283 -0
  798. package/src/sync/s3/messages.ts +102 -0
  799. package/src/sync/s3/s3-delete-index-404.test.ts +170 -0
  800. package/src/sync/s3/s3-delete-index-unreadable.test.ts +141 -0
  801. package/src/sync/s3/s3-msgkeys.test.ts +101 -0
  802. package/src/sync/s3/s3-providers.ts +213 -0
  803. package/src/sync/s3/s3-transport.test.ts +394 -0
  804. package/src/sync/s3/s3-transport.ts +651 -0
  805. package/src/sync/s3/s3-upload-index-404.test.ts +171 -0
  806. package/src/sync/s3/sigv4.test.ts +219 -0
  807. package/src/sync/s3/sigv4.ts +283 -0
  808. package/src/sync/snapshot-json.test.ts +35 -0
  809. package/src/sync/snapshot-json.ts +10 -3
  810. package/src/sync/sync-config.test.ts +305 -6
  811. package/src/sync/sync-config.ts +872 -27
  812. package/src/sync/sync-engine-download-tombstone.test.ts +73 -0
  813. package/src/sync/sync-engine.test.ts +31 -0
  814. package/src/sync/sync-engine.ts +86 -7
  815. package/src/sync/sync-history.ts +1 -1
  816. package/src/sync/transport.ts +5 -1
  817. package/src/sync/ui-prefs.test.ts +24 -0
  818. package/src/sync/ui-prefs.ts +16 -0
  819. package/src/sync/webdav/webdav-delete-index-404.test.ts +178 -0
  820. package/src/sync/webdav/webdav-delete-index-unreadable.test.ts +143 -0
  821. package/src/sync/webdav/webdav-msgkeys.test.ts +53 -0
  822. package/src/sync/webdav/webdav-transport.test.ts +9 -5
  823. package/src/sync/webdav/webdav-transport.ts +123 -19
  824. package/src/sync/webdav/webdav-upload-index-404.test.ts +177 -0
  825. package/src/ui/artifact-view.test.ts +203 -0
  826. package/src/ui/artifact-view.ts +410 -0
  827. package/src/ui/commands.test.ts +162 -0
  828. package/src/ui/commands.ts +242 -0
  829. package/src/ui/disk-usage-view.test.ts +181 -0
  830. package/src/ui/disk-usage-view.ts +271 -0
  831. package/src/ui/dsh-profiles-incomplete-view.test.ts +102 -0
  832. package/src/ui/dsh-profiles-view.test.ts +21 -2
  833. package/src/ui/dsh-profiles-view.ts +132 -0
  834. package/src/ui/foreign-view.test.ts +241 -0
  835. package/src/ui/foreign-view.ts +362 -0
  836. package/src/ui/history-model.ts +1 -1
  837. package/src/ui/i18n.ts +398 -0
  838. package/src/ui/import-wizard.test.ts +124 -5
  839. package/src/ui/import-wizard.ts +93 -3
  840. package/src/ui/menu-placement.test.ts +84 -0
  841. package/src/ui/menu-placement.ts +89 -0
  842. package/src/ui/mid-ellipsis.test.ts +7 -10
  843. package/src/ui/mid-ellipsis.ts +3 -2
  844. package/src/ui/nav-model.test.ts +86 -0
  845. package/src/ui/nav-model.ts +82 -0
  846. package/src/ui/next-steps.test.ts +39 -1
  847. package/src/ui/next-steps.ts +33 -2
  848. package/src/ui/overview-view.ts +1 -1
  849. package/src/ui/runs-view.ts +1 -1
  850. package/src/ui/session-format-disposition.test.ts +114 -0
  851. package/src/ui/session-format-disposition.ts +174 -0
  852. package/src/ui/session-inventory-view.test.ts +349 -0
  853. package/src/ui/session-inventory-view.ts +493 -0
  854. package/src/ui/session-layout-view.test.ts +361 -0
  855. package/src/ui/session-layout-view.ts +444 -0
  856. package/src/ui/sync-repository-picker.test.ts +130 -0
  857. package/src/ui/sync-repository-picker.ts +180 -0
  858. package/src/ui/sync-settings-view.test.ts +166 -0
  859. package/src/ui/sync-settings-view.ts +319 -9
  860. package/src/ui/test-helpers.ts +12 -6
  861. package/src/ui/types.ts +171 -2
  862. package/src/utils/asar-read.test.ts +330 -0
  863. package/src/utils/asar-read.ts +606 -0
  864. package/src/utils/env-lock.test.ts +55 -0
  865. package/src/utils/env-lock.ts +6 -1
  866. package/src/utils/json.ts +14 -0
  867. package/src/utils/patch-yaml.test.ts +112 -0
  868. package/src/utils/patch-yaml.ts +72 -0
  869. package/src/utils/paths.test.ts +63 -0
  870. package/src/utils/paths.ts +25 -3
  871. package/src/utils/recursive-walk.test.ts +20 -0
  872. package/src/utils/recursive-walk.ts +12 -13
  873. package/src/utils/session-format.test.ts +218 -0
  874. package/src/utils/session-format.ts +237 -0
  875. package/src/utils/session-health-scan.test.ts +580 -0
  876. package/src/utils/session-health-scan.ts +784 -0
  877. package/src/utils/session-layout-repair-service.test.ts +362 -0
  878. package/src/utils/session-layout-repair-service.ts +568 -0
  879. package/src/utils/session-log-repair.test.ts +569 -0
  880. package/src/utils/session-log-repair.ts +783 -0
  881. package/src/utils/session-log.test.ts +13 -0
  882. package/src/utils/session-log.ts +78 -0
  883. package/src/utils/session-repair-service.test.ts +325 -0
  884. package/src/utils/session-repair-service.ts +612 -0
  885. package/src/utils/session-row-facts.test.ts +159 -0
  886. package/src/utils/session-row-facts.ts +305 -0
  887. package/src/utils/session-verify.test.ts +762 -0
  888. package/src/utils/session-verify.ts +784 -0
  889. package/src/utils/shared-constants.ts +106 -0
  890. package/src/utils/ttl-cache.test.ts +96 -0
  891. package/src/utils/ttl-cache.ts +90 -0
  892. package/lib/ui/nav-overflow.d.ts +0 -35
  893. package/lib/ui/nav-overflow.js +0 -41
  894. package/src/client/overview/overview-kind-labels.test.ts +0 -43
  895. package/src/client/snapshots/SnapshotsEmptyState.tsx +0 -31
  896. package/src/client/snapshots/SnapshotsListTable.tsx +0 -97
  897. package/src/client/snapshots/SnapshotsPanel.tsx +0 -811
  898. package/src/client/snapshots/report-dismiss.test.ts +0 -59
  899. package/src/ui/nav-overflow.test.ts +0 -57
  900. package/src/ui/nav-overflow.ts +0 -58
@@ -1,5 +1,5 @@
1
1
  import { msgOf, zhMsg } from '../core/messages.js';
2
- import { resolveNamespaces } from './settings.js';
2
+ import { namespaceFromMap, readNamespaceMap, resolveNamespaces } from './settings.js';
3
3
  import { sectionMeta } from '../schema/section-registry.js';
4
4
  import { validateJsonSection } from './json-section.js';
5
5
  /** 缺省 ref 收集:遍历 settings namespace,收集 llm apiKeyEnv / providers[].apiKeyEnv,
@@ -9,9 +9,12 @@ import { validateJsonSection } from './json-section.js';
9
9
  export function defaultCredentialRefs(namespaces) {
10
10
  return async (ctx) => {
11
11
  const refs = new Set();
12
- for (const ns of await resolveNamespaces(namespaces, ctx)) {
12
+ const names = await resolveNamespaces(namespaces, ctx);
13
+ // 快路径:宿主能一次读回全部 namespace 时只读一趟(理由见 settings.ts readNamespaceMap)
14
+ const all = names.length > 0 ? await readNamespaceMap(ctx) : null;
15
+ for (const ns of names) {
13
16
  try {
14
- const info = await ctx.settings.describe(ns, { redactSecrets: true });
17
+ const info = all !== null ? namespaceFromMap(all, ns) : await ctx.settings.describe(ns, { redactSecrets: true });
15
18
  const value = (info.value ?? {});
16
19
  // 引用类字段名(值 = env/凭据引用名,非秘密本身)
17
20
  const REFERENCE_REF_FIELDS = new Set([
@@ -1,4 +1,5 @@
1
1
  import type { MsgFunc } from '../core/messages.ts';
2
+ import type { RecursiveListing } from '../utils/recursive-walk.ts';
2
3
  import type { FilesSection, SectionId } from '../schema/types.ts';
3
4
  import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, ExportUnit, HostContext, ImportContext, PlanItem, Portability, SectionPreview, ValidationResult } from '../core/types.ts';
4
5
  /**
@@ -87,6 +88,14 @@ export declare abstract class FileCollectionAdapter implements ConfigAdapter<Fil
87
88
  * `FilesSection` 是备份格式的一部分,多带内部字段会被序列化出去;体积走旁路 `sizes`。
88
89
  */
89
90
  private collect;
91
+ /**
92
+ * 分区清单枚举(`export` / `preview` 共用;两者必须走同一份清单,否则预览与导出分叉)。
93
+ *
94
+ * **单文件分区必须覆写它**:`AgentInstructionsAdapter` 的 `baseDir` 是 ''(AGENTS.md 就放在
95
+ * `$DSH_HOME` 根),走缺省实现会递归**整个 home** —— 实测 4016 个文件 / 242 MB / 4.8 s,而且
96
+ * 选择器会把 `profiles/`、`sessions/`、`attachments/` 这些无关目录当成该分区的「单元」。
97
+ */
98
+ protected listRelPaths(ctx: HostContext): Promise<RecursiveListing>;
90
99
  /**
91
100
  * 读一个成员文件。
92
101
  *
@@ -94,10 +103,30 @@ export declare abstract class FileCollectionAdapter implements ConfigAdapter<Fil
94
103
  * - `size`:优先 `ctx.fs.statSize`(只 stat,不读内容);门面未实现或返回 null(文件消失 /
95
104
  * 读不到)时**退回 readFile** —— 与改造前把「读不到」暴露成同一个错误,绝不把未知体积
96
105
  * 当成 0(0 会让字节闸门少算,把本该剔除的单元放进预览,预览与导出就分叉了)。
106
+ *
107
+ * 受保护而非私有(issue #71):`SkillsAdapter` 的成员有一部分**不在磁盘上**
108
+ * (来自 `ctx.skills` 技能服务的技能被重建成 `<name>/SKILL.md` 虚拟路径),
109
+ * 需要在这里按路径分流 —— 覆写后基类的单元分组 / 字节闸门 / 告警 / 预览-导出一致性全部照旧。
97
110
  */
98
- private readMember;
111
+ protected readMember(ctx: HostContext, rel: string, mode: 'content' | 'size', statTimes?: Map<string, number>): Promise<{
112
+ data: Uint8Array;
113
+ sizeBytes: number;
114
+ contentHash: string;
115
+ }>;
99
116
  /** 单元清单(零 I/O:直接由 export 产物归并)。 */
100
117
  listUnits(section: ExportSection<FilesSection>): ExportUnit[];
118
+ /**
119
+ * 幂等比对用的内容(相对 baseDir 路径 + 盘上/包内字节 → 参与哈希比对的字节)。
120
+ *
121
+ * 缺省 = **原字节**:对文件类分区来说「相对路径 + 内容哈希」就是身份语义,不得改动。
122
+ * 覆写者只应用于剔除**不属于「配置身份」的易变字段**(见 SelfAdapter 的两个调度配置:
123
+ * 运行态每轮都被改写,整文件哈希会恒判 Conflict —— issue #73)。两条契约:
124
+ * 1. **必须收敛**:applyItem 写盘后再分析一次必须判 Skip,否则同步永不收敛;
125
+ * 2. **未改写时返回同一引用** —— 调用方据此复用导入侧已算好的 contentHash,
126
+ * 不给 sessions 这类大分区白算一遍哈希。
127
+ * 判不出可比形态(解析失败等)时必须**原样返回**:绝不把「判不出来」当成「相同」或「不同」。
128
+ */
129
+ protected comparableContent(_relativePath: string, data: Uint8Array): Uint8Array;
101
130
  analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;
102
131
  applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
103
132
  /**
@@ -129,6 +129,8 @@ export class FileCollectionAdapter {
129
129
  },
130
130
  sizeBytes: collected.totalBytes,
131
131
  items: unitsFromEntries(this.id, collected.sizes, (rel) => this.unitIdOf(rel)),
132
+ // 本趟 stat 顺带取到的时间(宿主有 statInfo 时才有):调用方据此省掉第二趟逐文件 stat。
133
+ ...(collected.statTimes.size > 0 ? { statTimes: collected.statTimes } : {}),
132
134
  };
133
135
  }
134
136
  /**
@@ -145,14 +147,16 @@ export class FileCollectionAdapter {
145
147
  */
146
148
  async collect(ctx, options, mode) {
147
149
  const files = [];
150
+ const statTimes = new Map();
148
151
  const sizes = [];
149
152
  const warnings = [];
150
153
  const allow = options.includeItems?.[this.id];
151
154
  // issue #37:用「跟随 junction/符号链接」的遍历,并把跟随/跳过的链接写进告警——
152
155
  // 此前链接目录及其全部内容被静默排除,备份仍报成功。
156
+ // 枚举走 listRelPaths 钩子(单文件分区覆写它,避免对 home 根整目录递归)。
153
157
  let listing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
154
158
  try {
155
- listing = await listFilesDetailed(ctx.fs, this.baseDir);
159
+ listing = await this.listRelPaths(ctx);
156
160
  }
157
161
  catch {
158
162
  // 目录不存在视为空
@@ -198,7 +202,7 @@ export class FileCollectionAdapter {
198
202
  const pending = [];
199
203
  let unitBytes = 0;
200
204
  for (const member of unitEntries.get(unitId) ?? []) {
201
- const read = await this.readMember(ctx, member.rel, mode);
205
+ const read = await this.readMember(ctx, member.rel, mode, statTimes);
202
206
  unitBytes += read.sizeBytes;
203
207
  if (unitBytes > room)
204
208
  break;
@@ -239,7 +243,17 @@ export class FileCollectionAdapter {
239
243
  if (rels.length === 0)
240
244
  warnings.push(msgOf(ctx)('adapter.dirEmpty', { type: this.displayName }));
241
245
  warnings.push(...linkWarnings(msgOf(ctx), this.displayName, listing));
242
- return { entries: files, sizes, totalBytes: usedBytes, warnings };
246
+ return { entries: files, sizes, totalBytes: usedBytes, warnings, statTimes };
247
+ }
248
+ /**
249
+ * 分区清单枚举(`export` / `preview` 共用;两者必须走同一份清单,否则预览与导出分叉)。
250
+ *
251
+ * **单文件分区必须覆写它**:`AgentInstructionsAdapter` 的 `baseDir` 是 ''(AGENTS.md 就放在
252
+ * `$DSH_HOME` 根),走缺省实现会递归**整个 home** —— 实测 4016 个文件 / 242 MB / 4.8 s,而且
253
+ * 选择器会把 `profiles/`、`sessions/`、`attachments/` 这些无关目录当成该分区的「单元」。
254
+ */
255
+ async listRelPaths(ctx) {
256
+ return listFilesDetailed(ctx.fs, this.baseDir);
243
257
  }
244
258
  /**
245
259
  * 读一个成员文件。
@@ -248,9 +262,22 @@ export class FileCollectionAdapter {
248
262
  * - `size`:优先 `ctx.fs.statSize`(只 stat,不读内容);门面未实现或返回 null(文件消失 /
249
263
  * 读不到)时**退回 readFile** —— 与改造前把「读不到」暴露成同一个错误,绝不把未知体积
250
264
  * 当成 0(0 会让字节闸门少算,把本该剔除的单元放进预览,预览与导出就分叉了)。
265
+ *
266
+ * 受保护而非私有(issue #71):`SkillsAdapter` 的成员有一部分**不在磁盘上**
267
+ * (来自 `ctx.skills` 技能服务的技能被重建成 `<name>/SKILL.md` 虚拟路径),
268
+ * 需要在这里按路径分流 —— 覆写后基类的单元分组 / 字节闸门 / 告警 / 预览-导出一致性全部照旧。
251
269
  */
252
- async readMember(ctx, rel, mode) {
270
+ async readMember(ctx, rel, mode, statTimes) {
253
271
  if (mode === 'size') {
272
+ // 优先 statInfo:一次 stat 同时拿体积与 mtime(sessions 的排序时间顺带就有了,
273
+ // 省掉 unitActivityTimes 的第二趟逐文件 stat —— 实测会话树 983 个文件)。
274
+ if (ctx.fs.statInfo !== undefined) {
275
+ const info = await ctx.fs.statInfo(rel);
276
+ if (info !== null) {
277
+ statTimes?.set(rel, info.mtimeMs);
278
+ return { data: EMPTY_DATA, sizeBytes: info.size, contentHash: '' };
279
+ }
280
+ }
254
281
  const size = (await ctx.fs.statSize?.(rel)) ?? null;
255
282
  if (size !== null)
256
283
  return { data: EMPTY_DATA, sizeBytes: size, contentHash: '' };
@@ -262,6 +289,20 @@ export class FileCollectionAdapter {
262
289
  listUnits(section) {
263
290
  return unitsFromFiles(this.id, section.data.files, (rel) => this.unitIdOf(rel));
264
291
  }
292
+ /**
293
+ * 幂等比对用的内容(相对 baseDir 路径 + 盘上/包内字节 → 参与哈希比对的字节)。
294
+ *
295
+ * 缺省 = **原字节**:对文件类分区来说「相对路径 + 内容哈希」就是身份语义,不得改动。
296
+ * 覆写者只应用于剔除**不属于「配置身份」的易变字段**(见 SelfAdapter 的两个调度配置:
297
+ * 运行态每轮都被改写,整文件哈希会恒判 Conflict —— issue #73)。两条契约:
298
+ * 1. **必须收敛**:applyItem 写盘后再分析一次必须判 Skip,否则同步永不收敛;
299
+ * 2. **未改写时返回同一引用** —— 调用方据此复用导入侧已算好的 contentHash,
300
+ * 不给 sessions 这类大分区白算一遍哈希。
301
+ * 判不出可比形态(解析失败等)时必须**原样返回**:绝不把「判不出来」当成「相同」或「不同」。
302
+ */
303
+ comparableContent(_relativePath, data) {
304
+ return data;
305
+ }
265
306
  async analyzeImport(data, ctx) {
266
307
  const msg = ctx.msg;
267
308
  const items = [];
@@ -294,15 +335,21 @@ export class FileCollectionAdapter {
294
335
  target: { adapter: this.id, ref: file.relativePath },
295
336
  });
296
337
  }
297
- else if (sha256Hex(current) === file.contentHash) {
298
- items.push({ id, unitId, kind: 'Skip', adapter: this.id, description: msg('adapter.fileSame', { path: file.relativePath }), severity: 'info' });
299
- }
300
338
  else {
301
- items.push({
302
- id, unitId, kind: 'Conflict', adapter: this.id,
303
- description: msg('adapter.fileDiff', { path: file.relativePath }), severity: 'warning',
304
- target: { adapter: this.id, ref: file.relativePath },
305
- });
339
+ // 幂等判定统一走 comparableContent(缺省 = 原字节);包内文件未被改写时复用已算好的
340
+ // contentHash,不给 sessions 这类大分区白算一遍哈希(盘上文件本来就要现算)。
341
+ const importedComparable = this.comparableContent(file.relativePath, file.data);
342
+ const importedHash = importedComparable === file.data ? file.contentHash : sha256Hex(importedComparable);
343
+ if (sha256Hex(this.comparableContent(file.relativePath, current)) === importedHash) {
344
+ items.push({ id, unitId, kind: 'Skip', adapter: this.id, description: msg('adapter.fileSame', { path: file.relativePath }), severity: 'info' });
345
+ }
346
+ else {
347
+ items.push({
348
+ id, unitId, kind: 'Conflict', adapter: this.id,
349
+ description: msg('adapter.fileDiff', { path: file.relativePath }), severity: 'warning',
350
+ target: { adapter: this.id, ref: file.relativePath },
351
+ });
352
+ }
306
353
  }
307
354
  }
308
355
  return items;
@@ -1,18 +1,28 @@
1
1
  import type { MsgFunc } from '../core/messages.ts';
2
2
  import type { McpServerEntry } from '../schema/types.ts';
3
3
  import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
4
- /** 导出记录:McpServerEntry 之外附加来源 patch 行 id(导入写回定位用) */
4
+ /** 导出记录:McpServerEntry 之外附加来源 patch 行 id 与所在层(导入写回定位用) */
5
5
  export interface McpExportEntry extends McpServerEntry {
6
6
  sourceLineId: string;
7
+ /**
8
+ * 来源 patch 层(相对 homeDir 的 POSIX 路径;见 core/patch-layers.ts)。
9
+ * 缺省 = 用户层(旧备份包没有该字段 → 按旧行为写回 home 层)。
10
+ */
11
+ sourceFile?: string;
7
12
  }
8
13
  export interface McpExportSection {
9
14
  version: 1;
10
15
  servers: McpExportEntry[];
11
16
  }
12
- /** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
17
+ /**
18
+ * 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定)。
19
+ *
20
+ * 行对象可带 `file`(层身份):带上时逐条目记录 `sourceFile`,导入写回落回原层。
21
+ */
13
22
  export declare function extractMcpServers(lines: {
14
23
  lineId: string;
15
24
  raw: unknown;
25
+ file?: string;
16
26
  }[]): McpExportEntry[];
17
27
  /** 由导出条目构造写回 patch 行 raw(单行形态,与 dsh-mcp-client 组合 config 语义一致) */
18
28
  export declare function buildMcpPatchLine(lineId: string, server: McpServerEntry): Record<string, unknown>;
@@ -5,13 +5,22 @@
5
5
  * dependencyChecker 执行(§15:缺失不阻塞,标记 Requires Attention)。
6
6
  *
7
7
  * patch 行 raw 支持两种形态:单行 { id, name, config } 与块 { insert: [{ id, name, config }] }。
8
+ *
9
+ * issue #71(真机报告「备份不到外壳 mcp」):数据源是**两层** patch —— home 层
10
+ * ($DSH_HOME/cordis.patch.yml)与 profile 层($DSH_HOME/profiles/<p>/cordis.patch.yml)。
11
+ * 此前只读 home 层,而 DSH 自己的 MCP 配置工具把 server 写进 profile 层,于是备份里
12
+ * servers 恒为空数组。现在两层都读,每行记住自己来自哪个文件,写回时落回原层。
8
13
  */
9
14
  import { isDeepStrictEqual } from 'node:util';
10
15
  import { msgOf, zhMsg } from '../core/messages.js';
11
16
  import { sectionMeta } from '../schema/section-registry.js';
12
17
  import { validateJsonSection } from './json-section.js';
13
- import { USER_PATCH_FILE } from './plugins.js';
14
- /** 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定) */
18
+ import { locatePatchLineLayer, readEffectivePatchLines, resolveWriteLayer } from '../core/patch-layers.js';
19
+ /**
20
+ * 从 patch 行数组中提取 MCP server 条目(以 config.serverName 存在为判定)。
21
+ *
22
+ * 行对象可带 `file`(层身份):带上时逐条目记录 `sourceFile`,导入写回落回原层。
23
+ */
15
24
  export function extractMcpServers(lines) {
16
25
  const servers = [];
17
26
  for (const line of lines) {
@@ -34,6 +43,7 @@ export function extractMcpServers(lines) {
34
43
  url: typeof c['url'] === 'string' ? c['url'] : undefined,
35
44
  headers: c['headers'] !== null && typeof c['headers'] === 'object' ? c['headers'] : undefined,
36
45
  sourceLineId: line.lineId,
46
+ ...(line.file !== undefined ? { sourceFile: line.file } : {}),
37
47
  });
38
48
  }
39
49
  }
@@ -85,14 +95,12 @@ export class McpAdapter {
85
95
  portability = sectionMeta('mcp').portability;
86
96
  async export(ctx, _options) {
87
97
  const warnings = [];
88
- let lines = [];
89
- try {
90
- lines = await ctx.patchFile.readPatchLines(USER_PATCH_FILE);
91
- }
92
- catch (err) {
93
- warnings.push(msgOf(ctx)('adapter.patchReadFailedMCP', { reason: err instanceof Error ? err.message : String(err) }));
98
+ // issue #71:两层都读(home 层优先),否则 profile 层里的 server 全部丢失
99
+ const read = await readEffectivePatchLines(ctx.patchFile, ctx.profile);
100
+ for (const f of read.failures) {
101
+ warnings.push(msgOf(ctx)('adapter.patchReadFailedMCP', { reason: `${f.file}: ${f.reason}` }));
94
102
  }
95
- const servers = extractMcpServers(lines);
103
+ const servers = extractMcpServers(read.lines);
96
104
  return {
97
105
  sectionId: 'mcp',
98
106
  data: { version: 1, servers },
@@ -103,13 +111,15 @@ export class McpAdapter {
103
111
  async analyzeImport(data, ctx) {
104
112
  const msg = ctx.msg;
105
113
  const items = [];
106
- const targetLines = await ctx.target.patchFile.readPatchLines(USER_PATCH_FILE);
114
+ // issue #71:目标行跨两层;同一 lineId 只保留优先级最高的一层(与 DSH 合并序一致)
115
+ const targetLines = (await readEffectivePatchLines(ctx.target.patchFile, ctx.target.profile)).lines;
107
116
  const targetServers = extractMcpServers(targetLines);
108
117
  for (const server of data.servers) {
109
118
  const id = `mcp:${server.serverName}`;
110
119
  const existing = targetServers.find((s) => s.serverName === server.serverName);
111
120
  const comparable = { ...server };
112
121
  delete comparable.sourceLineId;
122
+ delete comparable.sourceFile;
113
123
  if (!existing) {
114
124
  const lineId = server.sourceLineId && targetLines.some((l) => l.lineId === server.sourceLineId)
115
125
  ? `${server.sourceLineId}-imported`
@@ -119,12 +129,14 @@ export class McpAdapter {
119
129
  description: msg('adapter.mcpCreate', { serverName: server.serverName }),
120
130
  detail: `${server.type === 'stdio' ? `${server.command} ${(server.args ?? []).join(' ')}` : server.url}`,
121
131
  severity: 'info',
122
- target: { adapter: 'mcp', ref: lineId },
132
+ // 目标层随计划项一起走:导入前快照要按**这一层**记原行,回滚才能写回原文件
133
+ target: { adapter: 'mcp', ref: lineId, file: resolveWriteLayer(server.sourceFile, ctx.target.profile) },
123
134
  });
124
135
  }
125
136
  else {
126
137
  const existingComparable = { ...existing };
127
138
  delete existingComparable.sourceLineId;
139
+ delete existingComparable.sourceFile;
128
140
  if (isDeepStrictEqual(existingComparable, comparable)) {
129
141
  items.push({ id, kind: 'Skip', adapter: 'mcp', description: msg('adapter.mcpSame', { serverName: server.serverName }), severity: 'info' });
130
142
  }
@@ -134,7 +146,7 @@ export class McpAdapter {
134
146
  description: msg('adapter.mcpDiff', { serverName: server.serverName }),
135
147
  detail: `current=${JSON.stringify(existingComparable)} imported=${JSON.stringify(comparable)}`.slice(0, 200),
136
148
  severity: 'warning',
137
- target: { adapter: 'mcp', ref: existing.sourceLineId },
149
+ target: { adapter: 'mcp', ref: existing.sourceLineId, file: existing.sourceFile },
138
150
  });
139
151
  }
140
152
  }
@@ -152,7 +164,12 @@ export class McpAdapter {
152
164
  if (!ref)
153
165
  return { ok: false, message: msg('adapter.missingTargetRef') };
154
166
  const raw = buildMcpPatchLine(ref, server);
155
- await ctx.target.patchFile.applyPatchChanges(USER_PATCH_FILE, [
167
+ // issue #71:Create 落「来源层的同语义层」(来源是 profile 层 → 目标机当前 profile 层),
168
+ // Update 落「该行当前所在层」—— 两种情况都不把行搬到另一层去(否则同一 lineId 两层各一份)
169
+ const file = item.kind === 'Create'
170
+ ? resolveWriteLayer(server.sourceFile, ctx.target.profile)
171
+ : await locatePatchLineLayer(ctx.target.patchFile, ctx.target.profile, ref);
172
+ await ctx.target.patchFile.applyPatchChanges(file, [
156
173
  { lineId: ref, raw, action: item.kind === 'Create' ? 'insert' : 'update' },
157
174
  ]);
158
175
  return { ok: true, needsRestart: true, message: msg('adapter.mcpWritten', { serverName }) };
@@ -12,6 +12,14 @@ export declare class PluginFilesAdapter implements ConfigAdapter<FilesSection> {
12
12
  private readonly collectDir?;
13
13
  constructor(whitelist?: string[], collectDir?: string);
14
14
  export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<FilesSection>>;
15
+ /**
16
+ * 读失败之后判定「这个路径本来就不存在」还是「存在但读不到」(ui-F2)。
17
+ *
18
+ * 为什么不用错误码:门面错误的形状并不统一(内存 mock 与各平台 EACCES 文案不同),
19
+ * 而 `exists()` 是 FileSystemFacade 契约里唯一稳定的存在性判据。`exists()` 自己也失败时
20
+ * 按「存在」处理(保守:宁可让这次导出显式失败,也不静默丢一个文件)。
21
+ */
22
+ private absentOnDisk;
15
23
  /** 单元清单(零 I/O):逐文件单元,id 与导入侧 `pluginFiles:<relPath>` 一致。 */
16
24
  listUnits(section: ExportSection<FilesSection>): ExportUnit[];
17
25
  analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;
@@ -45,8 +45,12 @@ export class PluginFilesAdapter {
45
45
  files.push({ relativePath: rel, data, contentHash: sha256Hex(data) });
46
46
  seen.add(rel);
47
47
  }
48
- catch {
49
- /* 白名单文件不存在则跳过 */
48
+ catch (err) {
49
+ // ui-F2:白名单文件**不存在**是正常形态(按需创建 → 跳过);**读不到**(权限/竞态/断链)
50
+ // 绝不能静默丢内容 —— 与文件集合基类同一语义:整分区显式失败,导出报告可见。
51
+ if (await this.absentOnDisk(ctx, rel))
52
+ continue;
53
+ throw err;
50
54
  }
51
55
  }
52
56
  // 2) 约定配置目录递归收集(相对 ~/.dsh 根的完整路径;与白名单文件去重)
@@ -69,8 +73,11 @@ export class PluginFilesAdapter {
69
73
  const data = await ctx.fs.readFile(rel);
70
74
  files.push({ relativePath: rel, data, contentHash: sha256Hex(data) });
71
75
  }
72
- catch {
73
- continue;
76
+ catch (err) {
77
+ // ui-F2:同上 —— 目录遍历刚点到的文件却读不到,是真实的读取失败,不是「不存在」。
78
+ if (await this.absentOnDisk(ctx, rel))
79
+ continue;
80
+ throw err;
74
81
  }
75
82
  }
76
83
  }
@@ -81,6 +88,21 @@ export class PluginFilesAdapter {
81
88
  warnings: linkWarnings(msgOf(ctx), this.displayName, listing),
82
89
  };
83
90
  }
91
+ /**
92
+ * 读失败之后判定「这个路径本来就不存在」还是「存在但读不到」(ui-F2)。
93
+ *
94
+ * 为什么不用错误码:门面错误的形状并不统一(内存 mock 与各平台 EACCES 文案不同),
95
+ * 而 `exists()` 是 FileSystemFacade 契约里唯一稳定的存在性判据。`exists()` 自己也失败时
96
+ * 按「存在」处理(保守:宁可让这次导出显式失败,也不静默丢一个文件)。
97
+ */
98
+ async absentOnDisk(ctx, rel) {
99
+ try {
100
+ return (await ctx.fs.exists(rel)) === false;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
84
106
  /** 单元清单(零 I/O):逐文件单元,id 与导入侧 `pluginFiles:<relPath>` 一致。 */
85
107
  listUnits(section) {
86
108
  return unitsFromFiles(this.id, section.data.files, (rel) => rel);
@@ -144,6 +166,16 @@ export class PluginFilesAdapter {
144
166
  return validateJsonSection('pluginFiles', data, msg, (section, issues) => {
145
167
  if (!Array.isArray(section.files)) {
146
168
  issues.push({ path: 'files', message: msg('adapter.validate.array', { subject: 'files' }), severity: 'error' });
169
+ return;
170
+ }
171
+ // ui-F3:条目级形状校验必须与 analyzeImport 的假设对齐(它直接读 file.relativePath)。
172
+ // 只校验「是数组」会放过 files:[null] / relativePath:42 这类畸形,analyzeImport 抛内部
173
+ // TypeError → ImportAnalysis.valid=false → 整包被拦下,用户看到的是内部异常文本。
174
+ for (const [i, f] of section.files.entries()) {
175
+ const entry = f;
176
+ if (entry === null || typeof entry !== 'object' || typeof entry.relativePath !== 'string' || entry.relativePath === '') {
177
+ issues.push({ path: `files[${i}]`, message: msg('adapter.validate.fileRelativePath'), severity: 'error' });
178
+ }
147
179
  }
148
180
  });
149
181
  }
@@ -1,13 +1,20 @@
1
1
  import type { PackLocalPluginsResult } from '../core/local-plugin-pack.ts';
2
2
  import type { MsgFunc } from '../core/messages.ts';
3
+ import { USER_PATCH_FILE } from '../core/patch-layers.ts';
3
4
  import type { PluginEntry, PluginsSection } from '../schema/types.ts';
4
- import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, ExportUnit, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
5
- export declare const USER_PATCH_FILE = "cordis.patch.yml";
5
+ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, ExportUnit, HostContext, ImportContext, PlanItem, SectionPreview, ValidationResult } from '../core/types.ts';
6
+ /**
7
+ * 用户(home)patch 层路径 —— 定义已移到 `core/patch-layers.ts`(issue #71:patch 有 home /
8
+ * profile 两层,层身份属于引擎知识,不属于某个 adapter)。此处 re-export 保持既有 import 面。
9
+ */
10
+ export { USER_PATCH_FILE };
6
11
  /**
7
12
  * 本地源插件打包钩子(由宿主注入,见 src/index.ts createAdapters)。
8
13
  * 返回打包结果(含字节与重写后的 spec);不注入 = 不做本地源打包(保持旧行为)。
9
14
  */
10
- export type LocalPluginPackHook = (plugins: PluginEntry[], ctx: HostContext) => Promise<PackLocalPluginsResult>;
15
+ export type LocalPluginPackHook = (plugins: PluginEntry[],
16
+ /** 结构化类型:HostContext 满足它(core 侧刻意不 import adapters 的 HostContext 类型) */
17
+ ctx: Pick<HostContext, 'profile' | 'profileDir'>) => Promise<PackLocalPluginsResult>;
11
18
  /** 本地插件 tarball 解包到 homeDir 内的固定缓存目录(相对 $DSH_HOME)。 */
12
19
  export declare const LOCAL_TARBALL_CACHE_REL = "dsh-config-manager/local-plugins";
13
20
  /** 归档内相对路径 → 缓存目录下的安全文件名(拒绝任何路径穿越)。 */
@@ -29,6 +36,27 @@ export declare class PluginsAdapter implements ConfigAdapter<PluginsSection> {
29
36
  private readonly localPack;
30
37
  constructor(selfName?: string, localPack?: LocalPluginPackHook);
31
38
  export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<PluginsSection>>;
39
+ /**
40
+ * 只读预览(`/export-preview` 用):与 `export()` **同口径**,但不做任何本地源打包。
41
+ *
42
+ * 为什么必须覆写:基类缺省会让调用方退回 `export()`,而 `export()` 会为每个 `link:`/`file:`
43
+ * 插件 spawn 一次 `npm pack`(真机 12 个本地源插件 ≈12 s)。「总览」页只想知道条目数与体积,
44
+ * 却因此每次都要打包一遍全部本地插件 —— 预览是纯只读路径,不该有任何进程副作用。
45
+ *
46
+ * 与 `export()` 的**唯一**差异:
47
+ * - 不注入 `localTarballs`(没有字节),故清单 spec 保持原样(不做 `file:<tarball>` 改写);
48
+ * - 插件单元体积改为 `file:` 源的一次 stat(`measureLocalSources`),`link:` 目录按 0;
49
+ * - 体积用一次 `stringify` 算 UTF-8 字节数(与 /export-preview 对 JSON 分区的口径一致)。
50
+ * 其余(清单过滤、patch 行、pnpm-workspace、patchFiles、条目级白名单、告警文案、单元分组)逐字相同。
51
+ */
52
+ preview(ctx: HostContext, options: ExportOptions): Promise<SectionPreview<PluginsSection>>;
53
+ /**
54
+ * 导出与预览的共同内核。
55
+ *
56
+ * `pack=false` 时跳过本地源打包(预览路径)—— 除该段外两条路径逐字共用,
57
+ * 避免「预览说会带什么」与「导出真的带什么」分叉。
58
+ */
59
+ private collect;
32
60
  /**
33
61
  * 单元清单(Phase 1;零 I/O:直接由 export 产物派生)。
34
62
  *