@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,87 @@
1
+ /**
2
+ * 撤销 / 重做语义(Phase 1 P0-2)。
3
+ *
4
+ * 与竞品 dsh-undo-savepoint 的语义对齐(其「撤销不会撤销掉自己」的等价物在本仓库
5
+ * 由两处保证:`pre-restore` 快照自身不参与候选,以及恢复后按内容指纹做回声抑制):
6
+ *
7
+ * 自动快照在变更**之后**采集,因此「最新快照」通常等于当前状态。
8
+ * 真实撤销 = 回退到与当前状态**内容不同**的最新快照(跳过等于当前状态的那些)。
9
+ * 全部相同 → 明确返回「没有可撤销的变化」,而不是做一次空操作。
10
+ *
11
+ * 重做的护栏:撤销时先落一个 `pre-restore` 快照记录「撤销前状态」;
12
+ * 一旦其后产生任何更新的快照(= 用户又改了),重做即失效——避免把用户的
13
+ * 新改动覆盖回旧状态。
14
+ *
15
+ * 本模块是**纯规划**:不读盘、不写盘、不碰 adapter。执行侧见 config-snapshot.ts。
16
+ */
17
+ import { statesEqual } from './config-state.js';
18
+ /** 按 createdAt 倒序(新→旧);同刻按 id 稳定排序保证确定性。 */
19
+ export function sortNewestFirst(items) {
20
+ return [...items].sort((a, b) => {
21
+ if (a.createdAt !== b.createdAt)
22
+ return a.createdAt < b.createdAt ? 1 : -1;
23
+ return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
24
+ });
25
+ }
26
+ /** 可作为撤销目标/状态基准的快照(剔除 pre-restore 与无 state 的旧格式) */
27
+ function comparableCandidates(snapshots) {
28
+ return sortNewestFirst(snapshots).filter((s) => s.kind !== 'pre-restore' && s.state !== undefined);
29
+ }
30
+ /**
31
+ * 规划一次撤销:返回第一个内容与 current 不同的快照。
32
+ * `skippedNewer` = 跳过的「与当前状态相同」的更新快照数(通常 ≥1,因为自动快照
33
+ * 落在变更之后)。
34
+ */
35
+ export function planUndo(current, snapshots) {
36
+ const candidates = comparableCandidates(snapshots);
37
+ if (candidates.length === 0)
38
+ return { kind: 'none', reason: 'no-snapshots' };
39
+ for (let i = 0; i < candidates.length; i++) {
40
+ const candidate = candidates[i];
41
+ if (!statesEqual(current, candidate.state)) {
42
+ return { kind: 'undo', targetId: candidate.id, skippedNewer: i, reason: 'content-differs' };
43
+ }
44
+ }
45
+ return { kind: 'none', reason: 'already-at-state' };
46
+ }
47
+ /** 是否存在可撤销的变化(供 UI 按钮可用性;等价于 planUndo 是否命中)。 */
48
+ export function canUndo(current, snapshots) {
49
+ return planUndo(current, snapshots).kind === 'undo';
50
+ }
51
+ /**
52
+ * 规划一次重做:取最新的未消费 pre-restore。
53
+ * 若其后存在任何更新的快照(含未消费的 pre-restore)→ superseded-by-newer-change。
54
+ */
55
+ export function planRedo(snapshots) {
56
+ const ordered = sortNewestFirst(snapshots);
57
+ const pre = ordered.find((s) => s.kind === 'pre-restore' && s.consumed !== true);
58
+ if (pre === undefined)
59
+ return { kind: 'none', reason: 'no-pre-restore' };
60
+ const superseded = ordered.some((s) => s.createdAt > pre.createdAt && (s.kind !== 'pre-restore' || s.consumed !== true));
61
+ if (superseded)
62
+ return { kind: 'none', reason: 'superseded-by-newer-change' };
63
+ return { kind: 'redo', targetId: pre.id, reason: 'unconsumed-pre-restore' };
64
+ }
65
+ /** 是否存在可重做的撤销(供 UI 按钮可用性)。 */
66
+ export function canRedo(snapshots) {
67
+ return planRedo(snapshots).kind === 'redo';
68
+ }
69
+ /**
70
+ * 撤销后需要标记为「已被跨过」的快照 id 列表。
71
+ * 语义:撤销回退到 targetId 之后,比 targetId 更新、且内容等于**撤销前状态**的那些
72
+ * 快照,代表的是被撤销掉的那一步;重做回到撤销前状态后应清除该标记。
73
+ * 返回 targetId 之后(更旧侧不涉及)需要标记的 id。
74
+ */
75
+ export function steppedIds(snapshots, targetId, beforeState) {
76
+ const target = snapshots.find((s) => s.id === targetId);
77
+ if (target === undefined)
78
+ return [];
79
+ return snapshots
80
+ .filter((s) => s.kind !== 'pre-restore'
81
+ && s.id !== targetId
82
+ && s.createdAt > target.createdAt
83
+ && s.state !== undefined
84
+ && statesEqual(s.state, beforeState))
85
+ .map((s) => s.id);
86
+ }
87
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1,18 @@
1
+ import type { SectionId } from '../schema/types.ts';
2
+ import type { CompatibilityInput, CompatibilityScore, ValidationResult } from './types.ts';
3
+ export { validateManifest } from '../schema/manifest.ts';
4
+ export { validateSectionData } from '../schema/config.ts';
5
+ /** 校验 ZIP 内分区数据集合(空对象=合法);返回合并的校验结果 */
6
+ export declare function validateSections(sections: ReadonlyMap<SectionId, unknown>): ValidationResult;
7
+ /**
8
+ * 兼容性评分(规则驱动,不凭感觉):
9
+ * unsupported — schema 超出本插件支持范围(过高/过低)
10
+ * partial — 跨平台、分区缺失、或备份 DSH 比目标新
11
+ * good — 备份 DSH 比目标旧(向后兼容)且其余正常
12
+ * excellent — 同平台、无缺失、schema 支持
13
+ */
14
+ export declare function computeCompatibility(input: CompatibilityInput): CompatibilityScore;
15
+ /** 兼容性得分的可读描述(UI 报告用) */
16
+ export declare function describeCompatibility(score: CompatibilityScore): string;
17
+ /** 用 describeVersion 生成 schema 状态的用户可读说明 */
18
+ export declare function describeSchemaStatus(schemaVersion: number): string;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 整体校验与兼容性评分(规范 §18 完整性 / §30 兼容性评分 / 导入后校验)。
3
+ */
4
+ import { validateManifest } from '../schema/manifest.js';
5
+ import { validateSectionData } from '../schema/config.js';
6
+ import { canImport, describeVersion } from '../schema/versions.js';
7
+ export { validateManifest } from '../schema/manifest.js';
8
+ export { validateSectionData } from '../schema/config.js';
9
+ /** 校验 ZIP 内分区数据集合(空对象=合法);返回合并的校验结果 */
10
+ export function validateSections(sections) {
11
+ const issues = [];
12
+ for (const [sectionId, data] of sections) {
13
+ const sectionIssues = validateSectionData(sectionId, data);
14
+ for (const issue of sectionIssues) {
15
+ issues.push({ ...issue, path: `${sectionId}:${issue.path}` });
16
+ }
17
+ }
18
+ return { valid: issues.every((i) => i.severity !== 'error'), issues };
19
+ }
20
+ function parseVersion(v) {
21
+ const m = /^(\d+)\.(\d+)(?:\.(\d+))?(?:-(?:rc\.?)?(\d+))?/i.exec(v);
22
+ if (!m)
23
+ return { major: 0, minor: 0, patch: 0, pre: 0 };
24
+ return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3] ?? 0), pre: Number(m[4] ?? Infinity) };
25
+ }
26
+ /** 版本比较:-1=a<b,0=相等,1=a>b(预发布 < 正式版) */
27
+ function compareVersions(a, b) {
28
+ for (const k of ['major', 'minor', 'patch']) {
29
+ if (a[k] !== b[k])
30
+ return a[k] < b[k] ? -1 : 1;
31
+ }
32
+ if (a.pre !== b.pre)
33
+ return a.pre < b.pre ? -1 : 1;
34
+ return 0;
35
+ }
36
+ /**
37
+ * 兼容性评分(规则驱动,不凭感觉):
38
+ * unsupported — schema 超出本插件支持范围(过高/过低)
39
+ * partial — 跨平台、分区缺失、或备份 DSH 比目标新
40
+ * good — 备份 DSH 比目标旧(向后兼容)且其余正常
41
+ * excellent — 同平台、无缺失、schema 支持
42
+ */
43
+ export function computeCompatibility(input) {
44
+ if (!canImport(input.schemaVersion))
45
+ return 'unsupported';
46
+ let score = 'excellent';
47
+ if (input.sourcePlatform !== input.targetPlatform)
48
+ score = 'partial';
49
+ if (input.missingSections.length > 0)
50
+ score = 'partial';
51
+ const src = parseVersion(input.sourceDsh);
52
+ const tgt = parseVersion(input.targetDsh);
53
+ const cmp = compareVersions(src, tgt);
54
+ if (cmp > 0) {
55
+ // 备份来自更新的 DSH → 可能有目标版本不认识的配置
56
+ score = 'partial';
57
+ }
58
+ else if (cmp < 0) {
59
+ // 备份来自更旧的 DSH → 目标向后兼容
60
+ score = 'good';
61
+ }
62
+ return score;
63
+ }
64
+ /** 兼容性得分的可读描述(UI 报告用) */
65
+ export function describeCompatibility(score) {
66
+ switch (score) {
67
+ case 'excellent': return 'Excellent — 完美兼容(同平台、无缺失、schema 支持)';
68
+ case 'good': return 'Good — 兼容(旧版备份导入新版 DSH)';
69
+ case 'partial': return 'Partial — 部分兼容(跨平台/分区缺失/版本超前,需人工确认)';
70
+ case 'unsupported': return 'Unsupported — 不受支持(schema 超出范围)';
71
+ }
72
+ }
73
+ /** 用 describeVersion 生成 schema 状态的用户可读说明 */
74
+ export function describeSchemaStatus(schemaVersion) {
75
+ return describeVersion(schemaVersion);
76
+ }
77
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1,21 @@
1
+ import type { RecoveryVerification, RecoveryVerificationVerdict } from './journal.ts';
2
+ import type { HostContext, Snapshot } from './types.ts';
3
+ export interface VerifyRecoveryOptions {
4
+ /** 快照根目录(<snapshotsDir>,<snapshotDir> 的直接父级)。 */
5
+ snapshotsRoot: string;
6
+ /** 当前环境指纹(必须与 snapshot binding 一致,否则 WRONG_ENVIRONMENT)。 */
7
+ environmentFingerprint: string;
8
+ /** 期望的 operationId(journal.operationId)。快照 operationId 必须与之双向一致,否则 WRONG_OPERATION。 */
9
+ expectedOperationId?: string;
10
+ }
11
+ /**
12
+ * Post-recovery verification(§6.4)。
13
+ *
14
+ * @param snapshot 被恢复 operation 的 journal 引用快照(id + binding 用于定位与交叉校验;
15
+ * 实际校验以磁盘重读为准,不信任内存对象)。
16
+ * @param ctx HostContext(homeDir / fs / settings / plugins 门面)。
17
+ * @param opts snapshotsRoot + environmentFingerprint。
18
+ */
19
+ export declare function verifyRecovery(snapshot: Snapshot, ctx: HostContext, opts: VerifyRecoveryOptions): Promise<RecoveryVerification>;
20
+ /** 便捷:把 RecoveryVerification 映射为 journal 终态(供 verify 路由单次原子 update 使用)。 */
21
+ export declare function recoveryTerminalState(verdict: RecoveryVerificationVerdict): 'ROLLED_BACK' | 'RECOVERED' | 'NEEDS_ATTENTION';
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Phase 5 Post-Recovery Verification(§6)。
3
+ *
4
+ * 职责:recovery/rollback 完成后,验证目标状态与 trusted snapshot 匹配。
5
+ * 核心不变量(VERIFIED):**No recovery/rollback may be considered complete until the
6
+ * target state is verified to match the trusted snapshot** —— 验证失败不能 COMMITTED。
7
+ *
8
+ * 关键规则(§6.2 / §6.3 / §11.1):
9
+ * - **verify 时刻重验**:不信任 execute 时「已校验过」的结论(TOCTOU 窗口,快照可能被替换)。
10
+ * 本函数在 verify 时刻重跑完整 `validateSnapshotForRestore`(含 symlink + provenance +
11
+ * env binding + `requireOperationBound=true`),并从磁盘重读 snapshot.json 作为权威。
12
+ * - **只消费 TRUSTED_OPERATION_SNAPSHOT**:非 operation-bound(TRUSTED_MANUAL_LOCAL /
13
+ * LEGACY_REQUIRES_CONFIRMATION)一律拒绝,绝不当作 MATCH。
14
+ * - **verdict 语义严格区分**:
15
+ * MATCH 目标状态与 snapshot 完全匹配
16
+ * PARTIAL_MATCH 核心可验证状态匹配,但存在无法可靠验证的部分(如凭据值不可回读)
17
+ * MISMATCH 能确定目标状态仍不同于 snapshot(文件 hash 不匹配 / 应删仍存在 /
18
+ * plugin/settings 明确不同)
19
+ * VERIFICATION_ERROR 验证过程本身无法可靠完成(快照重验失败 / fs 读错误 / 异常)
20
+ * 绝不把 VERIFICATION_ERROR 降级成 PARTIAL_MATCH。
21
+ * - 输出结构兼容 Step 2 的 `recoveryVerification`(verdict/details[]/manualHints[]/at);
22
+ * details/manualHints 写入 journal 前由调用方过 `redactJournalText`(本函数只产出文本,
23
+ * 不含 secret 值——凭据值不可回读,settings 只比对不输出值)。
24
+ *
25
+ * 本模块为纯函数(fs 经 node:fs + HostContext 门面注入),可独立测试。
26
+ */
27
+ import fs from 'node:fs/promises';
28
+ import path from 'node:path';
29
+ import { isDeepStrictEqual } from 'node:util';
30
+ import { resolveFileTarget } from './backup.js';
31
+ import { validateSnapshotForRestore } from './restore.js';
32
+ /** 越界防御:abs 必须等于 homeDir 或在 homeDir 之内 */
33
+ function isWithinHome(homeDir, abs) {
34
+ const root = path.resolve(homeDir);
35
+ return abs === root || abs.startsWith(root + path.sep);
36
+ }
37
+ /** 绝对路径 → 相对 homeDir(跨平台归一为 / 分隔;越界返回空串) */
38
+ function toRelPath(homeDir, abs) {
39
+ if (!isWithinHome(homeDir, abs))
40
+ return '';
41
+ return path.relative(homeDir, abs).split(path.sep).join('/');
42
+ }
43
+ /** 快照目录内 blob 绝对路径(防 snapshot.json 里伪造 ../ 越界读) */
44
+ function blobAbs(snapshotDir, blobPath) {
45
+ const abs = path.resolve(snapshotDir, blobPath);
46
+ if (!isWithinHome(snapshotDir, abs))
47
+ throw new Error(`blob 路径越界: ${blobPath}`);
48
+ return abs;
49
+ }
50
+ /** Uint8Array 字节级相等 */
51
+ function bufEq(a, b) {
52
+ if (a.length !== b.length)
53
+ return false;
54
+ for (let i = 0; i < a.length; i++)
55
+ if (a[i] !== b[i])
56
+ return false;
57
+ return true;
58
+ }
59
+ /** file 类条目的目标绝对路径(复用 backup.ts 的基准目录规则;仅读 homeDir) */
60
+ function resolveFileAbs(homeDir, adapter, ref) {
61
+ return resolveFileTarget({ homeDir }, adapter, ref);
62
+ }
63
+ /**
64
+ * Post-recovery verification(§6.4)。
65
+ *
66
+ * @param snapshot 被恢复 operation 的 journal 引用快照(id + binding 用于定位与交叉校验;
67
+ * 实际校验以磁盘重读为准,不信任内存对象)。
68
+ * @param ctx HostContext(homeDir / fs / settings / plugins 门面)。
69
+ * @param opts snapshotsRoot + environmentFingerprint。
70
+ */
71
+ export async function verifyRecovery(snapshot, ctx, opts) {
72
+ const details = [];
73
+ const manualHints = [];
74
+ const at = new Date().toISOString();
75
+ const snapshotDir = path.join(opts.snapshotsRoot, snapshot.id);
76
+ // 1) verify 时刻重验快照(TOCTOU):重跑完整 validateSnapshotForRestore,requireOperationBound=true。
77
+ // 只接受 TRUSTED_OPERATION_SNAPSHOT;任何其它 verdict(含 TRUSTED_MANUAL_LOCAL / LEGACY)→ VERIFICATION_ERROR。
78
+ const val = await validateSnapshotForRestore(snapshotDir, opts.snapshotsRoot, {
79
+ environmentFingerprint: opts.environmentFingerprint,
80
+ });
81
+ if (val.verdict !== 'TRUSTED_OPERATION_SNAPSHOT') {
82
+ return {
83
+ verdict: 'VERIFICATION_ERROR',
84
+ details: [`快照重验失败: ${val.verdict}${val.reason ? ` — ${val.reason}` : ''}`],
85
+ manualHints: [],
86
+ at,
87
+ };
88
+ }
89
+ details.push('快照重验通过(TRUSTED_OPERATION_SNAPSHOT)');
90
+ // 2) 从磁盘重读 snapshot.json 作为权威(不信任传入的 snapshot 对象,防 execute→verify 间替换)。
91
+ let diskSnapshot;
92
+ try {
93
+ const raw = await fs.readFile(path.join(snapshotDir, 'snapshot.json'), 'utf8');
94
+ diskSnapshot = JSON.parse(raw);
95
+ }
96
+ catch (err) {
97
+ return { verdict: 'VERIFICATION_ERROR', details: [...details, `snapshot.json 重读失败: ${err instanceof Error ? err.message : String(err)}`], manualHints: [], at };
98
+ }
99
+ // 3) environment binding(validateSnapshotForRestore 已查,此处双保险)
100
+ if (diskSnapshot.environmentFingerprint && diskSnapshot.environmentFingerprint !== opts.environmentFingerprint) {
101
+ return { verdict: 'VERIFICATION_ERROR', details: [...details, 'environmentFingerprint 不匹配(WRONG_ENVIRONMENT)'], manualHints: [], at };
102
+ }
103
+ // 3b) operation binding(journal.operationId ↔ snapshot.operationId 双向一致;WRONG_OPERATION 拒绝)
104
+ if (opts.expectedOperationId !== undefined && diskSnapshot.operationId !== opts.expectedOperationId) {
105
+ return { verdict: 'VERIFICATION_ERROR', details: [...details, `operationId 不匹配(WRONG_OPERATION): snapshot=${diskSnapshot.operationId ?? '(无)'} expected=${opts.expectedOperationId}`], manualHints: [], at };
106
+ }
107
+ let mismatch = false;
108
+ let unverifiable = false;
109
+ // 4) host files(hostFileBackups):existed=true 应存在且内容 hash 匹配 blob;existed=false 应不存在。
110
+ // 经 ctx.fs(HostContext 注入,homeDir 相对)读写,与 rollback.ts 一致。
111
+ for (const backup of diskSnapshot.hostFileBackups ?? []) {
112
+ const rel = backup.relPath;
113
+ if (backup.existed) {
114
+ if (backup.blobPath === '') {
115
+ unverifiable = true;
116
+ details.push(`host file 无 blob 信息: ${rel}`);
117
+ continue;
118
+ }
119
+ if (!(await ctx.fs.exists(rel))) {
120
+ mismatch = true;
121
+ details.push(`host file 缺失: ${rel}`);
122
+ continue;
123
+ }
124
+ let blob;
125
+ try {
126
+ blob = await fs.readFile(blobAbs(snapshotDir, backup.blobPath));
127
+ }
128
+ catch {
129
+ unverifiable = true;
130
+ details.push(`host file blob 读取失败: ${rel}`);
131
+ continue;
132
+ }
133
+ const current = await ctx.fs.readFile(rel);
134
+ if (!bufEq(current, blob)) {
135
+ mismatch = true;
136
+ details.push(`host file 内容不匹配: ${rel}`);
137
+ }
138
+ else {
139
+ details.push(`host file 匹配: ${rel}`);
140
+ }
141
+ }
142
+ else {
143
+ // snapshot expects absent → target 必须不存在(删除动作失败 → 残留被检测)
144
+ if (await ctx.fs.exists(rel)) {
145
+ mismatch = true;
146
+ details.push(`应删除目标仍存在: ${rel}`);
147
+ }
148
+ else {
149
+ details.push(`删除目标已不存在: ${rel}`);
150
+ }
151
+ }
152
+ }
153
+ // 5) file 类条目(entries kind='file'):existed=true 应存在且内容匹配 blob;existed=false 应不存在。
154
+ for (const entry of diskSnapshot.entries ?? []) {
155
+ if (entry.kind !== 'file')
156
+ continue;
157
+ const rel = toRelPath(ctx.homeDir, resolveFileAbs(ctx.homeDir, entry.adapter, entry.ref));
158
+ if (rel === '') {
159
+ unverifiable = true;
160
+ details.push(`file 路径越界: ${entry.adapter}:${entry.ref}`);
161
+ continue;
162
+ }
163
+ if (entry.existed && entry.copiedTo) {
164
+ if (!(await ctx.fs.exists(rel))) {
165
+ mismatch = true;
166
+ details.push(`file 缺失: ${entry.adapter}:${entry.ref}`);
167
+ continue;
168
+ }
169
+ let blob;
170
+ try {
171
+ blob = await fs.readFile(blobAbs(snapshotDir, entry.copiedTo));
172
+ }
173
+ catch {
174
+ unverifiable = true;
175
+ details.push(`file blob 读取失败: ${entry.adapter}:${entry.ref}`);
176
+ continue;
177
+ }
178
+ const current = await ctx.fs.readFile(rel);
179
+ if (!bufEq(current, blob)) {
180
+ mismatch = true;
181
+ details.push(`file 内容不匹配: ${entry.adapter}:${entry.ref}`);
182
+ }
183
+ else {
184
+ details.push(`file 匹配: ${entry.adapter}:${entry.ref}`);
185
+ }
186
+ }
187
+ else if (!entry.existed) {
188
+ if (await ctx.fs.exists(rel)) {
189
+ mismatch = true;
190
+ details.push(`应删除 file 目标仍存在: ${entry.adapter}:${entry.ref}`);
191
+ }
192
+ else {
193
+ details.push(`删除 file 目标已不存在: ${entry.adapter}:${entry.ref}`);
194
+ }
195
+ }
196
+ }
197
+ // 6) plugin state(best-effort):导入期间新增插件应被移除。可精确验证 → 纳入 MATCH 判断;
198
+ // 无法可靠验证(无基线 / listInstalled 失败)→ PARTIAL_MATCH + manualHint。
199
+ const beforePlugins = diskSnapshot.beforePlugins;
200
+ if (beforePlugins === undefined) {
201
+ unverifiable = true;
202
+ details.push('快照无 beforePlugins 基线,插件状态不可验证');
203
+ manualHints.push('插件状态无法自动验证(快照无基线),请人工确认已装插件');
204
+ }
205
+ else {
206
+ try {
207
+ const current = await ctx.plugins.listInstalled();
208
+ const beforeNames = new Set(beforePlugins.map((p) => p.name));
209
+ const added = current.filter((p) => !beforeNames.has(p.name));
210
+ if (added.length > 0) {
211
+ mismatch = true;
212
+ details.push(`导入期间新增插件未移除: ${added.map((p) => p.name).join(', ')}`);
213
+ }
214
+ else {
215
+ details.push('插件状态匹配(无新增插件)');
216
+ }
217
+ }
218
+ catch {
219
+ unverifiable = true;
220
+ details.push('插件状态无法验证(listInstalled 失败)');
221
+ manualHints.push('插件状态无法自动验证(listInstalled 失败),请人工确认已装插件');
222
+ }
223
+ }
224
+ // 7) settings(best-effort):settingsNamespace 条目 before 值应与当前 settings 一致。
225
+ const settingsEntries = (diskSnapshot.entries ?? []).filter((e) => e.kind === 'settingsNamespace');
226
+ for (const entry of settingsEntries) {
227
+ try {
228
+ const current = await ctx.settings.describe(entry.ref);
229
+ if (!isDeepStrictEqual(current.value, entry.before)) {
230
+ mismatch = true;
231
+ details.push(`settings 不匹配: ${entry.ref}`);
232
+ }
233
+ else {
234
+ details.push(`settings 匹配: ${entry.ref}`);
235
+ }
236
+ }
237
+ catch {
238
+ unverifiable = true;
239
+ details.push(`settings 无法验证: ${entry.ref}`);
240
+ manualHints.push(`settings 无法自动验证(${entry.ref}),请人工确认`);
241
+ }
242
+ }
243
+ // 8) verdict:MISMATCH 优先(能确定目标状态不同);其次 PARTIAL_MATCH(存在不可验证项);否则 MATCH。
244
+ if (mismatch)
245
+ return { verdict: 'MISMATCH', details, manualHints, at };
246
+ if (unverifiable)
247
+ return { verdict: 'PARTIAL_MATCH', details, manualHints, at };
248
+ return { verdict: 'MATCH', details, manualHints, at };
249
+ }
250
+ /** 便捷:把 RecoveryVerification 映射为 journal 终态(供 verify 路由单次原子 update 使用)。 */
251
+ export function recoveryTerminalState(verdict) {
252
+ if (verdict === 'MATCH' || verdict === 'PARTIAL_MATCH')
253
+ return 'ROLLED_BACK';
254
+ return 'NEEDS_ATTENTION';
255
+ }
256
+ //# sourceMappingURL=verify-recovery.js.map
@@ -0,0 +1,101 @@
1
+ /** 监听句柄(fs.watch 返回值的结构子集,便于测试伪造) */
2
+ export interface WatchHandle {
3
+ close(): void;
4
+ }
5
+ /** 监听工厂(真实实现注入 fs.watch) */
6
+ export type WatchFactory = (dir: string, onEvent: (eventType: string, filename: string | null) => void) => WatchHandle;
7
+ /** 定时器注入(测试用假实现驱动时序) */
8
+ export interface TimerApi {
9
+ setTimeout(fn: () => void, ms: number): unknown;
10
+ clearTimeout(handle: unknown): void;
11
+ }
12
+ export interface WatcherEvent {
13
+ /** 事件发生的目录(绝对路径) */
14
+ dir: string;
15
+ /** 文件名(fs.watch 可能给 null) */
16
+ filename: string;
17
+ }
18
+ export interface DebouncedWatcherOptions {
19
+ /** 要监听的目录(绝对路径;不存在的目录由 watchFactory 抛错,本类记 onError 后跳过) */
20
+ dirs: readonly string[];
21
+ /** 防抖窗口(毫秒) */
22
+ debounceMs: number;
23
+ watchFactory: WatchFactory;
24
+ /** 防抖到期后回调(事件已合并;同一目录同一文件去重) */
25
+ onFlush: (events: WatcherEvent[]) => void;
26
+ /** 监听失败 / 运行时 error 回调(不抛;默认静默) */
27
+ onError?: (dir: string, error: unknown) => void;
28
+ timers?: TimerApi;
29
+ }
30
+ /** 临时文件 / 编辑器残留:默认忽略,避免「保存一次触发多轮快照」 */
31
+ export declare function isIgnorableFileName(name: string): boolean;
32
+ /**
33
+ * 防抖监听器:把高频文件事件合并成一次 `onFlush`。
34
+ *
35
+ * 生命周期:`start()` 建立监听;`stop()` 关闭全部监听并清掉待发定时器;
36
+ * `start()` 可在 `stop()` 后重新调用(幂等重建——宿主改配置后需要重建监听集)。
37
+ */
38
+ export declare class DebouncedWatcher {
39
+ private readonly options;
40
+ private readonly timers;
41
+ private handles;
42
+ private timer;
43
+ private pending;
44
+ private suppressCount;
45
+ private running;
46
+ constructor(options: DebouncedWatcherOptions);
47
+ get isRunning(): boolean;
48
+ /** 当前抑制深度(>0 表示正在执行会自写文件的操作) */
49
+ get suppressDepth(): number;
50
+ /** 待发事件数(测试与诊断用) */
51
+ get pendingCount(): number;
52
+ start(): void;
53
+ stop(): void;
54
+ /**
55
+ * 进入抑制窗口:期间产生的文件事件**直接丢弃**。
56
+ * 调用方必须在 finally 里 `endSuppress()`,否则自动快照会永久停摆。
57
+ */
58
+ beginSuppress(): void;
59
+ /**
60
+ * 退出抑制窗口:深度归零时**顺带清空待发事件**——那些事件正是被抑制的写操作
61
+ * 自己产生的,若放行会立刻产生一个回声快照(挡住重做)。
62
+ */
63
+ endSuppress(): void;
64
+ /** 同步便捷包装:抑制窗口内执行 fn,异常也保证退出抑制。 */
65
+ suppressWhile<T>(fn: () => T): T;
66
+ /** 异步便捷包装(恢复/回滚等长操作)。 */
67
+ suppressWhileAsync<T>(fn: () => Promise<T>): Promise<T>;
68
+ private onEvent;
69
+ private schedule;
70
+ }
71
+ /**
72
+ * 回声登记表:记录「我们刚刚写回的内容指纹」,用于识别监听器收到的
73
+ * 「恢复动作自己的写」这一回声。
74
+ *
75
+ * 与 `beginSuppress/endSuppress` 的分工:抑制窗口覆盖**写操作进行中**的事件;
76
+ * 本表覆盖**窗口之后才被投递**的延迟事件(macOS / 网络盘 / 杀毒软件扫描后
77
+ * 重写文件都可能造成事件晚到)。两条防线缺一不可——竞品的注释与 v0.4.6
78
+ * CHANGELOG 都记录了「macOS 延迟投递的回声快照挡住 redo」这一实际事故。
79
+ */
80
+ export declare class EchoRegistry {
81
+ private readonly hashes;
82
+ /** 登记一次写回:key 建议 `${scope}:${relPath}`(如 `config:cordis.patch.yml`) */
83
+ record(key: string, hash: string): void;
84
+ /** 是否仍等于我们写回的内容(= 回声,应忽略) */
85
+ isEcho(key: string, hash: string): boolean;
86
+ has(key: string): boolean;
87
+ get size(): number;
88
+ /**
89
+ * 清空。
90
+ * 语义:**真实变更一旦发生就清空整表**——否则后续对同一路径的合法修改
91
+ * 只要恰好等于某次历史写回内容就会被误判为回声而漏拍。
92
+ */
93
+ clear(): void;
94
+ }
95
+ /**
96
+ * 把事件批按「目录 → 文件名」聚合,便于调用方按关注目录分类。
97
+ * 返回 Map<dir, Set<filename>>(顺序无关,便于集合判定)。
98
+ */
99
+ export declare function groupEventsByDir(events: readonly WatcherEvent[]): Map<string, Set<string>>;
100
+ /** 事件批里是否包含关注的文件名(跨目录;用于「配置目录里出现了被关注的 basename」判定) */
101
+ export declare function hasWatchedBasename(events: readonly WatcherEvent[], watched: ReadonlySet<string>): boolean;