@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,165 @@
1
+ /**
2
+ * 各分区数据结构:ZIP 内 JSON 分区文件 → 解析/校验,以及分区文件路径表。
3
+ * 类型本体在 types.ts;本文件是「分区数据如何落盘/读回」的唯一出口。
4
+ */
5
+ import { parseJsonSafe } from '../utils/json.js';
6
+ import { isPathSafe } from '../utils/paths.js';
7
+ /** 全部分区 id(manifest 校验与 adapter registry 共用) */
8
+ export const SECTION_IDS = [
9
+ 'settings', 'ui', 'providers', 'plugins', 'mcp', 'prompts',
10
+ 'skills', 'agentPresets', 'agentInstructions', 'workspaces', 'pluginFiles',
11
+ 'credentialsStatus', 'secrets', 'sessions', 'self',
12
+ ];
13
+ /** JSON 分区在 ZIP 内的相对路径;文件类分区(skills 等)走目录前缀,不在此表 */
14
+ export const SECTION_JSON_PATHS = {
15
+ settings: 'config/settings.json',
16
+ ui: 'config/ui.json',
17
+ providers: 'ai/providers.json',
18
+ plugins: 'plugins/plugins.json',
19
+ mcp: 'mcp/servers.json',
20
+ prompts: 'custom/prompts.json',
21
+ workspaces: 'workspaces/workspaces.json',
22
+ credentialsStatus: 'security/credentials.json',
23
+ };
24
+ /** 文件类分区在 ZIP 内的目录前缀 */
25
+ export const SECTION_FILE_PREFIXES = {
26
+ skills: 'custom/skills/',
27
+ agentPresets: 'agents/presets/',
28
+ agentInstructions: 'custom/agent-instructions/',
29
+ pluginFiles: 'plugin-files/',
30
+ sessions: 'sessions/',
31
+ self: 'self/',
32
+ };
33
+ /** 该分区是否是「文件类」分区(ZIP 内以真实文件存放而非 JSON) */
34
+ export function isFileSection(sectionId) {
35
+ return sectionId in SECTION_FILE_PREFIXES;
36
+ }
37
+ /** 从 ZIP 内 JSON 解析分区数据(深度保护 + 结构校验) */
38
+ export function parseSectionJson(sectionId, raw) {
39
+ const parsed = parseJsonSafe(raw);
40
+ const issues = validateSectionData(sectionId, parsed);
41
+ const errors = issues.filter((i) => i.severity === 'error');
42
+ if (errors.length > 0) {
43
+ throw new Error(`分区 ${sectionId} 数据无效: ${errors.map((e) => e.message).join('; ')}`);
44
+ }
45
+ return parsed;
46
+ }
47
+ /** 分区数据结构基础校验(version 字段 + 顶层形状) */
48
+ export function validateSectionData(sectionId, data) {
49
+ const issues = [];
50
+ if (data === null || typeof data !== 'object') {
51
+ return [{ path: '$', message: `分区 ${sectionId} 数据必须是对象`, severity: 'error' }];
52
+ }
53
+ const obj = data;
54
+ if (obj['version'] !== 1) {
55
+ issues.push({ path: 'version', message: `分区 ${sectionId} 的 version 必须为 1(收到 ${String(obj['version'])})`, severity: 'error' });
56
+ return issues;
57
+ }
58
+ switch (sectionId) {
59
+ case 'settings':
60
+ case 'ui': {
61
+ const ns = obj['namespaces'];
62
+ if (ns === null || typeof ns !== 'object') {
63
+ issues.push({ path: 'namespaces', message: `分区 ${sectionId} 缺少 namespaces 对象`, severity: 'error' });
64
+ }
65
+ else {
66
+ for (const [name, rec] of Object.entries(ns)) {
67
+ if (rec === null || typeof rec !== 'object') {
68
+ issues.push({ path: `namespaces.${name}`, message: 'namespace 记录必须是对象', severity: 'error' });
69
+ continue;
70
+ }
71
+ const r = rec;
72
+ if (typeof r['revision'] !== 'number')
73
+ issues.push({ path: `namespaces.${name}.revision`, message: 'revision 必须是数字', severity: 'error' });
74
+ if (!('value' in r))
75
+ issues.push({ path: `namespaces.${name}.value`, message: '缺少 value', severity: 'error' });
76
+ const secrets = r['secrets'];
77
+ if (secrets !== undefined && !Array.isArray(secrets))
78
+ issues.push({ path: `namespaces.${name}.secrets`, message: 'secrets 必须是数组', severity: 'error' });
79
+ }
80
+ }
81
+ break;
82
+ }
83
+ case 'providers': {
84
+ const p = obj['providers'];
85
+ if (p === null || typeof p !== 'object')
86
+ issues.push({ path: 'providers', message: '缺少 providers 对象', severity: 'error' });
87
+ break;
88
+ }
89
+ case 'plugins': {
90
+ if (!Array.isArray(obj['plugins']))
91
+ issues.push({ path: 'plugins', message: 'plugins 必须是数组', severity: 'error' });
92
+ if (obj['patch'] !== undefined && !Array.isArray(obj['patch']))
93
+ issues.push({ path: 'patch', message: 'patch 必须是数组', severity: 'error' });
94
+ // issue #35:patchFiles 会被写到 <home>/profiles/<profile>/<relativePath>,必须逐项校验
95
+ // (不可信 bundle 的路径穿越向量——与 file 类分区同级的防线)。
96
+ const patchFiles = obj['patchFiles'];
97
+ if (patchFiles !== undefined) {
98
+ if (!Array.isArray(patchFiles)) {
99
+ issues.push({ path: 'patchFiles', message: 'patchFiles 必须是数组', severity: 'error' });
100
+ }
101
+ else {
102
+ patchFiles.forEach((pf, i) => {
103
+ const rec = (pf !== null && typeof pf === 'object') ? pf : null;
104
+ if (rec === null) {
105
+ issues.push({ path: `patchFiles[${i}]`, message: 'patch 文件条目必须是对象', severity: 'error' });
106
+ return;
107
+ }
108
+ const rel = rec['relativePath'];
109
+ if (typeof rel !== 'string' || rel === '' || !isPathSafe(rel)) {
110
+ issues.push({ path: `patchFiles[${i}].relativePath`, message: 'patch 文件路径必须是安全的相对路径(不得为绝对路径或含 ..)', severity: 'error' });
111
+ }
112
+ if (typeof rec['base64'] !== 'string') {
113
+ issues.push({ path: `patchFiles[${i}].base64`, message: 'patch 文件内容必须是 base64 字符串', severity: 'error' });
114
+ }
115
+ });
116
+ }
117
+ }
118
+ break;
119
+ }
120
+ case 'mcp': {
121
+ if (!Array.isArray(obj['servers']))
122
+ issues.push({ path: 'servers', message: 'servers 必须是数组', severity: 'error' });
123
+ break;
124
+ }
125
+ case 'prompts': {
126
+ if (!Array.isArray(obj['prompts']))
127
+ issues.push({ path: 'prompts', message: 'prompts 必须是数组', severity: 'error' });
128
+ break;
129
+ }
130
+ case 'workspaces': {
131
+ if (!Array.isArray(obj['workspaces']))
132
+ issues.push({ path: 'workspaces', message: 'workspaces 必须是数组', severity: 'error' });
133
+ break;
134
+ }
135
+ case 'credentialsStatus': {
136
+ if (!Array.isArray(obj['credentials']))
137
+ issues.push({ path: 'credentials', message: 'credentials 必须是数组', severity: 'error' });
138
+ break;
139
+ }
140
+ case 'skills':
141
+ case 'agentPresets':
142
+ case 'agentInstructions':
143
+ case 'pluginFiles':
144
+ case 'sessions':
145
+ case 'self': {
146
+ if (!Array.isArray(obj['files']))
147
+ issues.push({ path: 'files', message: 'files 必须是数组', severity: 'error' });
148
+ break;
149
+ }
150
+ default:
151
+ issues.push({ path: '$', message: `未知分区 ${sectionId}`, severity: 'error' });
152
+ }
153
+ return issues;
154
+ }
155
+ /** 分区 JSON 载荷的类型收窄(供 adapter / analyzer 使用) */
156
+ export function asSettingsSection(data) { return data; }
157
+ export function asUiSection(data) { return data; }
158
+ export function asProvidersSection(data) { return data; }
159
+ export function asPluginsSection(data) { return data; }
160
+ export function asMcpSection(data) { return data; }
161
+ export function asPromptsSection(data) { return data; }
162
+ export function asWorkspacesSection(data) { return data; }
163
+ export function asCredentialsSection(data) { return data; }
164
+ export function asFilesSection(data) { return data; }
165
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `dsh-config-manager/schema` 的运行时入口(`exports["./schema"]`)。
3
+ *
4
+ * 定位:**零 DSH 依赖、零 `node:` 依赖**的 schema 公共面。第三方在不安装
5
+ * 任何 `@deepseek-ai/*`、React 或 Node 内建模块的前提下,即可:
6
+ * - 判定 `manifest.schemaVersion` 的兼容性(`isCurrent` / `isSupported` / `needsMigration` /
7
+ * `isTooNew` / `canImport` / `describeVersion` / `UnsupportedSchemaError`);
8
+ * - 取到 `SECTION_IDS` / `SECTION_JSON_PATHS` / `SECTION_FILE_PREFIXES` / `isFileSection`,
9
+ * 据此实现自己的 importer / exporter(ZIP 内路径与文件类分区前缀的唯一权威);
10
+ * - 引用 Bundle Format v1 的全部载荷类型。
11
+ *
12
+ * 为什么需要它:此前的 `exports["./schema"]` 指向 `./lib/schema/types.js`,而 `types.ts`
13
+ * 是**纯类型文件**——编译产物是一个**空模块**(运行时零导出)。第三方
14
+ * `import('dsh-config-manager/schema')` 只会拿到 `{}`,`CURRENT_SCHEMA_VERSION` 等
15
+ * 版本工具**在包外无法导入**(它们定义在 `./versions.ts`,不在任何 exports 映射内)。
16
+ * 本文件把 `./schema` 变成真实可用的运行时入口。
17
+ *
18
+ * 约束(勿破):本文件只 import 同目录模块(`./config.ts` / `./types.ts` / `./versions.ts`)。
19
+ * 不 import `../core/*`、不 import `node:*`、不 import 任何 UI / DSH 包——否则会破坏
20
+ * headless 消费(见 `docs/spec/headless-consumption.md`)与 `tests/architecture-boundaries.test.ts`。
21
+ */
22
+ export { CURRENT_SCHEMA_VERSION, MIN_SUPPORTED_SCHEMA_VERSION, UnsupportedSchemaError, isCurrent, isSupported, needsMigration, isTooNew, canImport, describeVersion, } from './versions.ts';
23
+ export { SECTION_IDS, SECTION_JSON_PATHS, SECTION_FILE_PREFIXES, isFileSection, } from './config.ts';
24
+ export type { Platform, SectionId, EncryptionInfo, Manifest, NamespaceRecord, SettingsSection, UiMigrationNote, UiSection, ProviderEntry, ProvidersSection, PluginEntry, PatchLine, LocalPluginTarball, PluginsSection, McpServerEntry, McpSection, PromptEntry, PromptsSection, WorkspaceRecord, WorkspacesSection, CredentialStatus, CredentialsSection, FileEntry, FilesSection, SectionData, } from './types.ts';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `dsh-config-manager/schema` 的运行时入口(`exports["./schema"]`)。
3
+ *
4
+ * 定位:**零 DSH 依赖、零 `node:` 依赖**的 schema 公共面。第三方在不安装
5
+ * 任何 `@deepseek-ai/*`、React 或 Node 内建模块的前提下,即可:
6
+ * - 判定 `manifest.schemaVersion` 的兼容性(`isCurrent` / `isSupported` / `needsMigration` /
7
+ * `isTooNew` / `canImport` / `describeVersion` / `UnsupportedSchemaError`);
8
+ * - 取到 `SECTION_IDS` / `SECTION_JSON_PATHS` / `SECTION_FILE_PREFIXES` / `isFileSection`,
9
+ * 据此实现自己的 importer / exporter(ZIP 内路径与文件类分区前缀的唯一权威);
10
+ * - 引用 Bundle Format v1 的全部载荷类型。
11
+ *
12
+ * 为什么需要它:此前的 `exports["./schema"]` 指向 `./lib/schema/types.js`,而 `types.ts`
13
+ * 是**纯类型文件**——编译产物是一个**空模块**(运行时零导出)。第三方
14
+ * `import('dsh-config-manager/schema')` 只会拿到 `{}`,`CURRENT_SCHEMA_VERSION` 等
15
+ * 版本工具**在包外无法导入**(它们定义在 `./versions.ts`,不在任何 exports 映射内)。
16
+ * 本文件把 `./schema` 变成真实可用的运行时入口。
17
+ *
18
+ * 约束(勿破):本文件只 import 同目录模块(`./config.ts` / `./types.ts` / `./versions.ts`)。
19
+ * 不 import `../core/*`、不 import `node:*`、不 import 任何 UI / DSH 包——否则会破坏
20
+ * headless 消费(见 `docs/spec/headless-consumption.md`)与 `tests/architecture-boundaries.test.ts`。
21
+ */
22
+ /* —— 版本判定(唯一出口,业务代码零散落 `if (v === 1)`) —— */
23
+ export { CURRENT_SCHEMA_VERSION, MIN_SUPPORTED_SCHEMA_VERSION, UnsupportedSchemaError, isCurrent, isSupported, needsMigration, isTooNew, canImport, describeVersion, } from './versions.js';
24
+ /* —— 分区表(第三方实现 importer/exporter 的路径权威) —— */
25
+ export { SECTION_IDS, SECTION_JSON_PATHS, SECTION_FILE_PREFIXES, isFileSection, } from './config.js';
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,28 @@
1
+ import type { Manifest, Platform, SectionId } from './types.ts';
2
+ export declare const MANIFEST_FILE = "manifest.json";
3
+ export declare const CHECKSUMS_FILE = "integrity/checksums.json";
4
+ export declare const EXPORTER_NAME = "DSH Config Manager";
5
+ export interface BuildManifestInput {
6
+ exporterVersion: string;
7
+ dshVersion: string;
8
+ platform: Platform;
9
+ arch: string;
10
+ sections: Record<SectionId, boolean>;
11
+ containsSecrets: boolean;
12
+ encrypted: boolean;
13
+ encryption: Manifest['security']['encryption'];
14
+ exportedAt?: string;
15
+ }
16
+ /** 构造 manifest(schemaVersion 恒为当前版本,集中于此) */
17
+ export declare function buildManifest(input: BuildManifestInput): Manifest;
18
+ /** 序列化(pretty JSON,供 ZIP 内 manifest.json) */
19
+ export declare function serializeManifest(manifest: Manifest): string;
20
+ /** 解析 + 结构校验;非法输入抛错(导入第一道闸之一) */
21
+ export declare function parseManifest(raw: string): Manifest;
22
+ export interface ManifestIssue {
23
+ path: string;
24
+ message: string;
25
+ severity: 'error' | 'warning';
26
+ }
27
+ /** 结构校验:字段类型 / sections 键集合 / security 形状 */
28
+ export declare function validateManifest(m: unknown): ManifestIssue[];
@@ -0,0 +1,109 @@
1
+ /**
2
+ * manifest.json 的类型、解析、序列化与结构校验。
3
+ * 类型本体在 types.ts(§13.1);本文件负责 manifest 的读写与结构完整性。
4
+ */
5
+ import { parseJsonSafe } from '../utils/json.js';
6
+ import { CURRENT_SCHEMA_VERSION } from './versions.js';
7
+ import { SECTION_IDS } from './config.js';
8
+ export const MANIFEST_FILE = 'manifest.json';
9
+ export const CHECKSUMS_FILE = 'integrity/checksums.json';
10
+ export const EXPORTER_NAME = 'DSH Config Manager';
11
+ /** 构造 manifest(schemaVersion 恒为当前版本,集中于此) */
12
+ export function buildManifest(input) {
13
+ return {
14
+ schemaVersion: CURRENT_SCHEMA_VERSION,
15
+ exporter: { name: EXPORTER_NAME, version: input.exporterVersion },
16
+ source: {
17
+ dshVersion: input.dshVersion,
18
+ platform: input.platform,
19
+ arch: input.arch,
20
+ },
21
+ exportedAt: input.exportedAt ?? new Date().toISOString(),
22
+ sections: input.sections,
23
+ security: {
24
+ containsSecrets: input.containsSecrets,
25
+ encrypted: input.encrypted,
26
+ encryption: input.encryption,
27
+ },
28
+ };
29
+ }
30
+ /** 序列化(pretty JSON,供 ZIP 内 manifest.json) */
31
+ export function serializeManifest(manifest) {
32
+ return JSON.stringify(manifest, null, 2);
33
+ }
34
+ /** 解析 + 结构校验;非法输入抛错(导入第一道闸之一) */
35
+ export function parseManifest(raw) {
36
+ const parsed = parseJsonSafe(raw);
37
+ const issues = validateManifest(parsed);
38
+ const errors = issues.filter((i) => i.severity === 'error');
39
+ if (errors.length > 0) {
40
+ throw new Error(`manifest.json 无效: ${errors.map((e) => e.message).join('; ')}`);
41
+ }
42
+ return parsed;
43
+ }
44
+ /** 结构校验:字段类型 / sections 键集合 / security 形状 */
45
+ export function validateManifest(m) {
46
+ const issues = [];
47
+ if (m === null || typeof m !== 'object') {
48
+ return [{ path: '$', message: 'manifest 必须是对象', severity: 'error' }];
49
+ }
50
+ const obj = m;
51
+ if (typeof obj['schemaVersion'] !== 'number') {
52
+ issues.push({ path: 'schemaVersion', message: 'schemaVersion 必须是数字', severity: 'error' });
53
+ }
54
+ const exporter = obj['exporter'];
55
+ if (!exporter || typeof exporter !== 'object') {
56
+ issues.push({ path: 'exporter', message: 'exporter 缺失', severity: 'error' });
57
+ }
58
+ else {
59
+ if (typeof exporter['name'] !== 'string')
60
+ issues.push({ path: 'exporter.name', message: 'exporter.name 必须是字符串', severity: 'error' });
61
+ if (typeof exporter['version'] !== 'string')
62
+ issues.push({ path: 'exporter.version', message: 'exporter.version 必须是字符串', severity: 'error' });
63
+ }
64
+ const source = obj['source'];
65
+ if (!source || typeof source !== 'object') {
66
+ issues.push({ path: 'source', message: 'source 缺失', severity: 'error' });
67
+ }
68
+ else {
69
+ if (typeof source['dshVersion'] !== 'string')
70
+ issues.push({ path: 'source.dshVersion', message: 'source.dshVersion 必须是字符串', severity: 'error' });
71
+ if (typeof source['platform'] !== 'string')
72
+ issues.push({ path: 'source.platform', message: 'source.platform 必须是字符串', severity: 'error' });
73
+ if (typeof source['arch'] !== 'string')
74
+ issues.push({ path: 'source.arch', message: 'source.arch 必须是字符串', severity: 'error' });
75
+ }
76
+ if (typeof obj['exportedAt'] !== 'string' || Number.isNaN(Date.parse(obj['exportedAt']))) {
77
+ issues.push({ path: 'exportedAt', message: 'exportedAt 必须是合法 ISO-8601 时间', severity: 'error' });
78
+ }
79
+ const sections = obj['sections'];
80
+ if (!sections || typeof sections !== 'object') {
81
+ issues.push({ path: 'sections', message: 'sections 缺失', severity: 'error' });
82
+ }
83
+ else {
84
+ for (const key of Object.keys(sections)) {
85
+ if (!SECTION_IDS.includes(key)) {
86
+ issues.push({ path: `sections.${key}`, message: `未知分区 "${key}"`, severity: 'warning' });
87
+ }
88
+ if (typeof sections[key] !== 'boolean') {
89
+ issues.push({ path: `sections.${key}`, message: `sections.${key} 必须是布尔`, severity: 'error' });
90
+ }
91
+ }
92
+ }
93
+ const security = obj['security'];
94
+ if (!security || typeof security !== 'object') {
95
+ issues.push({ path: 'security', message: 'security 缺失', severity: 'error' });
96
+ }
97
+ else {
98
+ if (typeof security['containsSecrets'] !== 'boolean')
99
+ issues.push({ path: 'security.containsSecrets', message: 'containsSecrets 必须是布尔', severity: 'error' });
100
+ if (typeof security['encrypted'] !== 'boolean')
101
+ issues.push({ path: 'security.encrypted', message: 'encrypted 必须是布尔', severity: 'error' });
102
+ const enc = security['encryption'];
103
+ if (enc !== null && (typeof enc !== 'object' || enc === undefined)) {
104
+ issues.push({ path: 'security.encryption', message: 'encryption 必须是对象或 null', severity: 'error' });
105
+ }
106
+ }
107
+ return issues;
108
+ }
109
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 删除墓碑(tombstone)机制(F4):记录用户明确删除的条目,
3
+ * 导入/恢复时据此过滤,防止「旧备份复活已删条目」。
4
+ *
5
+ * 设计(借鉴 uagent-sync workspace-state-codec):墓碑按 kind+id 去重,
6
+ * 过滤时把恢复数据里的条目映射为 {kind, id} 键,命中墓碑的条目剔除。
7
+ *
8
+ * 持久化:<dataDir>/tombstones.json(self 分区数据目录 $DSH_HOME/dsh-config-manager/ 下)。
9
+ * 本模块零依赖(不 import node 模块 / 核心层类型),fs 由调用方注入,
10
+ * 纯逻辑可独立测试;dataDir 与文件名形态由调用方保持一致。
11
+ *
12
+ * 安全语义:墓碑文件缺失/损坏一律安全降级为空列表(尽力而为的过滤,
13
+ * 绝不因墓碑文件问题阻塞导入主流程)。
14
+ */
15
+ /** 墓碑条目类型:插件 / 技能(skills 分区文件)/ 整个分区 / 其他文件(agentPresets 等文件类分区) */
16
+ export type TombstoneKind = 'plugin' | 'skill' | 'section' | 'file';
17
+ export interface Tombstone {
18
+ kind: TombstoneKind;
19
+ /** 该类型内的标识:插件 → 包名;技能/文件 → 相对路径;分区 → SectionId */
20
+ id: string;
21
+ /** 删除时间(ISO-8601 UTC) */
22
+ deletedAt: string;
23
+ /** 删除原因(用户可读,可选) */
24
+ reason?: string;
25
+ }
26
+ /** 墓碑持久化文件名(位于 <dataDir>/ 下) */
27
+ export declare const TOMBSTONES_FILE = "tombstones.json";
28
+ /** 墓碑持久化所需的最小 fs 门面(结构化兼容 HostContext.fs / 测试内存实现) */
29
+ export interface TombstoneFs {
30
+ readFile(relPath: string): Promise<Uint8Array>;
31
+ writeFile(relPath: string, data: Uint8Array): Promise<void>;
32
+ }
33
+ /** 注册墓碑:按 kind+id 去重(同键已存在 → 用新条目替换,保留最新 deletedAt/reason);返回新数组 */
34
+ export declare function addTombstone(list: Tombstone[], entry: Tombstone): Tombstone[];
35
+ /** 某条目(kind+id)是否已被墓碑覆盖 */
36
+ export declare function isTombstoned(kind: TombstoneKind, id: string, tombstones: readonly Tombstone[]): boolean;
37
+ /** 过滤 {kind,id} 形态数组:剔除墓碑命中的条目,保留其余(返回新数组,不改原数组)。
38
+ * 非 TombstoneKind 的 kind(如导入计划项的 Install/Update 等)永远不会误命中。 */
39
+ export declare function filterTombstoned<T extends {
40
+ kind: string;
41
+ id: string;
42
+ }>(items: readonly T[], tombstones: readonly Tombstone[]): T[];
43
+ /** 读取 <dataDir>/tombstones.json;文件不存在 / 解析失败 / 结构非法 → [](安全降级,不抛错) */
44
+ export declare function loadTombstones(fs: TombstoneFs, dataDir: string): Promise<Tombstone[]>;
45
+ /** 写入 <dataDir>/tombstones.json(覆盖写;dataDir 目录由调用方保证可写) */
46
+ export declare function saveTombstones(fs: TombstoneFs, dataDir: string, list: Tombstone[]): Promise<void>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 删除墓碑(tombstone)机制(F4):记录用户明确删除的条目,
3
+ * 导入/恢复时据此过滤,防止「旧备份复活已删条目」。
4
+ *
5
+ * 设计(借鉴 uagent-sync workspace-state-codec):墓碑按 kind+id 去重,
6
+ * 过滤时把恢复数据里的条目映射为 {kind, id} 键,命中墓碑的条目剔除。
7
+ *
8
+ * 持久化:<dataDir>/tombstones.json(self 分区数据目录 $DSH_HOME/dsh-config-manager/ 下)。
9
+ * 本模块零依赖(不 import node 模块 / 核心层类型),fs 由调用方注入,
10
+ * 纯逻辑可独立测试;dataDir 与文件名形态由调用方保持一致。
11
+ *
12
+ * 安全语义:墓碑文件缺失/损坏一律安全降级为空列表(尽力而为的过滤,
13
+ * 绝不因墓碑文件问题阻塞导入主流程)。
14
+ */
15
+ /** 墓碑持久化文件名(位于 <dataDir>/ 下) */
16
+ export const TOMBSTONES_FILE = 'tombstones.json';
17
+ /** 注册墓碑:按 kind+id 去重(同键已存在 → 用新条目替换,保留最新 deletedAt/reason);返回新数组 */
18
+ export function addTombstone(list, entry) {
19
+ const idx = list.findIndex((t) => t.kind === entry.kind && t.id === entry.id);
20
+ if (idx === -1)
21
+ return [...list, entry];
22
+ const next = [...list];
23
+ next[idx] = entry;
24
+ return next;
25
+ }
26
+ /** 某条目(kind+id)是否已被墓碑覆盖 */
27
+ export function isTombstoned(kind, id, tombstones) {
28
+ return tombstones.some((t) => t.kind === kind && t.id === id);
29
+ }
30
+ /** 过滤 {kind,id} 形态数组:剔除墓碑命中的条目,保留其余(返回新数组,不改原数组)。
31
+ * 非 TombstoneKind 的 kind(如导入计划项的 Install/Update 等)永远不会误命中。 */
32
+ export function filterTombstoned(items, tombstones) {
33
+ if (tombstones.length === 0)
34
+ return [...items];
35
+ return items.filter((item) => !isTombstoned(item.kind, item.id, tombstones));
36
+ }
37
+ /** 读取 <dataDir>/tombstones.json;文件不存在 / 解析失败 / 结构非法 → [](安全降级,不抛错) */
38
+ export async function loadTombstones(fs, dataDir) {
39
+ try {
40
+ const raw = await fs.readFile(`${dataDir}/${TOMBSTONES_FILE}`);
41
+ const parsed = JSON.parse(new TextDecoder().decode(raw));
42
+ if (!Array.isArray(parsed))
43
+ return [];
44
+ return parsed.filter(isTombstone);
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ }
50
+ /** 写入 <dataDir>/tombstones.json(覆盖写;dataDir 目录由调用方保证可写) */
51
+ export async function saveTombstones(fs, dataDir, list) {
52
+ const payload = JSON.stringify(list, null, 2);
53
+ await fs.writeFile(`${dataDir}/${TOMBSTONES_FILE}`, new TextEncoder().encode(payload));
54
+ }
55
+ /** 形状校验:逐条过滤非法条目(容忍部分损坏,保留合法项) */
56
+ function isTombstone(v) {
57
+ if (v === null || typeof v !== 'object')
58
+ return false;
59
+ const t = v;
60
+ return ((t['kind'] === 'plugin' || t['kind'] === 'skill' || t['kind'] === 'section' || t['kind'] === 'file')
61
+ && typeof t['id'] === 'string' && t['id'] !== ''
62
+ && typeof t['deletedAt'] === 'string');
63
+ }
64
+ //# sourceMappingURL=tombstones.js.map