@logictan/dsh-config-manager 0.1.60

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 (429) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/cordis.patch.yml +21 -0
  4. package/lib/adapters/agent-instructions.d.ts +13 -0
  5. package/lib/adapters/agent-instructions.js +46 -0
  6. package/lib/adapters/agent-presets.d.ts +13 -0
  7. package/lib/adapters/agent-presets.js +14 -0
  8. package/lib/adapters/credentials.d.ts +37 -0
  9. package/lib/adapters/credentials.js +122 -0
  10. package/lib/adapters/file-collection.d.ts +15 -0
  11. package/lib/adapters/file-collection.js +127 -0
  12. package/lib/adapters/index.d.ts +57 -0
  13. package/lib/adapters/index.js +55 -0
  14. package/lib/adapters/link-report.d.ts +16 -0
  15. package/lib/adapters/link-report.js +47 -0
  16. package/lib/adapters/mcp.d.ts +28 -0
  17. package/lib/adapters/mcp.js +178 -0
  18. package/lib/adapters/plugin-files.d.ts +18 -0
  19. package/lib/adapters/plugin-files.js +142 -0
  20. package/lib/adapters/plugins.d.ts +41 -0
  21. package/lib/adapters/plugins.js +603 -0
  22. package/lib/adapters/pnpm-workspace.d.ts +26 -0
  23. package/lib/adapters/pnpm-workspace.js +147 -0
  24. package/lib/adapters/prompts.d.ts +30 -0
  25. package/lib/adapters/prompts.js +231 -0
  26. package/lib/adapters/providers.d.ts +34 -0
  27. package/lib/adapters/providers.js +155 -0
  28. package/lib/adapters/self.d.ts +17 -0
  29. package/lib/adapters/self.js +71 -0
  30. package/lib/adapters/sessions.d.ts +14 -0
  31. package/lib/adapters/sessions.js +15 -0
  32. package/lib/adapters/settings.d.ts +28 -0
  33. package/lib/adapters/settings.js +165 -0
  34. package/lib/adapters/skills.d.ts +13 -0
  35. package/lib/adapters/skills.js +14 -0
  36. package/lib/adapters/test-helpers.d.ts +125 -0
  37. package/lib/adapters/test-helpers.js +236 -0
  38. package/lib/adapters/ui.d.ts +27 -0
  39. package/lib/adapters/ui.js +89 -0
  40. package/lib/adapters/workspaces.d.ts +13 -0
  41. package/lib/adapters/workspaces.js +106 -0
  42. package/lib/client.d.ts +1026 -0
  43. package/lib/client.js +10047 -0
  44. package/lib/core/analyzer.d.ts +99 -0
  45. package/lib/core/analyzer.js +934 -0
  46. package/lib/core/backup.d.ts +138 -0
  47. package/lib/core/backup.js +513 -0
  48. package/lib/core/boot-rescue.d.ts +146 -0
  49. package/lib/core/boot-rescue.js +618 -0
  50. package/lib/core/cache-cleaner.d.ts +31 -0
  51. package/lib/core/cache-cleaner.js +95 -0
  52. package/lib/core/config-lifecycle.d.ts +180 -0
  53. package/lib/core/config-lifecycle.js +481 -0
  54. package/lib/core/config-snapshot.d.ts +135 -0
  55. package/lib/core/config-snapshot.js +424 -0
  56. package/lib/core/config-state.d.ts +70 -0
  57. package/lib/core/config-state.js +177 -0
  58. package/lib/core/consult-source.d.ts +58 -0
  59. package/lib/core/consult-source.js +260 -0
  60. package/lib/core/crash-report.d.ts +84 -0
  61. package/lib/core/crash-report.js +317 -0
  62. package/lib/core/exporter.d.ts +68 -0
  63. package/lib/core/exporter.js +375 -0
  64. package/lib/core/ghost-sweep.d.ts +19 -0
  65. package/lib/core/ghost-sweep.js +52 -0
  66. package/lib/core/importer.d.ts +60 -0
  67. package/lib/core/importer.js +29 -0
  68. package/lib/core/index.d.ts +14 -0
  69. package/lib/core/index.js +14 -0
  70. package/lib/core/journal.d.ts +197 -0
  71. package/lib/core/journal.js +418 -0
  72. package/lib/core/local-plugin-host.d.ts +35 -0
  73. package/lib/core/local-plugin-host.js +120 -0
  74. package/lib/core/local-plugin-pack.d.ts +135 -0
  75. package/lib/core/local-plugin-pack.js +278 -0
  76. package/lib/core/messages.d.ts +310 -0
  77. package/lib/core/messages.js +621 -0
  78. package/lib/core/migration-consult.d.ts +117 -0
  79. package/lib/core/migration-consult.js +363 -0
  80. package/lib/core/migration-history.d.ts +153 -0
  81. package/lib/core/migration-history.js +411 -0
  82. package/lib/core/model-tools.d.ts +50 -0
  83. package/lib/core/model-tools.js +148 -0
  84. package/lib/core/msg-types.d.ts +8 -0
  85. package/lib/core/msg-types.js +6 -0
  86. package/lib/core/phase3-child-crash.d.ts +1 -0
  87. package/lib/core/phase3-child-crash.js +87 -0
  88. package/lib/core/phase3-host.d.ts +138 -0
  89. package/lib/core/phase3-host.js +359 -0
  90. package/lib/core/phase3-prod-child.d.ts +1 -0
  91. package/lib/core/phase3-prod-child.js +35 -0
  92. package/lib/core/phase4-crash-child.d.ts +1 -0
  93. package/lib/core/phase4-crash-child.js +103 -0
  94. package/lib/core/plugin-cli.d.ts +150 -0
  95. package/lib/core/plugin-cli.js +497 -0
  96. package/lib/core/reconcile.d.ts +120 -0
  97. package/lib/core/reconcile.js +389 -0
  98. package/lib/core/recovery-orchestrator.d.ts +94 -0
  99. package/lib/core/recovery-orchestrator.js +327 -0
  100. package/lib/core/restore.d.ts +162 -0
  101. package/lib/core/restore.js +703 -0
  102. package/lib/core/rollback.d.ts +12 -0
  103. package/lib/core/rollback.js +145 -0
  104. package/lib/core/run-registry.d.ts +85 -0
  105. package/lib/core/run-registry.js +157 -0
  106. package/lib/core/startup-barrier.d.ts +53 -0
  107. package/lib/core/startup-barrier.js +77 -0
  108. package/lib/core/types.d.ts +515 -0
  109. package/lib/core/types.js +27 -0
  110. package/lib/core/undo.d.ts +73 -0
  111. package/lib/core/undo.js +87 -0
  112. package/lib/core/validator.d.ts +18 -0
  113. package/lib/core/validator.js +77 -0
  114. package/lib/core/verify-recovery.d.ts +21 -0
  115. package/lib/core/verify-recovery.js +256 -0
  116. package/lib/core/watcher.d.ts +101 -0
  117. package/lib/core/watcher.js +219 -0
  118. package/lib/index.d.ts +238 -0
  119. package/lib/index.js +2764 -0
  120. package/lib/migrations/index.d.ts +29 -0
  121. package/lib/migrations/index.js +56 -0
  122. package/lib/migrations/v1-to-v2.d.ts +9 -0
  123. package/lib/migrations/v1-to-v2.js +10 -0
  124. package/lib/profiles/index.d.ts +4 -0
  125. package/lib/profiles/index.js +5 -0
  126. package/lib/profiles/profile-manager.d.ts +106 -0
  127. package/lib/profiles/profile-manager.js +543 -0
  128. package/lib/schema/config.d.ts +28 -0
  129. package/lib/schema/config.js +165 -0
  130. package/lib/schema/index.d.ts +24 -0
  131. package/lib/schema/index.js +26 -0
  132. package/lib/schema/manifest.d.ts +28 -0
  133. package/lib/schema/manifest.js +109 -0
  134. package/lib/schema/tombstones.d.ts +46 -0
  135. package/lib/schema/tombstones.js +64 -0
  136. package/lib/schema/types.d.ts +232 -0
  137. package/lib/schema/types.js +8 -0
  138. package/lib/schema/versions.d.ts +27 -0
  139. package/lib/schema/versions.js +50 -0
  140. package/lib/security/encryption.d.ts +85 -0
  141. package/lib/security/encryption.js +279 -0
  142. package/lib/security/index.d.ts +16 -0
  143. package/lib/security/index.js +17 -0
  144. package/lib/security/integrity.d.ts +30 -0
  145. package/lib/security/integrity.js +78 -0
  146. package/lib/security/redaction.d.ts +26 -0
  147. package/lib/security/redaction.js +95 -0
  148. package/lib/security/secret-scanner.d.ts +86 -0
  149. package/lib/security/secret-scanner.js +404 -0
  150. package/lib/security/vault.d.ts +59 -0
  151. package/lib/security/vault.js +151 -0
  152. package/lib/security/zip-security.d.ts +27 -0
  153. package/lib/security/zip-security.js +154 -0
  154. package/lib/sync/autosync-config.d.ts +39 -0
  155. package/lib/sync/autosync-config.js +151 -0
  156. package/lib/sync/autosync-scheduler.d.ts +139 -0
  157. package/lib/sync/autosync-scheduler.js +564 -0
  158. package/lib/sync/fs.d.ts +18 -0
  159. package/lib/sync/fs.js +49 -0
  160. package/lib/sync/git/git-transport.d.ts +100 -0
  161. package/lib/sync/git/git-transport.js +437 -0
  162. package/lib/sync/github-auth.d.ts +131 -0
  163. package/lib/sync/github-auth.js +264 -0
  164. package/lib/sync/layout.d.ts +43 -0
  165. package/lib/sync/layout.js +203 -0
  166. package/lib/sync/snapshot-json.d.ts +9 -0
  167. package/lib/sync/snapshot-json.js +107 -0
  168. package/lib/sync/sync-config.d.ts +81 -0
  169. package/lib/sync/sync-config.js +281 -0
  170. package/lib/sync/sync-engine.d.ts +269 -0
  171. package/lib/sync/sync-engine.js +591 -0
  172. package/lib/sync/sync-history.d.ts +42 -0
  173. package/lib/sync/sync-history.js +78 -0
  174. package/lib/sync/sync-selection.d.ts +29 -0
  175. package/lib/sync/sync-selection.js +127 -0
  176. package/lib/sync/sync-session.d.ts +40 -0
  177. package/lib/sync/sync-session.js +54 -0
  178. package/lib/sync/sync-state.d.ts +37 -0
  179. package/lib/sync/sync-state.js +108 -0
  180. package/lib/sync/transport.d.ts +75 -0
  181. package/lib/sync/transport.js +55 -0
  182. package/lib/sync/ui-prefs.d.ts +34 -0
  183. package/lib/sync/ui-prefs.js +109 -0
  184. package/lib/sync/webdav/webdav-transport.d.ts +77 -0
  185. package/lib/sync/webdav/webdav-transport.js +451 -0
  186. package/lib/ui/errors.d.ts +19 -0
  187. package/lib/ui/errors.js +81 -0
  188. package/lib/ui/export-flow.d.ts +82 -0
  189. package/lib/ui/export-flow.js +98 -0
  190. package/lib/ui/i18n.d.ts +289 -0
  191. package/lib/ui/i18n.js +581 -0
  192. package/lib/ui/migration-consult-view.d.ts +91 -0
  193. package/lib/ui/migration-consult-view.js +115 -0
  194. package/lib/ui/progress.d.ts +38 -0
  195. package/lib/ui/progress.js +87 -0
  196. package/lib/ui/report.d.ts +23 -0
  197. package/lib/ui/report.js +153 -0
  198. package/lib/ui/test-helpers.d.ts +69 -0
  199. package/lib/ui/test-helpers.js +154 -0
  200. package/lib/ui/types.d.ts +257 -0
  201. package/lib/ui/types.js +21 -0
  202. package/lib/utils/atomic-write.d.ts +88 -0
  203. package/lib/utils/atomic-write.js +399 -0
  204. package/lib/utils/env-lock.d.ts +393 -0
  205. package/lib/utils/env-lock.js +930 -0
  206. package/lib/utils/hashing.d.ts +17 -0
  207. package/lib/utils/hashing.js +41 -0
  208. package/lib/utils/json.d.ts +24 -0
  209. package/lib/utils/json.js +76 -0
  210. package/lib/utils/logger.d.ts +39 -0
  211. package/lib/utils/logger.js +82 -0
  212. package/lib/utils/paths.d.ts +42 -0
  213. package/lib/utils/paths.js +189 -0
  214. package/lib/utils/proxy.d.ts +69 -0
  215. package/lib/utils/proxy.js +455 -0
  216. package/lib/utils/recursive-walk.d.ts +24 -0
  217. package/lib/utils/recursive-walk.js +134 -0
  218. package/lib/utils/zip.d.ts +54 -0
  219. package/lib/utils/zip.js +285 -0
  220. package/package.json +188 -0
  221. package/src/adapters/agent-instructions.ts +48 -0
  222. package/src/adapters/agent-presets.ts +14 -0
  223. package/src/adapters/credentials.test.ts +90 -0
  224. package/src/adapters/credentials.ts +143 -0
  225. package/src/adapters/file-collection.ts +137 -0
  226. package/src/adapters/files.test.ts +353 -0
  227. package/src/adapters/index.ts +94 -0
  228. package/src/adapters/link-report.test.ts +65 -0
  229. package/src/adapters/link-report.ts +61 -0
  230. package/src/adapters/mcp.test.ts +96 -0
  231. package/src/adapters/mcp.ts +185 -0
  232. package/src/adapters/plugin-files.ts +146 -0
  233. package/src/adapters/plugins.test.ts +326 -0
  234. package/src/adapters/plugins.ts +615 -0
  235. package/src/adapters/pnpm-workspace.test.ts +87 -0
  236. package/src/adapters/pnpm-workspace.ts +150 -0
  237. package/src/adapters/prompts.test.ts +110 -0
  238. package/src/adapters/prompts.ts +230 -0
  239. package/src/adapters/providers.test.ts +126 -0
  240. package/src/adapters/providers.ts +173 -0
  241. package/src/adapters/roundtrip.test.ts +190 -0
  242. package/src/adapters/self.test.ts +92 -0
  243. package/src/adapters/self.ts +75 -0
  244. package/src/adapters/sessions.ts +15 -0
  245. package/src/adapters/settings.test.ts +100 -0
  246. package/src/adapters/settings.ts +187 -0
  247. package/src/adapters/skills.ts +14 -0
  248. package/src/adapters/test-helpers.ts +248 -0
  249. package/src/adapters/ui.test.ts +57 -0
  250. package/src/adapters/ui.ts +108 -0
  251. package/src/adapters/workspaces.test.ts +106 -0
  252. package/src/adapters/workspaces.ts +109 -0
  253. package/src/client/ConfigManagerSection.tsx +90 -0
  254. package/src/client/api.ts +66 -0
  255. package/src/client/client-types.ts +18 -0
  256. package/src/client/common/ErrorBanner.tsx +73 -0
  257. package/src/client/common/Icon.tsx +129 -0
  258. package/src/client/common/Modal.tsx +173 -0
  259. package/src/client/common/ToastViewport.tsx +73 -0
  260. package/src/client/common/toast-store.test.ts +192 -0
  261. package/src/client/common/toast-store.ts +181 -0
  262. package/src/client/common/toast-types.ts +7 -0
  263. package/src/client/common/ui.tsx +342 -0
  264. package/src/client/config-manager.module.css +2971 -0
  265. package/src/client/css-modules.d.ts +8 -0
  266. package/src/client/index.ts +65 -0
  267. package/src/client/locales.ts +56 -0
  268. package/src/client/lucide-icons.d.ts +38 -0
  269. package/src/client/run-store.test.ts +223 -0
  270. package/src/client/run-store.ts +372 -0
  271. package/src/client/sync/SyncConfirmView.tsx +379 -0
  272. package/src/client/sync/SyncHistoryView.test.ts +40 -0
  273. package/src/client/sync/SyncHistoryView.tsx +220 -0
  274. package/src/client/sync/SyncSettingsView.tsx +1535 -0
  275. package/src/client/sync/history-model.test.ts +194 -0
  276. package/src/client/sync/history-model.ts +195 -0
  277. package/src/client/sync/sync-api.test.ts +585 -0
  278. package/src/client/sync/sync-api.ts +543 -0
  279. package/src/client/sync/sync-locales.ts +416 -0
  280. package/src/client/sync/sync-push-preview.test.ts +52 -0
  281. package/src/client/sync/sync-view-v2.test.ts +213 -0
  282. package/src/client/sync/sync-view.test.ts +487 -0
  283. package/src/client/sync/sync-view.ts +759 -0
  284. package/src/core/analyzer.ts +1068 -0
  285. package/src/core/backup.ts +605 -0
  286. package/src/core/boot-rescue.test.ts +847 -0
  287. package/src/core/boot-rescue.ts +737 -0
  288. package/src/core/cache-cleaner.test.ts +169 -0
  289. package/src/core/cache-cleaner.ts +121 -0
  290. package/src/core/config-lifecycle.test.ts +727 -0
  291. package/src/core/config-lifecycle.ts +573 -0
  292. package/src/core/config-snapshot.test.ts +369 -0
  293. package/src/core/config-snapshot.ts +522 -0
  294. package/src/core/config-state.test.ts +381 -0
  295. package/src/core/config-state.ts +207 -0
  296. package/src/core/consult-source.test.ts +199 -0
  297. package/src/core/consult-source.ts +293 -0
  298. package/src/core/crash-report.test.ts +380 -0
  299. package/src/core/crash-report.ts +369 -0
  300. package/src/core/exporter.ts +417 -0
  301. package/src/core/ghost-sweep.test.ts +94 -0
  302. package/src/core/ghost-sweep.ts +67 -0
  303. package/src/core/importer.ts +83 -0
  304. package/src/core/index.ts +56 -0
  305. package/src/core/journal.test.ts +257 -0
  306. package/src/core/journal.ts +518 -0
  307. package/src/core/local-plugin-host.ts +152 -0
  308. package/src/core/local-plugin-pack.test.ts +359 -0
  309. package/src/core/local-plugin-pack.ts +363 -0
  310. package/src/core/messages.test.ts +54 -0
  311. package/src/core/messages.ts +664 -0
  312. package/src/core/migration-consult.test.ts +315 -0
  313. package/src/core/migration-consult.ts +494 -0
  314. package/src/core/migration-history.test.ts +319 -0
  315. package/src/core/migration-history.ts +484 -0
  316. package/src/core/model-tools.test.ts +190 -0
  317. package/src/core/model-tools.ts +208 -0
  318. package/src/core/msg-types.ts +10 -0
  319. package/src/core/phase3-child-crash.ts +95 -0
  320. package/src/core/phase3-host.ts +411 -0
  321. package/src/core/phase3-p1.test.ts +212 -0
  322. package/src/core/phase3-p2b-fingerprint.test.ts +189 -0
  323. package/src/core/phase3-prod-child.ts +36 -0
  324. package/src/core/phase3-production-integration.test.ts +286 -0
  325. package/src/core/phase4-crash-child.ts +101 -0
  326. package/src/core/phase4-crash-injection.test.ts +166 -0
  327. package/src/core/plugin-cli.fs.test.ts +184 -0
  328. package/src/core/plugin-cli.test.ts +137 -0
  329. package/src/core/plugin-cli.ts +563 -0
  330. package/src/core/reconcile.test.ts +392 -0
  331. package/src/core/reconcile.ts +498 -0
  332. package/src/core/recovery-orchestrator.test.ts +145 -0
  333. package/src/core/recovery-orchestrator.ts +366 -0
  334. package/src/core/restore-manage.test.ts +94 -0
  335. package/src/core/restore.ts +849 -0
  336. package/src/core/rollback.ts +151 -0
  337. package/src/core/run-progress.test.ts +237 -0
  338. package/src/core/run-registry.test.ts +232 -0
  339. package/src/core/run-registry.ts +209 -0
  340. package/src/core/smoke.test.ts +842 -0
  341. package/src/core/startup-barrier.ts +99 -0
  342. package/src/core/types.ts +542 -0
  343. package/src/core/undo.ts +116 -0
  344. package/src/core/validator.ts +84 -0
  345. package/src/core/verify-recovery.ts +265 -0
  346. package/src/core/watcher.test.ts +326 -0
  347. package/src/core/watcher.ts +263 -0
  348. package/src/index.facade.test.ts +265 -0
  349. package/src/index.sync.test.ts +217 -0
  350. package/src/index.ts +2975 -0
  351. package/src/migrations/index.ts +71 -0
  352. package/src/migrations/v1-to-v2.ts +17 -0
  353. package/src/profiles/index.ts +8 -0
  354. package/src/profiles/profile-manager.test.ts +282 -0
  355. package/src/profiles/profile-manager.ts +630 -0
  356. package/src/schema/config.ts +165 -0
  357. package/src/schema/index.ts +71 -0
  358. package/src/schema/manifest-schema.test.ts +519 -0
  359. package/src/schema/manifest.ts +124 -0
  360. package/src/schema/tombstones.test.ts +131 -0
  361. package/src/schema/tombstones.ts +89 -0
  362. package/src/schema/types.ts +245 -0
  363. package/src/schema/versions.ts +55 -0
  364. package/src/security/encryption.ts +335 -0
  365. package/src/security/index.ts +16 -0
  366. package/src/security/integrity.ts +102 -0
  367. package/src/security/redaction.ts +101 -0
  368. package/src/security/secret-scanner.ts +445 -0
  369. package/src/security/security.test.ts +1215 -0
  370. package/src/security/vault.test.ts +164 -0
  371. package/src/security/vault.ts +198 -0
  372. package/src/security/zip-security.ts +180 -0
  373. package/src/sync/autosync-config.test.ts +147 -0
  374. package/src/sync/autosync-config.ts +179 -0
  375. package/src/sync/autosync-scheduler.test.ts +490 -0
  376. package/src/sync/autosync-scheduler.ts +646 -0
  377. package/src/sync/fs.ts +48 -0
  378. package/src/sync/git/git-transport.test.ts +673 -0
  379. package/src/sync/git/git-transport.ts +491 -0
  380. package/src/sync/github-auth.test.ts +280 -0
  381. package/src/sync/github-auth.ts +356 -0
  382. package/src/sync/layout.test.ts +270 -0
  383. package/src/sync/layout.ts +222 -0
  384. package/src/sync/snapshot-json.test.ts +119 -0
  385. package/src/sync/snapshot-json.ts +128 -0
  386. package/src/sync/sync-config.test.ts +310 -0
  387. package/src/sync/sync-config.ts +299 -0
  388. package/src/sync/sync-engine.test.ts +1074 -0
  389. package/src/sync/sync-engine.ts +754 -0
  390. package/src/sync/sync-history.test.ts +116 -0
  391. package/src/sync/sync-history.ts +120 -0
  392. package/src/sync/sync-selection.test.ts +186 -0
  393. package/src/sync/sync-selection.ts +143 -0
  394. package/src/sync/sync-session.test.ts +143 -0
  395. package/src/sync/sync-session.ts +77 -0
  396. package/src/sync/sync-state.test.ts +164 -0
  397. package/src/sync/sync-state.ts +131 -0
  398. package/src/sync/transport.test.ts +111 -0
  399. package/src/sync/transport.ts +118 -0
  400. package/src/sync/ui-prefs.test.ts +152 -0
  401. package/src/sync/ui-prefs.ts +135 -0
  402. package/src/sync/webdav/webdav-transport.test.ts +871 -0
  403. package/src/sync/webdav/webdav-transport.ts +535 -0
  404. package/src/ui/errors.test.ts +47 -0
  405. package/src/ui/errors.ts +100 -0
  406. package/src/ui/export-flow.test.ts +87 -0
  407. package/src/ui/export-flow.ts +156 -0
  408. package/src/ui/i18n.ts +590 -0
  409. package/src/ui/migration-consult-view.test.ts +151 -0
  410. package/src/ui/migration-consult-view.ts +178 -0
  411. package/src/ui/progress.test.ts +47 -0
  412. package/src/ui/progress.ts +102 -0
  413. package/src/ui/report.test.ts +101 -0
  414. package/src/ui/report.ts +146 -0
  415. package/src/ui/test-helpers.ts +184 -0
  416. package/src/ui/types.ts +325 -0
  417. package/src/utils/atomic-write.test.ts +312 -0
  418. package/src/utils/atomic-write.ts +422 -0
  419. package/src/utils/env-lock.test.ts +999 -0
  420. package/src/utils/env-lock.ts +1086 -0
  421. package/src/utils/hashing.ts +53 -0
  422. package/src/utils/json.ts +77 -0
  423. package/src/utils/logger.ts +114 -0
  424. package/src/utils/paths.ts +176 -0
  425. package/src/utils/proxy.test.ts +465 -0
  426. package/src/utils/proxy.ts +493 -0
  427. package/src/utils/recursive-walk.test.ts +148 -0
  428. package/src/utils/recursive-walk.ts +145 -0
  429. package/src/utils/zip.ts +332 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xiajiajun516
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # 🔄 DSH Config Manager
2
+
3
+ **DeepSeek Harness 配置远程同步插件。**
4
+
5
+ 在你的多台机器之间,通过**自有的私有通道**(Git 仓库或 WebDAV)同步完整的 DSH 配置 ——
6
+ 设置、模型 provider、插件清单、MCP server、技能、Agent 预设、工作区,以及 provider 密钥。
7
+
8
+ - ☁️ **Git / WebDAV** 双通道,各自独立配置与排期
9
+ - ⏰ **自动同步** —— 事件驱动:远端有新快照才拉,本地有改动才推
10
+ - 📄 **明文快照** —— 私有通道自用,勾选即同步,不加密、不脱敏、不做 diff/合并
11
+ - 🧩 分区可勾选:设置 / provider / 插件 / MCP / 技能 / 预设 / 工作区 / 凭据状态 等
12
+ - ↩️ 应用前自动落回滚快照,失败整体回滚
13
+ - 🔐 通道凭据走 DSH credentials 槽位,值永不回传浏览器
14
+
15
+ ---
16
+
17
+ ## 它解决什么
18
+
19
+ DSH 把配置散落在 `$DSH_HOME` 的多个文件里(`settings.yaml`、`.credentials.yaml`、
20
+ `cordis.patch.yml`、插件清单、技能目录…)。换一台机器就要重配一遍。
21
+
22
+ 本插件把这些分区打包成**快照**推到你自己配置的私有通道,另一台机器拉下来覆盖写入。
23
+ **不加密、不脱敏** —— 这是刻意的产品选择:通道是你自己的私有仓库,明文自用比丢配置更划算。
24
+ 因此**绝不要**把同步通道指向公开仓库。
25
+
26
+ ---
27
+
28
+ ## 安装
29
+
30
+ ```sh
31
+ dsh plugin --profile web add @logictan/dsh-config-manager@latest
32
+ ```
33
+
34
+ > 也可通过聚合包一次装上本仓库的全部插件:`@logictan/dsh-plugins-all`。
35
+
36
+ ## 使用
37
+
38
+ 设置 → 插件 → **DSH Config Manager**(只有一个「同步」标签页):
39
+
40
+ 1. **通道**:选 Git(私有仓库地址 + token)或 WebDAV(地址 + 用户名 + 口令);
41
+ 两者配置与自动同步排期**互相独立**。
42
+ 2. **同步范围**:默认模式同步全部推荐分区;高级模式手动勾选。
43
+ 3. **推送 / 拉取**:手动推送前会先给出「将推送什么」的只读预览。
44
+ 4. **一键同步**:拉远端最新快照 → 差异确认 → 逐项采纳 → 写入本地(应用前自动落快照)。
45
+ 5. **自动同步**:开启后后台按间隔兜底轮询,真正驱动是「远端有新快照」/「本地有改动」。
46
+
47
+ ---
48
+
49
+ ## 安全边界
50
+
51
+ | 项 | 说明 |
52
+ |---|---|
53
+ | 快照内容 | **明文**,含 provider 密钥等一切隐私信息(`manifest.security.containsSecrets` 按实际内容如实标注) |
54
+ | 通道要求 | 必须是你自有的**私有**仓库;本插件不会、也无法阻止你把它指向公开仓库 |
55
+ | 通道凭据 | 存于 DSH credentials(Git 用 `DSH_CONFIG_MANAGER_SYNC_TOKEN`,WebDAV 用 `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD`),值只在宿主内部读取,绝不落盘到快照/日志/浏览器 |
56
+ | 日志 | 全程脱敏(`security/redaction.ts`) |
57
+ | 导入 | 应用前强制落回滚快照;任一失败整体回滚 |
58
+
59
+ ---
60
+
61
+ ## 架构
62
+
63
+ 双面 Cordis 插件:
64
+
65
+ - **宿主半边** `src/index.ts` —— `/api/dsh-config-manager/sync/*` 路由族 + 同步引擎
66
+ (`src/sync/sync-engine.ts`)+ 自动同步调度器 + Agent 工具(`config_sync_push` / `config_sync_pull`)
67
+ - **浏览器半边** `src/client/` —— 设置页里唯一的「同步」标签
68
+
69
+ `src/core/` 与 DSH 解耦(`ConfigAdapter` / `HostContext` + 内存 mock),
70
+ 业务逻辑放 `src/ui/`,React 壳只做装配。
71
+
72
+ 传输通道:`src/sync/git/` 与 `src/sync/webdav/`,共同的 `SyncTransport` 契约在 `src/sync/transport.ts`。
73
+
74
+ ---
75
+
76
+ ## 开发
77
+
78
+ ```bash
79
+ npm install --legacy-peer-deps # 部分 DSH 核心只在 peerDependencies
80
+ npm run typecheck # tsc --noEmit
81
+ npm test # node --test
82
+ npm run build # tsc(host lib/) + tsdown(client lib/client.js)
83
+ npm run dev:watch # 改 src/client/** 自动重建产物(浏览器原位替换)
84
+ ```
85
+
86
+ - 无 lint/format 脚本,只以 typecheck 兜底。
87
+ - 样式全在 `src/client/config-manager.module.css`(CSS Modules,tsdown 编译为内联注入)。
88
+ - **import 一律带 `.ts` 后缀**(Deno-style)。
89
+
90
+ ### 测试的 TMPDIR 陷阱(macOS)
91
+
92
+ `src/utils/recursive-walk.test.ts` 建真实符号链接,而 macOS 的 `/var/folders/...` 是
93
+ `/private/var/...` 的符号链接 —— `realpath` 会把 home 解析到 `/private/...`,导致
94
+ 「home 内」判定失败。跑测试前:
95
+
96
+ ```bash
97
+ mkdir -p /private/tmp/realhome
98
+ TMPDIR=/private/tmp/realhome/ node --test "src/**/*.test.ts" "tests/**/*.test.ts"
99
+ ```
100
+
101
+ ---
102
+
103
+ ## 许可证
104
+
105
+ MIT
@@ -0,0 +1,21 @@
1
+ # dsh-config-manager bundle patch: inserts the dual-face plugin row into the
2
+ # profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
3
+ # manifest field in package.json) over dsh-base; activate with
4
+ # dsh plugin --profile <name> add @logictan/dsh-config-manager@latest # published
5
+ # dsh plugin --profile <name> add link:<abs-or-~-path> # local dev
6
+ # (the CLI forwards to pnpm inside the profile directory).
7
+ #
8
+ # CLI hard criterion: a package is appended to the target profile's
9
+ # `dsh.profile.bundles` only when its manifest carries `dsh.bundle.patch`
10
+ # (package.json does — it points at this file); without that field the CLI
11
+ # treats the package as a plain dependency and warns. So this file plus the
12
+ # package.json `dsh.bundle.patch` key are what make the package a real bundle.
13
+ #
14
+ # One bare plugin row by package name: the node half (exports ".") runs in the
15
+ # host process (/api/dsh-config-manager routes, sync engine, agent tools), and
16
+ # the `dsh.client` declaration in package.json makes the browser half
17
+ # (exports "./client") load in the web GUI. Row id equals the host plugin's
18
+ # `export const name` in src/index.ts.
19
+ - insert:
20
+ - id: config-manager
21
+ name: '@logictan/dsh-config-manager'
@@ -0,0 +1,13 @@
1
+ import { FileCollectionAdapter } from './file-collection.ts';
2
+ import type { FilesSection } from '../schema/types.ts';
3
+ import type { ExportOptions, ExportSection, HostContext } from '../core/types.ts';
4
+ export declare class AgentInstructionsAdapter extends FileCollectionAdapter {
5
+ readonly id: 'agentInstructions';
6
+ readonly displayName = "Agent Instructions";
7
+ readonly defaultIncluded = true;
8
+ readonly portability: 'portable';
9
+ readonly baseDir = "";
10
+ /** 用户全局指令文件(相对 homeDir;同时是 schema 端该分区唯一的文件) */
11
+ static readonly FILE = "AGENTS.md";
12
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<FilesSection>>;
13
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * agentInstructions 分区 adapter(设计 §3.3 / dsh-agent-instructions 插件):
3
+ * 数据源 = ~/.dsh/AGENTS.md(用户全局指令文件,dsh-agent-instructions 固定读取 $DSH_HOME/AGENTS.md,
4
+ * 注入每个会话的基线指令)。
5
+ *
6
+ * 与 skills/agentPresets 的差异:AGENTS.md 位于 $DSH_HOME 根目录,不能对 homeDir 整目录递归枚举,
7
+ * 因此不复用 FileCollectionAdapter 的目录扫描,而是白名单固定收集单文件 AGENTS.md。
8
+ * 仅迁移全局文件;项目级 AGENTS.md / CLAUDE.md 属于各项目仓库,默认不迁(研究报告 §2.2 同策略)。
9
+ * baseDir = ''(homeDir 根),与 core/backup.ts FILE_BASES 的 '' 基准一致,保证通用快照/回滚路径正确。
10
+ */
11
+ import { sha256Hex } from '../utils/hashing.js';
12
+ import { msgOf } from '../core/messages.js';
13
+ import { FileCollectionAdapter } from './file-collection.js';
14
+ export class AgentInstructionsAdapter extends FileCollectionAdapter {
15
+ id = 'agentInstructions';
16
+ displayName = 'Agent Instructions';
17
+ defaultIncluded = true;
18
+ portability = 'portable';
19
+ baseDir = '';
20
+ /** 用户全局指令文件(相对 homeDir;同时是 schema 端该分区唯一的文件) */
21
+ static FILE = 'AGENTS.md';
22
+ async export(ctx, _options) {
23
+ const files = [];
24
+ try {
25
+ const data = await ctx.fs.readFile(AgentInstructionsAdapter.FILE);
26
+ files.push({
27
+ relativePath: AgentInstructionsAdapter.FILE,
28
+ data,
29
+ contentHash: sha256Hex(data),
30
+ });
31
+ }
32
+ catch {
33
+ // 文件不存在 → 空分区(与 skills 目录不存在视为空一致),下方归档 dirEmpty 提示
34
+ }
35
+ const warnings = [];
36
+ if (files.length === 0)
37
+ warnings.push(msgOf(ctx)('adapter.dirEmpty', { type: this.displayName }));
38
+ return {
39
+ sectionId: this.id,
40
+ data: { version: 1, files },
41
+ counts: { files: files.length },
42
+ warnings,
43
+ };
44
+ }
45
+ }
46
+ //# sourceMappingURL=agent-instructions.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * agentPresets 分区 adapter(设计 §3.3):
3
+ * 数据源 = ~/.dsh/.agent-presets/(用户可写目录,每预设一个目录:agent.cordis.yml + preset.yml)。
4
+ * system 预设(安装目录)只记引用不复制(本 adapter 只读用户目录)。
5
+ */
6
+ import { FileCollectionAdapter } from './file-collection.ts';
7
+ export declare class AgentPresetsAdapter extends FileCollectionAdapter {
8
+ readonly id: 'agentPresets';
9
+ readonly displayName = "Agent Presets";
10
+ readonly defaultIncluded = true;
11
+ readonly portability: 'portable';
12
+ readonly baseDir = ".agent-presets";
13
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * agentPresets 分区 adapter(设计 §3.3):
3
+ * 数据源 = ~/.dsh/.agent-presets/(用户可写目录,每预设一个目录:agent.cordis.yml + preset.yml)。
4
+ * system 预设(安装目录)只记引用不复制(本 adapter 只读用户目录)。
5
+ */
6
+ import { FileCollectionAdapter } from './file-collection.js';
7
+ export class AgentPresetsAdapter extends FileCollectionAdapter {
8
+ id = 'agentPresets';
9
+ displayName = 'Agent Presets';
10
+ defaultIncluded = true;
11
+ portability = 'portable';
12
+ baseDir = '.agent-presets';
13
+ }
14
+ //# sourceMappingURL=agent-presets.js.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * credentialsStatus 分区 adapter(设计 §3.3/§7.1):
3
+ * 数据源 = ctx.credentials.describe(ref) 的状态(configured/source/writable),
4
+ * 以及 settings secrets 标记 / llm apiKeyEnv 中引用的凭据 ref 名。
5
+ *
6
+ * 安全不变量:永不导出值(hasValue 恒 false);导入生成 MissingSecret 清单,
7
+ * 用户补录值经 ctx.secretInputs / decryptedCredentials(仅内存)→ credentials.set()。
8
+ * .credentials.yaml 文件字节交由 m4 加密层处理,本 adapter 不触碰。
9
+ */
10
+ import type { CredentialsSection } from '../schema/types.ts';
11
+ import type { MsgFunc } from '../core/messages.ts';
12
+ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
13
+ import { type NamespaceProvider } from './settings.ts';
14
+ export type CredentialRefsProvider = (ctx: HostContext) => Promise<string[]>;
15
+ /** 缺省 ref 收集:遍历 settings namespace,收集 llm apiKeyEnv / providers[].apiKeyEnv,
16
+ * 以及 secrets 标记中「引用类字段」(apiKeyEnv/tokenEnv…)的字段值。
17
+ * 注意 secrets[].path[0] 是 settings 文档内的字段路径(如 ['apiKey']),本身不是凭据 ref(设计 §4.2),
18
+ * 只有指向 env 名的引用字段才值得收集。 */
19
+ export declare function defaultCredentialRefs(namespaces: string[] | NamespaceProvider): CredentialRefsProvider;
20
+ export interface CredentialsAdapterOptions {
21
+ /** 凭据 ref 名收集器(缺省从 settings 推断) */
22
+ refs?: CredentialRefsProvider;
23
+ /** 供缺省 refs 使用的 namespace 清单 */
24
+ namespaces?: string[] | NamespaceProvider;
25
+ }
26
+ export declare class CredentialsAdapter implements ConfigAdapter<CredentialsSection> {
27
+ readonly id: 'credentialsStatus';
28
+ readonly displayName = "Credentials";
29
+ readonly defaultIncluded = true;
30
+ readonly portability: 'deviceSpecific';
31
+ private readonly refs;
32
+ constructor(options?: CredentialsAdapterOptions);
33
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<CredentialsSection>>;
34
+ analyzeImport(_data: CredentialsSection, _ctx: ImportContext): Promise<PlanItem[]>;
35
+ applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
36
+ validate(data: CredentialsSection, msg?: MsgFunc): Promise<ValidationResult>;
37
+ }
@@ -0,0 +1,122 @@
1
+ import { msgOf, zhMsg } from '../core/messages.js';
2
+ import { resolveNamespaces } from './settings.js';
3
+ /** 缺省 ref 收集:遍历 settings namespace,收集 llm apiKeyEnv / providers[].apiKeyEnv,
4
+ * 以及 secrets 标记中「引用类字段」(apiKeyEnv/tokenEnv…)的字段值。
5
+ * 注意 secrets[].path[0] 是 settings 文档内的字段路径(如 ['apiKey']),本身不是凭据 ref(设计 §4.2),
6
+ * 只有指向 env 名的引用字段才值得收集。 */
7
+ export function defaultCredentialRefs(namespaces) {
8
+ return async (ctx) => {
9
+ const refs = new Set();
10
+ for (const ns of await resolveNamespaces(namespaces, ctx)) {
11
+ try {
12
+ const info = await ctx.settings.describe(ns, { redactSecrets: true });
13
+ const value = (info.value ?? {});
14
+ // 引用类字段名(值 = env/凭据引用名,非秘密本身)
15
+ const REFERENCE_REF_FIELDS = new Set([
16
+ 'apikeyenv', 'api_key_env', 'apikeyname', 'tokenenv', 'accesstokenenv',
17
+ 'refreshtokenenv', 'clientsecretenv', 'passwordenv',
18
+ ]);
19
+ for (const s of info.secrets) {
20
+ const first = s.path?.[0];
21
+ if (typeof first !== 'string' || first === '')
22
+ continue;
23
+ const norm = first.toLowerCase().replace(/[^a-z0-9]/g, '');
24
+ if (!REFERENCE_REF_FIELDS.has(norm))
25
+ continue;
26
+ const val = value[first];
27
+ if (typeof val === 'string' && val !== '')
28
+ refs.add(val);
29
+ }
30
+ if (typeof value['apiKeyEnv'] === 'string' && value['apiKeyEnv'] !== '')
31
+ refs.add(value['apiKeyEnv']);
32
+ const providers = value['providers'];
33
+ if (providers !== null && typeof providers === 'object') {
34
+ for (const pv of Object.values(providers)) {
35
+ if (pv !== null && typeof pv === 'object' && typeof pv.apiKeyEnv === 'string' && pv.apiKeyEnv !== '') {
36
+ refs.add(pv.apiKeyEnv);
37
+ }
38
+ }
39
+ }
40
+ }
41
+ catch {
42
+ // namespace 不存在则跳过
43
+ }
44
+ }
45
+ return [...refs];
46
+ };
47
+ }
48
+ export class CredentialsAdapter {
49
+ id = 'credentialsStatus';
50
+ displayName = 'Credentials';
51
+ defaultIncluded = true;
52
+ portability = 'deviceSpecific';
53
+ refs;
54
+ constructor(options = {}) {
55
+ this.refs = options.refs ?? defaultCredentialRefs(options.namespaces ?? []);
56
+ }
57
+ async export(ctx, _options) {
58
+ const credentials = [];
59
+ const warnings = [];
60
+ for (const ref of await this.refs(ctx)) {
61
+ try {
62
+ const status = await ctx.credentials.describe(ref);
63
+ credentials.push({
64
+ ref,
65
+ required: true,
66
+ configured: status.configured,
67
+ source: status.source ?? 'file',
68
+ hasValue: false, // 值未导出(安全不变量)
69
+ });
70
+ }
71
+ catch (err) {
72
+ warnings.push(msgOf(ctx)('adapter.credStatusReadFailed', { ref, reason: err instanceof Error ? err.message : String(err) }));
73
+ }
74
+ }
75
+ return {
76
+ sectionId: 'credentialsStatus',
77
+ data: { version: 1, credentials },
78
+ counts: { credentials: credentials.length },
79
+ warnings,
80
+ };
81
+ }
82
+ async analyzeImport(_data, _ctx) {
83
+ // MissingSecret 计划项由引擎在 createImportPlan 兜底生成(analyzer.ensureMissingSecrets),
84
+ // 依据 = 本分区里 configured=true 的凭据 → 用户补录清单。这里保持零写入纯计算。
85
+ return [];
86
+ }
87
+ async applyItem(item, ctx) {
88
+ const ref = item.target?.ref;
89
+ if (!ref)
90
+ return { ok: false, message: ctx.msg('adapter.missingTargetRef') };
91
+ const value = ctx.secretInputs[ref] ?? ctx.decryptedCredentials?.get(ref);
92
+ if (value === undefined || value === '')
93
+ return { ok: false, message: ctx.msg('adapter.credentialValueMissing') };
94
+ await ctx.target.credentials.set(ref, value);
95
+ return { ok: true };
96
+ }
97
+ async validate(data, msg = zhMsg) {
98
+ const issues = [];
99
+ if (data === null || typeof data !== 'object') {
100
+ return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'credentials' }), severity: 'error' }] };
101
+ }
102
+ if (data.version !== 1) {
103
+ issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
104
+ }
105
+ if (!Array.isArray(data.credentials)) {
106
+ issues.push({ path: 'credentials', message: msg('adapter.validate.array', { subject: 'credentials' }), severity: 'error' });
107
+ }
108
+ else {
109
+ for (const c of data.credentials) {
110
+ if (c === null || typeof c !== 'object' || typeof c.ref !== 'string' || c.ref === '') {
111
+ issues.push({ path: 'credentials[]', message: msg('adapter.validate.credentialRef'), severity: 'error' });
112
+ }
113
+ if (c.hasValue === true) {
114
+ // 安全不变量:普通导出恒不携带值;若备份声称有值,视为结构异常
115
+ issues.push({ path: `credentials.${c.ref}.hasValue`, message: msg('adapter.validate.hasValueFalse'), severity: 'error' });
116
+ }
117
+ }
118
+ }
119
+ return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
120
+ }
121
+ }
122
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1,15 @@
1
+ import type { MsgFunc } from '../core/messages.ts';
2
+ import type { FilesSection, SectionId } from '../schema/types.ts';
3
+ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, Portability, ValidationResult } from '../core/types.ts';
4
+ export declare abstract class FileCollectionAdapter implements ConfigAdapter<FilesSection> {
5
+ abstract readonly id: SectionId;
6
+ abstract readonly displayName: string;
7
+ abstract readonly defaultIncluded: boolean;
8
+ abstract readonly portability: Portability;
9
+ /** 相对 homeDir 的基准目录(与 core/backup.ts FILE_BASES 一致) */
10
+ abstract readonly baseDir: string;
11
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<FilesSection>>;
12
+ analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;
13
+ applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
14
+ validate(data: FilesSection, msg?: MsgFunc): Promise<ValidationResult>;
15
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * 文件类分区共享 adapter 基类(skills / agentPresets / sessions 复用;
3
+ * agentInstructions 因位于 homeDir 根、只能白名单收集,覆写 export 后复用本类其余逻辑)。
4
+ * 基准目录与 core/backup.ts 的 FILE_BASES 保持一致(skills→'skills'、agentPresets→'.agent-presets'、sessions→'sessions'),
5
+ * 保证引擎通用快照/回滚(resolveFileTarget)与 applyItem 的写入路径完全一致。
6
+ *
7
+ * 文件内容以真实文件进入 ZIP(custom/skills/… 等前缀由 exporter 按 SECTION_FILE_PREFIXES 处理)。
8
+ * 幂等:相对路径 + 内容 SHA-256 hash 比对(重复导入 → Skip)。
9
+ */
10
+ import path from 'node:path';
11
+ import { sha256Hex } from '../utils/hashing.js';
12
+ import { isReservedInternalRel, normalizePath } from '../utils/paths.js';
13
+ import { msgOf, zhMsg } from '../core/messages.js';
14
+ import { linkWarnings, listFilesDetailed } from './link-report.js';
15
+ export class FileCollectionAdapter {
16
+ async export(ctx, _options) {
17
+ const files = [];
18
+ const warnings = [];
19
+ // issue #37:用「跟随 junction/符号链接」的遍历,并把跟随/跳过的链接写进告警——
20
+ // 此前链接目录及其全部内容被静默排除,备份仍报成功。
21
+ let listing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
22
+ try {
23
+ listing = await listFilesDetailed(ctx.fs, this.baseDir);
24
+ }
25
+ catch {
26
+ // 目录不存在视为空
27
+ }
28
+ const rels = listing.paths;
29
+ for (const rel of rels) {
30
+ const data = await ctx.fs.readFile(rel);
31
+ const relPath = this.baseDir === '' ? rel : rel.replace(new RegExp(`^${escapeRegExp(this.baseDir)}[\\/]`), '');
32
+ files.push({ relativePath: relPath, data, contentHash: sha256Hex(data) });
33
+ }
34
+ if (rels.length === 0)
35
+ warnings.push(msgOf(ctx)('adapter.dirEmpty', { type: this.displayName }));
36
+ warnings.push(...linkWarnings(msgOf(ctx), this.displayName, listing));
37
+ return {
38
+ sectionId: this.id,
39
+ data: { version: 1, files },
40
+ counts: { files: files.length },
41
+ warnings,
42
+ };
43
+ }
44
+ async analyzeImport(data, ctx) {
45
+ const msg = ctx.msg;
46
+ const items = [];
47
+ for (const file of data.files) {
48
+ const id = `${this.id}:${file.relativePath}`;
49
+ // F23 修复:不可信 import 不得写内部 control-plane namespace。
50
+ // 检查 baseDir+ref 解析后的 homeDir 相对路径(self 适配器 baseDir='dsh-config-manager' 是主投毒向量)。
51
+ const resolvedRel = normalizePath(path.join(this.baseDir, file.relativePath));
52
+ if (isReservedInternalRel(resolvedRel)) {
53
+ items.push({
54
+ id, kind: 'Error', adapter: this.id,
55
+ description: msg('adapter.fileReserved', { path: file.relativePath }), severity: 'error',
56
+ });
57
+ continue;
58
+ }
59
+ let current = null;
60
+ try {
61
+ current = await ctx.target.fs.readFile(path.join(this.baseDir, file.relativePath));
62
+ }
63
+ catch {
64
+ current = null;
65
+ }
66
+ if (current === null) {
67
+ items.push({
68
+ id, kind: 'Create', adapter: this.id,
69
+ description: msg('adapter.fileCreate', { type: this.displayName, path: file.relativePath }), severity: 'info',
70
+ target: { adapter: this.id, ref: file.relativePath },
71
+ });
72
+ }
73
+ else if (sha256Hex(current) === file.contentHash) {
74
+ items.push({ id, kind: 'Skip', adapter: this.id, description: msg('adapter.fileSame', { path: file.relativePath }), severity: 'info' });
75
+ }
76
+ else {
77
+ items.push({
78
+ id, kind: 'Conflict', adapter: this.id,
79
+ description: msg('adapter.fileDiff', { path: file.relativePath }), severity: 'warning',
80
+ target: { adapter: this.id, ref: file.relativePath },
81
+ });
82
+ }
83
+ }
84
+ return items;
85
+ }
86
+ async applyItem(item, ctx) {
87
+ const msg = ctx.msg;
88
+ const ref = item.target?.ref;
89
+ if (!ref)
90
+ return { ok: false, message: msg('adapter.missingTargetRef') };
91
+ // F23 修复:apply 前拒绝写内部 control-plane namespace(纵深防御,analyzeImport 已标 Error)
92
+ const resolvedRel = normalizePath(path.join(this.baseDir, ref));
93
+ if (isReservedInternalRel(resolvedRel)) {
94
+ return { ok: false, message: msg('adapter.fileReserved', { path: ref }) };
95
+ }
96
+ const data = ctx.sections.get(this.id);
97
+ const file = data?.files.find((f) => f.relativePath === ref);
98
+ if (!file)
99
+ return { ok: false, message: msg('adapter.dataMissingFile', { ref }) };
100
+ await ctx.target.fs.writeFile(path.join(this.baseDir, ref), file.data);
101
+ return { ok: true };
102
+ }
103
+ async validate(data, msg = zhMsg) {
104
+ const issues = [];
105
+ if (data === null || typeof data !== 'object') {
106
+ return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.fileSection'), severity: 'error' }] };
107
+ }
108
+ if (data.version !== 1) {
109
+ issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
110
+ }
111
+ if (!Array.isArray(data.files)) {
112
+ issues.push({ path: 'files', message: msg('adapter.validate.array', { subject: 'files' }), severity: 'error' });
113
+ }
114
+ else {
115
+ for (const f of data.files) {
116
+ if (f === null || typeof f !== 'object' || typeof f.relativePath !== 'string' || f.relativePath === '') {
117
+ issues.push({ path: 'files[]', message: msg('adapter.validate.fileRelativePath'), severity: 'error' });
118
+ }
119
+ }
120
+ }
121
+ return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
122
+ }
123
+ }
124
+ function escapeRegExp(s) {
125
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
126
+ }
127
+ //# sourceMappingURL=file-collection.js.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 适配器 registry(设计 §12.1 / §3.3):
3
+ * 组装全部 ConfigAdapter(导出顺序参考 APPLY_ORDER:settings → ui → providers → prompts
4
+ * → skills → agentPresets → agentInstructions → workspaces → pluginFiles → mcp → plugins → credentialsStatus)。
5
+ *
6
+ * 明确不实现的 adapter(研究报告 §2.2 确认 DSH 无对应概念):keybindings / workflows / commands ——
7
+ * manifest 中不出现这些 section,产品 UI 说明「DSH 当前无此配置」;
8
+ * rules 概念已由 agentInstructions(~/.dsh/AGENTS.md)承接。
9
+ */
10
+ import type { ConfigAdapter } from '../core/types.ts';
11
+ import { type NamespaceProvider } from './settings.ts';
12
+ import { type LocalPluginPackHook } from './plugins.ts';
13
+ import { type CredentialRefsProvider } from './credentials.ts';
14
+ export interface AdapterRegistryOptions {
15
+ /** settings namespace 清单(宿主从 settings.yaml 顶层 key 解析注入;缺省 [] → settings/ui/credentials 为空) */
16
+ namespaces?: string[] | NamespaceProvider;
17
+ /** 凭据 ref 收集器(缺省从 settings secrets 标记 + llm apiKeyEnv 推断) */
18
+ credentialsRefs?: CredentialRefsProvider;
19
+ /** pluginFiles 白名单(相对 ~/.dsh 根;缺省 dsh-ssh.json + pet.json) */
20
+ pluginFiles?: string[];
21
+ /** pluginFiles 约定的配置目录(相对 ~/.dsh 根),递归收集其下所有插件配置文件 */
22
+ pluginFilesDir?: string;
23
+ /** 是否包含 sessions 分区(默认关,研究报告 §4.9) */
24
+ includeSessions?: boolean;
25
+ /** 插件自身包名:导出 plugins 分区时不列自己(避免自引用;缺省 dsh-config-manager) */
26
+ selfPluginName?: string;
27
+ /**
28
+ * T1:本地源(link:/file:)插件打包钩子。注入后,导出 plugins 分区时会为这类插件
29
+ * 执行 `npm pack` 并把 tarball 随备份带走;不注入 = 保持改造前行为(本地插件换机丢失)。
30
+ */
31
+ localPluginPack?: LocalPluginPackHook;
32
+ /**
33
+ * self 分区:插件自身配置目录(相对 ~/.dsh 根,如 'dsh-config-manager')。
34
+ * 缺省 'dsh-config-manager';传入 '' 表示不挂载(宿主自定义 dataDir 在 homeDir 外时)。
35
+ */
36
+ selfDir?: string;
37
+ }
38
+ /** 组装默认 adapter 列表(pluginFiles/sessions 恒挂载但 defaultIncluded=false,用户勾选才导出) */
39
+ export declare function createAdapters(options?: AdapterRegistryOptions): ConfigAdapter[];
40
+ export type { NamespaceProvider } from './settings.ts';
41
+ export type { CredentialRefsProvider } from './credentials.ts';
42
+ export { SettingsAdapter } from './settings.ts';
43
+ export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, UI_MIGRATION_NOTES } from './ui.ts';
44
+ export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES, type ProviderExportEntry, type ProviderExportSection } from './providers.ts';
45
+ export { PluginsAdapter, USER_PATCH_FILE } from './plugins.ts';
46
+ export type { LocalPluginPackHook } from './plugins.ts';
47
+ export { McpAdapter, extractMcpServers, buildMcpPatchLine, type McpExportEntry, type McpExportSection } from './mcp.ts';
48
+ export { PromptsAdapter, extractPrompts, mergePromptIntoLine, buildPromptLine, type PromptExportEntry, type PromptsExportSection } from './prompts.ts';
49
+ export { SkillsAdapter } from './skills.ts';
50
+ export { AgentPresetsAdapter } from './agent-presets.ts';
51
+ export { AgentInstructionsAdapter } from './agent-instructions.ts';
52
+ export { WorkspacesAdapter } from './workspaces.ts';
53
+ export { CredentialsAdapter, defaultCredentialRefs } from './credentials.ts';
54
+ export { PluginFilesAdapter, DEFAULT_PLUGIN_FILE_WHITELIST } from './plugin-files.ts';
55
+ export { SessionsAdapter } from './sessions.ts';
56
+ export { SelfAdapter, SELF_CONFIG_FILES } from './self.ts';
57
+ export { FileCollectionAdapter } from './file-collection.ts';