@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
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,aAAa,EACb,QAAQ,EACR,aAAa,GACd,MAAM,0BAA0B,CAAC;AAoBlC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAevE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAWrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAItE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AASlE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAkCnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC","sourcesContent":["export {\n attach,\n createSandbox,\n dispatch,\n openWorkspace,\n} from \"./application/outpost.ts\";\n\nexport type {\n AttachOptions,\n AttachResult,\n ContinuationOptions,\n DispatchResult,\n Sandbox,\n SandboxOptions,\n WarmDispatchResult,\n Workspace,\n WorkspaceOptions,\n} from \"./application/outpost.ts\";\n\nexport type {\n DispatchOptions,\n Execution,\n Turn,\n} from \"./application/execution.ts\";\n\nexport { WorkflowFailure, task, workflow } from \"./domain/workflow.ts\";\n\nexport type {\n Retry,\n Task,\n TaskContext,\n TaskOptions,\n TaskRecord,\n TaskStatus,\n Workflow,\n WorkflowEvent,\n WorkflowOptions,\n WorkflowResult,\n} from \"./domain/workflow.ts\";\n\nexport { agentTask, commandTask, isolatedTask } from \"./application/tasks.ts\";\n\nexport { campaign } from \"./application/campaign.ts\";\n\nexport type {\n CampaignEvent,\n CampaignOptions,\n CampaignResult,\n IssueOutcome,\n} from \"./application/campaign.ts\";\n\nexport type { Assignment, Backlog, Issue } from \"./domain/backlog.ts\";\n\nexport { beadsBacklog, githubBacklog } from \"./providers/backlogs.ts\";\n\nexport type { BacklogSettings } from \"./providers/backlogs.ts\";\n\nexport { claude, codex } from \"./providers/agents.ts\";\n\nexport { agentVersions } from \"./providers/versions.ts\";\n\nexport { conversations } from \"./infrastructure/conversations.ts\";\n\nexport type {\n ConversationFormat,\n ConversationLocation,\n} from \"./infrastructure/conversations.ts\";\n\nexport type { ClaudeSettings, CodexSettings } from \"./providers/agents.ts\";\n\nexport { mountedProvider, remoteProvider } from \"./providers/factories.ts\";\n\nexport { ResponseError, response } from \"./domain/response.ts\";\n\nexport type { ResponseSpec, StandardValidator } from \"./domain/response.ts\";\n\nexport { OutpostError, recoveryDetails } from \"./domain/errors.ts\";\n\nexport type { FaultCode } from \"./domain/errors.ts\";\n\nexport type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentObservation,\n BranchPolicy,\n Channel,\n Command,\n CommandResult,\n Commit,\n ConversationContext,\n ConversationRecord,\n ConversationStore,\n Disposal,\n LifecycleHooks,\n SandboxContext,\n SandboxLease,\n SandboxProvider,\n StageLimits,\n TransferOptions,\n Usage,\n Variables,\n Volume,\n WorkspaceRecord,\n} from \"./domain/ports.ts\";\n\nexport type { Brief, PromptVariables } from \"./domain/prompts.ts\";\n\nexport type { Logging } from \"./infrastructure/journal.ts\";\n\nexport { reporter } from \"./infrastructure/reporter.ts\";\n\nexport type { ReporterOptions } from \"./infrastructure/reporter.ts\";\n\nexport type { VariableQuestion } from \"./application/interactive-brief.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,aAAa,EACb,QAAQ,EACR,aAAa,GACd,MAAM,0BAA0B,CAAC;AAoBlC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,IAAI,EACJ,QAAQ,GACT,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE9E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AASlE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoCnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAMxD,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,qCAAqC,CAAC;AAS7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAMhF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAWlE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC","sourcesContent":["export {\n attach,\n createSandbox,\n dispatch,\n openWorkspace,\n} from \"./application/outpost.ts\";\n\nexport type {\n AttachOptions,\n AttachResult,\n ContinuationOptions,\n DispatchResult,\n Sandbox,\n SandboxOptions,\n WarmDispatchResult,\n Workspace,\n WorkspaceOptions,\n} from \"./application/outpost.ts\";\n\nexport type {\n DispatchOptions,\n Execution,\n Turn,\n} from \"./application/execution.ts\";\n\nexport {\n WorkflowFailure,\n WorkflowBudgetExceeded,\n task,\n workflow,\n} from \"./domain/workflow.ts\";\n\nexport type {\n Retry,\n Task,\n TaskContext,\n TaskOptions,\n TaskRecord,\n TaskStatus,\n Workflow,\n WorkflowEvent,\n WorkflowOptions,\n WorkflowBudget,\n WorkflowUsage,\n WorkflowResult,\n} from \"./domain/workflow.ts\";\n\nexport { agentTask, commandTask, isolatedTask } from \"./application/tasks.ts\";\n\nexport { claude, codex } from \"./providers/agents.ts\";\n\nexport { agentVersions } from \"./providers/versions.ts\";\n\nexport { conversations } from \"./infrastructure/conversations.ts\";\n\nexport type {\n ConversationFormat,\n ConversationLocation,\n} from \"./infrastructure/conversations.ts\";\n\nexport type { ClaudeSettings, CodexSettings } from \"./providers/agents.ts\";\n\nexport { mountedProvider, remoteProvider } from \"./providers/factories.ts\";\n\nexport { ResponseError, response } from \"./domain/response.ts\";\n\nexport type { ResponseSpec, StandardValidator } from \"./domain/response.ts\";\n\nexport { OutpostError, recoveryDetails } from \"./domain/errors.ts\";\n\nexport type { FaultCode } from \"./domain/errors.ts\";\n\nexport type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentObservation,\n BranchPolicy,\n Channel,\n Command,\n CommandResult,\n Commit,\n ConversationContext,\n ConversationRecord,\n ConversationStore,\n Disposal,\n LifecycleHooks,\n FileManifestEntry,\n FileTransfers,\n SandboxContext,\n SandboxLease,\n SandboxProvider,\n StageLimits,\n TransferOptions,\n Usage,\n Variables,\n Volume,\n WorkspaceRecord,\n} from \"./domain/ports.ts\";\n\nexport type { Brief, PromptVariables } from \"./domain/prompts.ts\";\n\nexport type { Logging } from \"./infrastructure/journal.ts\";\n\nexport { reporter } from \"./infrastructure/reporter.ts\";\n\nexport type { ReporterOptions } from \"./infrastructure/reporter.ts\";\n\nexport type { VariableQuestion } from \"./application/interactive-brief.ts\";\n\nexport {\n planRecoveryRetention,\n pruneRecoveryRetention,\n assertRecoveryQuota,\n} from \"./application/recovery-retention.ts\";\nexport type {\n RecoveryRetentionPolicy,\n RecoveryRetentionOptions,\n RecoveryRetentionPlan,\n RecoveryRetentionEntry,\n RecoveryPruneResult,\n RecoveryQuotaOptions,\n} from \"./application/recovery-retention.types.ts\";\nexport { verifyRecoveryTransfer } from \"./application/recovery-verification.ts\";\nexport type {\n RecoveryVerification,\n RecoveryVerificationOptions,\n} from \"./application/recovery-verification.types.ts\";\n\nexport { diagnoseSandbox } from \"./application/doctor-sandbox.ts\";\nexport type {\n SandboxDiagnosticOptions,\n SandboxDiagnosticReport,\n DiagnosticCapability,\n} from \"./application/doctor-sandbox.types.ts\";\nexport type {\n DiagnosticCheck,\n DiagnosticStatus,\n DoctorAgent,\n} from \"./application/doctor.types.ts\";\nexport { diagnoseAgentProtocol } from \"./application/doctor-protocol.ts\";\nexport type { AgentProtocolReport } from \"./application/doctor-protocol.types.ts\";\n"]}
@@ -0,0 +1,6 @@
1
+ import type { FileManifestEntry } from "../domain/sandbox.types.ts";
2
+ export declare function fileManifest(root: string, path: string): Promise<FileManifestEntry>;
3
+ export declare function sameFile(left: FileManifestEntry, right: FileManifestEntry): boolean;
4
+ export declare function sameLocalFile(remote: FileManifestEntry, local: FileManifestEntry): boolean;
5
+ export declare function validateFilePaths(paths: readonly string[]): void;
6
+ export declare function parseManifest(value: unknown, paths: readonly string[]): FileManifestEntry[];
@@ -0,0 +1,91 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { lstat, readlink } from "node:fs/promises";
4
+ import { OutpostError } from "../domain/errors.js";
5
+ import { safeDestination } from "./files.js";
6
+ export async function fileManifest(root, path) {
7
+ const target = await safeDestination(root, path);
8
+ const info = await lstat(target);
9
+ if (!info.isFile() && !info.isSymbolicLink())
10
+ throw new OutpostError("provider", "Unsupported manifest file type", {
11
+ path,
12
+ });
13
+ const hash = createHash("sha256");
14
+ let size = info.size;
15
+ if (info.isSymbolicLink()) {
16
+ const value = Buffer.from(await readlink(target));
17
+ size = value.length;
18
+ hash.update(value);
19
+ }
20
+ else {
21
+ for await (const chunk of createReadStream(target))
22
+ hash.update(chunk);
23
+ }
24
+ return {
25
+ path,
26
+ kind: info.isSymbolicLink() ? "link" : "file",
27
+ mode: info.mode & 0o777,
28
+ size,
29
+ sha256: hash.digest("hex"),
30
+ };
31
+ }
32
+ export function sameFile(left, right) {
33
+ return (left.path === right.path &&
34
+ left.kind === right.kind &&
35
+ left.mode === right.mode &&
36
+ left.size === right.size &&
37
+ left.sha256 === right.sha256);
38
+ }
39
+ export function sameLocalFile(remote, local) {
40
+ if (process.platform !== "win32")
41
+ return sameFile(remote, local);
42
+ return sameFile({ ...remote, mode: remote.mode & 0o200 }, { ...local, mode: local.mode & 0o200 });
43
+ }
44
+ export function validateFilePaths(paths) {
45
+ if (new Set(paths).size !== paths.length ||
46
+ paths.some((path) => !path ||
47
+ path.includes("\\") ||
48
+ path.includes("\0") ||
49
+ path
50
+ .split("/")
51
+ .some((part) => !part ||
52
+ part === "." ||
53
+ part === ".." ||
54
+ part.toLowerCase() === ".git")))
55
+ throw new OutpostError("provider", "Unsafe or duplicate manifest path");
56
+ }
57
+ export function parseManifest(value, paths) {
58
+ validateFilePaths(paths);
59
+ if (!Array.isArray(value) || value.length !== paths.length)
60
+ throw new OutpostError("provider", "Invalid file manifest");
61
+ return value.map((entry, index) => {
62
+ if (!entry ||
63
+ typeof entry !== "object" ||
64
+ !("path" in entry) ||
65
+ typeof entry.path !== "string" ||
66
+ entry.path !== paths[index] ||
67
+ !("kind" in entry) ||
68
+ (entry.kind !== "file" && entry.kind !== "link") ||
69
+ !("mode" in entry) ||
70
+ typeof entry.mode !== "number" ||
71
+ !Number.isInteger(entry.mode) ||
72
+ entry.mode < 0 ||
73
+ entry.mode > 0o777 ||
74
+ !("size" in entry) ||
75
+ typeof entry.size !== "number" ||
76
+ !Number.isSafeInteger(entry.size) ||
77
+ entry.size < 0 ||
78
+ !("sha256" in entry) ||
79
+ typeof entry.sha256 !== "string" ||
80
+ !/^[a-f0-9]{64}$/.test(entry.sha256))
81
+ throw new OutpostError("provider", "Invalid file manifest entry");
82
+ return {
83
+ path: entry.path,
84
+ kind: entry.kind,
85
+ mode: entry.mode,
86
+ size: entry.size,
87
+ sha256: entry.sha256,
88
+ };
89
+ });
90
+ }
91
+ //# sourceMappingURL=file-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-manifest.js","sourceRoot":"","sources":["../../src/infrastructure/file-manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createReadStream } from \"node:fs\";\nimport { lstat, readlink } from \"node:fs/promises\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { FileManifestEntry } from \"../domain/sandbox.types.ts\";\nimport { safeDestination } from \"./files.ts\";\n\nexport async function fileManifest(\n root: string,\n path: string,\n): Promise<FileManifestEntry> {\n const target = await safeDestination(root, path);\n const info = await lstat(target);\n if (!info.isFile() && !info.isSymbolicLink())\n throw new OutpostError(\"provider\", \"Unsupported manifest file type\", {\n path,\n });\n const hash = createHash(\"sha256\");\n let size = info.size;\n if (info.isSymbolicLink()) {\n const value = Buffer.from(await readlink(target));\n size = value.length;\n hash.update(value);\n } else {\n for await (const chunk of createReadStream(target)) hash.update(chunk);\n }\n return {\n path,\n kind: info.isSymbolicLink() ? \"link\" : \"file\",\n mode: info.mode & 0o777,\n size,\n sha256: hash.digest(\"hex\"),\n };\n}\n\nexport function sameFile(\n left: FileManifestEntry,\n right: FileManifestEntry,\n): boolean {\n return (\n left.path === right.path &&\n left.kind === right.kind &&\n left.mode === right.mode &&\n left.size === right.size &&\n left.sha256 === right.sha256\n );\n}\n\nexport function sameLocalFile(\n remote: FileManifestEntry,\n local: FileManifestEntry,\n): boolean {\n if (process.platform !== \"win32\") return sameFile(remote, local);\n return sameFile(\n { ...remote, mode: remote.mode & 0o200 },\n { ...local, mode: local.mode & 0o200 },\n );\n}\n\nexport function validateFilePaths(paths: readonly string[]): void {\n if (\n new Set(paths).size !== paths.length ||\n paths.some(\n (path) =>\n !path ||\n path.includes(\"\\\\\") ||\n path.includes(\"\\0\") ||\n path\n .split(\"/\")\n .some(\n (part) =>\n !part ||\n part === \".\" ||\n part === \"..\" ||\n part.toLowerCase() === \".git\",\n ),\n )\n )\n throw new OutpostError(\"provider\", \"Unsafe or duplicate manifest path\");\n}\n\nexport function parseManifest(\n value: unknown,\n paths: readonly string[],\n): FileManifestEntry[] {\n validateFilePaths(paths);\n if (!Array.isArray(value) || value.length !== paths.length)\n throw new OutpostError(\"provider\", \"Invalid file manifest\");\n return value.map((entry: unknown, index) => {\n if (\n !entry ||\n typeof entry !== \"object\" ||\n !(\"path\" in entry) ||\n typeof entry.path !== \"string\" ||\n entry.path !== paths[index] ||\n !(\"kind\" in entry) ||\n (entry.kind !== \"file\" && entry.kind !== \"link\") ||\n !(\"mode\" in entry) ||\n typeof entry.mode !== \"number\" ||\n !Number.isInteger(entry.mode) ||\n entry.mode < 0 ||\n entry.mode > 0o777 ||\n !(\"size\" in entry) ||\n typeof entry.size !== \"number\" ||\n !Number.isSafeInteger(entry.size) ||\n entry.size < 0 ||\n !(\"sha256\" in entry) ||\n typeof entry.sha256 !== \"string\" ||\n !/^[a-f0-9]{64}$/.test(entry.sha256)\n )\n throw new OutpostError(\"provider\", \"Invalid file manifest entry\");\n return {\n path: entry.path,\n kind: entry.kind,\n mode: entry.mode,\n size: entry.size,\n sha256: entry.sha256,\n };\n });\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare const lockInspectionDefaults: {
2
+ readonly maxBytes: 4096;
3
+ readonly maxPid: 2147483647;
4
+ };
@@ -0,0 +1,5 @@
1
+ export const lockInspectionDefaults = {
2
+ maxBytes: 4096,
3
+ maxPid: 2_147_483_647,
4
+ };
5
+ //# sourceMappingURL=lock-inspection.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-inspection.constants.js","sourceRoot":"","sources":["../../../src/infrastructure/git/lock-inspection.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE,aAAa;CACb,CAAC","sourcesContent":["export const lockInspectionDefaults = {\n maxBytes: 4096,\n maxPid: 2_147_483_647,\n} as const;\n"]}
@@ -0,0 +1,3 @@
1
+ import type { StorageEntry } from "../storage-inventory.types.ts";
2
+ import type { LockInspection, LockPidProbe } from "./lock-inspection.types.ts";
3
+ export declare function inspectLocks(entries: readonly StorageEntry[], probe?: LockPidProbe): Promise<LockInspection>;
@@ -0,0 +1,64 @@
1
+ import { observeOwnership } from "./process-identity.js";
2
+ import { readInspectionFile } from "../inspection-file.js";
3
+ import { lockInspectionDefaults } from "./lock-inspection.constants.js";
4
+ async function readPid(path) {
5
+ const record = JSON.parse((await readInspectionFile(path, lockInspectionDefaults.maxBytes)).toString("utf8"));
6
+ if (!record || typeof record !== "object" || !("pid" in record))
7
+ return undefined;
8
+ const pid = record.pid;
9
+ if (typeof pid !== "number" ||
10
+ !Number.isInteger(pid) ||
11
+ pid <= 0 ||
12
+ pid > lockInspectionDefaults.maxPid)
13
+ return undefined;
14
+ return {
15
+ pid,
16
+ ...("identity" in record ? { identity: record.identity } : {}),
17
+ };
18
+ }
19
+ async function inspectEntry(entry, probe) {
20
+ if (entry.kind !== "file")
21
+ return { state: "skipped", reason: "NOT_FILE" };
22
+ let record;
23
+ try {
24
+ record = await readPid(entry.path);
25
+ }
26
+ catch {
27
+ return { state: "unknown", reason: "LOCK_READ_FAILED" };
28
+ }
29
+ if (record === undefined)
30
+ return { state: "unknown", reason: "INVALID_PID" };
31
+ const { pid, identity } = record;
32
+ const ownership = await observeOwnership(pid, identity);
33
+ try {
34
+ probe(pid);
35
+ return { state: "present", pid, ownership };
36
+ }
37
+ catch (error) {
38
+ if (error &&
39
+ typeof error === "object" &&
40
+ "code" in error &&
41
+ error.code === "ESRCH")
42
+ return { state: "absent", pid, ownership };
43
+ return { state: "unknown", pid, reason: "PID_PROBE_FAILED", ownership };
44
+ }
45
+ }
46
+ export async function inspectLocks(entries, probe = (pid) => {
47
+ process.kill(pid, 0);
48
+ }) {
49
+ const locks = [];
50
+ const issues = [];
51
+ for (const entry of entries) {
52
+ const state = await inspectEntry(entry, probe);
53
+ locks.push({ name: entry.name, path: entry.path, ...state });
54
+ if (state.state === "unknown")
55
+ issues.push({ path: entry.path, code: state.reason });
56
+ }
57
+ return {
58
+ scope: "local-pid",
59
+ complete: issues.length === 0,
60
+ entries: locks,
61
+ issues,
62
+ };
63
+ }
64
+ //# sourceMappingURL=lock-inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-inspection.js","sourceRoot":"","sources":["../../../src/infrastructure/git/lock-inspection.ts"],"names":[],"mappings":"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","sourcesContent":["import { observeOwnership } from \"./process-identity.ts\";\nimport { readInspectionFile } from \"../inspection-file.ts\";\nimport type { StorageEntry, StorageIssue } from \"../storage-inventory.types.ts\";\nimport { lockInspectionDefaults } from \"./lock-inspection.constants.ts\";\nimport type {\n LockIdentityRecord,\n LockInspection,\n LockInspectionEntry,\n LockInspectionState,\n LockPidProbe,\n} from \"./lock-inspection.types.ts\";\n\nasync function readPid(path: string): Promise<LockIdentityRecord | undefined> {\n const record: unknown = JSON.parse(\n (await readInspectionFile(path, lockInspectionDefaults.maxBytes)).toString(\n \"utf8\",\n ),\n );\n if (!record || typeof record !== \"object\" || !(\"pid\" in record))\n return undefined;\n const pid = record.pid;\n if (\n typeof pid !== \"number\" ||\n !Number.isInteger(pid) ||\n pid <= 0 ||\n pid > lockInspectionDefaults.maxPid\n )\n return undefined;\n return {\n pid,\n ...(\"identity\" in record ? { identity: record.identity } : {}),\n };\n}\n\nasync function inspectEntry(\n entry: StorageEntry,\n probe: LockPidProbe,\n): Promise<LockInspectionState> {\n if (entry.kind !== \"file\") return { state: \"skipped\", reason: \"NOT_FILE\" };\n let record: LockIdentityRecord | undefined;\n try {\n record = await readPid(entry.path);\n } catch {\n return { state: \"unknown\", reason: \"LOCK_READ_FAILED\" };\n }\n if (record === undefined) return { state: \"unknown\", reason: \"INVALID_PID\" };\n const { pid, identity } = record;\n const ownership = await observeOwnership(pid, identity);\n try {\n probe(pid);\n return { state: \"present\", pid, ownership };\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ESRCH\"\n )\n return { state: \"absent\", pid, ownership };\n return { state: \"unknown\", pid, reason: \"PID_PROBE_FAILED\", ownership };\n }\n}\n\nexport async function inspectLocks(\n entries: readonly StorageEntry[],\n probe: LockPidProbe = (pid) => {\n process.kill(pid, 0);\n },\n): Promise<LockInspection> {\n const locks: LockInspectionEntry[] = [];\n const issues: StorageIssue[] = [];\n for (const entry of entries) {\n const state = await inspectEntry(entry, probe);\n locks.push({ name: entry.name, path: entry.path, ...state });\n if (state.state === \"unknown\")\n issues.push({ path: entry.path, code: state.reason });\n }\n return {\n scope: \"local-pid\",\n complete: issues.length === 0,\n entries: locks,\n issues,\n };\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { LockOwnership } from "./process-identity.types.ts";
2
+ import type { StorageEntry, StorageIssue } from "../storage-inventory.types.ts";
3
+ export type LockPidProbe = (pid: number) => void;
4
+ export interface LockIdentityRecord {
5
+ readonly pid: number;
6
+ readonly identity?: unknown;
7
+ }
8
+ export type LockInspectionState = {
9
+ readonly ownership?: LockOwnership;
10
+ } & ({
11
+ readonly state: "present" | "absent";
12
+ readonly pid: number;
13
+ } | {
14
+ readonly state: "unknown";
15
+ readonly reason: string;
16
+ readonly pid?: number;
17
+ } | {
18
+ readonly state: "skipped";
19
+ readonly reason: "NOT_FILE";
20
+ });
21
+ export type LockInspectionEntry = Pick<StorageEntry, "name" | "path"> & LockInspectionState;
22
+ export interface LockInspection {
23
+ readonly complete: boolean;
24
+ readonly scope: "local-pid";
25
+ readonly entries: readonly LockInspectionEntry[];
26
+ readonly issues: readonly StorageIssue[];
27
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lock-inspection.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-inspection.types.js","sourceRoot":"","sources":["../../../src/infrastructure/git/lock-inspection.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { LockOwnership } from \"./process-identity.types.ts\";\nimport type { StorageEntry, StorageIssue } from \"../storage-inventory.types.ts\";\n\nexport type LockPidProbe = (pid: number) => void;\n\nexport interface LockIdentityRecord {\n readonly pid: number;\n readonly identity?: unknown;\n}\n\nexport type LockInspectionState = { readonly ownership?: LockOwnership } & (\n | { readonly state: \"present\" | \"absent\"; readonly pid: number }\n | {\n readonly state: \"unknown\";\n readonly reason: string;\n readonly pid?: number;\n }\n | { readonly state: \"skipped\"; readonly reason: \"NOT_FILE\" }\n);\n\nexport type LockInspectionEntry = Pick<StorageEntry, \"name\" | \"path\"> &\n LockInspectionState;\n\nexport interface LockInspection {\n readonly complete: boolean;\n readonly scope: \"local-pid\";\n readonly entries: readonly LockInspectionEntry[];\n readonly issues: readonly StorageIssue[];\n}\n"]}
@@ -1 +1,2 @@
1
+ export declare function lockPath(root: string, key: string): string;
1
2
  export declare function lock(root: string, key: string): Promise<() => Promise<void>>;
@@ -1,36 +1,77 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { mkdir, open, readFile, rm } from "node:fs/promises";
2
+ import { mkdir, open, rm } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { OutpostError } from "../../domain/errors.js";
5
- export async function lock(root, key) {
6
- const folder = join(root, ".outpost", "locks");
7
- await mkdir(folder, { recursive: true });
8
- const path = join(folder, `${createHash("sha256").update(key).digest("hex").slice(0, 20)}.json`);
9
- for (let attempt = 0; attempt < 2; attempt++) {
10
- try {
11
- const file = await open(path, "wx", 0o600);
12
- await file.writeFile(JSON.stringify({ pid: process.pid, nonce: randomUUID() }));
13
- await file.close();
14
- return () => rm(path, { force: true });
15
- }
16
- catch (cause) {
17
- if (cause.code !== "EEXIST")
18
- throw cause;
19
- const owner = JSON.parse(await readFile(path, "utf8"));
20
- let alive = true;
21
- try {
22
- if (!owner.pid)
23
- throw new Error();
24
- process.kill(owner.pid, 0);
25
- }
26
- catch (error) {
27
- alive = error.code === "EPERM";
28
- }
29
- if (alive)
30
- throw new OutpostError("conflict", `Workspace is already in use: ${key}`, { lock: path, pid: owner.pid });
5
+ import { localProcessIdentity, observeOwnership } from "./process-identity.js";
6
+ import { readInspectionFile } from "../inspection-file.js";
7
+ import { lockInspectionDefaults } from "./lock-inspection.constants.js";
8
+ export function lockPath(root, key) {
9
+ return join(root, ".outpost", "locks", `${createHash("sha256").update(key).digest("hex").slice(0, 20)}.json`);
10
+ }
11
+ async function readOwner(path) {
12
+ return JSON.parse((await readInspectionFile(path, lockInspectionDefaults.maxBytes)).toString("utf8"));
13
+ }
14
+ async function createLock(path) {
15
+ const file = await open(path, "wx", 0o600);
16
+ const nonce = randomUUID();
17
+ try {
18
+ await file.writeFile(JSON.stringify({
19
+ pid: process.pid,
20
+ nonce,
21
+ identity: await localProcessIdentity(),
22
+ }));
23
+ }
24
+ finally {
25
+ await file.close();
26
+ }
27
+ return async () => {
28
+ const current = await readOwner(path).catch(() => null);
29
+ if (current &&
30
+ typeof current === "object" &&
31
+ "nonce" in current &&
32
+ current.nonce === nonce)
31
33
  await rm(path, { force: true });
32
- }
34
+ };
35
+ }
36
+ export async function lock(root, key) {
37
+ await mkdir(join(root, ".outpost", "locks"), { recursive: true });
38
+ const path = lockPath(root, key);
39
+ try {
40
+ return await createLock(path);
41
+ }
42
+ catch (cause) {
43
+ if (!(cause &&
44
+ typeof cause === "object" &&
45
+ "code" in cause &&
46
+ cause.code === "EEXIST"))
47
+ throw cause;
48
+ }
49
+ const conflict = () => new OutpostError("conflict", `Workspace is already in use or ownership is unknown: ${key}`, { lock: path });
50
+ // Serialize stale reclamation so another contender cannot unlink a new owner.
51
+ const releaseGuard = await createLock(`${path}.reclaim`).catch(() => {
52
+ throw conflict();
53
+ });
54
+ try {
55
+ const owner = await readOwner(path).catch(() => null);
56
+ const pid = owner && typeof owner === "object" && "pid" in owner
57
+ ? owner.pid
58
+ : undefined;
59
+ const identity = owner && typeof owner === "object" && "identity" in owner
60
+ ? owner.identity
61
+ : undefined;
62
+ const ownership = typeof pid === "number" &&
63
+ Number.isSafeInteger(pid) &&
64
+ pid > 0 &&
65
+ pid <= lockInspectionDefaults.maxPid
66
+ ? await observeOwnership(pid, identity)
67
+ : undefined;
68
+ if (ownership?.status !== "inactive")
69
+ throw conflict();
70
+ await rm(path, { force: true });
71
+ return await createLock(path);
72
+ }
73
+ finally {
74
+ await releaseGuard();
33
75
  }
34
- throw new OutpostError("conflict", "Unable to acquire workspace lock");
35
76
  }
36
77
  //# sourceMappingURL=lock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../src/infrastructure/git/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGtD,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,IAAY,EACZ,GAAW;IAEX,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CACf,MAAM,EACN,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CACtE,CAAC;IACF,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3C,MAAM,IAAI,CAAC,SAAS,CAClB,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CAC1D,CAAC;YACF,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAc,CAAC;YACpE,IAAI,KAAK,GAAG,IAAI,CAAC;YACjB,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,CAAC,GAAG;oBAAE,MAAM,IAAI,KAAK,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,GAAI,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;YAC5D,CAAC;YACD,IAAI,KAAK;gBACP,MAAM,IAAI,YAAY,CACpB,UAAU,EACV,gCAAgC,GAAG,EAAE,EACrC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAC/B,CAAC;YACJ,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,kCAAkC,CAAC,CAAC;AACzE,CAAC","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { OutpostError } from \"../../domain/errors.ts\";\nimport type { LockOwner } from \"./lock.types.ts\";\n\nexport async function lock(\n root: string,\n key: string,\n): Promise<() => Promise<void>> {\n const folder = join(root, \".outpost\", \"locks\");\n await mkdir(folder, { recursive: true });\n const path = join(\n folder,\n `${createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 20)}.json`,\n );\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const file = await open(path, \"wx\", 0o600);\n await file.writeFile(\n JSON.stringify({ pid: process.pid, nonce: randomUUID() }),\n );\n await file.close();\n return () => rm(path, { force: true });\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== \"EEXIST\") throw cause;\n const owner = JSON.parse(await readFile(path, \"utf8\")) as LockOwner;\n let alive = true;\n try {\n if (!owner.pid) throw new Error();\n process.kill(owner.pid, 0);\n } catch (error) {\n alive = (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n if (alive)\n throw new OutpostError(\n \"conflict\",\n `Workspace is already in use: ${key}`,\n { lock: path, pid: owner.pid },\n );\n await rm(path, { force: true });\n }\n }\n throw new OutpostError(\"conflict\", \"Unable to acquire workspace lock\");\n}\n"]}
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../src/infrastructure/git/lock.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { OutpostError } from \"../../domain/errors.ts\";\nimport { localProcessIdentity, observeOwnership } from \"./process-identity.ts\";\nimport { readInspectionFile } from \"../inspection-file.ts\";\nimport { lockInspectionDefaults } from \"./lock-inspection.constants.ts\";\n\nexport function lockPath(root: string, key: string): string {\n return join(\n root,\n \".outpost\",\n \"locks\",\n `${createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 20)}.json`,\n );\n}\n\nasync function readOwner(path: string): Promise<unknown> {\n return JSON.parse(\n (await readInspectionFile(path, lockInspectionDefaults.maxBytes)).toString(\n \"utf8\",\n ),\n );\n}\n\nasync function createLock(path: string): Promise<() => Promise<void>> {\n const file = await open(path, \"wx\", 0o600);\n const nonce = randomUUID();\n try {\n await file.writeFile(\n JSON.stringify({\n pid: process.pid,\n nonce,\n identity: await localProcessIdentity(),\n }),\n );\n } finally {\n await file.close();\n }\n return async () => {\n const current = await readOwner(path).catch(() => null);\n if (\n current &&\n typeof current === \"object\" &&\n \"nonce\" in current &&\n current.nonce === nonce\n )\n await rm(path, { force: true });\n };\n}\n\nexport async function lock(\n root: string,\n key: string,\n): Promise<() => Promise<void>> {\n await mkdir(join(root, \".outpost\", \"locks\"), { recursive: true });\n const path = lockPath(root, key);\n try {\n return await createLock(path);\n } catch (cause) {\n if (!(\n cause &&\n typeof cause === \"object\" &&\n \"code\" in cause &&\n cause.code === \"EEXIST\"\n ))\n throw cause;\n }\n const conflict = () =>\n new OutpostError(\n \"conflict\",\n `Workspace is already in use or ownership is unknown: ${key}`,\n { lock: path },\n );\n // Serialize stale reclamation so another contender cannot unlink a new owner.\n const releaseGuard = await createLock(`${path}.reclaim`).catch(() => {\n throw conflict();\n });\n try {\n const owner = await readOwner(path).catch(() => null);\n const pid =\n owner && typeof owner === \"object\" && \"pid\" in owner\n ? owner.pid\n : undefined;\n const identity =\n owner && typeof owner === \"object\" && \"identity\" in owner\n ? owner.identity\n : undefined;\n const ownership =\n typeof pid === \"number\" &&\n Number.isSafeInteger(pid) &&\n pid > 0 &&\n pid <= lockInspectionDefaults.maxPid\n ? await observeOwnership(pid, identity)\n : undefined;\n if (ownership?.status !== \"inactive\") throw conflict();\n await rm(path, { force: true });\n return await createLock(path);\n } finally {\n await releaseGuard();\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { LocalProcessIdentity, LockOwnership } from "./process-identity.types.ts";
2
+ export declare function localProcessIdentity(pid?: number): Promise<LocalProcessIdentity | undefined>;
3
+ export declare function observeOwnership(pid: number, identity: unknown): Promise<LockOwnership>;
@@ -0,0 +1,73 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, readlink } from "node:fs/promises";
3
+ export async function localProcessIdentity(pid = process.pid) {
4
+ if (process.platform !== "linux")
5
+ return undefined;
6
+ try {
7
+ const [machine, boot, namespace, stat] = await Promise.all([
8
+ readFile("/etc/machine-id", "utf8"),
9
+ readFile("/proc/sys/kernel/random/boot_id", "utf8"),
10
+ readlink(`/proc/${pid}/ns/pid`),
11
+ readFile(`/proc/${pid}/stat`, "utf8"),
12
+ ]);
13
+ const started = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19];
14
+ if (!machine.trim() || !boot.trim() || !started || !/^\d+$/.test(started))
15
+ return undefined;
16
+ return {
17
+ host: createHash("sha256").update(machine.trim()).digest("hex"),
18
+ boot: boot.trim(),
19
+ namespace,
20
+ started,
21
+ };
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ function validIdentity(value) {
28
+ if (!value || typeof value !== "object")
29
+ return false;
30
+ return ("host" in value &&
31
+ typeof value.host === "string" &&
32
+ value.host.length > 0 &&
33
+ "boot" in value &&
34
+ typeof value.boot === "string" &&
35
+ value.boot.length > 0 &&
36
+ "namespace" in value &&
37
+ typeof value.namespace === "string" &&
38
+ value.namespace.length > 0 &&
39
+ "started" in value &&
40
+ typeof value.started === "string" &&
41
+ /^\d+$/.test(value.started));
42
+ }
43
+ export async function observeOwnership(pid, identity) {
44
+ if (!validIdentity(identity))
45
+ return { status: "unknown", reason: "LEGACY_OR_INVALID_IDENTITY" };
46
+ const local = await localProcessIdentity();
47
+ if (!local)
48
+ return { status: "unknown", reason: "LOCAL_IDENTITY_UNAVAILABLE" };
49
+ if (identity.host !== local.host)
50
+ return { status: "unknown", reason: "OTHER_HOST" };
51
+ if (identity.boot !== local.boot)
52
+ return { status: "unknown", reason: "OTHER_BOOT" };
53
+ if (identity.namespace !== local.namespace)
54
+ return { status: "unknown", reason: "OTHER_PID_NAMESPACE" };
55
+ try {
56
+ process.kill(pid, 0);
57
+ }
58
+ catch (error) {
59
+ if (error &&
60
+ typeof error === "object" &&
61
+ "code" in error &&
62
+ error.code === "ESRCH")
63
+ return { status: "inactive", reason: "PROCESS_EXITED" };
64
+ return { status: "unknown", reason: "PROCESS_INACCESSIBLE" };
65
+ }
66
+ const current = await localProcessIdentity(pid);
67
+ if (!current)
68
+ return { status: "unknown", reason: "PROCESS_IDENTITY_UNAVAILABLE" };
69
+ if (current.started !== identity.started)
70
+ return { status: "unknown", reason: "PID_REUSED" };
71
+ return { status: "active", reason: "LOCAL_IDENTITY_MATCH" };
72
+ }
73
+ //# sourceMappingURL=process-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-identity.js","sourceRoot":"","sources":["../../../src/infrastructure/git/process-identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMtD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAG,GAAG,OAAO,CAAC,GAAG;IAEjB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;YACnC,QAAQ,CAAC,iCAAiC,EAAE,MAAM,CAAC;YACnD,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAC;YAC/B,QAAQ,CAAC,SAAS,GAAG,OAAO,EAAE,MAAM,CAAC;SACtC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YACvE,OAAO,SAAS,CAAC;QACnB,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/D,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YACjB,SAAS;YACT,OAAO;SACR,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,OAAO,CACL,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACrB,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACrB,WAAW,IAAI,KAAK;QACpB,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;QACnC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QAC1B,SAAS,IAAI,KAAK;QAClB,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAC5B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,QAAiB;IAEjB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAC3C,IAAI,CAAC,KAAK;QACR,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACrE,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;QAC9B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACrD,IAAI,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;QAC9B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACrD,IAAI,QAAQ,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;QACxC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,KAAK;YACL,OAAO,KAAK,KAAK,QAAQ;YACzB,MAAM,IAAI,KAAK;YACf,KAAK,CAAC,IAAI,KAAK,OAAO;YAEtB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAC1D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IAC/D,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO;QACV,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;IACvE,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO;QACtC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACrD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;AAC9D,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile, readlink } from \"node:fs/promises\";\nimport type {\n LocalProcessIdentity,\n LockOwnership,\n} from \"./process-identity.types.ts\";\n\nexport async function localProcessIdentity(\n pid = process.pid,\n): Promise<LocalProcessIdentity | undefined> {\n if (process.platform !== \"linux\") return undefined;\n try {\n const [machine, boot, namespace, stat] = await Promise.all([\n readFile(\"/etc/machine-id\", \"utf8\"),\n readFile(\"/proc/sys/kernel/random/boot_id\", \"utf8\"),\n readlink(`/proc/${pid}/ns/pid`),\n readFile(`/proc/${pid}/stat`, \"utf8\"),\n ]);\n const started = stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \")[19];\n if (!machine.trim() || !boot.trim() || !started || !/^\\d+$/.test(started))\n return undefined;\n return {\n host: createHash(\"sha256\").update(machine.trim()).digest(\"hex\"),\n boot: boot.trim(),\n namespace,\n started,\n };\n } catch {\n return undefined;\n }\n}\n\nfunction validIdentity(value: unknown): value is LocalProcessIdentity {\n if (!value || typeof value !== \"object\") return false;\n return (\n \"host\" in value &&\n typeof value.host === \"string\" &&\n value.host.length > 0 &&\n \"boot\" in value &&\n typeof value.boot === \"string\" &&\n value.boot.length > 0 &&\n \"namespace\" in value &&\n typeof value.namespace === \"string\" &&\n value.namespace.length > 0 &&\n \"started\" in value &&\n typeof value.started === \"string\" &&\n /^\\d+$/.test(value.started)\n );\n}\n\nexport async function observeOwnership(\n pid: number,\n identity: unknown,\n): Promise<LockOwnership> {\n if (!validIdentity(identity))\n return { status: \"unknown\", reason: \"LEGACY_OR_INVALID_IDENTITY\" };\n const local = await localProcessIdentity();\n if (!local)\n return { status: \"unknown\", reason: \"LOCAL_IDENTITY_UNAVAILABLE\" };\n if (identity.host !== local.host)\n return { status: \"unknown\", reason: \"OTHER_HOST\" };\n if (identity.boot !== local.boot)\n return { status: \"unknown\", reason: \"OTHER_BOOT\" };\n if (identity.namespace !== local.namespace)\n return { status: \"unknown\", reason: \"OTHER_PID_NAMESPACE\" };\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ESRCH\"\n )\n return { status: \"inactive\", reason: \"PROCESS_EXITED\" };\n return { status: \"unknown\", reason: \"PROCESS_INACCESSIBLE\" };\n }\n const current = await localProcessIdentity(pid);\n if (!current)\n return { status: \"unknown\", reason: \"PROCESS_IDENTITY_UNAVAILABLE\" };\n if (current.started !== identity.started)\n return { status: \"unknown\", reason: \"PID_REUSED\" };\n return { status: \"active\", reason: \"LOCAL_IDENTITY_MATCH\" };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export interface LocalProcessIdentity {
2
+ readonly host: string;
3
+ readonly boot: string;
4
+ readonly namespace: string;
5
+ readonly started: string;
6
+ }
7
+ export interface LockOwnership {
8
+ readonly status: "active" | "inactive" | "unknown";
9
+ readonly reason: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=process-identity.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-identity.types.js","sourceRoot":"","sources":["../../../src/infrastructure/git/process-identity.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface LocalProcessIdentity {\n readonly host: string;\n readonly boot: string;\n readonly namespace: string;\n readonly started: string;\n}\nexport interface LockOwnership {\n readonly status: \"active\" | \"inactive\" | \"unknown\";\n readonly reason: string;\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function verificationGit(directory: string, args: readonly string[]): Promise<string>;
@@ -0,0 +1,36 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { gitDefaults } from "./git.constants.js";
4
+ export async function verificationGit(directory, args) {
5
+ const environment = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.toUpperCase().startsWith("GIT_")));
6
+ const result = await promisify(execFile)("git", [
7
+ "-c",
8
+ `core.hooksPath=${process.platform === "win32" ? "NUL" : "/dev/null"}`,
9
+ "-c",
10
+ "core.fsmonitor=false",
11
+ "-c",
12
+ "maintenance.auto=false",
13
+ "-c",
14
+ "gc.auto=0",
15
+ ...args,
16
+ ], {
17
+ cwd: directory,
18
+ env: {
19
+ ...environment,
20
+ GIT_CONFIG_NOSYSTEM: "1",
21
+ GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null",
22
+ GIT_ATTR_NOSYSTEM: "1",
23
+ GIT_NO_LAZY_FETCH: "1",
24
+ GIT_ALLOW_PROTOCOL: "file",
25
+ GIT_TERMINAL_PROMPT: "0",
26
+ GIT_OPTIONAL_LOCKS: "0",
27
+ LC_ALL: "C",
28
+ },
29
+ timeout: gitDefaults.deadlineMs,
30
+ maxBuffer: gitDefaults.retainBytes,
31
+ encoding: "utf8",
32
+ windowsHide: true,
33
+ });
34
+ return result.stdout;
35
+ }
36
+ //# sourceMappingURL=verification-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-command.js","sourceRoot":"","sources":["../../../src/infrastructure/git/verification-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,IAAuB;IAEvB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CACpC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAChC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CACjD,CACF,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,CACtC,KAAK,EACL;QACE,IAAI;QACJ,kBAAkB,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,EAAE;QACtE,IAAI;QACJ,sBAAsB;QACtB,IAAI;QACJ,wBAAwB;QACxB,IAAI;QACJ,WAAW;QACX,GAAG,IAAI;KACR,EACD;QACE,GAAG,EAAE,SAAS;QACd,GAAG,EAAE;YACH,GAAG,WAAW;YACd,mBAAmB,EAAE,GAAG;YACxB,iBAAiB,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW;YACrE,iBAAiB,EAAE,GAAG;YACtB,iBAAiB,EAAE,GAAG;YACtB,kBAAkB,EAAE,MAAM;YAC1B,mBAAmB,EAAE,GAAG;YACxB,kBAAkB,EAAE,GAAG;YACvB,MAAM,EAAE,GAAG;SACZ;QACD,OAAO,EAAE,WAAW,CAAC,UAAU;QAC/B,SAAS,EAAE,WAAW,CAAC,WAAW;QAClC,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,IAAI;KAClB,CACF,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { gitDefaults } from \"./git.constants.ts\";\n\nexport async function verificationGit(\n directory: string,\n args: readonly string[],\n): Promise<string> {\n const environment = Object.fromEntries(\n Object.entries(process.env).filter(\n ([key]) => !key.toUpperCase().startsWith(\"GIT_\"),\n ),\n );\n const result = await promisify(execFile)(\n \"git\",\n [\n \"-c\",\n `core.hooksPath=${process.platform === \"win32\" ? \"NUL\" : \"/dev/null\"}`,\n \"-c\",\n \"core.fsmonitor=false\",\n \"-c\",\n \"maintenance.auto=false\",\n \"-c\",\n \"gc.auto=0\",\n ...args,\n ],\n {\n cwd: directory,\n env: {\n ...environment,\n GIT_CONFIG_NOSYSTEM: \"1\",\n GIT_CONFIG_GLOBAL: process.platform === \"win32\" ? \"NUL\" : \"/dev/null\",\n GIT_ATTR_NOSYSTEM: \"1\",\n GIT_NO_LAZY_FETCH: \"1\",\n GIT_ALLOW_PROTOCOL: \"file\",\n GIT_TERMINAL_PROMPT: \"0\",\n GIT_OPTIONAL_LOCKS: \"0\",\n LC_ALL: \"C\",\n },\n timeout: gitDefaults.deadlineMs,\n maxBuffer: gitDefaults.retainBytes,\n encoding: \"utf8\",\n windowsHide: true,\n },\n );\n return result.stdout;\n}\n"]}
@@ -45,6 +45,7 @@ export function workspaceLease(record, options, unlock) {
45
45
  "status",
46
46
  "--porcelain",
47
47
  "--untracked-files=normal",
48
+ "--ignored",
48
49
  ]);
49
50
  if (dirty.trim())
50
51
  return { retainedDirectory: workdir };