@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
@@ -0,0 +1,147 @@
1
+ /**
2
+ * pnpm-workspace.yaml 的 `patchedDependencies` 处理(issue #35)。
3
+ *
4
+ * 背景:plugins 分区会**整段搬运** pnpm-workspace.yaml,但 `patchedDependencies` 引用的
5
+ * `patches/*.patch` 文件不在任何被同步的分区里。目标机因此拿到「声明存在、文件不存在」的
6
+ * 组合,此后**任何** `pnpm add`(含 dsh plugin add)都失败于
7
+ * `Failed to read patch file ... (os error 2)` —— 用户实测 13/13 插件安装全灭。
8
+ *
9
+ * 本模块做两件事,且**不做整文件重写**(保留用户注释与格式):
10
+ * 1. `parsePnpmPatchedDependencies`:按行解析声明(供导出侧收集 patch 文件);
11
+ * 2. `sanitizePnpmWorkspacePatches`:把「目标机没有对应文件」的条目整行删掉
12
+ * (含其上方注释),全部被删时连键一起删 —— 宁可少迁移一条 patch 声明,
13
+ * 也不让目标机的 pnpm 从此拒绝一切安装。
14
+ */
15
+ import { isPathSafe, normalizePath } from '../utils/paths.js';
16
+ const KEY_RE = /^patchedDependencies\s*:(.*)$/;
17
+ const ENTRY_RE = /^([ \t]+)([^\s#][^:]*?)\s*:\s*(.*)$/;
18
+ /** 去引号 + 去行尾注释(保守:只在引号外找 ` #`) */
19
+ function cleanScalar(raw) {
20
+ let v = raw.trim();
21
+ if (v.startsWith("'") || v.startsWith('"')) {
22
+ const quote = v[0];
23
+ const end = v.indexOf(quote, 1);
24
+ if (end > 0)
25
+ return v.slice(1, end);
26
+ }
27
+ const hash = v.indexOf(' #');
28
+ if (hash >= 0)
29
+ v = v.slice(0, hash);
30
+ return v.trim();
31
+ }
32
+ /** 解析 patchedDependencies 的声明(纯函数;不校验文件是否存在)。 */
33
+ export function parsePnpmPatchedDependencies(text) {
34
+ const declared = [];
35
+ if (text === '')
36
+ return { declared, unsupported: null };
37
+ const lines = text.split(/\r?\n/);
38
+ for (let i = 0; i < lines.length; i++) {
39
+ const m = KEY_RE.exec(lines[i] ?? '');
40
+ if (m === null)
41
+ continue;
42
+ const inline = (m[1] ?? '').trim();
43
+ // 单行 flow 形态(patchedDependencies: {a: b})无法按行安全删条目 → 不改写,交给调用方告警
44
+ if (inline !== '' && !inline.startsWith('#')) {
45
+ return { declared, unsupported: lines[i] ?? '' };
46
+ }
47
+ for (let j = i + 1; j < lines.length; j++) {
48
+ const line = lines[j] ?? '';
49
+ if (line.trim() === '' || /^[ \t]*#/.test(line))
50
+ continue;
51
+ const e = ENTRY_RE.exec(line);
52
+ if (e === null)
53
+ break; // 缩进块结束(回到顶层键)
54
+ declared.push({ name: (e[2] ?? '').trim(), path: cleanScalar(e[3] ?? '') });
55
+ }
56
+ break; // patchedDependencies 只处理第一处(YAML 重复键本就非法)
57
+ }
58
+ return { declared, unsupported: null };
59
+ }
60
+ /**
61
+ * 删除 patchedDependencies 中「目标机不存在对应 patch 文件」的条目。
62
+ * @param available 判断某相对路径(相对 profile 目录)在**导入后**是否存在
63
+ */
64
+ export function sanitizePnpmWorkspacePatches(text, available) {
65
+ const parsed = parsePnpmPatchedDependencies(text);
66
+ if (parsed.unsupported !== null) {
67
+ return { text, declared: parsed.declared, dropped: [], unsupported: parsed.unsupported };
68
+ }
69
+ if (parsed.declared.length === 0)
70
+ return { text, declared: [], dropped: [], unsupported: null };
71
+ const droppedNames = new Set();
72
+ for (const d of parsed.declared) {
73
+ const rel = normalizePath(d.path);
74
+ // 越界/绝对路径的声明在目标机同样不可读(pnpm 也会拒绝)→ 一并移除
75
+ if (rel === '' || !isPathSafe(rel) || !available(rel))
76
+ droppedNames.add(d.name);
77
+ }
78
+ if (droppedNames.size === 0)
79
+ return { text, declared: parsed.declared, dropped: [], unsupported: null };
80
+ const eol = text.includes('\r\n') ? '\r\n' : '\n';
81
+ const lines = text.split(/\r?\n/);
82
+ const out = [];
83
+ let removed = 0;
84
+ for (let i = 0; i < lines.length; i++) {
85
+ const line = lines[i] ?? '';
86
+ const m = KEY_RE.exec(line);
87
+ if (m === null) {
88
+ out.push(line);
89
+ continue;
90
+ }
91
+ const inline = (m[1] ?? '').trim();
92
+ if (inline !== '' && !inline.startsWith('#')) {
93
+ out.push(line);
94
+ continue;
95
+ }
96
+ // 收集整块(键 + 缩进条目 + 其上的注释)
97
+ const block = [];
98
+ let j = i + 1;
99
+ for (; j < lines.length; j++) {
100
+ const l = lines[j] ?? '';
101
+ if (l.trim() === '' || /^[ \t]*#/.test(l)) {
102
+ block.push(l);
103
+ continue;
104
+ }
105
+ if (ENTRY_RE.exec(l) === null)
106
+ break;
107
+ block.push(l);
108
+ }
109
+ // 逐条判定:保留/删除(注释与空行随其**下一条**条目一起处置;尾部孤立注释一律保留)
110
+ const kept = [];
111
+ let pending = [];
112
+ let keptEntries = 0;
113
+ for (const bl of block) {
114
+ const e = ENTRY_RE.exec(bl);
115
+ if (e === null) {
116
+ pending.push(bl);
117
+ continue;
118
+ }
119
+ const name = (e[2] ?? '').trim();
120
+ if (droppedNames.has(name)) {
121
+ removed += 1;
122
+ pending = [];
123
+ continue;
124
+ }
125
+ kept.push(...pending, bl);
126
+ pending = [];
127
+ keptEntries += 1;
128
+ }
129
+ kept.push(...pending);
130
+ if (keptEntries > 0) {
131
+ out.push(line); // 键保留
132
+ out.push(...kept);
133
+ }
134
+ else {
135
+ removed += 1; // 键本身也删(没有任何条目留下)
136
+ }
137
+ i = j - 1;
138
+ }
139
+ // 全部条目都没了 → 键与块一起消失;否则写回(保留原 EOL)
140
+ return {
141
+ text: out.join(eol),
142
+ declared: parsed.declared,
143
+ dropped: parsed.declared.filter((d) => droppedNames.has(d.name)),
144
+ unsupported: null,
145
+ };
146
+ }
147
+ //# sourceMappingURL=pnpm-workspace.js.map
@@ -0,0 +1,30 @@
1
+ import type { MsgFunc } from '../core/messages.ts';
2
+ import type { PromptEntry } from '../schema/types.ts';
3
+ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
4
+ /** 导出记录:PromptEntry 之外记录来源行名(导入需要重建行时使用) */
5
+ export interface PromptExportEntry extends PromptEntry {
6
+ sourceLineName?: string;
7
+ }
8
+ export interface PromptsExportSection {
9
+ version: 1;
10
+ prompts: PromptExportEntry[];
11
+ }
12
+ /** 从 patch 行 config 中提取 prompt 条目(systemPrompt.persona / planMode.sections[].text) */
13
+ export declare function extractPrompts(lines: {
14
+ lineId: string;
15
+ raw: unknown;
16
+ }[]): PromptExportEntry[];
17
+ /** 把 prompt 文本合并进目标行 config(systemPrompt.persona 或 planMode.sections) */
18
+ export declare function mergePromptIntoLine(raw: unknown, prompt: PromptExportEntry): Record<string, unknown>;
19
+ /** 由导出条目构造「重建行」raw(Create 导入用;行名取自来源行,缺失时用占位名) */
20
+ export declare function buildPromptLine(lineId: string, prompt: PromptExportEntry): Record<string, unknown>;
21
+ export declare class PromptsAdapter implements ConfigAdapter<PromptsExportSection> {
22
+ readonly id: 'prompts';
23
+ readonly displayName = "Prompts";
24
+ readonly defaultIncluded = true;
25
+ readonly portability: 'portable';
26
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<PromptsExportSection>>;
27
+ analyzeImport(data: PromptsExportSection, ctx: ImportContext): Promise<PlanItem[]>;
28
+ applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
29
+ validate(data: PromptsExportSection, msg?: MsgFunc): Promise<ValidationResult>;
30
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * prompts 分区 adapter(设计 §3.3/§1.2):
3
+ * 数据源 = 组合 patch config 中的 systemPrompt persona / plan-mode section(研究报告 §2.2:
4
+ * DSH 无独立 prompt/rules 文件,persona 与 plan-mode 均经 patch config 注入)。
5
+ *
6
+ * v1 策略(诚实降级):
7
+ * - 目标存在同 lineId 行 → 同内容 Skip / 不同 Conflict(更新该行 config);
8
+ * - 目标无该行但存在同名 prompt → Conflict(更新其所在行);
9
+ * - 均不存在 → Warning(不编造行名自动创建,提示手动配置)。
10
+ * rules/commands 无独立存储(研究报告 §2.2),不实现。
11
+ */
12
+ import { isDeepStrictEqual } from 'node:util';
13
+ import { msgOf, zhMsg } from '../core/messages.js';
14
+ import { USER_PATCH_FILE } from './plugins.js';
15
+ /** 从 patch 行 config 中提取 prompt 条目(systemPrompt.persona / planMode.sections[].text) */
16
+ export function extractPrompts(lines) {
17
+ const prompts = [];
18
+ for (const line of lines) {
19
+ for (const entry of entriesOf(line.raw)) {
20
+ const config = entry.config;
21
+ if (config === null || typeof config !== 'object')
22
+ continue;
23
+ const c = config;
24
+ const sourceLineName = typeof entry.name === 'string' ? entry.name : undefined;
25
+ const sp = c['systemPrompt'];
26
+ if (typeof sp === 'string' && sp.trim() !== '') {
27
+ prompts.push({ id: `prompt:${line.lineId}:persona`, name: `${line.lineId}:persona`, kind: 'systemPrompt', text: sp, sourceLineId: line.lineId, sourceLineName });
28
+ }
29
+ else if (sp !== null && typeof sp === 'object' && typeof sp['persona'] === 'string') {
30
+ const persona = sp['persona'];
31
+ if (persona.trim() !== '') {
32
+ prompts.push({ id: `prompt:${line.lineId}:persona`, name: `${line.lineId}:persona`, kind: 'systemPrompt', text: persona, sourceLineId: line.lineId, sourceLineName });
33
+ }
34
+ }
35
+ const pm = c['planMode'];
36
+ if (pm !== null && typeof pm === 'object' && Array.isArray(pm['sections'])) {
37
+ const sections = pm['sections'];
38
+ for (const [i, s] of sections.entries()) {
39
+ if (s === null || typeof s !== 'object' || typeof s['text'] !== 'string' || s['text'].trim() === '')
40
+ continue;
41
+ const name = typeof s['name'] === 'string' && s['name'] !== '' ? s['name'] : `${line.lineId}:section${i}`;
42
+ prompts.push({ id: `prompt:${line.lineId}:${name}`, name, kind: 'planMode', text: s['text'], sourceLineId: line.lineId, sourceLineName });
43
+ }
44
+ }
45
+ }
46
+ }
47
+ return prompts;
48
+ }
49
+ /** patch 行 → 可枚举 entry(兼容单行与 insert 块;与 mcp.ts 共用形态) */
50
+ function entriesOf(raw) {
51
+ if (raw === null || typeof raw !== 'object')
52
+ return [];
53
+ const obj = raw;
54
+ if (Array.isArray(obj['insert'])) {
55
+ return obj['insert']
56
+ .filter((e) => e !== null && typeof e === 'object')
57
+ .map((e) => e);
58
+ }
59
+ if (obj['id'] !== undefined || obj['name'] !== undefined) {
60
+ return [obj];
61
+ }
62
+ return [];
63
+ }
64
+ /** 把 prompt 文本合并进目标行 config(systemPrompt.persona 或 planMode.sections) */
65
+ export function mergePromptIntoLine(raw, prompt) {
66
+ const cloned = structuredClone(raw);
67
+ if (cloned['config'] === null || typeof cloned['config'] !== 'object')
68
+ cloned['config'] = {};
69
+ const config = cloned['config'];
70
+ if (prompt.kind === 'systemPrompt') {
71
+ const sp = config['systemPrompt'];
72
+ if (typeof sp === 'string')
73
+ config['systemPrompt'] = { persona: sp };
74
+ else if (sp === null || typeof sp !== 'object')
75
+ config['systemPrompt'] = { persona: prompt.text };
76
+ else
77
+ config['systemPrompt']['persona'] = prompt.text;
78
+ }
79
+ else {
80
+ if (config['planMode'] === null || typeof config['planMode'] !== 'object')
81
+ config['planMode'] = {};
82
+ const pm = config['planMode'];
83
+ if (!Array.isArray(pm['sections']))
84
+ pm['sections'] = [];
85
+ const sections = pm['sections'];
86
+ const sec = sections.find((s) => s !== null && typeof s === 'object' && s['name'] === prompt.name);
87
+ if (sec)
88
+ sec['text'] = prompt.text;
89
+ else
90
+ sections.push({ name: prompt.name, text: prompt.text });
91
+ }
92
+ return cloned;
93
+ }
94
+ /** 由导出条目构造「重建行」raw(Create 导入用;行名取自来源行,缺失时用占位名) */
95
+ export function buildPromptLine(lineId, prompt) {
96
+ const name = prompt.sourceLineName ?? 'dsh-config-manager';
97
+ if (prompt.kind === 'planMode') {
98
+ return { id: lineId, name, config: { planMode: { sections: [{ name: prompt.name, text: prompt.text }] } } };
99
+ }
100
+ return { id: lineId, name, config: { systemPrompt: { persona: prompt.text } } };
101
+ }
102
+ export class PromptsAdapter {
103
+ id = 'prompts';
104
+ displayName = 'Prompts';
105
+ defaultIncluded = true;
106
+ portability = 'portable';
107
+ async export(ctx, _options) {
108
+ const warnings = [];
109
+ let lines = [];
110
+ try {
111
+ lines = await ctx.patchFile.readPatchLines(USER_PATCH_FILE);
112
+ }
113
+ catch (err) {
114
+ warnings.push(msgOf(ctx)('adapter.patchReadFailedPrompts', { reason: err instanceof Error ? err.message : String(err) }));
115
+ }
116
+ const prompts = extractPrompts(lines);
117
+ return {
118
+ sectionId: 'prompts',
119
+ data: { version: 1, prompts },
120
+ counts: { prompts: prompts.length },
121
+ warnings,
122
+ };
123
+ }
124
+ async analyzeImport(data, ctx) {
125
+ const msg = ctx.msg;
126
+ const items = [];
127
+ const targetLines = await ctx.target.patchFile.readPatchLines(USER_PATCH_FILE);
128
+ const targetPrompts = extractPrompts(targetLines);
129
+ for (const p of data.prompts) {
130
+ const id = p.id;
131
+ const sameName = targetPrompts.find((t) => t.name === p.name);
132
+ const targetLine = targetLines.find((l) => l.lineId === p.sourceLineId);
133
+ if (targetLine && sameName && sameName.sourceLineId === p.sourceLineId) {
134
+ if (sameName.text === p.text && sameName.kind === p.kind) {
135
+ items.push({ id, kind: 'Skip', adapter: 'prompts', description: msg('adapter.promptSame', { name: p.name }), severity: 'info' });
136
+ }
137
+ else {
138
+ items.push({
139
+ id, kind: 'Conflict', adapter: 'prompts',
140
+ description: msg('adapter.promptDiff', { name: p.name }),
141
+ detail: `行 ${p.sourceLineId} current=${JSON.stringify(sameName.text).slice(0, 80)} imported=${JSON.stringify(p.text).slice(0, 80)}`,
142
+ severity: 'warning',
143
+ target: { adapter: 'prompts', ref: p.sourceLineId ?? '' },
144
+ });
145
+ }
146
+ }
147
+ else if (sameName) {
148
+ // 目标存在同名 prompt(不同来源行)→ 更新其所在行
149
+ if (sameName.text === p.text && sameName.kind === p.kind) {
150
+ items.push({ id, kind: 'Skip', adapter: 'prompts', description: msg('adapter.promptSame', { name: p.name }), severity: 'info' });
151
+ }
152
+ else {
153
+ items.push({
154
+ id, kind: 'Conflict', adapter: 'prompts',
155
+ description: msg('adapter.promptDiffWithLine', { name: p.name, line: sameName.sourceLineId ?? '' }),
156
+ severity: 'warning',
157
+ target: { adapter: 'prompts', ref: sameName.sourceLineId ?? '' },
158
+ });
159
+ }
160
+ }
161
+ else if (p.sourceLineName) {
162
+ // 目标无来源行但记录到行名 → 重建行(Create);幂等键 = 行 id
163
+ const lineId = p.sourceLineId ?? `prompt-${p.name}`;
164
+ items.push({
165
+ id, kind: 'Create', adapter: 'prompts',
166
+ description: msg('adapter.promptCreate', { name: p.name, line: lineId }), severity: 'info',
167
+ target: { adapter: 'prompts', ref: lineId },
168
+ });
169
+ }
170
+ else {
171
+ items.push({
172
+ id, kind: 'Warning', adapter: 'prompts',
173
+ description: msg('adapter.promptManual', { name: p.name }),
174
+ severity: 'info',
175
+ });
176
+ }
177
+ }
178
+ return items;
179
+ }
180
+ async applyItem(item, ctx) {
181
+ const msg = ctx.msg;
182
+ if (item.kind === 'Warning')
183
+ return { ok: true, message: msg('adapter.promptWarningNoWrite') };
184
+ const ref = item.target?.ref;
185
+ if (!ref)
186
+ return { ok: false, message: msg('adapter.missingTargetRef') };
187
+ const data = ctx.sections.get('prompts');
188
+ const prompt = data?.prompts.find((p) => p.id === item.id);
189
+ if (!prompt)
190
+ return { ok: false, message: msg('adapter.promptMissing', { id: item.id }) };
191
+ // Create:目标无来源行 → 用记录的行名重建 patch 行(insert)
192
+ if (item.kind === 'Create') {
193
+ const raw = buildPromptLine(ref, prompt);
194
+ await ctx.target.patchFile.applyPatchChanges(USER_PATCH_FILE, [
195
+ { lineId: ref, raw, action: 'insert' },
196
+ ]);
197
+ return { ok: true, needsRestart: true, message: msg('adapter.promptCreated', { name: prompt.name, ref }) };
198
+ }
199
+ // Update / Conflict(useImported):合并进目标行 config
200
+ const lines = await ctx.target.patchFile.readPatchLines(USER_PATCH_FILE);
201
+ const line = lines.find((l) => l.lineId === ref);
202
+ if (!line)
203
+ return { ok: false, message: msg('adapter.patchLineMissing', { ref }) };
204
+ const newRaw = mergePromptIntoLine(line.raw, prompt);
205
+ await ctx.target.patchFile.applyPatchChanges(USER_PATCH_FILE, [
206
+ { lineId: ref, raw: newRaw, action: 'update' },
207
+ ]);
208
+ return { ok: true, needsRestart: true, message: msg('adapter.promptWritten', { name: prompt.name, ref }) };
209
+ }
210
+ async validate(data, msg = zhMsg) {
211
+ const issues = [];
212
+ if (data === null || typeof data !== 'object') {
213
+ return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'prompts' }), severity: 'error' }] };
214
+ }
215
+ if (data.version !== 1) {
216
+ issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
217
+ }
218
+ if (!Array.isArray(data.prompts)) {
219
+ issues.push({ path: 'prompts', message: msg('adapter.validate.array', { subject: 'prompts' }), severity: 'error' });
220
+ }
221
+ else {
222
+ for (const p of data.prompts) {
223
+ if (p === null || typeof p !== 'object' || typeof p.name !== 'string' || typeof p.text !== 'string') {
224
+ issues.push({ path: 'prompts[]', message: msg('adapter.validate.promptIdentity'), severity: 'error' });
225
+ }
226
+ }
227
+ }
228
+ return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
229
+ }
230
+ }
231
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1,34 @@
1
+ import type { MsgFunc } from '../core/messages.ts';
2
+ import type { ProviderEntry } from '../schema/types.ts';
3
+ import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
4
+ import { type NamespaceProvider } from './settings.ts';
5
+ /** 导出记录:ProviderEntry 之外附加 namespace 级元数据(导入写回用),满足 ProvidersSection 形状 */
6
+ export interface ProviderExportEntry extends ProviderEntry {
7
+ /** 来源 settings namespace(llm-deepseek / llm-pi-ai) */
8
+ namespace: string;
9
+ /** 乐观锁 revision(导入时检测并发修改) */
10
+ revision: number;
11
+ secrets?: {
12
+ path: string[];
13
+ set: boolean;
14
+ }[];
15
+ /** 该 namespace 的完整 redacted 值(导入整体写回,避免按字段重建丢失结构) */
16
+ raw?: Record<string, unknown>;
17
+ }
18
+ export interface ProviderExportSection {
19
+ version: 1;
20
+ providers: Record<string, ProviderExportEntry>;
21
+ }
22
+ export declare const DEFAULT_PROVIDER_NAMESPACES: readonly string[];
23
+ export declare class ProvidersAdapter implements ConfigAdapter<ProviderExportSection> {
24
+ readonly id: 'providers';
25
+ readonly displayName = "Providers & Models";
26
+ readonly defaultIncluded = true;
27
+ readonly portability: 'portable';
28
+ private readonly namespaces;
29
+ constructor(namespaces?: string[] | NamespaceProvider);
30
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<ProviderExportSection>>;
31
+ analyzeImport(data: ProviderExportSection, ctx: ImportContext): Promise<PlanItem[]>;
32
+ applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
33
+ validate(data: ProviderExportSection, msg?: MsgFunc): Promise<ValidationResult>;
34
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * providers 分区 adapter(设计 §3.3/§13.1):
3
+ * 数据源 = settings.yaml 的 llm-deepseek / llm-pi-ai section(providers 与 models 天然同 section,不拆文件)。
4
+ *
5
+ * 导入走 settings 写通道(replace llm-* namespace + 乐观锁):
6
+ * 每项携带 raw(该 namespace 的完整 redacted 值)用于整体写回,apiKey 值经 credentials 状态占位(不导出值)。
7
+ */
8
+ import { isDeepStrictEqual } from 'node:util';
9
+ import { msgOf, zhMsg } from '../core/messages.js';
10
+ import { resolveNamespaces, isEmptyValue } from './settings.js';
11
+ export const DEFAULT_PROVIDER_NAMESPACES = ['llm-deepseek', 'llm-pi-ai'];
12
+ /** 无 raw 时的兜底重建:只取 ProviderEntry 已知字段 */
13
+ function stripEntry(entry) {
14
+ const out = {};
15
+ for (const key of ['apiKeyEnv', 'displayName', 'baseURL', 'models', 'modelOverrides', 'reasoning', 'transport', 'retryPolicy']) {
16
+ if (entry[key] !== undefined)
17
+ out[key] = entry[key];
18
+ }
19
+ return out;
20
+ }
21
+ export class ProvidersAdapter {
22
+ id = 'providers';
23
+ displayName = 'Providers & Models';
24
+ defaultIncluded = true;
25
+ portability = 'portable';
26
+ namespaces;
27
+ constructor(namespaces = DEFAULT_PROVIDER_NAMESPACES) {
28
+ this.namespaces = namespaces;
29
+ }
30
+ async export(ctx, _options) {
31
+ const providers = {};
32
+ const warnings = [];
33
+ for (const ns of await resolveNamespaces(this.namespaces, ctx)) {
34
+ let info;
35
+ try {
36
+ info = await ctx.settings.describe(ns, { redactSecrets: true });
37
+ }
38
+ catch (err) {
39
+ warnings.push(msgOf(ctx)('adapter.providerNsReadFailed', { ns, reason: err instanceof Error ? err.message : String(err) }));
40
+ continue;
41
+ }
42
+ const value = (info.value ?? {});
43
+ // llm-pi-ai 形态:value.providers = { route: {...} };llm-deepseek 形态:value 即单 provider 配置
44
+ const routes = value.providers !== null && typeof value.providers === 'object'
45
+ ? Object.entries(value.providers)
46
+ : [[ns, value]];
47
+ for (const [route, pv] of routes) {
48
+ // 每个 route 独立深拷贝 secrets / pv / raw:同一 namespace 的多 route 若共享引用,
49
+ // m4 强化版 createSecretScanner 的 visited 循环检测会误判「检测到循环引用」(见 providers.test.ts)。
50
+ providers[route] = {
51
+ route,
52
+ namespace: ns,
53
+ revision: info.revision,
54
+ secrets: structuredClone(info.secrets),
55
+ ...structuredClone(pv),
56
+ raw: structuredClone(value),
57
+ };
58
+ }
59
+ }
60
+ return {
61
+ sectionId: 'providers',
62
+ data: { version: 1, providers },
63
+ counts: { providers: Object.keys(providers).length },
64
+ warnings,
65
+ };
66
+ }
67
+ async analyzeImport(data, ctx) {
68
+ const msg = ctx.msg;
69
+ const items = [];
70
+ for (const [route, entry] of Object.entries(data.providers)) {
71
+ const id = `provider:${route}`;
72
+ let current = null;
73
+ let targetUnavailable = false;
74
+ try {
75
+ current = await ctx.target.settings.describe(entry.namespace, { redactSecrets: true });
76
+ }
77
+ catch {
78
+ // 目标未注册该 namespace(缺少提供它的插件)→ 写入必失败,按依赖缺失处理(§15)
79
+ targetUnavailable = true;
80
+ }
81
+ if (targetUnavailable) {
82
+ items.push({
83
+ id, kind: 'MissingDependency', adapter: 'providers',
84
+ description: msg('adapter.providerUnregistered', { route, ns: entry.namespace }),
85
+ severity: 'warning', target: { adapter: 'providers', ref: entry.namespace },
86
+ });
87
+ }
88
+ else if (current === null || isEmptyValue(current.value)) {
89
+ // 目标已注册但为空 → 初始化(Create)
90
+ items.push({
91
+ id, kind: 'Create', adapter: 'providers',
92
+ description: msg('adapter.providerCreate', { route, ns: entry.namespace }), severity: 'info',
93
+ target: { adapter: 'providers', ref: entry.namespace },
94
+ });
95
+ }
96
+ else if (isDeepStrictEqual(current.value, entry.raw ?? stripEntry(entry))) {
97
+ items.push({ id, kind: 'Skip', adapter: 'providers', description: msg('adapter.providerSame', { route }), severity: 'info' });
98
+ }
99
+ else {
100
+ items.push({
101
+ id, kind: 'Conflict', adapter: 'providers',
102
+ description: msg('adapter.providerDiff', { route }),
103
+ detail: `namespace=${entry.namespace} current=${JSON.stringify(current.value)} imported=${JSON.stringify(entry.raw ?? stripEntry(entry))}`.slice(0, 200),
104
+ severity: 'warning', target: { adapter: 'providers', ref: entry.namespace },
105
+ });
106
+ }
107
+ }
108
+ return items;
109
+ }
110
+ async applyItem(item, ctx) {
111
+ const route = item.id.replace(/^provider:/, '');
112
+ const data = ctx.sections.get('providers');
113
+ const entry = data?.providers[route];
114
+ if (!entry)
115
+ return { ok: false, message: ctx.msg('adapter.providerMissing', { route }) };
116
+ const value = entry.raw ?? stripEntry(entry);
117
+ let expected;
118
+ try {
119
+ expected = (await ctx.target.settings.describe(entry.namespace, { redactSecrets: true })).revision;
120
+ }
121
+ catch {
122
+ expected = undefined; // 目标 namespace 不存在(Create)
123
+ }
124
+ await ctx.target.settings.replace(entry.namespace, value, expected);
125
+ return { ok: true };
126
+ }
127
+ async validate(data, msg = zhMsg) {
128
+ const issues = [];
129
+ if (data === null || typeof data !== 'object') {
130
+ return { valid: false, issues: [{ path: '$', message: msg('adapter.validate.object', { subject: 'providers' }), severity: 'error' }] };
131
+ }
132
+ if (data.version !== 1) {
133
+ issues.push({ path: 'version', message: msg('adapter.validate.version', { value: String(data.version) }), severity: 'error' });
134
+ }
135
+ if (data.providers === null || typeof data.providers !== 'object') {
136
+ issues.push({ path: 'providers', message: msg('adapter.validate.missingObject', { subject: 'providers' }), severity: 'error' });
137
+ }
138
+ else {
139
+ for (const [route, entry] of Object.entries(data.providers)) {
140
+ if (entry === null || typeof entry !== 'object') {
141
+ issues.push({ path: `providers.${route}`, message: msg('adapter.validate.recordObject', { subject: 'provider' }), severity: 'error' });
142
+ continue;
143
+ }
144
+ if (typeof entry.namespace !== 'string' || entry.namespace === '') {
145
+ issues.push({ path: `providers.${route}.namespace`, message: msg('adapter.validate.sourceNamespace'), severity: 'error' });
146
+ }
147
+ if (typeof entry.revision !== 'number') {
148
+ issues.push({ path: `providers.${route}.revision`, message: msg('adapter.validate.number', { subject: 'revision' }), severity: 'error' });
149
+ }
150
+ }
151
+ }
152
+ return { valid: issues.filter((i) => i.severity === 'error').length === 0, issues };
153
+ }
154
+ }
155
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1,17 @@
1
+ import type { ExportOptions, ExportSection, HostContext } from '../core/types.ts';
2
+ import type { FilesSection } from '../schema/types.ts';
3
+ import { FileCollectionAdapter } from './file-collection.ts';
4
+ /** self 分区白名单文件(相对 baseDir,即 $DSH_HOME/dsh-config-manager/)。 */
5
+ export declare const SELF_CONFIG_FILES: readonly string[];
6
+ export declare class SelfAdapter extends FileCollectionAdapter {
7
+ readonly id: 'self';
8
+ readonly displayName = "Plugin Self Config";
9
+ readonly defaultIncluded = true;
10
+ readonly portability: 'portable';
11
+ /** 插件自身配置目录(相对 homeDir;宿主按 dataDir 解析注入,缺省 dsh-config-manager) */
12
+ readonly baseDir: string;
13
+ constructor(baseDir?: string);
14
+ /** 白名单收集:只导出配置类文件(存在才收),不递归(排除快照/历史/缓存/临时产物)。
15
+ * relativePath 产出相对 baseDir 的路径(与 analyzeImport/applyItem 的 path.join(baseDir, ref) 匹配)。 */
16
+ export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<FilesSection>>;
17
+ }