@elie-laloum/outpost 1.1.4 → 3.0.0

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 (368) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +12 -6
  3. package/dist/adapters/agents/claude-diagnostics.d.ts +1 -0
  4. package/dist/adapters/agents/claude-diagnostics.js +7 -0
  5. package/dist/adapters/agents/claude-diagnostics.js.map +1 -0
  6. package/dist/adapters/agents/cli-diagnostics.constants.d.ts +12 -0
  7. package/dist/adapters/agents/cli-diagnostics.constants.js +24 -0
  8. package/dist/adapters/agents/cli-diagnostics.constants.js.map +1 -0
  9. package/dist/adapters/agents/cli-diagnostics.d.ts +4 -0
  10. package/dist/adapters/agents/cli-diagnostics.js +14 -0
  11. package/dist/adapters/agents/cli-diagnostics.js.map +1 -0
  12. package/dist/adapters/agents/cli-diagnostics.types.d.ts +13 -0
  13. package/dist/adapters/agents/cli-diagnostics.types.js +2 -0
  14. package/dist/adapters/agents/cli-diagnostics.types.js.map +1 -0
  15. package/dist/adapters/agents/codex-diagnostics.d.ts +1 -0
  16. package/dist/adapters/agents/codex-diagnostics.js +7 -0
  17. package/dist/adapters/agents/codex-diagnostics.js.map +1 -0
  18. package/dist/adapters/agents/protocol-fixtures.constants.d.ts +2 -0
  19. package/dist/adapters/agents/protocol-fixtures.constants.js +68 -0
  20. package/dist/adapters/agents/protocol-fixtures.constants.js.map +1 -0
  21. package/dist/adapters/agents/protocol-fixtures.types.d.ts +6 -0
  22. package/dist/adapters/agents/protocol-fixtures.types.js +2 -0
  23. package/dist/adapters/agents/protocol-fixtures.types.js.map +1 -0
  24. package/dist/application/diagnostic-probe.d.ts +3 -0
  25. package/dist/application/diagnostic-probe.js +55 -0
  26. package/dist/application/diagnostic-probe.js.map +1 -0
  27. package/dist/application/doctor-agent.constants.d.ts +9 -0
  28. package/dist/application/doctor-agent.constants.js +8 -0
  29. package/dist/application/doctor-agent.constants.js.map +1 -0
  30. package/dist/application/doctor-agent.d.ts +3 -0
  31. package/dist/application/doctor-agent.js +53 -0
  32. package/dist/application/doctor-agent.js.map +1 -0
  33. package/dist/application/doctor-image.d.ts +3 -0
  34. package/dist/application/doctor-image.js +133 -0
  35. package/dist/application/doctor-image.js.map +1 -0
  36. package/dist/application/doctor-protocol.d.ts +3 -0
  37. package/dist/application/doctor-protocol.js +29 -0
  38. package/dist/application/doctor-protocol.js.map +1 -0
  39. package/dist/application/doctor-protocol.types.d.ts +10 -0
  40. package/dist/application/doctor-protocol.types.js +2 -0
  41. package/dist/application/doctor-protocol.types.js.map +1 -0
  42. package/dist/application/doctor-sandbox.constants.d.ts +5 -0
  43. package/dist/application/doctor-sandbox.constants.js +6 -0
  44. package/dist/application/doctor-sandbox.constants.js.map +1 -0
  45. package/dist/application/doctor-sandbox.d.ts +4 -0
  46. package/dist/application/doctor-sandbox.js +133 -0
  47. package/dist/application/doctor-sandbox.js.map +1 -0
  48. package/dist/application/doctor-sandbox.types.d.ts +23 -0
  49. package/dist/application/doctor-sandbox.types.js +2 -0
  50. package/dist/application/doctor-sandbox.types.js.map +1 -0
  51. package/dist/application/doctor-transfers.constants.d.ts +6 -0
  52. package/dist/application/doctor-transfers.constants.js +7 -0
  53. package/dist/application/doctor-transfers.constants.js.map +1 -0
  54. package/dist/application/doctor-transfers.d.ts +4 -0
  55. package/dist/application/doctor-transfers.js +88 -0
  56. package/dist/application/doctor-transfers.js.map +1 -0
  57. package/dist/application/doctor.constants.d.ts +9 -0
  58. package/dist/application/doctor.constants.js +15 -0
  59. package/dist/application/doctor.constants.js.map +1 -0
  60. package/dist/application/doctor.d.ts +3 -0
  61. package/dist/application/doctor.js +106 -0
  62. package/dist/application/doctor.js.map +1 -0
  63. package/dist/application/doctor.types.d.ts +39 -0
  64. package/dist/application/doctor.types.js +2 -0
  65. package/dist/application/doctor.types.js.map +1 -0
  66. package/dist/application/execution.js +1 -0
  67. package/dist/application/execution.js.map +1 -1
  68. package/dist/application/outpost.types.d.ts +2 -0
  69. package/dist/application/outpost.types.js.map +1 -1
  70. package/dist/application/recovery-checksum-capture.d.ts +2 -0
  71. package/dist/application/recovery-checksum-capture.js +32 -0
  72. package/dist/application/recovery-checksum-capture.js.map +1 -0
  73. package/dist/application/recovery-checksum-paths.d.ts +2 -0
  74. package/dist/application/recovery-checksum-paths.js +11 -0
  75. package/dist/application/recovery-checksum-paths.js.map +1 -0
  76. package/dist/application/recovery-checksums.constants.d.ts +5 -0
  77. package/dist/application/recovery-checksums.constants.js +6 -0
  78. package/dist/application/recovery-checksums.constants.js.map +1 -0
  79. package/dist/application/recovery-checksums.d.ts +3 -0
  80. package/dist/application/recovery-checksums.js +94 -0
  81. package/dist/application/recovery-checksums.js.map +1 -0
  82. package/dist/application/recovery-checksums.types.d.ts +17 -0
  83. package/dist/application/recovery-checksums.types.js +2 -0
  84. package/dist/application/recovery-checksums.types.js.map +1 -0
  85. package/dist/application/recovery-inspection.d.ts +2 -0
  86. package/dist/application/recovery-inspection.js +27 -0
  87. package/dist/application/recovery-inspection.js.map +1 -0
  88. package/dist/application/recovery-inspection.types.d.ts +15 -0
  89. package/dist/application/recovery-inspection.types.js +2 -0
  90. package/dist/application/recovery-inspection.types.js.map +1 -0
  91. package/dist/application/recovery-restorability.d.ts +2 -0
  92. package/dist/application/recovery-restorability.js +137 -0
  93. package/dist/application/recovery-restorability.js.map +1 -0
  94. package/dist/application/recovery-retention-policy.d.ts +2 -0
  95. package/dist/application/recovery-retention-policy.js +21 -0
  96. package/dist/application/recovery-retention-policy.js.map +1 -0
  97. package/dist/application/recovery-retention.constants.d.ts +3 -0
  98. package/dist/application/recovery-retention.constants.js +2 -0
  99. package/dist/application/recovery-retention.constants.js.map +1 -0
  100. package/dist/application/recovery-retention.d.ts +4 -0
  101. package/dist/application/recovery-retention.js +202 -0
  102. package/dist/application/recovery-retention.js.map +1 -0
  103. package/dist/application/recovery-retention.types.d.ts +52 -0
  104. package/dist/application/recovery-retention.types.js +2 -0
  105. package/dist/application/recovery-retention.types.js.map +1 -0
  106. package/dist/application/recovery-verification.constants.d.ts +5 -0
  107. package/dist/application/recovery-verification.constants.js +10 -0
  108. package/dist/application/recovery-verification.constants.js.map +1 -0
  109. package/dist/application/recovery-verification.d.ts +2 -0
  110. package/dist/application/recovery-verification.js +107 -0
  111. package/dist/application/recovery-verification.js.map +1 -0
  112. package/dist/application/recovery-verification.types.d.ts +26 -0
  113. package/dist/application/recovery-verification.types.js +2 -0
  114. package/dist/application/recovery-verification.types.js.map +1 -0
  115. package/dist/application/remote-apply.js +24 -6
  116. package/dist/application/remote-apply.js.map +1 -1
  117. package/dist/application/remote-backup.js +6 -2
  118. package/dist/application/remote-backup.js.map +1 -1
  119. package/dist/application/remote-download.js +36 -4
  120. package/dist/application/remote-download.js.map +1 -1
  121. package/dist/application/remote-validation.js +19 -0
  122. package/dist/application/remote-validation.js.map +1 -1
  123. package/dist/application/remote-workspace.constants.d.ts +3 -0
  124. package/dist/application/remote-workspace.constants.js +2 -0
  125. package/dist/application/remote-workspace.constants.js.map +1 -0
  126. package/dist/application/remote-workspace.js +6 -0
  127. package/dist/application/remote-workspace.js.map +1 -1
  128. package/dist/application/remote-workspace.types.d.ts +3 -1
  129. package/dist/application/remote-workspace.types.js.map +1 -1
  130. package/dist/application/sandbox.js +11 -0
  131. package/dist/application/sandbox.js.map +1 -1
  132. package/dist/application/task-usage.d.ts +4 -0
  133. package/dist/application/task-usage.js +41 -0
  134. package/dist/application/task-usage.js.map +1 -0
  135. package/dist/application/task-usage.types.d.ts +5 -0
  136. package/dist/application/task-usage.types.js +2 -0
  137. package/dist/application/task-usage.types.js.map +1 -0
  138. package/dist/application/tasks.js +23 -2
  139. package/dist/application/tasks.js.map +1 -1
  140. package/dist/cli/commands.js +4 -0
  141. package/dist/cli/commands.js.map +1 -1
  142. package/dist/cli/doctor-command.d.ts +3 -0
  143. package/dist/cli/doctor-command.js +38 -0
  144. package/dist/cli/doctor-command.js.map +1 -0
  145. package/dist/cli/image.js +1 -1
  146. package/dist/cli/image.js.map +1 -1
  147. package/dist/cli/init-command.js +4 -3
  148. package/dist/cli/init-command.js.map +1 -1
  149. package/dist/cli/main.constants.d.ts +29 -8
  150. package/dist/cli/main.constants.js +19 -7
  151. package/dist/cli/main.constants.js.map +1 -1
  152. package/dist/cli/project-settings.js +8 -2
  153. package/dist/cli/project-settings.js.map +1 -1
  154. package/dist/cli/recovery-command.d.ts +2 -0
  155. package/dist/cli/recovery-command.js +69 -0
  156. package/dist/cli/recovery-command.js.map +1 -0
  157. package/dist/cli/recovery-prune-command.d.ts +2 -0
  158. package/dist/cli/recovery-prune-command.js +34 -0
  159. package/dist/cli/recovery-prune-command.js.map +1 -0
  160. package/dist/cli/recovery-verify-command.d.ts +2 -0
  161. package/dist/cli/recovery-verify-command.js +43 -0
  162. package/dist/cli/recovery-verify-command.js.map +1 -0
  163. package/dist/cli/scaffold-files.d.ts +1 -1
  164. package/dist/cli/scaffold-files.js +26 -20
  165. package/dist/cli/scaffold-files.js.map +1 -1
  166. package/dist/cli/scaffold-validation.js +2 -5
  167. package/dist/cli/scaffold-validation.js.map +1 -1
  168. package/dist/cli/scaffold.constants.d.ts +0 -2
  169. package/dist/cli/scaffold.constants.js +1 -13
  170. package/dist/cli/scaffold.constants.js.map +1 -1
  171. package/dist/cli/scaffold.d.ts +1 -1
  172. package/dist/cli/scaffold.js +37 -29
  173. package/dist/cli/scaffold.js.map +1 -1
  174. package/dist/cli/scaffold.types.d.ts +3 -5
  175. package/dist/cli/scaffold.types.js.map +1 -1
  176. package/dist/cli/starters.d.ts +1 -2
  177. package/dist/cli/starters.js +42 -30
  178. package/dist/cli/starters.js.map +1 -1
  179. package/dist/domain/ports.d.ts +1 -1
  180. package/dist/domain/ports.js.map +1 -1
  181. package/dist/domain/sandbox.types.d.ts +12 -0
  182. package/dist/domain/sandbox.types.js.map +1 -1
  183. package/dist/domain/workflow/budget.constants.d.ts +1 -0
  184. package/dist/domain/workflow/budget.constants.js +7 -0
  185. package/dist/domain/workflow/budget.constants.js.map +1 -0
  186. package/dist/domain/workflow/budget.d.ts +9 -0
  187. package/dist/domain/workflow/budget.js +67 -0
  188. package/dist/domain/workflow/budget.js.map +1 -0
  189. package/dist/domain/workflow/budget.types.d.ts +15 -0
  190. package/dist/domain/workflow/budget.types.js +2 -0
  191. package/dist/domain/workflow/budget.types.js.map +1 -0
  192. package/dist/domain/workflow/task-runner.js +8 -1
  193. package/dist/domain/workflow/task-runner.js.map +1 -1
  194. package/dist/domain/workflow/workflow-scheduler.js +4 -2
  195. package/dist/domain/workflow/workflow-scheduler.js.map +1 -1
  196. package/dist/domain/workflow/workflow-state.js +29 -0
  197. package/dist/domain/workflow/workflow-state.js.map +1 -1
  198. package/dist/domain/workflow.d.ts +2 -0
  199. package/dist/domain/workflow.js +1 -0
  200. package/dist/domain/workflow.js.map +1 -1
  201. package/dist/domain/workflow.types.d.ts +10 -1
  202. package/dist/domain/workflow.types.js.map +1 -1
  203. package/dist/index.d.ts +12 -8
  204. package/dist/index.js +5 -3
  205. package/dist/index.js.map +1 -1
  206. package/dist/infrastructure/file-manifest.d.ts +6 -0
  207. package/dist/infrastructure/file-manifest.js +91 -0
  208. package/dist/infrastructure/file-manifest.js.map +1 -0
  209. package/dist/infrastructure/git/lock-inspection.constants.d.ts +4 -0
  210. package/dist/infrastructure/git/lock-inspection.constants.js +5 -0
  211. package/dist/infrastructure/git/lock-inspection.constants.js.map +1 -0
  212. package/dist/infrastructure/git/lock-inspection.d.ts +3 -0
  213. package/dist/infrastructure/git/lock-inspection.js +64 -0
  214. package/dist/infrastructure/git/lock-inspection.js.map +1 -0
  215. package/dist/infrastructure/git/lock-inspection.types.d.ts +27 -0
  216. package/dist/infrastructure/git/lock-inspection.types.js +2 -0
  217. package/dist/infrastructure/git/lock-inspection.types.js.map +1 -0
  218. package/dist/infrastructure/git/lock.d.ts +1 -0
  219. package/dist/infrastructure/git/lock.js +70 -29
  220. package/dist/infrastructure/git/lock.js.map +1 -1
  221. package/dist/infrastructure/git/process-identity.d.ts +3 -0
  222. package/dist/infrastructure/git/process-identity.js +73 -0
  223. package/dist/infrastructure/git/process-identity.js.map +1 -0
  224. package/dist/infrastructure/git/process-identity.types.d.ts +10 -0
  225. package/dist/infrastructure/git/process-identity.types.js +2 -0
  226. package/dist/infrastructure/git/process-identity.types.js.map +1 -0
  227. package/dist/infrastructure/git/verification-command.d.ts +1 -0
  228. package/dist/infrastructure/git/verification-command.js +36 -0
  229. package/dist/infrastructure/git/verification-command.js.map +1 -0
  230. package/dist/infrastructure/git/workspace-lease.js +1 -0
  231. package/dist/infrastructure/git/workspace-lease.js.map +1 -1
  232. package/dist/infrastructure/git/workspace.js +5 -1
  233. package/dist/infrastructure/git/workspace.js.map +1 -1
  234. package/dist/infrastructure/git/worktree-inspection.constants.d.ts +5 -0
  235. package/dist/infrastructure/git/worktree-inspection.constants.js +16 -0
  236. package/dist/infrastructure/git/worktree-inspection.constants.js.map +1 -0
  237. package/dist/infrastructure/git/worktree-inspection.d.ts +3 -0
  238. package/dist/infrastructure/git/worktree-inspection.js +120 -0
  239. package/dist/infrastructure/git/worktree-inspection.js.map +1 -0
  240. package/dist/infrastructure/git/worktree-inspection.types.d.ts +19 -0
  241. package/dist/infrastructure/git/worktree-inspection.types.js +2 -0
  242. package/dist/infrastructure/git/worktree-inspection.types.js.map +1 -0
  243. package/dist/infrastructure/inspection-file.constants.d.ts +1 -0
  244. package/dist/infrastructure/inspection-file.constants.js +5 -0
  245. package/dist/infrastructure/inspection-file.constants.js.map +1 -0
  246. package/dist/infrastructure/inspection-file.d.ts +1 -0
  247. package/dist/infrastructure/inspection-file.js +43 -0
  248. package/dist/infrastructure/inspection-file.js.map +1 -0
  249. package/dist/infrastructure/inspection-hash.constants.d.ts +1 -0
  250. package/dist/infrastructure/inspection-hash.constants.js +2 -0
  251. package/dist/infrastructure/inspection-hash.constants.js.map +1 -0
  252. package/dist/infrastructure/inspection-hash.d.ts +2 -0
  253. package/dist/infrastructure/inspection-hash.js +62 -0
  254. package/dist/infrastructure/inspection-hash.js.map +1 -0
  255. package/dist/infrastructure/inspection-hash.types.d.ts +5 -0
  256. package/dist/infrastructure/inspection-hash.types.js +2 -0
  257. package/dist/infrastructure/inspection-hash.types.js.map +1 -0
  258. package/dist/infrastructure/journal-retention.constants.d.ts +4 -0
  259. package/dist/infrastructure/journal-retention.constants.js +5 -0
  260. package/dist/infrastructure/journal-retention.constants.js.map +1 -0
  261. package/dist/infrastructure/journal-retention.d.ts +2 -0
  262. package/dist/infrastructure/journal-retention.js +51 -0
  263. package/dist/infrastructure/journal-retention.js.map +1 -0
  264. package/dist/infrastructure/journal.js +37 -13
  265. package/dist/infrastructure/journal.js.map +1 -1
  266. package/dist/infrastructure/opentelemetry.constants.d.ts +10 -0
  267. package/dist/infrastructure/opentelemetry.constants.js +11 -0
  268. package/dist/infrastructure/opentelemetry.constants.js.map +1 -0
  269. package/dist/infrastructure/opentelemetry.d.ts +3 -0
  270. package/dist/infrastructure/opentelemetry.js +131 -0
  271. package/dist/infrastructure/opentelemetry.js.map +1 -0
  272. package/dist/infrastructure/opentelemetry.types.d.ts +21 -0
  273. package/dist/infrastructure/opentelemetry.types.js +2 -0
  274. package/dist/infrastructure/opentelemetry.types.js.map +1 -0
  275. package/dist/infrastructure/storage-inventory.constants.d.ts +6 -0
  276. package/dist/infrastructure/storage-inventory.constants.js +11 -0
  277. package/dist/infrastructure/storage-inventory.constants.js.map +1 -0
  278. package/dist/infrastructure/storage-inventory.d.ts +2 -0
  279. package/dist/infrastructure/storage-inventory.js +123 -0
  280. package/dist/infrastructure/storage-inventory.js.map +1 -0
  281. package/dist/infrastructure/storage-inventory.types.d.ts +38 -0
  282. package/dist/infrastructure/storage-inventory.types.js +2 -0
  283. package/dist/infrastructure/storage-inventory.types.js.map +1 -0
  284. package/dist/infrastructure/transfer.js +12 -0
  285. package/dist/infrastructure/transfer.js.map +1 -1
  286. package/dist/providers/cloud.constants.d.ts +1 -0
  287. package/dist/providers/cloud.constants.js +1 -0
  288. package/dist/providers/cloud.constants.js.map +1 -1
  289. package/dist/providers/container-cache.constants.d.ts +11 -0
  290. package/dist/providers/container-cache.constants.js +9 -0
  291. package/dist/providers/container-cache.constants.js.map +1 -0
  292. package/dist/providers/container-cache.d.ts +4 -0
  293. package/dist/providers/container-cache.js +48 -0
  294. package/dist/providers/container-cache.js.map +1 -0
  295. package/dist/providers/container-cache.types.d.ts +8 -0
  296. package/dist/providers/container-cache.types.js +2 -0
  297. package/dist/providers/container-cache.types.js.map +1 -0
  298. package/dist/providers/container-plan.js +3 -1
  299. package/dist/providers/container-plan.js.map +1 -1
  300. package/dist/providers/container.js +31 -1
  301. package/dist/providers/container.js.map +1 -1
  302. package/dist/providers/container.types.d.ts +2 -0
  303. package/dist/providers/container.types.js.map +1 -1
  304. package/dist/providers/daytona-command.js +7 -4
  305. package/dist/providers/daytona-command.js.map +1 -1
  306. package/dist/providers/daytona.js +13 -4
  307. package/dist/providers/daytona.js.map +1 -1
  308. package/dist/providers/diagnostic-container.d.ts +4 -0
  309. package/dist/providers/diagnostic-container.js +15 -0
  310. package/dist/providers/diagnostic-container.js.map +1 -0
  311. package/dist/providers/docker.d.ts +1 -0
  312. package/dist/providers/docker.js.map +1 -1
  313. package/dist/providers/file-batches.constants.d.ts +10 -0
  314. package/dist/providers/file-batches.constants.js +57 -0
  315. package/dist/providers/file-batches.constants.js.map +1 -0
  316. package/dist/providers/file-batches.d.ts +2 -0
  317. package/dist/providers/file-batches.js +151 -0
  318. package/dist/providers/file-batches.js.map +1 -0
  319. package/dist/providers/podman.d.ts +1 -0
  320. package/dist/providers/podman.js.map +1 -1
  321. package/dist/providers/vercel.js +3 -1
  322. package/dist/providers/vercel.js.map +1 -1
  323. package/package.json +12 -1
  324. package/dist/adapters/backlogs/backlog.constants.d.ts +0 -4
  325. package/dist/adapters/backlogs/backlog.constants.js +0 -5
  326. package/dist/adapters/backlogs/backlog.constants.js.map +0 -1
  327. package/dist/adapters/backlogs/beads-adapter.d.ts +0 -4
  328. package/dist/adapters/backlogs/beads-adapter.js +0 -31
  329. package/dist/adapters/backlogs/beads-adapter.js.map +0 -1
  330. package/dist/adapters/backlogs/cli.d.ts +0 -3
  331. package/dist/adapters/backlogs/cli.js +0 -20
  332. package/dist/adapters/backlogs/cli.js.map +0 -1
  333. package/dist/adapters/backlogs/github-adapter.d.ts +0 -4
  334. package/dist/adapters/backlogs/github-adapter.js +0 -28
  335. package/dist/adapters/backlogs/github-adapter.js.map +0 -1
  336. package/dist/adapters/backlogs/issue.d.ts +0 -2
  337. package/dist/adapters/backlogs/issue.js +0 -13
  338. package/dist/adapters/backlogs/issue.js.map +0 -1
  339. package/dist/adapters/backlogs/settings.types.d.ts +0 -5
  340. package/dist/adapters/backlogs/settings.types.js +0 -2
  341. package/dist/adapters/backlogs/settings.types.js.map +0 -1
  342. package/dist/application/campaign-integration.d.ts +0 -2
  343. package/dist/application/campaign-integration.js +0 -44
  344. package/dist/application/campaign-integration.js.map +0 -1
  345. package/dist/application/campaign-planner.d.ts +0 -3
  346. package/dist/application/campaign-planner.js +0 -35
  347. package/dist/application/campaign-planner.js.map +0 -1
  348. package/dist/application/campaign-worker.d.ts +0 -3
  349. package/dist/application/campaign-worker.js +0 -54
  350. package/dist/application/campaign-worker.js.map +0 -1
  351. package/dist/application/campaign.constants.d.ts +0 -7
  352. package/dist/application/campaign.constants.js +0 -8
  353. package/dist/application/campaign.constants.js.map +0 -1
  354. package/dist/application/campaign.d.ts +0 -3
  355. package/dist/application/campaign.js +0 -96
  356. package/dist/application/campaign.js.map +0 -1
  357. package/dist/application/campaign.types.d.ts +0 -43
  358. package/dist/application/campaign.types.js +0 -2
  359. package/dist/application/campaign.types.js.map +0 -1
  360. package/dist/domain/backlog.d.ts +0 -3
  361. package/dist/domain/backlog.js +0 -23
  362. package/dist/domain/backlog.js.map +0 -1
  363. package/dist/domain/backlog.types.d.ts +0 -15
  364. package/dist/domain/backlog.types.js +0 -2
  365. package/dist/domain/backlog.types.js.map +0 -1
  366. package/dist/providers/backlogs.d.ts +0 -3
  367. package/dist/providers/backlogs.js +0 -3
  368. package/dist/providers/backlogs.js.map +0 -1
@@ -0,0 +1,137 @@
1
+ import { copyFile, lstat, mkdir, mkdtemp, realpath, rm, } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { directory, safeDestination } from "../infrastructure/files.js";
5
+ import { verificationGit as git } from "../infrastructure/git/verification-command.js";
6
+ import { hashInspectionEntry } from "../infrastructure/inspection-hash.js";
7
+ import { recoveryTransferPatches } from "./recovery-verification.constants.js";
8
+ export async function verifyRecoveryRestorability(root, state, repository) {
9
+ const temporary = await realpath(await mkdtemp(join(tmpdir(), "outpost-restore-check-")));
10
+ const checks = [];
11
+ let stage = "REPOSITORY_OBJECTS";
12
+ try {
13
+ const source = await directory(repository);
14
+ stage = "SOURCE_UNSUPPORTED";
15
+ const common = (await git(source, [
16
+ "rev-parse",
17
+ "--path-format=absolute",
18
+ "--git-common-dir",
19
+ ])).trim();
20
+ const configuration = await git(source, [
21
+ "config",
22
+ "--local",
23
+ "--null",
24
+ "--list",
25
+ ]);
26
+ if (/(?:extensions\.partialclone|remote\.[^\n\0]+\.(?:promisor|partialclonefilter))\n/i.test(configuration))
27
+ throw new Error("Partial repositories are unsupported");
28
+ for (const name of [
29
+ "shallow",
30
+ "objects/info/alternates",
31
+ "objects/info/http-alternates",
32
+ ]) {
33
+ const exists = await lstat(join(common, name)).then(() => true, (error) => {
34
+ if (error &&
35
+ typeof error === "object" &&
36
+ "code" in error &&
37
+ error.code === "ENOENT")
38
+ return false;
39
+ throw error;
40
+ });
41
+ if (exists)
42
+ throw new Error("Repository depends on external or shallow objects");
43
+ }
44
+ const checkout = join(temporary, "checkout");
45
+ const snapshot = join(temporary, "transfer");
46
+ await mkdir(snapshot);
47
+ for (const name of [
48
+ ...recoveryTransferPatches,
49
+ ...(state.previous !== state.next ? ["commits.bundle"] : []),
50
+ ]) {
51
+ stage = "TRANSFER_SNAPSHOT";
52
+ const path = await safeDestination(root, name);
53
+ const before = await hashInspectionEntry(path);
54
+ if (before.kind !== "file")
55
+ throw new Error("Expected a regular transfer file");
56
+ await copyFile(path, join(snapshot, name));
57
+ const copied = await hashInspectionEntry(join(snapshot, name));
58
+ if (before.sha256 !== copied.sha256 || before.bytes !== copied.bytes)
59
+ throw new Error("Transfer changed while copying");
60
+ }
61
+ stage = "REPOSITORY_OBJECTS";
62
+ await git(temporary, [
63
+ "clone",
64
+ "--local",
65
+ "--no-hardlinks",
66
+ "--no-checkout",
67
+ "--",
68
+ source,
69
+ checkout,
70
+ ]);
71
+ if (state.previous !== state.next) {
72
+ stage = "BUNDLE_OBJECTS";
73
+ await git(checkout, [
74
+ "bundle",
75
+ "verify",
76
+ join(snapshot, "commits.bundle"),
77
+ ]);
78
+ await git(checkout, [
79
+ "bundle",
80
+ "unbundle",
81
+ join(snapshot, "commits.bundle"),
82
+ ]);
83
+ checks.push({
84
+ path: join(root, "commits.bundle"),
85
+ status: "pass",
86
+ code: "BUNDLE_OBJECTS_VALID",
87
+ });
88
+ }
89
+ stage = "COMMIT_OBJECTS";
90
+ await git(checkout, ["cat-file", "-e", `${state.previous}^{commit}`]);
91
+ await git(checkout, ["cat-file", "-e", `${state.next}^{commit}`]);
92
+ await git(checkout, [
93
+ "fsck",
94
+ "--strict",
95
+ "--no-reflogs",
96
+ state.previous,
97
+ state.next,
98
+ ]);
99
+ checks.push({ path: root, status: "pass", code: "COMMIT_OBJECTS_VALID" });
100
+ for (const [name, commit, cached] of [
101
+ ["previous.patch", state.previous, false],
102
+ ["previous-index.patch", state.previous, true],
103
+ ["remote.patch", state.next, false],
104
+ ]) {
105
+ stage = `PATCH_${name.toUpperCase().replaceAll(/[.-]/g, "_")}`;
106
+ await git(checkout, ["reset", "--hard", commit]);
107
+ const patch = join(snapshot, name);
108
+ if ((await lstat(patch)).size) {
109
+ const args = [
110
+ "apply",
111
+ "--binary",
112
+ ...(cached ? ["--cached"] : []),
113
+ patch,
114
+ ];
115
+ await git(checkout, [...args.slice(0, -1), "--check", patch]);
116
+ await git(checkout, args);
117
+ }
118
+ checks.push({
119
+ path: join(root, name),
120
+ status: "pass",
121
+ code: "PATCH_APPLIES",
122
+ });
123
+ }
124
+ }
125
+ catch {
126
+ checks.push({
127
+ path: root,
128
+ status: "fail",
129
+ code: stage === "SOURCE_UNSUPPORTED" ? stage : `${stage}_FAILED`,
130
+ });
131
+ }
132
+ finally {
133
+ await rm(temporary, { recursive: true, force: true, maxRetries: 3 });
134
+ }
135
+ return checks;
136
+ }
137
+ //# sourceMappingURL=recovery-restorability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-restorability.js","sourceRoot":"","sources":["../../src/application/recovery-restorability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,EAAE,GACH,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,eAAe,IAAI,GAAG,EAAE,MAAM,+CAA+C,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAM/E,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,IAAY,EACZ,KAA4B,EAC5B,UAAkB;IAElB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAC9B,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CACxD,CAAC;IACF,MAAM,MAAM,GAA6B,EAAE,CAAC;IAC5C,IAAI,KAAK,GAAG,oBAAoB,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;QAC3C,KAAK,GAAG,oBAAoB,CAAC;QAC7B,MAAM,MAAM,GAAG,CACb,MAAM,GAAG,CAAC,MAAM,EAAE;YAChB,WAAW;YACX,wBAAwB;YACxB,kBAAkB;SACnB,CAAC,CACH,CAAC,IAAI,EAAE,CAAC;QACT,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE;YACtC,QAAQ;YACR,SAAS;YACT,QAAQ;YACR,QAAQ;SACT,CAAC,CAAC;QACH,IACE,mFAAmF,CAAC,IAAI,CACtF,aAAa,CACd;YAED,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,KAAK,MAAM,IAAI,IAAI;YACjB,SAAS;YACT,yBAAyB;YACzB,8BAA8B;SAC/B,EAAE,CAAC;YACF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CACjD,GAAG,EAAE,CAAC,IAAI,EACV,CAAC,KAAc,EAAE,EAAE;gBACjB,IACE,KAAK;oBACL,OAAO,KAAK,KAAK,QAAQ;oBACzB,MAAM,IAAI,KAAK;oBACf,KAAK,CAAC,IAAI,KAAK,QAAQ;oBAEvB,OAAO,KAAK,CAAC;gBACf,MAAM,KAAK,CAAC;YACd,CAAC,CACF,CAAC;YACF,IAAI,MAAM;gBACR,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI;YACjB,GAAG,uBAAuB;YAC1B,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7D,EAAE,CAAC;YACF,KAAK,GAAG,mBAAmB,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;gBACxB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACtD,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK;gBAClE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACtD,CAAC;QACD,KAAK,GAAG,oBAAoB,CAAC;QAC7B,MAAM,GAAG,CAAC,SAAS,EAAE;YACnB,OAAO;YACP,SAAS;YACT,gBAAgB;YAChB,eAAe;YACf,IAAI;YACJ,MAAM;YACN,QAAQ;SACT,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAClC,KAAK,GAAG,gBAAgB,CAAC;YACzB,MAAM,GAAG,CAAC,QAAQ,EAAE;gBAClB,QAAQ;gBACR,QAAQ;gBACR,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC;aACjC,CAAC,CAAC;YACH,MAAM,GAAG,CAAC,QAAQ,EAAE;gBAClB,QAAQ;gBACR,UAAU;gBACV,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC;aACjC,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC;gBAClC,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,sBAAsB;aAC7B,CAAC,CAAC;QACL,CAAC;QACD,KAAK,GAAG,gBAAgB,CAAC;QACzB,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,QAAQ,WAAW,CAAC,CAAC,CAAC;QACtE,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC;QAClE,MAAM,GAAG,CAAC,QAAQ,EAAE;YAClB,MAAM;YACN,UAAU;YACV,cAAc;YACd,KAAK,CAAC,QAAQ;YACd,KAAK,CAAC,IAAI;SACX,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI;YACnC,CAAC,gBAAgB,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC;YACzC,CAAC,sBAAsB,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC;YAC9C,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;SAC3B,EAAE,CAAC;YACX,KAAK,GAAG,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;YAC/D,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG;oBACX,OAAO;oBACP,UAAU;oBACV,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/B,KAAK;iBACN,CAAC;gBACF,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;gBAC9D,MAAM,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;YACD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;gBACtB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,eAAe;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,KAAK,KAAK,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS;SACjE,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import {\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n realpath,\n rm,\n} from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { directory, safeDestination } from \"../infrastructure/files.ts\";\nimport { verificationGit as git } from \"../infrastructure/git/verification-command.ts\";\nimport { hashInspectionEntry } from \"../infrastructure/inspection-hash.ts\";\nimport { recoveryTransferPatches } from \"./recovery-verification.constants.ts\";\nimport type {\n RecoveryStructureCheck,\n RecoveryTransferState,\n} from \"./recovery-verification.types.ts\";\n\nexport async function verifyRecoveryRestorability(\n root: string,\n state: RecoveryTransferState,\n repository: string,\n): Promise<readonly RecoveryStructureCheck[]> {\n const temporary = await realpath(\n await mkdtemp(join(tmpdir(), \"outpost-restore-check-\")),\n );\n const checks: RecoveryStructureCheck[] = [];\n let stage = \"REPOSITORY_OBJECTS\";\n try {\n const source = await directory(repository);\n stage = \"SOURCE_UNSUPPORTED\";\n const common = (\n await git(source, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--git-common-dir\",\n ])\n ).trim();\n const configuration = await git(source, [\n \"config\",\n \"--local\",\n \"--null\",\n \"--list\",\n ]);\n if (\n /(?:extensions\\.partialclone|remote\\.[^\\n\\0]+\\.(?:promisor|partialclonefilter))\\n/i.test(\n configuration,\n )\n )\n throw new Error(\"Partial repositories are unsupported\");\n for (const name of [\n \"shallow\",\n \"objects/info/alternates\",\n \"objects/info/http-alternates\",\n ]) {\n const exists = await lstat(join(common, name)).then(\n () => true,\n (error: unknown) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n )\n return false;\n throw error;\n },\n );\n if (exists)\n throw new Error(\"Repository depends on external or shallow objects\");\n }\n const checkout = join(temporary, \"checkout\");\n const snapshot = join(temporary, \"transfer\");\n await mkdir(snapshot);\n for (const name of [\n ...recoveryTransferPatches,\n ...(state.previous !== state.next ? [\"commits.bundle\"] : []),\n ]) {\n stage = \"TRANSFER_SNAPSHOT\";\n const path = await safeDestination(root, name);\n const before = await hashInspectionEntry(path);\n if (before.kind !== \"file\")\n throw new Error(\"Expected a regular transfer file\");\n await copyFile(path, join(snapshot, name));\n const copied = await hashInspectionEntry(join(snapshot, name));\n if (before.sha256 !== copied.sha256 || before.bytes !== copied.bytes)\n throw new Error(\"Transfer changed while copying\");\n }\n stage = \"REPOSITORY_OBJECTS\";\n await git(temporary, [\n \"clone\",\n \"--local\",\n \"--no-hardlinks\",\n \"--no-checkout\",\n \"--\",\n source,\n checkout,\n ]);\n if (state.previous !== state.next) {\n stage = \"BUNDLE_OBJECTS\";\n await git(checkout, [\n \"bundle\",\n \"verify\",\n join(snapshot, \"commits.bundle\"),\n ]);\n await git(checkout, [\n \"bundle\",\n \"unbundle\",\n join(snapshot, \"commits.bundle\"),\n ]);\n checks.push({\n path: join(root, \"commits.bundle\"),\n status: \"pass\",\n code: \"BUNDLE_OBJECTS_VALID\",\n });\n }\n stage = \"COMMIT_OBJECTS\";\n await git(checkout, [\"cat-file\", \"-e\", `${state.previous}^{commit}`]);\n await git(checkout, [\"cat-file\", \"-e\", `${state.next}^{commit}`]);\n await git(checkout, [\n \"fsck\",\n \"--strict\",\n \"--no-reflogs\",\n state.previous,\n state.next,\n ]);\n checks.push({ path: root, status: \"pass\", code: \"COMMIT_OBJECTS_VALID\" });\n for (const [name, commit, cached] of [\n [\"previous.patch\", state.previous, false],\n [\"previous-index.patch\", state.previous, true],\n [\"remote.patch\", state.next, false],\n ] as const) {\n stage = `PATCH_${name.toUpperCase().replaceAll(/[.-]/g, \"_\")}`;\n await git(checkout, [\"reset\", \"--hard\", commit]);\n const patch = join(snapshot, name);\n if ((await lstat(patch)).size) {\n const args = [\n \"apply\",\n \"--binary\",\n ...(cached ? [\"--cached\"] : []),\n patch,\n ];\n await git(checkout, [...args.slice(0, -1), \"--check\", patch]);\n await git(checkout, args);\n }\n checks.push({\n path: join(root, name),\n status: \"pass\",\n code: \"PATCH_APPLIES\",\n });\n }\n } catch {\n checks.push({\n path: root,\n status: \"fail\",\n code: stage === \"SOURCE_UNSUPPORTED\" ? stage : `${stage}_FAILED`,\n });\n } finally {\n await rm(temporary, { recursive: true, force: true, maxRetries: 3 });\n }\n return checks;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { RecoveryRetentionPolicy } from "./recovery-retention.types.ts";
2
+ export declare function retentionPolicy(value: unknown): asserts value is RecoveryRetentionPolicy;
@@ -0,0 +1,21 @@
1
+ import { invariant } from "../domain/errors.js";
2
+ export function retentionPolicy(value) {
3
+ invariant(!!value && typeof value === "object", "Retention policy must be an object");
4
+ invariant("version" in value && value.version === 1, "Retention policy version must be 1");
5
+ invariant("scopes" in value &&
6
+ Array.isArray(value.scopes) &&
7
+ value.scopes.length > 0 &&
8
+ value.scopes.every((scope) => scope === "clean-workspaces" || scope === "closed-logs"), "Retention scopes must contain clean-workspaces or closed-logs");
9
+ invariant("minAgeMs" in value &&
10
+ typeof value.minAgeMs === "number" &&
11
+ Number.isSafeInteger(value.minAgeMs) &&
12
+ value.minAgeMs >= 0, "minAgeMs must be a nonnegative integer");
13
+ for (const key of ["maxBytes", "maxWorkspaces"]) {
14
+ if (!(key in value))
15
+ continue;
16
+ const limit = Object.getOwnPropertyDescriptor(value, key)?.value;
17
+ invariant(typeof limit === "number" && Number.isSafeInteger(limit) && limit >= 0, `${key} must be a nonnegative integer`);
18
+ }
19
+ invariant(Object.keys(value).every((key) => ["version", "scopes", "minAgeMs", "maxBytes", "maxWorkspaces"].includes(key)), "Unsupported retention policy field");
20
+ }
21
+ //# sourceMappingURL=recovery-retention-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-retention-policy.js","sourceRoot":"","sources":["../../src/application/recovery-retention-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,MAAM,UAAU,eAAe,CAC7B,KAAc;IAEd,SAAS,CACP,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EACpC,oCAAoC,CACrC,CAAC;IACF,SAAS,CACP,SAAS,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,EACzC,oCAAoC,CACrC,CAAC;IACF,SAAS,CACP,QAAQ,IAAI,KAAK;QACf,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3B,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACvB,KAAK,CAAC,MAAM,CAAC,KAAK,CAChB,CAAC,KAAc,EAAE,EAAE,CACjB,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,aAAa,CAC1D,EACH,+DAA+D,CAChE,CAAC;IACF,SAAS,CACP,UAAU,IAAI,KAAK;QACjB,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAClC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC;QACpC,KAAK,CAAC,QAAQ,IAAI,CAAC,EACrB,wCAAwC,CACzC,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAU,EAAE,CAAC;QACzD,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC;YAAE,SAAS;QAC9B,MAAM,KAAK,GAAY,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC;QAC1E,SAAS,CACP,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EACtE,GAAG,GAAG,gCAAgC,CACvC,CAAC;IACJ,CAAC;IACD,SAAS,CACP,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC/B,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,QAAQ,CACrE,GAAG,CACJ,CACF,EACD,oCAAoC,CACrC,CAAC;AACJ,CAAC","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type { RecoveryRetentionPolicy } from \"./recovery-retention.types.ts\";\n\nexport function retentionPolicy(\n value: unknown,\n): asserts value is RecoveryRetentionPolicy {\n invariant(\n !!value && typeof value === \"object\",\n \"Retention policy must be an object\",\n );\n invariant(\n \"version\" in value && value.version === 1,\n \"Retention policy version must be 1\",\n );\n invariant(\n \"scopes\" in value &&\n Array.isArray(value.scopes) &&\n value.scopes.length > 0 &&\n value.scopes.every(\n (scope: unknown) =>\n scope === \"clean-workspaces\" || scope === \"closed-logs\",\n ),\n \"Retention scopes must contain clean-workspaces or closed-logs\",\n );\n invariant(\n \"minAgeMs\" in value &&\n typeof value.minAgeMs === \"number\" &&\n Number.isSafeInteger(value.minAgeMs) &&\n value.minAgeMs >= 0,\n \"minAgeMs must be a nonnegative integer\",\n );\n for (const key of [\"maxBytes\", \"maxWorkspaces\"] as const) {\n if (!(key in value)) continue;\n const limit: unknown = Object.getOwnPropertyDescriptor(value, key)?.value;\n invariant(\n typeof limit === \"number\" && Number.isSafeInteger(limit) && limit >= 0,\n `${key} must be a nonnegative integer`,\n );\n }\n invariant(\n Object.keys(value).every((key) =>\n [\"version\", \"scopes\", \"minAgeMs\", \"maxBytes\", \"maxWorkspaces\"].includes(\n key,\n ),\n ),\n \"Unsupported retention policy field\",\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const recoveryRetentionLimits: {
2
+ readonly maxPolicyBytes: 65536;
3
+ };
@@ -0,0 +1,2 @@
1
+ export const recoveryRetentionLimits = { maxPolicyBytes: 65_536 };
2
+ //# sourceMappingURL=recovery-retention.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-retention.constants.js","sourceRoot":"","sources":["../../src/application/recovery-retention.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,cAAc,EAAE,MAAM,EAAW,CAAC","sourcesContent":["export const recoveryRetentionLimits = { maxPolicyBytes: 65_536 } as const;\n"]}
@@ -0,0 +1,4 @@
1
+ import type { RecoveryPruneResult, RecoveryQuotaOptions, RecoveryRetentionOptions, RecoveryRetentionPlan } from "./recovery-retention.types.ts";
2
+ export declare function planRecoveryRetention(options: RecoveryRetentionOptions): Promise<RecoveryRetentionPlan>;
3
+ export declare function pruneRecoveryRetention(plan: RecoveryRetentionPlan): Promise<RecoveryPruneResult>;
4
+ export declare function assertRecoveryQuota(options: RecoveryQuotaOptions): Promise<void>;
@@ -0,0 +1,202 @@
1
+ import { lstat, rm } from "node:fs/promises";
2
+ import { OutpostError, invariant } from "../domain/errors.js";
3
+ import { git } from "../infrastructure/git/command.js";
4
+ import { lock, lockPath } from "../infrastructure/git/lock.js";
5
+ import { closedJournal } from "../infrastructure/journal-retention.js";
6
+ import { inspectRecovery } from "./recovery-inspection.js";
7
+ import { retentionPolicy } from "./recovery-retention-policy.js";
8
+ async function workspaceReason(entry, inspection) {
9
+ const workspace = inspection.git?.workspaces.find((value) => value.path === entry.path);
10
+ if (!workspace || workspace.state !== "registered")
11
+ return "WORKSPACE_UNREGISTERED_OR_UNKNOWN";
12
+ if (workspace.branch === null)
13
+ return "DETACHED_WORKSPACE";
14
+ if (workspace.dirty)
15
+ return "DIRTY_WORKSPACE";
16
+ if (workspace.locked)
17
+ return "GIT_LOCKED_WORKSPACE";
18
+ try {
19
+ await lstat(lockPath(inspection.repository, workspace.branch));
20
+ return "OPERATION_LOCK_PRESENT";
21
+ }
22
+ catch (error) {
23
+ if (!(error &&
24
+ typeof error === "object" &&
25
+ "code" in error &&
26
+ error.code === "ENOENT"))
27
+ return "OWNERSHIP_UNKNOWN";
28
+ }
29
+ try {
30
+ const ignored = await git(entry.path, [
31
+ "ls-files",
32
+ "--others",
33
+ "--ignored",
34
+ "--exclude-standard",
35
+ "-z",
36
+ ]);
37
+ if (ignored.length)
38
+ return "IGNORED_FILES";
39
+ }
40
+ catch {
41
+ return "WORKSPACE_STATE_UNKNOWN";
42
+ }
43
+ return "ELIGIBLE";
44
+ }
45
+ export async function planRecoveryRetention(options) {
46
+ retentionPolicy(options.policy);
47
+ const policy = structuredClone(options.policy);
48
+ const inspection = await inspectRecovery({
49
+ ...options,
50
+ git: true,
51
+ locks: true,
52
+ });
53
+ const entries = [];
54
+ const inspectedAt = new Date().toISOString();
55
+ const complete = inspection.complete && inspection.git?.complete !== false;
56
+ for (const category of inspection.categories) {
57
+ for (const entry of category.entries) {
58
+ let reason = "RECOVERY_DATA_PROTECTED";
59
+ if (category.name === "locks")
60
+ reason = "OWNERSHIP_RECORD_PROTECTED";
61
+ if (category.name === "logs")
62
+ reason = "LOG_ACTIVITY_OR_CONTENT_UNKNOWN";
63
+ if (category.name === "workspaces")
64
+ reason = "SCOPE_NOT_SELECTED";
65
+ if (category.name === "workspaces" &&
66
+ policy.scopes.includes("clean-workspaces"))
67
+ reason = await workspaceReason(entry, inspection);
68
+ if (category.name === "logs" &&
69
+ policy.scopes.includes("closed-logs") &&
70
+ entry.kind === "file" &&
71
+ entry.name.endsWith(".jsonl") &&
72
+ (await closedJournal(entry.path)))
73
+ reason = "ELIGIBLE";
74
+ if (category.name === "logs" && entry.name.endsWith(".closed.json"))
75
+ reason = "CLOSURE_RECORD_PROTECTED";
76
+ if (reason === "ELIGIBLE" &&
77
+ (!entry.modifiedAt ||
78
+ Date.parse(inspectedAt) - Date.parse(entry.modifiedAt) <
79
+ policy.minAgeMs))
80
+ reason = "RETENTION_AGE";
81
+ if (reason === "ELIGIBLE" && (!complete || !entry.complete))
82
+ reason = "INCOMPLETE_INVENTORY";
83
+ const workspace = inspection.git?.workspaces.find((value) => value.path === entry.path);
84
+ entries.push({
85
+ path: entry.path,
86
+ category: category.name,
87
+ bytes: entry.bytes,
88
+ eligible: reason === "ELIGIBLE",
89
+ reason,
90
+ ...(entry.modifiedAt ? { modifiedAt: entry.modifiedAt } : {}),
91
+ ...(workspace?.state === "registered" && workspace.branch
92
+ ? { branch: workspace.branch, head: workspace.head }
93
+ : {}),
94
+ });
95
+ }
96
+ }
97
+ const projectedBytes = inspection.usage.bytes -
98
+ entries
99
+ .filter((entry) => entry.eligible)
100
+ .reduce((sum, entry) => sum +
101
+ entry.bytes +
102
+ (entry.category === "logs"
103
+ ? (entries.find((other) => other.path === `${entry.path}.closed.json`)?.bytes ?? 0)
104
+ : 0), 0);
105
+ const workspaces = entries.filter((entry) => entry.category === "workspaces" && !entry.eligible).length;
106
+ const exceeded = (policy.maxBytes !== undefined && projectedBytes > policy.maxBytes) ||
107
+ (policy.maxWorkspaces !== undefined && workspaces > policy.maxWorkspaces);
108
+ return {
109
+ repository: inspection.repository,
110
+ policy,
111
+ inspectedAt,
112
+ inspection,
113
+ entries,
114
+ complete,
115
+ usageBytes: inspection.usage.bytes,
116
+ projectedBytes,
117
+ quota: !complete ? "unknown" : exceeded ? "exceeded" : "within",
118
+ };
119
+ }
120
+ export async function pruneRecoveryRetention(plan) {
121
+ retentionPolicy(plan.policy);
122
+ invariant(plan.complete, "Cannot prune an incomplete retention plan");
123
+ const removed = [];
124
+ const retained = [];
125
+ for (const candidate of plan.entries.filter((entry) => entry.eligible)) {
126
+ const fresh = await planRecoveryRetention({
127
+ repository: plan.repository,
128
+ policy: plan.policy,
129
+ });
130
+ const entry = fresh.entries.find((value) => value.path === candidate.path);
131
+ if (!entry?.eligible ||
132
+ entry.head !== candidate.head ||
133
+ entry.branch !== candidate.branch ||
134
+ entry.bytes !== candidate.bytes ||
135
+ entry.modifiedAt !== candidate.modifiedAt) {
136
+ retained.push({ path: candidate.path, reason: "PLAN_CHANGED" });
137
+ continue;
138
+ }
139
+ try {
140
+ if (entry.category === "workspaces" && entry.branch) {
141
+ const release = await lock(plan.repository, entry.branch);
142
+ try {
143
+ const status = await git(entry.path, [
144
+ "status",
145
+ "--porcelain=v1",
146
+ "--ignored",
147
+ "--untracked-files=all",
148
+ ]);
149
+ invariant(!status.length, "Workspace changed before pruning");
150
+ invariant((await git(entry.path, ["rev-parse", "HEAD"])).trim() ===
151
+ entry.head, "Workspace HEAD changed before pruning");
152
+ invariant((await git(entry.path, ["symbolic-ref", "--short", "HEAD"])).trim() === entry.branch, "Workspace branch changed before pruning");
153
+ await git(plan.repository, ["worktree", "remove", entry.path]);
154
+ }
155
+ finally {
156
+ await release();
157
+ }
158
+ }
159
+ else {
160
+ const release = await lock(plan.repository, `journal:${entry.path}`);
161
+ try {
162
+ invariant(entry.category === "logs" && (await closedJournal(entry.path)), "Journal changed before pruning");
163
+ await rm(entry.path);
164
+ await rm(`${entry.path}.closed.json`);
165
+ }
166
+ finally {
167
+ await release();
168
+ }
169
+ }
170
+ removed.push(entry.path);
171
+ }
172
+ catch {
173
+ retained.push({
174
+ path: candidate.path,
175
+ reason: "REVALIDATION_OR_REMOVAL_FAILED",
176
+ });
177
+ }
178
+ }
179
+ return {
180
+ removed,
181
+ retained,
182
+ after: await planRecoveryRetention({
183
+ repository: plan.repository,
184
+ policy: plan.policy,
185
+ }),
186
+ };
187
+ }
188
+ export async function assertRecoveryQuota(options) {
189
+ invariant(Number.isSafeInteger(options.maxBytes) && options.maxBytes >= 0, "maxBytes must be a nonnegative integer");
190
+ const reserve = options.reserveBytes ?? 0;
191
+ invariant(Number.isSafeInteger(reserve) && reserve >= 0, "reserveBytes must be a nonnegative integer");
192
+ const inspection = await inspectRecovery(options);
193
+ if (!inspection.complete ||
194
+ inspection.usage.bytes + reserve > options.maxBytes)
195
+ throw new OutpostError("workspace", "Outpost storage quota admission refused", {
196
+ usageBytes: inspection.usage.bytes,
197
+ reserveBytes: reserve,
198
+ maxBytes: options.maxBytes,
199
+ complete: inspection.complete,
200
+ });
201
+ }
202
+ //# sourceMappingURL=recovery-retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-retention.js","sourceRoot":"","sources":["../../src/application/recovery-retention.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, rm } from \"node:fs/promises\";\nimport { OutpostError, invariant } from \"../domain/errors.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { lock, lockPath } from \"../infrastructure/git/lock.ts\";\nimport { closedJournal } from \"../infrastructure/journal-retention.ts\";\nimport type { StorageEntry } from \"../infrastructure/storage-inventory.types.ts\";\nimport type { RecoveryInspection } from \"./recovery-inspection.types.ts\";\nimport { inspectRecovery } from \"./recovery-inspection.ts\";\nimport { retentionPolicy } from \"./recovery-retention-policy.ts\";\nimport type {\n RecoveryPruneResult,\n RecoveryRetainedEntry,\n RecoveryQuotaOptions,\n RecoveryRetentionEntry,\n RecoveryRetentionOptions,\n RecoveryRetentionPlan,\n} from \"./recovery-retention.types.ts\";\n\nasync function workspaceReason(\n entry: StorageEntry,\n inspection: RecoveryInspection,\n): Promise<string> {\n const workspace = inspection.git?.workspaces.find(\n (value) => value.path === entry.path,\n );\n if (!workspace || workspace.state !== \"registered\")\n return \"WORKSPACE_UNREGISTERED_OR_UNKNOWN\";\n if (workspace.branch === null) return \"DETACHED_WORKSPACE\";\n if (workspace.dirty) return \"DIRTY_WORKSPACE\";\n if (workspace.locked) return \"GIT_LOCKED_WORKSPACE\";\n try {\n await lstat(lockPath(inspection.repository, workspace.branch));\n return \"OPERATION_LOCK_PRESENT\";\n } catch (error) {\n if (!(\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ))\n return \"OWNERSHIP_UNKNOWN\";\n }\n try {\n const ignored = await git(entry.path, [\n \"ls-files\",\n \"--others\",\n \"--ignored\",\n \"--exclude-standard\",\n \"-z\",\n ]);\n if (ignored.length) return \"IGNORED_FILES\";\n } catch {\n return \"WORKSPACE_STATE_UNKNOWN\";\n }\n return \"ELIGIBLE\";\n}\n\nexport async function planRecoveryRetention(\n options: RecoveryRetentionOptions,\n): Promise<RecoveryRetentionPlan> {\n retentionPolicy(options.policy);\n const policy = structuredClone(options.policy);\n const inspection = await inspectRecovery({\n ...options,\n git: true,\n locks: true,\n });\n const entries: RecoveryRetentionEntry[] = [];\n const inspectedAt = new Date().toISOString();\n const complete = inspection.complete && inspection.git?.complete !== false;\n for (const category of inspection.categories) {\n for (const entry of category.entries) {\n let reason = \"RECOVERY_DATA_PROTECTED\";\n if (category.name === \"locks\") reason = \"OWNERSHIP_RECORD_PROTECTED\";\n if (category.name === \"logs\") reason = \"LOG_ACTIVITY_OR_CONTENT_UNKNOWN\";\n if (category.name === \"workspaces\") reason = \"SCOPE_NOT_SELECTED\";\n if (\n category.name === \"workspaces\" &&\n policy.scopes.includes(\"clean-workspaces\")\n )\n reason = await workspaceReason(entry, inspection);\n if (\n category.name === \"logs\" &&\n policy.scopes.includes(\"closed-logs\") &&\n entry.kind === \"file\" &&\n entry.name.endsWith(\".jsonl\") &&\n (await closedJournal(entry.path))\n )\n reason = \"ELIGIBLE\";\n if (category.name === \"logs\" && entry.name.endsWith(\".closed.json\"))\n reason = \"CLOSURE_RECORD_PROTECTED\";\n if (\n reason === \"ELIGIBLE\" &&\n (!entry.modifiedAt ||\n Date.parse(inspectedAt) - Date.parse(entry.modifiedAt) <\n policy.minAgeMs)\n )\n reason = \"RETENTION_AGE\";\n if (reason === \"ELIGIBLE\" && (!complete || !entry.complete))\n reason = \"INCOMPLETE_INVENTORY\";\n const workspace = inspection.git?.workspaces.find(\n (value) => value.path === entry.path,\n );\n entries.push({\n path: entry.path,\n category: category.name,\n bytes: entry.bytes,\n eligible: reason === \"ELIGIBLE\",\n reason,\n ...(entry.modifiedAt ? { modifiedAt: entry.modifiedAt } : {}),\n ...(workspace?.state === \"registered\" && workspace.branch\n ? { branch: workspace.branch, head: workspace.head }\n : {}),\n });\n }\n }\n const projectedBytes =\n inspection.usage.bytes -\n entries\n .filter((entry) => entry.eligible)\n .reduce(\n (sum, entry) =>\n sum +\n entry.bytes +\n (entry.category === \"logs\"\n ? (entries.find(\n (other) => other.path === `${entry.path}.closed.json`,\n )?.bytes ?? 0)\n : 0),\n 0,\n );\n const workspaces = entries.filter(\n (entry) => entry.category === \"workspaces\" && !entry.eligible,\n ).length;\n const exceeded =\n (policy.maxBytes !== undefined && projectedBytes > policy.maxBytes) ||\n (policy.maxWorkspaces !== undefined && workspaces > policy.maxWorkspaces);\n return {\n repository: inspection.repository,\n policy,\n inspectedAt,\n inspection,\n entries,\n complete,\n usageBytes: inspection.usage.bytes,\n projectedBytes,\n quota: !complete ? \"unknown\" : exceeded ? \"exceeded\" : \"within\",\n };\n}\n\nexport async function pruneRecoveryRetention(\n plan: RecoveryRetentionPlan,\n): Promise<RecoveryPruneResult> {\n retentionPolicy(plan.policy);\n invariant(plan.complete, \"Cannot prune an incomplete retention plan\");\n const removed: string[] = [];\n const retained: RecoveryRetainedEntry[] = [];\n for (const candidate of plan.entries.filter((entry) => entry.eligible)) {\n const fresh = await planRecoveryRetention({\n repository: plan.repository,\n policy: plan.policy,\n });\n const entry = fresh.entries.find((value) => value.path === candidate.path);\n if (\n !entry?.eligible ||\n entry.head !== candidate.head ||\n entry.branch !== candidate.branch ||\n entry.bytes !== candidate.bytes ||\n entry.modifiedAt !== candidate.modifiedAt\n ) {\n retained.push({ path: candidate.path, reason: \"PLAN_CHANGED\" });\n continue;\n }\n try {\n if (entry.category === \"workspaces\" && entry.branch) {\n const release = await lock(plan.repository, entry.branch);\n try {\n const status = await git(entry.path, [\n \"status\",\n \"--porcelain=v1\",\n \"--ignored\",\n \"--untracked-files=all\",\n ]);\n invariant(!status.length, \"Workspace changed before pruning\");\n invariant(\n (await git(entry.path, [\"rev-parse\", \"HEAD\"])).trim() ===\n entry.head,\n \"Workspace HEAD changed before pruning\",\n );\n invariant(\n (\n await git(entry.path, [\"symbolic-ref\", \"--short\", \"HEAD\"])\n ).trim() === entry.branch,\n \"Workspace branch changed before pruning\",\n );\n await git(plan.repository, [\"worktree\", \"remove\", entry.path]);\n } finally {\n await release();\n }\n } else {\n const release = await lock(plan.repository, `journal:${entry.path}`);\n try {\n invariant(\n entry.category === \"logs\" && (await closedJournal(entry.path)),\n \"Journal changed before pruning\",\n );\n await rm(entry.path);\n await rm(`${entry.path}.closed.json`);\n } finally {\n await release();\n }\n }\n removed.push(entry.path);\n } catch {\n retained.push({\n path: candidate.path,\n reason: \"REVALIDATION_OR_REMOVAL_FAILED\",\n });\n }\n }\n return {\n removed,\n retained,\n after: await planRecoveryRetention({\n repository: plan.repository,\n policy: plan.policy,\n }),\n };\n}\n\nexport async function assertRecoveryQuota(\n options: RecoveryQuotaOptions,\n): Promise<void> {\n invariant(\n Number.isSafeInteger(options.maxBytes) && options.maxBytes >= 0,\n \"maxBytes must be a nonnegative integer\",\n );\n const reserve = options.reserveBytes ?? 0;\n invariant(\n Number.isSafeInteger(reserve) && reserve >= 0,\n \"reserveBytes must be a nonnegative integer\",\n );\n const inspection = await inspectRecovery(options);\n if (\n !inspection.complete ||\n inspection.usage.bytes + reserve > options.maxBytes\n )\n throw new OutpostError(\n \"workspace\",\n \"Outpost storage quota admission refused\",\n {\n usageBytes: inspection.usage.bytes,\n reserveBytes: reserve,\n maxBytes: options.maxBytes,\n complete: inspection.complete,\n },\n );\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import type { RecoveryInspection } from "./recovery-inspection.types.ts";
2
+ export interface RecoveryRetentionPolicy {
3
+ readonly version: 1;
4
+ readonly scopes: readonly ("clean-workspaces" | "closed-logs")[];
5
+ readonly minAgeMs: number;
6
+ readonly maxBytes?: number;
7
+ readonly maxWorkspaces?: number;
8
+ }
9
+ export interface RecoveryRetentionOptions {
10
+ readonly repository?: string;
11
+ readonly policy: RecoveryRetentionPolicy;
12
+ readonly maxEntries?: number;
13
+ }
14
+ export interface RecoveryRetentionEntry {
15
+ readonly path: string;
16
+ readonly category: string;
17
+ readonly bytes: number;
18
+ readonly eligible: boolean;
19
+ readonly reason: string;
20
+ readonly branch?: string;
21
+ readonly head?: string;
22
+ readonly modifiedAt?: string;
23
+ }
24
+ export interface RecoveryRetentionPlan {
25
+ readonly repository: string;
26
+ readonly policy: RecoveryRetentionPolicy;
27
+ readonly inspectedAt: string;
28
+ readonly inspection: RecoveryInspection;
29
+ readonly entries: readonly RecoveryRetentionEntry[];
30
+ readonly complete: boolean;
31
+ readonly usageBytes: number;
32
+ readonly projectedBytes: number;
33
+ readonly quota: "within" | "exceeded" | "unknown";
34
+ }
35
+ export interface RecoveryRetainedEntry {
36
+ readonly path: string;
37
+ readonly reason: string;
38
+ }
39
+ export interface RecoveryPruneResult {
40
+ readonly removed: readonly string[];
41
+ readonly retained: readonly {
42
+ readonly path: string;
43
+ readonly reason: string;
44
+ }[];
45
+ readonly after: RecoveryRetentionPlan;
46
+ }
47
+ export interface RecoveryQuotaOptions {
48
+ readonly repository?: string;
49
+ readonly maxBytes: number;
50
+ readonly reserveBytes?: number;
51
+ readonly maxEntries?: number;
52
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=recovery-retention.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-retention.types.js","sourceRoot":"","sources":["../../src/application/recovery-retention.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RecoveryInspection } from \"./recovery-inspection.types.ts\";\nexport interface RecoveryRetentionPolicy {\n readonly version: 1;\n readonly scopes: readonly (\"clean-workspaces\" | \"closed-logs\")[];\n readonly minAgeMs: number;\n readonly maxBytes?: number;\n readonly maxWorkspaces?: number;\n}\nexport interface RecoveryRetentionOptions {\n readonly repository?: string;\n readonly policy: RecoveryRetentionPolicy;\n readonly maxEntries?: number;\n}\nexport interface RecoveryRetentionEntry {\n readonly path: string;\n readonly category: string;\n readonly bytes: number;\n readonly eligible: boolean;\n readonly reason: string;\n readonly branch?: string;\n readonly head?: string;\n readonly modifiedAt?: string;\n}\nexport interface RecoveryRetentionPlan {\n readonly repository: string;\n readonly policy: RecoveryRetentionPolicy;\n readonly inspectedAt: string;\n readonly inspection: RecoveryInspection;\n readonly entries: readonly RecoveryRetentionEntry[];\n readonly complete: boolean;\n readonly usageBytes: number;\n readonly projectedBytes: number;\n readonly quota: \"within\" | \"exceeded\" | \"unknown\";\n}\nexport interface RecoveryRetainedEntry {\n readonly path: string;\n readonly reason: string;\n}\nexport interface RecoveryPruneResult {\n readonly removed: readonly string[];\n readonly retained: readonly {\n readonly path: string;\n readonly reason: string;\n }[];\n readonly after: RecoveryRetentionPlan;\n}\nexport interface RecoveryQuotaOptions {\n readonly repository?: string;\n readonly maxBytes: number;\n readonly reserveBytes?: number;\n readonly maxEntries?: number;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const recoveryVerificationDefaults: {
2
+ readonly maxStateBytes: 65536;
3
+ readonly maxPaths: 1000;
4
+ };
5
+ export declare const recoveryTransferPatches: readonly ["remote.patch", "previous.patch", "previous-index.patch"];
@@ -0,0 +1,10 @@
1
+ export const recoveryVerificationDefaults = {
2
+ maxStateBytes: 65_536,
3
+ maxPaths: 1000,
4
+ };
5
+ export const recoveryTransferPatches = [
6
+ "remote.patch",
7
+ "previous.patch",
8
+ "previous-index.patch",
9
+ ];
10
+ //# sourceMappingURL=recovery-verification.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-verification.constants.js","sourceRoot":"","sources":["../../src/application/recovery-verification.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,aAAa,EAAE,MAAM;IACrB,QAAQ,EAAE,IAAI;CACN,CAAC;AAEX,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,cAAc;IACd,gBAAgB;IAChB,sBAAsB;CACd,CAAC","sourcesContent":["export const recoveryVerificationDefaults = {\n maxStateBytes: 65_536,\n maxPaths: 1000,\n} as const;\n\nexport const recoveryTransferPatches = [\n \"remote.patch\",\n \"previous.patch\",\n \"previous-index.patch\",\n] as const;\n"]}
@@ -0,0 +1,2 @@
1
+ import type { RecoveryVerification, RecoveryVerificationOptions } from "./recovery-verification.types.ts";
2
+ export declare function verifyRecoveryTransfer(path: string, options?: RecoveryVerificationOptions): Promise<RecoveryVerification>;