@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,131 @@
1
+ /**
2
+ * 删除墓碑(tombstone)机制测试(F4):
3
+ * 覆盖 addTombstone 去重、isTombstoned 匹配、filterTombstoned 过滤、
4
+ * load/save 持久化 roundtrip、缺失/损坏文件安全降级。
5
+ * 基于内存 TombstoneFs(纯逻辑,零依赖)。
6
+ */
7
+ import test from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+
10
+ import {
11
+ addTombstone, filterTombstoned, isTombstoned, loadTombstones, saveTombstones, TOMBSTONES_FILE,
12
+ } from './tombstones.ts';
13
+ import type { Tombstone, TombstoneFs } from './tombstones.ts';
14
+
15
+ /** 内存 fs:完整路径为 key(与 loadTombstones 的 `${dataDir}/${TOMBSTONES_FILE}` 拼接一致) */
16
+ class MemTombstoneFs implements TombstoneFs {
17
+ files = new Map<string, Uint8Array>();
18
+ async readFile(relPath: string): Promise<Uint8Array> {
19
+ const v = this.files.get(relPath);
20
+ if (v === undefined) throw new Error(`ENOENT: ${relPath}`);
21
+ return v;
22
+ }
23
+ async writeFile(relPath: string, data: Uint8Array): Promise<void> {
24
+ this.files.set(relPath, data);
25
+ }
26
+ }
27
+
28
+ const DATA_DIR = '/home/alice/dsh-config-manager';
29
+
30
+ function ts(kind: Tombstone['kind'], id: string, deletedAt = '2026-08-20T00:00:00.000Z'): Tombstone {
31
+ return { kind, id, deletedAt };
32
+ }
33
+
34
+ test('T-01 addTombstone:追加新条目 + 同 kind+id 去重(替换保留最新)', () => {
35
+ const a = ts('plugin', '@x/pkg-a', '2026-08-20T00:00:00.000Z');
36
+ const b = ts('plugin', '@x/pkg-b');
37
+ const once = addTombstone([], a);
38
+ assert.deepEqual(once, [a], '空表追加');
39
+
40
+ const two = addTombstone(once, b);
41
+ assert.equal(two.length, 2, '不同 id 追加');
42
+
43
+ // 同 kind+id 再注册 → 替换(更新 deletedAt/reason),不重复
44
+ const updated = ts('plugin', '@x/pkg-a', '2026-08-21T00:00:00.000Z');
45
+ const deduped = addTombstone(two, updated);
46
+ assert.equal(deduped.length, 2, '同键去重不增长');
47
+ assert.deepEqual(deduped[0], updated, '旧条目被新条目替换');
48
+ assert.equal(deduped[1], b, '无关条目不受影响');
49
+
50
+ // 同 id 不同 kind 视为不同键
51
+ const asFile = ts('file', '@x/pkg-a');
52
+ const mixed = addTombstone(deduped, asFile);
53
+ assert.equal(mixed.length, 3, '同 id 不同 kind 是不同墓碑');
54
+ });
55
+
56
+ test('T-02 isTombstoned:kind+id 精确匹配', () => {
57
+ const list = [ts('plugin', '@x/pkg-a'), ts('skill', 'coding.md'), ts('section', 'agentPresets')];
58
+ assert.equal(isTombstoned('plugin', '@x/pkg-a', list), true);
59
+ assert.equal(isTombstoned('skill', 'coding.md', list), true);
60
+ assert.equal(isTombstoned('section', 'agentPresets', list), true);
61
+ assert.equal(isTombstoned('plugin', '@x/pkg-b', list), false, '未删除的插件不命中');
62
+ assert.equal(isTombstoned('file', 'coding.md', list), false, '同 id 不同 kind 不命中');
63
+ assert.equal(isTombstoned('plugin', '@x/pkg-a', []), false, '空表恒 false');
64
+ });
65
+
66
+ test('T-03 filterTombstoned:剔除命中条目、保留其余、非墓碑 kind 不误伤', () => {
67
+ const tombstones = [ts('plugin', '@x/pkg-a'), ts('skill', 'coding.md')];
68
+ const items = [
69
+ { kind: 'plugin', id: '@x/pkg-a' },
70
+ { kind: 'plugin', id: '@x/pkg-b' },
71
+ { kind: 'skill', id: 'coding.md' },
72
+ { kind: 'file', id: 'coding.md' },
73
+ // 非 TombstoneKind 的 kind(模拟导入计划项 kind)永不被误伤
74
+ { kind: 'Install', id: '@x/pkg-a' },
75
+ ];
76
+ const out = filterTombstoned(items, tombstones);
77
+ assert.deepEqual(
78
+ out.map((i) => i.id),
79
+ ['@x/pkg-b', 'coding.md', '@x/pkg-a'],
80
+ '命中 plugin/skill 墓碑的剔除;file 与 Install kind 保留',
81
+ );
82
+ // 原数组不被修改
83
+ assert.equal(items.length, 5);
84
+ // 空墓碑 → 原样返回(新数组)
85
+ const none = filterTombstoned(items, []);
86
+ assert.deepEqual(none, items);
87
+ });
88
+
89
+ test('T-04 loadTombstones/saveTombstones:roundtrip 一致', async () => {
90
+ const mem = new MemTombstoneFs();
91
+ const list = [
92
+ ts('plugin', '@x/pkg-a', '2026-08-20T01:02:03.000Z'),
93
+ { ...ts('skill', 'coding.md'), reason: '不再需要' },
94
+ ts('section', 'agentPresets'),
95
+ ];
96
+ await saveTombstones(mem, DATA_DIR, list);
97
+ assert.ok(mem.files.has(`${DATA_DIR}/${TOMBSTONES_FILE}`), '文件写入 <dataDir>/tombstones.json');
98
+ const loaded = await loadTombstones(mem, DATA_DIR);
99
+ assert.deepEqual(loaded, list, 'roundtrip 数据一致(含 deletedAt/reason)');
100
+ });
101
+
102
+ test('T-05 loadTombstones:文件不存在 → [](安全降级,不抛错)', async () => {
103
+ const mem = new MemTombstoneFs();
104
+ assert.deepEqual(await loadTombstones(mem, DATA_DIR), []);
105
+ });
106
+
107
+ test('T-06 loadTombstones:损坏 JSON / 非法结构 → 降级(非数组 → [];部分非法条目被剔除)', async () => {
108
+ const mem = new MemTombstoneFs();
109
+ // 损坏 JSON
110
+ await mem.writeFile(`${DATA_DIR}/${TOMBSTONES_FILE}`, new TextEncoder().encode('{not json'));
111
+ assert.deepEqual(await loadTombstones(mem, DATA_DIR), [], '损坏 JSON → []');
112
+
113
+ // 非数组结构
114
+ await mem.writeFile(`${DATA_DIR}/${TOMBSTONES_FILE}`, new TextEncoder().encode('{"kind":"plugin"}'));
115
+ assert.deepEqual(await loadTombstones(mem, DATA_DIR), [], '非数组 → []');
116
+
117
+ // 混合合法/非法条目 → 保留合法项
118
+ const raw = JSON.stringify([
119
+ ts('plugin', '@x/pkg-a'),
120
+ { kind: 'hacker', id: 'x' }, // 未知 kind → 剔除
121
+ { kind: 'plugin', id: '' }, // 空 id → 剔除
122
+ { kind: 'skill' }, // 缺 id → 剔除
123
+ { kind: 'file', id: 'keep.md', deletedAt: '2026-08-20T00:00:00.000Z' },
124
+ ]);
125
+ await mem.writeFile(`${DATA_DIR}/${TOMBSTONES_FILE}`, new TextEncoder().encode(raw));
126
+ const loaded = await loadTombstones(mem, DATA_DIR);
127
+ assert.deepEqual(loaded, [
128
+ ts('plugin', '@x/pkg-a'),
129
+ { kind: 'file', id: 'keep.md', deletedAt: '2026-08-20T00:00:00.000Z' },
130
+ ], '合法条目保留、非法条目剔除');
131
+ });
@@ -0,0 +1,89 @@
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
+
16
+ /** 墓碑条目类型:插件 / 技能(skills 分区文件)/ 整个分区 / 其他文件(agentPresets 等文件类分区) */
17
+ export type TombstoneKind = 'plugin' | 'skill' | 'section' | 'file';
18
+
19
+ export interface Tombstone {
20
+ kind: TombstoneKind;
21
+ /** 该类型内的标识:插件 → 包名;技能/文件 → 相对路径;分区 → SectionId */
22
+ id: string;
23
+ /** 删除时间(ISO-8601 UTC) */
24
+ deletedAt: string;
25
+ /** 删除原因(用户可读,可选) */
26
+ reason?: string;
27
+ }
28
+
29
+ /** 墓碑持久化文件名(位于 <dataDir>/ 下) */
30
+ export const TOMBSTONES_FILE = 'tombstones.json';
31
+
32
+ /** 墓碑持久化所需的最小 fs 门面(结构化兼容 HostContext.fs / 测试内存实现) */
33
+ export interface TombstoneFs {
34
+ readFile(relPath: string): Promise<Uint8Array>;
35
+ writeFile(relPath: string, data: Uint8Array): Promise<void>;
36
+ }
37
+
38
+ /** 注册墓碑:按 kind+id 去重(同键已存在 → 用新条目替换,保留最新 deletedAt/reason);返回新数组 */
39
+ export function addTombstone(list: Tombstone[], entry: Tombstone): Tombstone[] {
40
+ const idx = list.findIndex((t) => t.kind === entry.kind && t.id === entry.id);
41
+ if (idx === -1) return [...list, entry];
42
+ const next = [...list];
43
+ next[idx] = entry;
44
+ return next;
45
+ }
46
+
47
+ /** 某条目(kind+id)是否已被墓碑覆盖 */
48
+ export function isTombstoned(kind: TombstoneKind, id: string, tombstones: readonly Tombstone[]): boolean {
49
+ return tombstones.some((t) => t.kind === kind && t.id === id);
50
+ }
51
+
52
+ /** 过滤 {kind,id} 形态数组:剔除墓碑命中的条目,保留其余(返回新数组,不改原数组)。
53
+ * 非 TombstoneKind 的 kind(如导入计划项的 Install/Update 等)永远不会误命中。 */
54
+ export function filterTombstoned<T extends { kind: string; id: string }>(
55
+ items: readonly T[],
56
+ tombstones: readonly Tombstone[],
57
+ ): T[] {
58
+ if (tombstones.length === 0) return [...items];
59
+ return items.filter((item) => !isTombstoned(item.kind as TombstoneKind, item.id, tombstones));
60
+ }
61
+
62
+ /** 读取 <dataDir>/tombstones.json;文件不存在 / 解析失败 / 结构非法 → [](安全降级,不抛错) */
63
+ export async function loadTombstones(fs: TombstoneFs, dataDir: string): Promise<Tombstone[]> {
64
+ try {
65
+ const raw = await fs.readFile(`${dataDir}/${TOMBSTONES_FILE}`);
66
+ const parsed: unknown = JSON.parse(new TextDecoder().decode(raw));
67
+ if (!Array.isArray(parsed)) return [];
68
+ return parsed.filter(isTombstone);
69
+ } catch {
70
+ return [];
71
+ }
72
+ }
73
+
74
+ /** 写入 <dataDir>/tombstones.json(覆盖写;dataDir 目录由调用方保证可写) */
75
+ export async function saveTombstones(fs: TombstoneFs, dataDir: string, list: Tombstone[]): Promise<void> {
76
+ const payload = JSON.stringify(list, null, 2);
77
+ await fs.writeFile(`${dataDir}/${TOMBSTONES_FILE}`, new TextEncoder().encode(payload));
78
+ }
79
+
80
+ /** 形状校验:逐条过滤非法条目(容忍部分损坏,保留合法项) */
81
+ function isTombstone(v: unknown): v is Tombstone {
82
+ if (v === null || typeof v !== 'object') return false;
83
+ const t = v as Record<string, unknown>;
84
+ return (
85
+ (t['kind'] === 'plugin' || t['kind'] === 'skill' || t['kind'] === 'section' || t['kind'] === 'file')
86
+ && typeof t['id'] === 'string' && t['id'] !== ''
87
+ && typeof t['deletedAt'] === 'string'
88
+ );
89
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * 核心领域类型(Export Schema v1 的纯类型定义)。
3
+ *
4
+ * 对齐 Docs/design/architecture.md §13.1 —— m3/m5 的共同契约。
5
+ * 本文件只含与 DSH 运行时无关的纯数据形状;DSH Service 门面类型见 src/core/types.ts。
6
+ */
7
+
8
+ /** 运行平台(等价 NodeJS.Platform,但保持类型自包含,可在浏览器侧引用) */
9
+ export type Platform =
10
+ | 'win32' | 'darwin' | 'linux' | 'freebsd' | 'openbsd' | 'aix' | 'sunos'
11
+ | 'android' | 'cygwin' | 'haiku' | 'netbsd' | 'other';
12
+
13
+ /**
14
+ * 分区标识,与 ZIP 目录结构、manifest.sections 键一一对应。
15
+ * 明确不实现的分区(keybindings/workflows/commands)不在此列;
16
+ * rules 已由 agentInstructions(~/.dsh/AGENTS.md,dsh-agent-instructions 全局指令)承接。
17
+ */
18
+ export type SectionId =
19
+ | 'settings' | 'ui' | 'providers' | 'plugins' | 'mcp' | 'prompts'
20
+ | 'skills' | 'agentPresets' | 'agentInstructions' | 'workspaces' | 'pluginFiles'
21
+ | 'credentialsStatus' | 'secrets' | 'sessions' | 'self';
22
+
23
+ /** 加密备份的算法参数(非秘密:salt/iv/authTag 仅用于解密与完整性校验) */
24
+ export interface EncryptionInfo {
25
+ algorithm: 'aes-256-gcm';
26
+ kdf: 'scrypt';
27
+ kdfParams: { N: number; r: number; p: number; keyLength: number };
28
+ salt: string; // base64
29
+ iv: string; // base64
30
+ authTag: string;// base64(GCM 认证标签)
31
+ version: number;
32
+ }
33
+
34
+ /** 每个导出 ZIP 根部的 manifest.json */
35
+ export interface Manifest {
36
+ schemaVersion: number;
37
+ exporter: { name: string; version: string };
38
+ source: { dshVersion: string; platform: Platform; arch: string };
39
+ exportedAt: string; // ISO-8601 UTC
40
+ sections: Record<SectionId, boolean>;
41
+ security: {
42
+ containsSecrets: boolean;
43
+ encrypted: boolean;
44
+ encryption: EncryptionInfo | null;
45
+ };
46
+ }
47
+
48
+ /* —— settings / ui 分区 —— */
49
+
50
+ /** settings.yaml 中单个 namespace 的导出记录(redacted 值 + 乐观锁 revision) */
51
+ export interface NamespaceRecord {
52
+ value: unknown;
53
+ base?: unknown;
54
+ revision: number;
55
+ applies?: string[];
56
+ /** DSH describe({redactSecrets:true}) 报告的 secrets 位置(只含路径标记,不含值) */
57
+ secrets: { path: string[]; set: boolean }[];
58
+ }
59
+
60
+ export interface SettingsSection { version: 1; namespaces: Record<string, NamespaceRecord>; }
61
+
62
+ export interface UiMigrationNote {
63
+ plugin: string;
64
+ storage: string;
65
+ key: string;
66
+ migratable: false;
67
+ reason: string;
68
+ }
69
+
70
+ export interface UiSection {
71
+ version: 1;
72
+ namespaces: Record<string, NamespaceRecord>;
73
+ /** localStorage 等 Host 不可迁移项的说明(纯说明,不含任何值) */
74
+ uiMigrationNotes: UiMigrationNote[];
75
+ }
76
+
77
+ /* —— providers 分区 —— */
78
+
79
+ export interface ProviderEntry {
80
+ route: string;
81
+ apiKeyEnv?: string; // 只记环境变量名,值在 credentials
82
+ displayName?: string;
83
+ baseURL?: string;
84
+ models?: unknown[];
85
+ modelOverrides?: unknown;
86
+ reasoning?: unknown;
87
+ transport?: unknown;
88
+ retryPolicy?: unknown;
89
+ }
90
+
91
+ export interface ProvidersSection { version: 1; providers: Record<string, ProviderEntry>; }
92
+
93
+ /* —— plugins 分区 —— */
94
+
95
+ export interface PluginEntry {
96
+ name: string; // 包名
97
+ version: string;
98
+ /**
99
+ * 声明依赖 spec(profile package.json dependencies 原样),如 ^0.3.6、
100
+ * github:csyangwen/dsh-memory-evolve、file:/link: 等。
101
+ * 导入时非 registry 来源(github:/git+/file:/link:/workspace: 等)按此 spec 安装,
102
+ * 否则按裸包名走 npm 最新版(官方机制)。
103
+ */
104
+ spec?: string;
105
+ isBundle: boolean;
106
+ inBundles: string[];
107
+ enabled: boolean;
108
+ fiberPhase?: string;
109
+ }
110
+
111
+ export interface PatchLine { file: string; lineId: string; raw: unknown; }
112
+
113
+ /**
114
+ * 本地源插件的打包产物(T1)。
115
+ *
116
+ * 为什么放在 `plugins` 分区内(而非 `plugin-files/` 前缀):
117
+ * plugins 是 `defaultIncluded=true` 且 portable,而 pluginFiles 是 `deviceSpecific`
118
+ * 且 defaultIncluded=false —— 后者会让「快速导出」(默认路径)拿不到 tarball,
119
+ * 而「本地插件换机丢失」恰恰是最需要默认覆盖的场景。
120
+ *
121
+ * 为什么不新增分区 id:新增 SectionId 需同步 SECTION_IDS / SECTION_FILE_PREFIXES /
122
+ * manifest 校验 / analyzer / exporter / market BANNED / sync layout 共 7 处契约,
123
+ * 风险远大于收益;本字段落在既有 plugins 分区内即可被全部既有机制正确处理。
124
+ *
125
+ * 安全:base64 仅为传输编码,不做任何执行;导入端只把解包出的 tgz 交给官方
126
+ * `dsh plugin add` 通道。**市场侧已在发布与导入两端同时拒绝携带本字段的条目**
127
+ * (`market/prepare.ts` 6b3 + `market/security.ts`)——因为 tarball 是不可经公开仓库审阅的
128
+ * 不透明二进制,安装时可能执行 postinstall,等于给「分享配置」开一条携带任意代码、
129
+ * 绕过 BANNED 分区的通道。本地插件迁移只应发生在自己的备份里。
130
+ */
131
+ export interface LocalPluginTarball {
132
+ /** 包名(与 PluginEntry.name 对应) */
133
+ packageName: string;
134
+ /** 打包时的插件版本 */
135
+ version: string;
136
+ /** 归档内相对路径(`local-plugins/<x>.tgz`,恒正斜杠) */
137
+ relativePath: string;
138
+ /** tarball 字节的 base64(非秘密;仅本地源插件的源码包) */
139
+ base64: string;
140
+ }
141
+
142
+ /**
143
+ * profile 的 `patches/**` 文件(issue #35):pnpm-workspace.yaml 的 `patchedDependencies`
144
+ * 引用的补丁文件本身。声明与文件必须同进同出——只搬声明会让目标机的 pnpm 拒绝一切 `add`
145
+ * (`Failed to read patch file ... os error 2`)。
146
+ */
147
+ export interface PnpmPatchFile {
148
+ /** 相对 **profile 目录** 的路径(如 `patches/dsh-approval-gate.patch`,恒正斜杠) */
149
+ relativePath: string;
150
+ /** 文件字节的 base64(patch 是纯文本、非秘密;仅为传输编码) */
151
+ base64: string;
152
+ }
153
+
154
+ export interface PluginsSection {
155
+ version: 1;
156
+ plugins: PluginEntry[];
157
+ patch: PatchLine[];
158
+ /**
159
+ * profile 的 pnpm-workspace.yaml 原文(allowBuilds / minimumReleaseAgeExclude /
160
+ * nodeLinker 等)。随插件分区迁移,保证目标 profile 的 pnpm 能按来源配置安装
161
+ * (git 插件构建脚本白名单、新发布版本冷静期等)。缺省/无文件为 null。
162
+ */
163
+ pnpmWorkspace?: string | null;
164
+ /**
165
+ * 本地源(`link:` / `file:`)插件的打包产物(T1)。缺省/无本地源插件 = 不出现。
166
+ * 导出时由宿主注入的打包钩子填充;导入时据此把 spec 重写为 `file:<解包后的绝对路径>`。
167
+ */
168
+ localTarballs?: LocalPluginTarball[];
169
+ /**
170
+ * `patchedDependencies` 引用的 patch 文件(issue #35)。缺省 = 无声明或无文件可收集。
171
+ * 导入端先落文件、再写(已剔除不可满足声明的)pnpm-workspace.yaml。
172
+ */
173
+ patchFiles?: PnpmPatchFile[];
174
+ }
175
+
176
+
177
+ /* —— mcp 分区 —— */
178
+
179
+ export interface McpServerEntry {
180
+ serverName: string;
181
+ type: 'stdio' | 'streamable-http';
182
+ command?: string;
183
+ args?: string[];
184
+ env?: Record<string, string>;
185
+ cwd?: string;
186
+ url?: string;
187
+ headers?: Record<string, string>;
188
+ }
189
+
190
+ export interface McpSection { version: 1; servers: McpServerEntry[]; }
191
+
192
+ /* —— prompts 分区 —— */
193
+
194
+ export interface PromptEntry {
195
+ id: string;
196
+ name: string;
197
+ kind: 'systemPrompt' | 'planMode';
198
+ text: string;
199
+ sourceLineId?: string;
200
+ }
201
+
202
+ export interface PromptsSection { version: 1; prompts: PromptEntry[]; }
203
+
204
+ /* —— workspaces 分区(绝对路径 → 跨设备必须路径映射) —— */
205
+
206
+ export interface WorkspaceRecord {
207
+ id: string;
208
+ path: string;
209
+ title?: string;
210
+ sessionIds: string[];
211
+ createdAt?: string;
212
+ updatedAt?: string;
213
+ }
214
+
215
+ export interface WorkspacesSection { version: 1; workspaces: WorkspaceRecord[]; }
216
+
217
+ /* —— credentials 状态分区(永不含值) —— */
218
+
219
+ export interface CredentialStatus {
220
+ ref: string;
221
+ required: boolean;
222
+ configured: boolean;
223
+ source?: 'env' | 'file' | 'projectEnv' | 'other';
224
+ hasValue: boolean; // 普通备份恒 false(值未导出)
225
+ }
226
+
227
+ export interface CredentialsSection { version: 1; credentials: CredentialStatus[]; }
228
+
229
+ /* —— 文件类分区(skills/agentPresets/agentInstructions/pluginFiles/sessions) ——
230
+ * 这些分区以真实文件形式进入 ZIP(custom/skills/… 等),
231
+ * 内存中统一表示为 FilesSection(ZIP 内不落此 JSON,见 exporter)。 */
232
+
233
+ export interface FileEntry {
234
+ relativePath: string;
235
+ data: Uint8Array;
236
+ contentHash: string; // SHA-256 hex
237
+ }
238
+
239
+ export interface FilesSection { version: 1; files: FileEntry[]; }
240
+
241
+ /** 分区 JSON 文件的统一载荷(schema 校验用) */
242
+ export type SectionData =
243
+ | SettingsSection | UiSection | ProvidersSection | PluginsSection
244
+ | McpSection | PromptsSection | WorkspacesSection | CredentialsSection
245
+ | FilesSection;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Export Schema 版本判定 —— 集中唯一出口(对齐 Docs/design/architecture.md §10.3)。
3
+ *
4
+ * 业务代码零版本判断:所有模块一律通过本文件函数判定,
5
+ * 任何 `if (v === 1)` 式散落判断均为禁止项。
6
+ */
7
+
8
+ /** 当前 Export Schema 版本(v1 起点;未来随迁移链演进) */
9
+ export const CURRENT_SCHEMA_VERSION = 1 as const;
10
+
11
+ /** 本插件支持导入的最低 schema 版本 */
12
+ export const MIN_SUPPORTED_SCHEMA_VERSION = 1 as const;
13
+
14
+ /** 版本不受支持(过新或过旧)时的错误;携带版本号便于 UI 提示升级/更新备份 */
15
+ export class UnsupportedSchemaError extends Error {
16
+ readonly version: number;
17
+ constructor(version: number, message = `Export schema version ${version} is not supported (current: ${CURRENT_SCHEMA_VERSION})`) {
18
+ super(message);
19
+ this.name = 'UnsupportedSchemaError';
20
+ this.version = version;
21
+ }
22
+ }
23
+
24
+ /** 恰好是当前版本 */
25
+ export function isCurrent(v: number): boolean {
26
+ return v === CURRENT_SCHEMA_VERSION;
27
+ }
28
+
29
+ /** 版本在支持范围内(含需要迁移的旧版本) */
30
+ export function isSupported(v: number): boolean {
31
+ return v >= MIN_SUPPORTED_SCHEMA_VERSION && v <= CURRENT_SCHEMA_VERSION;
32
+ }
33
+
34
+ /** 旧版本:低于当前但仍在支持范围内 → 需要沿迁移链升级 */
35
+ export function needsMigration(v: number): boolean {
36
+ return v >= MIN_SUPPORTED_SCHEMA_VERSION && v < CURRENT_SCHEMA_VERSION;
37
+ }
38
+
39
+ /** 过新版本:高于当前 → 必须升级本插件才能导入 */
40
+ export function isTooNew(v: number): boolean {
41
+ return v > CURRENT_SCHEMA_VERSION;
42
+ }
43
+
44
+ /** 是否可导入(当前版本或可迁移的旧版本) */
45
+ export function canImport(v: number): boolean {
46
+ return isCurrent(v) || needsMigration(v);
47
+ }
48
+
49
+ /** 版本的可读描述(报告/日志用) */
50
+ export function describeVersion(v: number): string {
51
+ if (isTooNew(v)) return `schema v${v}(高于当前 ${CURRENT_SCHEMA_VERSION},需升级插件)`;
52
+ if (needsMigration(v)) return `schema v${v}(旧版,将迁移到 v${CURRENT_SCHEMA_VERSION})`;
53
+ if (isCurrent(v)) return `schema v${v}(当前版本)`;
54
+ return `schema v${v}(低于最低支持 ${MIN_SUPPORTED_SCHEMA_VERSION},不受支持)`;
55
+ }