@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,327 @@
1
+ /**
2
+ * Phase 5 Recovery Orchestration(§9.2「新增 recovery 编排层」)。
3
+ *
4
+ * 职责:把 recovery 路由的编排逻辑(decision / confirmation / authority / snapshot 校验 /
5
+ * journal 状态机 / 原子 verification+terminal / dismiss)提取为可测纯编排层。
6
+ * 路由(index.ts)只负责:loopback fence + withMutationLock + 把结果映射为 HTTP 响应。
7
+ *
8
+ * 安全不变量(§9.4 / §11):
9
+ * - **权威 snapshotId 只来自 j.snapshotId**(不接受请求体覆盖)。
10
+ * - 只消费 `TRUSTED_OPERATION_SNAPSHOT`(requireOperationBound=true);LEGACY/MANUAL/CORRUPT/
11
+ * INVALID/UNSAFE_PATH/WRONG_ENVIRONMENT/WRONG_OPERATION 一律拒绝。
12
+ * - destructive 动作必须 `userConfirmed=true`(双重校验:请求体 + journal 状态机)。
13
+ * - verify 的 verification 写入 + terminal state 为**单次原子 store.update**(§6.5)。
14
+ * - 不新建 journal / lock / transaction / snapshot-trust(复用现有 machinery)。
15
+ *
16
+ * 本模块不 import index.ts(避免循环依赖);restore/rollback 执行器由路由注入
17
+ * (`RecoveryExecutorFns`),测试注入 mock。
18
+ */
19
+ import { join } from 'node:path';
20
+ import { FileSnapshotStore } from './backup.js';
21
+ import { validateSnapshotForRestore } from './restore.js';
22
+ import { recomputeRecoveryDecision, executeRecovery } from './reconcile.js';
23
+ import { verifyRecovery, recoveryTerminalState } from './verify-recovery.js';
24
+ import { redactJournalText, isTerminalState, transitionJournalState, isValidOperationId, } from './journal.js';
25
+ import { RunRegistry } from './run-registry.js';
26
+ export function createRecoveryOrchestrator(deps) {
27
+ const { store, runs, snapshotsDir, host, msg, snapshotExists, getEnvironmentFingerprint, clearSafeMode, inspectLockState, recoverStaleLock } = deps;
28
+ /**
29
+ * 只读 recovery decision(不修改 journal)。**不用 reconcileActive**:其 §6.5 硬门控会把
30
+ * RECOVERING journal 迁移到 NEEDS_ATTENTION,若 status 轮询时触发会破坏 execute→verify 流程。
31
+ */
32
+ const decision = async (j) => {
33
+ if (j.state === 'NEEDS_ATTENTION') {
34
+ return recomputeRecoveryDecision(j, { snapshotExists });
35
+ }
36
+ if (j.state === 'RECOVERING') {
37
+ if (Object.keys(j.rollback.entryDone ?? {}).length > 0)
38
+ return 'rollback-continue';
39
+ return 'needs-attention';
40
+ }
41
+ return 'needs-attention'; // CREATED/SNAPSHOT_CREATED/APPLYING/VALIDATING → 保守
42
+ };
43
+ /** 校验 trusted snapshot(requireOperationBound=true);返回 verdict。 */
44
+ const validateSnapshotVerdict = async (snapshotId) => {
45
+ const v = await validateSnapshotForRestore(join(snapshotsDir, snapshotId), snapshotsDir, { environmentFingerprint: getEnvironmentFingerprint() });
46
+ return v.verdict;
47
+ };
48
+ /**
49
+ * recovery 成功后清除 SAFE MODE(§5.3 / §10.2「SAFE MODE 退出」)。
50
+ * 仅当 **不存在其他未解决 active journal**(active 全部为已解决 terminal:
51
+ * COMMITTED/ROLLED_BACK/RECOVERED,**NEEDS_ATTENTION 视为未解决**——它代表仍需
52
+ * 人工处理的 incident,必须保持 SAFE MODE 阻断)时,才清除 durable 标记与内存标志。
53
+ * fail-closed:扫描失败不强行清除(保守保留 SAFE MODE)。
54
+ */
55
+ const maybeClearSafeMode = async () => {
56
+ try {
57
+ const activeIds = await store.scanActive();
58
+ let allResolved = true;
59
+ for (const opId of activeIds) {
60
+ const j = await store.loadActive(opId);
61
+ if (j === null)
62
+ continue;
63
+ // NEEDS_ATTENTION 是 terminal 但代表未解决 incident → 不视为 resolved
64
+ if (j.state === 'NEEDS_ATTENTION') {
65
+ allResolved = false;
66
+ break;
67
+ }
68
+ if (!isTerminalState(j.state)) {
69
+ allResolved = false;
70
+ break;
71
+ }
72
+ }
73
+ if (allResolved) {
74
+ await clearSafeMode();
75
+ }
76
+ }
77
+ catch {
78
+ // 扫描失败保守:不清除 SAFE MODE(fail-closed)
79
+ }
80
+ };
81
+ /** 执行 recovery(execute/retry 共用):登记 run → 注入执行器工厂 → executeRecovery。 */
82
+ const runExecution = async (operationId, j, decisionKind, makeExecutors) => {
83
+ let run;
84
+ try {
85
+ run = runs.register('recovery');
86
+ }
87
+ catch (error) {
88
+ return { status: 409, body: { error: error instanceof Error ? error.message : String(error) } };
89
+ }
90
+ const runId = run.runId;
91
+ try {
92
+ const executors = makeExecutors(runId);
93
+ const result = await executeRecovery(store, {
94
+ operationId, action: 'rollback', snapshotId: j.snapshotId, decision: decisionKind,
95
+ performRestore: executors.performRestore, performRollback: executors.performRollback,
96
+ }, true);
97
+ if (result === 'failed') {
98
+ runs.fail(runId, 'recovery 执行失败');
99
+ return { status: 400, body: { error: 'recovery 执行失败', runId } };
100
+ }
101
+ runs.finish(runId, { decision: decisionKind, state: 'RECOVERING' });
102
+ return { status: 200, body: { ok: true, operationId, decision: decisionKind, state: 'RECOVERING', runId } };
103
+ }
104
+ catch (error) {
105
+ runs.fail(runId, error instanceof Error ? error.message : String(error));
106
+ return { status: 500, body: { error: error instanceof Error ? error.message : String(error), runId } };
107
+ }
108
+ };
109
+ return {
110
+ async status() {
111
+ const activeIds = await store.scanActive();
112
+ const incidents = [];
113
+ for (const opId of activeIds) {
114
+ const j = await store.loadActive(opId);
115
+ if (j === null)
116
+ continue;
117
+ if (isTerminalState(j.state) && j.state !== 'NEEDS_ATTENTION')
118
+ continue; // COMMITTED/ROLLED_BACK/RECOVERED → 非 incident
119
+ const d = await decision(j);
120
+ incidents.push({
121
+ operationId: opId,
122
+ operationType: j.operationType,
123
+ state: j.state,
124
+ decision: d,
125
+ snapshotId: j.snapshotId,
126
+ reason: redactJournalText(j.error || j.recovery.reason || ''),
127
+ createdAt: j.createdAt,
128
+ });
129
+ }
130
+ const running = runs.listActive().filter((r) => r.kind === 'recovery').map((r) => ({ runId: r.runId, status: r.status }));
131
+ // issue #31:附带环境锁分类。**只暴露分类,不暴露 owner pid/op/hostname**(内部诊断)。
132
+ // attention 只对「需用户显式处理」的状态为 true:STALE_LOCK_DETECTED(残留锁,重试不会自愈)
133
+ // 与 UNKNOWN_STATE(无法判定)→ 引导用户回收;LOCKED(另一任务活跃持有)会自行释放,
134
+ // 不该催用户去「回收」,否则又是一个误导。探测失败 → 保守 UNKNOWN_STATE + attention。
135
+ let lock;
136
+ try {
137
+ const insp = await inspectLockState();
138
+ lock = {
139
+ state: insp.state,
140
+ attention: insp.state === 'STALE_LOCK_DETECTED' || insp.state === 'UNKNOWN_STATE',
141
+ };
142
+ }
143
+ catch {
144
+ lock = { state: 'UNKNOWN_STATE', attention: true };
145
+ }
146
+ return { status: 200, body: { incidents, running, lock } };
147
+ },
148
+ async preview(operationId) {
149
+ if (!isValidOperationId(operationId))
150
+ return { status: 400, body: { error: 'invalid operationId' } };
151
+ const j = await store.loadActive(operationId);
152
+ if (j === null)
153
+ return { status: 404, body: { error: 'operation not found' } };
154
+ const d = await decision(j);
155
+ let snapshotVerdict = null;
156
+ let snapshotMeta = null;
157
+ if (j.snapshotId !== null && j.snapshotId !== '') {
158
+ snapshotVerdict = await validateSnapshotVerdict(j.snapshotId);
159
+ try {
160
+ const snap = await new FileSnapshotStore({ dir: snapshotsDir }).load(j.snapshotId);
161
+ snapshotMeta = { id: snap.id, createdAt: snap.createdAt, operationType: snap.operationType };
162
+ }
163
+ catch { /* 快照缺失 */ }
164
+ }
165
+ return {
166
+ status: 200,
167
+ body: {
168
+ operationId,
169
+ operationType: j.operationType,
170
+ state: j.state,
171
+ decision: d,
172
+ snapshotId: j.snapshotId,
173
+ snapshotVerdict,
174
+ snapshotMeta,
175
+ environmentFingerprint: j.environmentFingerprint,
176
+ environmentCompatible: j.environmentFingerprint === getEnvironmentFingerprint(),
177
+ reason: redactJournalText(j.error || j.recovery.reason || ''),
178
+ createdAt: j.createdAt,
179
+ },
180
+ };
181
+ },
182
+ async confirm(operationId, userConfirmed) {
183
+ if (!isValidOperationId(operationId))
184
+ return { status: 400, body: { error: 'invalid operationId' } };
185
+ if (userConfirmed !== true)
186
+ return { status: 400, body: { error: 'userConfirmed required' } };
187
+ const j = await store.loadActive(operationId);
188
+ if (j === null)
189
+ return { status: 404, body: { error: 'operation not found' } };
190
+ if (j.state !== 'NEEDS_ATTENTION')
191
+ return { status: 409, body: { error: `journal state ${j.state} 不允许 confirm` } };
192
+ if (j.snapshotId === null || j.snapshotId === '')
193
+ return { status: 400, body: { error: 'journal 无 trusted snapshot,无法恢复' } };
194
+ const verdict = await validateSnapshotVerdict(j.snapshotId);
195
+ if (verdict !== 'TRUSTED_OPERATION_SNAPSHOT')
196
+ return { status: 400, body: { error: `snapshot 不可信: ${verdict}` } };
197
+ // journal 保持 NEEDS_ATTENTION(confirm 不转 RECOVERING)
198
+ return { status: 200, body: { ok: true, operationId, snapshotId: j.snapshotId, verdict } };
199
+ },
200
+ async execute(operationId, userConfirmed, makeExecutors) {
201
+ if (!isValidOperationId(operationId))
202
+ return { status: 400, body: { error: 'invalid operationId' } };
203
+ if (userConfirmed !== true)
204
+ return { status: 400, body: { error: 'userConfirmed required' } };
205
+ const j = await store.loadActive(operationId);
206
+ if (j === null)
207
+ return { status: 404, body: { error: 'operation not found' } };
208
+ if (j.state !== 'NEEDS_ATTENTION' && j.state !== 'RECOVERING')
209
+ return { status: 409, body: { error: `journal state ${j.state} 不允许 execute` } };
210
+ if (j.snapshotId === null || j.snapshotId === '')
211
+ return { status: 400, body: { error: 'journal 无 trusted snapshot,无法恢复' } };
212
+ const d = await decision(j);
213
+ if (d === 'needs-attention')
214
+ return { status: 400, body: { error: 'recovery decision 为 needs-attention,无法自动执行' } };
215
+ const verdict = await validateSnapshotVerdict(j.snapshotId);
216
+ if (verdict !== 'TRUSTED_OPERATION_SNAPSHOT')
217
+ return { status: 400, body: { error: `snapshot 不可信: ${verdict}` } };
218
+ return runExecution(operationId, j, d, makeExecutors);
219
+ },
220
+ async verify(operationId) {
221
+ if (!isValidOperationId(operationId))
222
+ return { status: 400, body: { error: 'invalid operationId' } };
223
+ const j = await store.loadActive(operationId);
224
+ if (j === null)
225
+ return { status: 404, body: { error: 'operation not found' } };
226
+ if (j.state !== 'RECOVERING')
227
+ return { status: 409, body: { error: `journal state ${j.state} 不允许 verify` } };
228
+ if (j.snapshotId === null || j.snapshotId === '')
229
+ return { status: 400, body: { error: 'journal 无 trusted snapshot' } };
230
+ let snap;
231
+ try {
232
+ snap = await new FileSnapshotStore({ dir: snapshotsDir }).load(j.snapshotId);
233
+ }
234
+ catch {
235
+ return { status: 400, body: { error: 'snapshot 无法加载' } };
236
+ }
237
+ const verification = await verifyRecovery(snap, host, { snapshotsRoot: snapshotsDir, environmentFingerprint: getEnvironmentFingerprint(), expectedOperationId: j.operationId });
238
+ const terminal = recoveryTerminalState(verification.verdict);
239
+ // 单次原子 journal update:recoveryVerification + terminal state(§6.5)
240
+ await store.update(operationId, (cur) => {
241
+ let next = cur;
242
+ if (!isTerminalState(cur.state))
243
+ next = transitionJournalState(cur, terminal);
244
+ next = { ...next, recoveryVerification: { verdict: verification.verdict, details: verification.details.map(redactJournalText), manualHints: verification.manualHints.map(redactJournalText), at: verification.at } };
245
+ return next;
246
+ });
247
+ if (terminal === 'ROLLED_BACK')
248
+ await store.moveToCompleted(operationId).catch(() => undefined);
249
+ // §5.3 / §10.2:recovery 成功后若无其他未解决 incident → 清除 SAFE MODE(解除阻断)
250
+ if (terminal === 'ROLLED_BACK')
251
+ await maybeClearSafeMode();
252
+ return {
253
+ status: 200,
254
+ body: {
255
+ ok: true, operationId, verdict: verification.verdict, terminal,
256
+ details: verification.details.map(redactJournalText),
257
+ manualHints: verification.manualHints.map(redactJournalText),
258
+ },
259
+ };
260
+ },
261
+ async retry(operationId, userConfirmed, makeExecutors) {
262
+ if (!isValidOperationId(operationId))
263
+ return { status: 400, body: { error: 'invalid operationId' } };
264
+ if (userConfirmed !== true)
265
+ return { status: 400, body: { error: 'userConfirmed required' } };
266
+ const j = await store.loadActive(operationId);
267
+ if (j === null)
268
+ return { status: 404, body: { error: 'operation not found' } };
269
+ if (j.state !== 'NEEDS_ATTENTION')
270
+ return { status: 409, body: { error: `journal state ${j.state} 不允许 retry` } };
271
+ if (j.snapshotId === null || j.snapshotId === '')
272
+ return { status: 400, body: { error: 'journal 无 trusted snapshot' } };
273
+ const d = await decision(j);
274
+ if (d === 'needs-attention')
275
+ return { status: 400, body: { error: 'recovery decision 为 needs-attention,无法 retry' } };
276
+ const verdict = await validateSnapshotVerdict(j.snapshotId);
277
+ if (verdict !== 'TRUSTED_OPERATION_SNAPSHOT')
278
+ return { status: 400, body: { error: `snapshot 不可信: ${verdict}` } };
279
+ return runExecution(operationId, j, d, makeExecutors);
280
+ },
281
+ async dismiss(operationId, userConfirmed) {
282
+ if (!isValidOperationId(operationId))
283
+ return { status: 400, body: { error: 'invalid operationId' } };
284
+ if (userConfirmed !== true)
285
+ return { status: 400, body: { error: 'userConfirmed required' } };
286
+ const j = await store.loadActive(operationId);
287
+ if (j === null)
288
+ return { status: 404, body: { error: 'operation not found' } };
289
+ // dismiss → quarantine(用户放弃;不删除 snapshot/journal evidence,不强行 RECOVERED)
290
+ const result = await executeRecovery(store, { operationId, action: 'dismiss', snapshotId: j.snapshotId }, true);
291
+ if (result === 'failed')
292
+ return { status: 400, body: { error: 'dismiss 失败' } };
293
+ return { status: 200, body: { ok: true, operationId, dismissed: true } };
294
+ },
295
+ /**
296
+ * issue #31:显式回收 stale 残留锁。**无 operationId**——残留锁没有 journal(这正是
297
+ * 「事故恢复」面板过去恒空的原因),因此不复用 confirm/execute 那套 journal 状态机。
298
+ *
299
+ * 正确性边界(不放松「绝不自动摘锁」):
300
+ * - **不 acquire 锁**:要回收的正是挡住 acquire 的那把锁,先取锁必然失败;
301
+ * - 是否真的 stale 由 recoverStaleLock 内部重做判定(inspect + 二次验证),
302
+ * 本层拿到的 state/detail 只用于日志,绝不作为删除依据;
303
+ * - 活锁/判定不确定 → 内部拒绝,ok=false,不做任何改动(不误删别人的锁)。
304
+ */
305
+ async recoverStaleLock(userConfirmed) {
306
+ if (userConfirmed !== true)
307
+ return { status: 400, body: { error: 'userConfirmed required' } };
308
+ try {
309
+ const res = await recoverStaleLock();
310
+ if (res.detail !== undefined) {
311
+ // 内部诊断(op/pid/路径)只进日志
312
+ host.log[res.ok ? 'info' : 'warn'](`stale lock recovery: ok=${res.ok} state=${res.state} detail=${res.detail}`);
313
+ }
314
+ if (!res.ok) {
315
+ // 拒绝是正常结果(活锁/无法证明 stale/二次验证失败),不是 500
316
+ return { status: 200, body: { ok: false, removed: false, state: res.state, reason: res.detail ?? '未判定为 stale,已拒绝回收' } };
317
+ }
318
+ // 回收成功后 stale 分类已消失;锁态交由客户端重新拉取 status 刷新
319
+ return { status: 200, body: { ok: true, removed: res.removed, state: res.state } };
320
+ }
321
+ catch (error) {
322
+ return { status: 500, body: { error: error instanceof Error ? error.message : String(error) } };
323
+ }
324
+ },
325
+ };
326
+ }
327
+ //# sourceMappingURL=recovery-orchestrator.js.map
@@ -0,0 +1,162 @@
1
+ import type { MsgFunc } from './messages.ts';
2
+ import type { SnapshotStatus } from './types.ts';
3
+ export type RestoreActionKind = 'hostFileRestore' | 'hostFileRemove' | 'pluginRemove' | 'fileRestore' | 'fileRemove' | 'credentialHint' | 'skip';
4
+ export interface RestoreAction {
5
+ kind: RestoreActionKind;
6
+ /** 人类可读描述 */
7
+ description: string;
8
+ /** 目标:相对 $DSH_HOME 的 relPath(hostFile/file 类)或插件名(pluginRemove) */
9
+ target?: string;
10
+ /** hostFileRestore/fileRestore:快照内 blob 相对路径(相对 snapshotDir) */
11
+ blobPath?: string;
12
+ /** pluginRemove:插件包名 */
13
+ pluginName?: string;
14
+ /** credentialHint:人工提示文本 */
15
+ manualHint?: string;
16
+ /** 附加说明(旧快照缺基线 / 离线无法恢复等) */
17
+ detail?: string;
18
+ /** 该动作是否对应 settings 主文件(settingsPath 解析出的文件) */
19
+ isSettings?: boolean;
20
+ }
21
+ export interface RestorePlan {
22
+ snapshotId: string;
23
+ createdAt: string;
24
+ sourceZip: string;
25
+ actions: RestoreAction[];
26
+ summary: {
27
+ hostFileRestores: number;
28
+ hostFileRemoves: number;
29
+ pluginRemoves: number;
30
+ fileRestores: number;
31
+ fileRemoves: number;
32
+ credentialHints: number;
33
+ skips: number;
34
+ };
35
+ /** beforePlugins 基线是否可用(undefined=旧快照无基线 → 不计划任何插件卸载) */
36
+ pluginBaselineConfirmed: boolean;
37
+ }
38
+ export interface RestoreOptions {
39
+ /** 快照目录 <snapshotsDir>/<id>/(含 snapshot.json 与 blobs/) */
40
+ snapshotDir: string;
41
+ /** $DSH_HOME(默认 ~/.dsh) */
42
+ homeDir: string;
43
+ /** 管理的 DSH profile 名(插件卸载走 profiles/<profile>) */
44
+ profile: string;
45
+ /** 覆盖 settings 文件路径(默认探测 $DSH_HOME/settings.yaml → settings.json) */
46
+ settingsPath?: string;
47
+ /** 注入式插件卸载器(测试用;缺省走 runDshPlugin 官方通道) */
48
+ pluginUninstaller?: (name: string, profileDir: string, profile: string) => Promise<{
49
+ ok: boolean;
50
+ message?: string;
51
+ }>;
52
+ /** 每项动作执行回调(宿主路由埋点:更新 RunRegistry 进度;index/1-based、total=计划动作数、
53
+ * detail=动作描述。dry-run/planRestore 不触发) */
54
+ onAction?: (info: {
55
+ index: number;
56
+ total: number;
57
+ detail: string;
58
+ }) => void;
59
+ /** 消息翻译器(缺省 zh;宿主按 DSH 应用语言注入) */
60
+ msg?: MsgFunc;
61
+ /**
62
+ * Phase 4 统一恢复校验:快照根目录(<snapshotsDir>,<snapshotDir> 的直接父级)。
63
+ * 提供时,planRestore 顶端强制调用 validateSnapshotForRestore;CORRUPT/INVALID/UNSAFE_PATH/
64
+ * WRONG_ENVIRONMENT 拒绝计划。缺省 = 不强制(兼容旧调用;新入口都应传)。
65
+ */
66
+ snapshotsRoot?: string;
67
+ /** Phase 4 恢复校验时校验环境指纹(不匹配 → WRONG_ENVIRONMENT;缺省不校验)。 */
68
+ environmentFingerprint?: string;
69
+ /**
70
+ * Phase 4 恢复确认策略:缺省允许 LEGACY 显式恢复。若调用方要求「非 operation-bound 一律拒绝」,
71
+ * 可传 'requireOperationBound'。
72
+ */
73
+ requireOperationBound?: boolean;
74
+ }
75
+ export interface RestoreReport {
76
+ snapshotId: string;
77
+ /** 已还原/已删除的目标(相对 $DSH_HOME) */
78
+ restored: string[];
79
+ /** 已卸载的插件名 */
80
+ removedPlugins: string[];
81
+ /** 需人工处理的提示(凭据补录等) */
82
+ manualHints: string[];
83
+ /** 失败清单 */
84
+ failed: {
85
+ item: string;
86
+ reason: string;
87
+ }[];
88
+ /** 跳过项(无动作 / 离线无法处理) */
89
+ skipped: string[];
90
+ /**
91
+ * 幽灵会话(F5 失效归档清理):快照记录的 sessions 分区会话在磁盘 sessions 目录
92
+ * 已无任何文件(备份有记录、磁盘无文件)。DSH 官方无归档会话 API,降级本地校验,
93
+ * 仅供报告、需人工在 DSH 确认清理;无会话分区/无幽灵时为缺省或空数组。
94
+ */
95
+ ghostSessions?: string[];
96
+ }
97
+ export interface SnapshotMeta {
98
+ id: string;
99
+ createdAt: string;
100
+ sourceZip: string;
101
+ status?: SnapshotStatus;
102
+ entryCount: number;
103
+ hostFileBackupCount: number;
104
+ beforePluginCount: number;
105
+ /** 置顶标记(P1-⑧):置顶快照不参与自动保留清理(最多保留 N 个时的淘汰豁免) */
106
+ pinned?: boolean;
107
+ }
108
+ /**
109
+ * 快照恢复前可信校验的 verdict(统一供 Host API / ModelTools / CLI / sync rollback 消费)。
110
+ *
111
+ * - TRUSTED_OPERATION_SNAPSHOT:READY + manifest 完整 + blob hash 匹配 + 有 op/env/owner binding。
112
+ * 可授权自动/推荐回滚。
113
+ * - TRUSTED_MANUAL_LOCAL:READY + 完整校验通过,但无 Phase4 binding(如用户手动/pinned 快照)。
114
+ * 可显式手动恢复,不可作 Phase3 自动 recovery 证据。
115
+ * - LEGACY_REQUIRES_CONFIRMATION:旧快照(无 manifest / 无 readiness)。结构/路径校验通过即可显式确认恢复,
116
+ * 绝不可自动恢复。
117
+ * - INVALID / CORRUPT / UNSAFE_PATH / WRONG_ENVIRONMENT:拒绝。
118
+ */
119
+ export type RestoreSnapshotVerdict = 'TRUSTED_OPERATION_SNAPSHOT' | 'TRUSTED_MANUAL_LOCAL' | 'LEGACY_REQUIRES_CONFIRMATION' | 'WRONG_ENVIRONMENT' | 'CORRUPT' | 'INVALID' | 'UNSAFE_PATH';
120
+ export interface RestoreSnapshotValidation {
121
+ verdict: RestoreSnapshotVerdict;
122
+ reason?: string;
123
+ }
124
+ /**
125
+ * 统一恢复前校验(供 planRestore 顶端对所有 restore 类型强制调用):
126
+ * 1. isValidSnapshotId(语法)
127
+ * 2. snapshot.json + manifest 存在、id 匹配(结构)
128
+ * 3. verifySnapshot(磁盘重读:metadataHash + blob hashes + 路径安全)→ CORRUPT
129
+ * 4. symlink 检查(snapshot.json / manifest / blobs)→ UNSAFE_PATH
130
+ * 5. readiness + provenance:
131
+ * - 无 binding 字段(旧快照)→ LEGACY_REQUIRES_CONFIRMATION
132
+ * - 有 manifest/READY 但无 op binding → TRUSTED_MANUAL_LOCAL
133
+ * - 有 READY + manifest + op/env/owner binding → TRUSTED_OPERATION_SNAPSHOT
134
+ * 绝不把「不可证明」当成 trusted。
135
+ */
136
+ export declare function validateSnapshotForRestore(snapshotDir: string, snapshotsRoot: string, env?: {
137
+ environmentFingerprint?: string;
138
+ }): Promise<RestoreSnapshotValidation>;
139
+ /**
140
+ * 生成恢复动作计划(dry-run 预览的唯一入口;零写入):
141
+ * 读快照 + 探测当前文件/已装插件状态,输出按执行顺序排列的动作清单。
142
+ */
143
+ export declare function planRestore(opts: RestoreOptions): Promise<RestorePlan>;
144
+ /**
145
+ * 执行恢复:按计划顺序 整文件还原 → 插件卸载 → file 补偿,
146
+ * 覆盖/删除前全部先复制到 <snapshotDir>/pre-restore/(双保险),
147
+ * 逐项 try/catch(单项失败不拖垮其余),输出诚实报告。
148
+ */
149
+ export declare function restore(opts: RestoreOptions): Promise<RestoreReport>;
150
+ /**
151
+ * 扫描快照根目录下每个子目录的 snapshot.json,返回按 createdAt 倒序的元信息。
152
+ * 目录缺失/条目损坏自动跳过(不阻断其他快照)。
153
+ */
154
+ export declare function listSnapshots(dir: string): Promise<SnapshotMeta[]>;
155
+ /** 快照 id 安全校验(防路径穿越):字母数字 + - _ .,长度 1-80。 */
156
+ export declare function isValidSnapshotId(id: unknown): id is string;
157
+ /** 删除单个快照(手动删除;P1-⑧)。只接受合法 id(防穿越),目标是快照根下的 <id>/ 目录。
158
+ * 不存在视为成功(幂等)。返回是否实际删除。 */
159
+ export declare function deleteSnapshot(snapshotsDir: string, id: string): Promise<boolean>;
160
+ /** 切换置顶状态(P1-⑧):置顶快照豁免自动保留清理。重写 <dir>/<id>/snapshot.json 的 pinned 字段。
161
+ * 只接受合法 id;快照不存在抛错。 */
162
+ export declare function setSnapshotPinned(snapshotsDir: string, id: string, pinned: boolean): Promise<boolean>;