dorfl 0.14.2 → 0.15.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 (418) hide show
  1. package/dist/advance-ci-template.d.ts +17 -16
  2. package/dist/advance-ci-template.d.ts.map +1 -1
  3. package/dist/advance-ci-template.js +42 -53
  4. package/dist/advance-ci-template.js.map +1 -1
  5. package/dist/advance-drivers.d.ts +8 -0
  6. package/dist/advance-drivers.d.ts.map +1 -1
  7. package/dist/advance-drivers.js +28 -4
  8. package/dist/advance-drivers.js.map +1 -1
  9. package/dist/advance-isolated.d.ts.map +1 -1
  10. package/dist/advance-isolated.js +3 -2
  11. package/dist/advance-isolated.js.map +1 -1
  12. package/dist/advance-lifecycle-template.d.ts +41 -40
  13. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  14. package/dist/advance-lifecycle-template.js +309 -489
  15. package/dist/advance-lifecycle-template.js.map +1 -1
  16. package/dist/advance-loop-driver.d.ts.map +1 -1
  17. package/dist/advance-loop-driver.js +3 -2
  18. package/dist/advance-loop-driver.js.map +1 -1
  19. package/dist/advance-treeless-publish.d.ts +31 -0
  20. package/dist/advance-treeless-publish.d.ts.map +1 -1
  21. package/dist/advance-treeless-publish.js +40 -0
  22. package/dist/advance-treeless-publish.js.map +1 -1
  23. package/dist/advance.d.ts +63 -3
  24. package/dist/advance.d.ts.map +1 -1
  25. package/dist/advance.js +80 -12
  26. package/dist/advance.js.map +1 -1
  27. package/dist/advancing-lock.d.ts +9 -0
  28. package/dist/advancing-lock.d.ts.map +1 -1
  29. package/dist/advancing-lock.js +21 -1
  30. package/dist/advancing-lock.js.map +1 -1
  31. package/dist/agent-env.d.ts +61 -0
  32. package/dist/agent-env.d.ts.map +1 -0
  33. package/dist/agent-env.js +144 -0
  34. package/dist/agent-env.js.map +1 -0
  35. package/dist/agent-stop.d.ts +35 -0
  36. package/dist/agent-stop.d.ts.map +1 -1
  37. package/dist/agent-stop.js +62 -0
  38. package/dist/agent-stop.js.map +1 -1
  39. package/dist/apply-merge-action.d.ts +76 -34
  40. package/dist/apply-merge-action.d.ts.map +1 -1
  41. package/dist/apply-merge-action.js +339 -129
  42. package/dist/apply-merge-action.js.map +1 -1
  43. package/dist/apply-stuck-action.js +2 -2
  44. package/dist/apply-stuck-action.js.map +1 -1
  45. package/dist/ci-agent-result.d.ts +151 -0
  46. package/dist/ci-agent-result.d.ts.map +1 -0
  47. package/dist/ci-agent-result.js +314 -0
  48. package/dist/ci-agent-result.js.map +1 -0
  49. package/dist/ci-handoff-apply.d.ts +162 -0
  50. package/dist/ci-handoff-apply.d.ts.map +1 -0
  51. package/dist/ci-handoff-apply.js +497 -0
  52. package/dist/ci-handoff-apply.js.map +1 -0
  53. package/dist/ci-handoff-format.d.ts +369 -0
  54. package/dist/ci-handoff-format.d.ts.map +1 -0
  55. package/dist/ci-handoff-format.js +646 -0
  56. package/dist/ci-handoff-format.js.map +1 -0
  57. package/dist/ci-handoff-lfs.d.ts +146 -0
  58. package/dist/ci-handoff-lfs.d.ts.map +1 -0
  59. package/dist/ci-handoff-lfs.js +397 -0
  60. package/dist/ci-handoff-lfs.js.map +1 -0
  61. package/dist/ci-handoff.d.ts +99 -0
  62. package/dist/ci-handoff.d.ts.map +1 -0
  63. package/dist/ci-handoff.js +277 -0
  64. package/dist/ci-handoff.js.map +1 -0
  65. package/dist/ci-lock-outputs.d.ts +93 -0
  66. package/dist/ci-lock-outputs.d.ts.map +1 -0
  67. package/dist/ci-lock-outputs.js +223 -0
  68. package/dist/ci-lock-outputs.js.map +1 -0
  69. package/dist/ci-phase-build.d.ts +178 -0
  70. package/dist/ci-phase-build.d.ts.map +1 -0
  71. package/dist/ci-phase-build.js +989 -0
  72. package/dist/ci-phase-build.js.map +1 -0
  73. package/dist/ci-phase-driver.d.ts +247 -0
  74. package/dist/ci-phase-driver.d.ts.map +1 -0
  75. package/dist/ci-phase-driver.js +370 -0
  76. package/dist/ci-phase-driver.js.map +1 -0
  77. package/dist/ci-phase-intake.d.ts +172 -0
  78. package/dist/ci-phase-intake.d.ts.map +1 -0
  79. package/dist/ci-phase-intake.js +555 -0
  80. package/dist/ci-phase-intake.js.map +1 -0
  81. package/dist/ci-phase-tasking.d.ts +205 -0
  82. package/dist/ci-phase-tasking.d.ts.map +1 -0
  83. package/dist/ci-phase-tasking.js +906 -0
  84. package/dist/ci-phase-tasking.js.map +1 -0
  85. package/dist/ci-phase-treeless.d.ts +224 -0
  86. package/dist/ci-phase-treeless.d.ts.map +1 -0
  87. package/dist/ci-phase-treeless.js +1482 -0
  88. package/dist/ci-phase-treeless.js.map +1 -0
  89. package/dist/ci-read-token.d.ts +47 -0
  90. package/dist/ci-read-token.d.ts.map +1 -0
  91. package/dist/ci-read-token.js +80 -0
  92. package/dist/ci-read-token.js.map +1 -0
  93. package/dist/cli.d.ts.map +1 -1
  94. package/dist/cli.js +447 -22
  95. package/dist/cli.js.map +1 -1
  96. package/dist/close-job-template.d.ts +10 -1
  97. package/dist/close-job-template.d.ts.map +1 -1
  98. package/dist/close-job-template.js +46 -14
  99. package/dist/close-job-template.js.map +1 -1
  100. package/dist/close-job.d.ts +20 -5
  101. package/dist/close-job.d.ts.map +1 -1
  102. package/dist/close-job.js +65 -12
  103. package/dist/close-job.js.map +1 -1
  104. package/dist/complete.d.ts.map +1 -1
  105. package/dist/complete.js +20 -1
  106. package/dist/complete.js.map +1 -1
  107. package/dist/config.d.ts +41 -44
  108. package/dist/config.d.ts.map +1 -1
  109. package/dist/config.js +2 -4
  110. package/dist/config.js.map +1 -1
  111. package/dist/continue-branch.d.ts +63 -15
  112. package/dist/continue-branch.d.ts.map +1 -1
  113. package/dist/continue-branch.js +75 -8
  114. package/dist/continue-branch.js.map +1 -1
  115. package/dist/crashed-run-save.d.ts +73 -0
  116. package/dist/crashed-run-save.d.ts.map +1 -0
  117. package/dist/crashed-run-save.js +86 -0
  118. package/dist/crashed-run-save.js.map +1 -0
  119. package/dist/cwd-section.d.ts.map +1 -1
  120. package/dist/cwd-section.js +10 -2
  121. package/dist/cwd-section.js.map +1 -1
  122. package/dist/decision-engine.d.ts +10 -0
  123. package/dist/decision-engine.d.ts.map +1 -1
  124. package/dist/decision-engine.js.map +1 -1
  125. package/dist/do-config.d.ts +2 -24
  126. package/dist/do-config.d.ts.map +1 -1
  127. package/dist/do-config.js +1 -33
  128. package/dist/do-config.js.map +1 -1
  129. package/dist/do.d.ts +149 -1
  130. package/dist/do.d.ts.map +1 -1
  131. package/dist/do.js +115 -25
  132. package/dist/do.js.map +1 -1
  133. package/dist/dorfl-item-template.d.ts +98 -0
  134. package/dist/dorfl-item-template.d.ts.map +1 -0
  135. package/dist/dorfl-item-template.js +504 -0
  136. package/dist/dorfl-item-template.js.map +1 -0
  137. package/dist/env-config.d.ts.map +1 -1
  138. package/dist/env-config.js +7 -5
  139. package/dist/env-config.js.map +1 -1
  140. package/dist/frontmatter.d.ts +52 -0
  141. package/dist/frontmatter.d.ts.map +1 -1
  142. package/dist/frontmatter.js +136 -2
  143. package/dist/frontmatter.js.map +1 -1
  144. package/dist/gc.d.ts +13 -0
  145. package/dist/gc.d.ts.map +1 -1
  146. package/dist/gc.js +16 -2
  147. package/dist/gc.js.map +1 -1
  148. package/dist/git.d.ts +18 -1
  149. package/dist/git.d.ts.map +1 -1
  150. package/dist/git.js +70 -5
  151. package/dist/git.js.map +1 -1
  152. package/dist/harness.d.ts +44 -5
  153. package/dist/harness.d.ts.map +1 -1
  154. package/dist/harness.js +24 -1
  155. package/dist/harness.js.map +1 -1
  156. package/dist/index.d.ts +11 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +7 -3
  159. package/dist/index.js.map +1 -1
  160. package/dist/install-ci-action-pins.d.ts +123 -0
  161. package/dist/install-ci-action-pins.d.ts.map +1 -0
  162. package/dist/install-ci-action-pins.js +190 -0
  163. package/dist/install-ci-action-pins.js.map +1 -0
  164. package/dist/install-ci-actions-pr-creation.d.ts +70 -0
  165. package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
  166. package/dist/install-ci-actions-pr-creation.js +106 -0
  167. package/dist/install-ci-actions-pr-creation.js.map +1 -0
  168. package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
  169. package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
  170. package/dist/install-ci-capabilities/dorfl-item.js +26 -0
  171. package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
  172. package/dist/install-ci-core.d.ts +155 -37
  173. package/dist/install-ci-core.d.ts.map +1 -1
  174. package/dist/install-ci-core.js +304 -200
  175. package/dist/install-ci-core.js.map +1 -1
  176. package/dist/install-ci-github.d.ts +47 -2
  177. package/dist/install-ci-github.d.ts.map +1 -1
  178. package/dist/install-ci-github.js +113 -1
  179. package/dist/install-ci-github.js.map +1 -1
  180. package/dist/install-ci.d.ts +25 -6
  181. package/dist/install-ci.d.ts.map +1 -1
  182. package/dist/install-ci.js +72 -36
  183. package/dist/install-ci.js.map +1 -1
  184. package/dist/intake-marker.d.ts +6 -1
  185. package/dist/intake-marker.d.ts.map +1 -1
  186. package/dist/intake-marker.js.map +1 -1
  187. package/dist/intake-triage.d.ts.map +1 -1
  188. package/dist/intake-triage.js +6 -2
  189. package/dist/intake-triage.js.map +1 -1
  190. package/dist/intake-trigger-template.d.ts +31 -25
  191. package/dist/intake-trigger-template.d.ts.map +1 -1
  192. package/dist/intake-trigger-template.js +107 -292
  193. package/dist/intake-trigger-template.js.map +1 -1
  194. package/dist/intake.d.ts +186 -4
  195. package/dist/intake.d.ts.map +1 -1
  196. package/dist/intake.js +533 -416
  197. package/dist/intake.js.map +1 -1
  198. package/dist/integration-core.d.ts +154 -1
  199. package/dist/integration-core.d.ts.map +1 -1
  200. package/dist/integration-core.js +759 -486
  201. package/dist/integration-core.js.map +1 -1
  202. package/dist/integrator.d.ts +12 -0
  203. package/dist/integrator.d.ts.map +1 -1
  204. package/dist/integrator.js +21 -0
  205. package/dist/integrator.js.map +1 -1
  206. package/dist/isolation.d.ts +12 -2
  207. package/dist/isolation.d.ts.map +1 -1
  208. package/dist/isolation.js +19 -8
  209. package/dist/isolation.js.map +1 -1
  210. package/dist/item-lock.d.ts +93 -11
  211. package/dist/item-lock.d.ts.map +1 -1
  212. package/dist/item-lock.js +233 -65
  213. package/dist/item-lock.js.map +1 -1
  214. package/dist/land-question-residue.d.ts +58 -0
  215. package/dist/land-question-residue.d.ts.map +1 -0
  216. package/dist/land-question-residue.js +95 -0
  217. package/dist/land-question-residue.js.map +1 -0
  218. package/dist/lifecycle-gather.d.ts +16 -0
  219. package/dist/lifecycle-gather.d.ts.map +1 -1
  220. package/dist/lifecycle-gather.js +54 -6
  221. package/dist/lifecycle-gather.js.map +1 -1
  222. package/dist/merge-answer.d.ts +41 -0
  223. package/dist/merge-answer.d.ts.map +1 -0
  224. package/dist/merge-answer.js +60 -0
  225. package/dist/merge-answer.js.map +1 -0
  226. package/dist/merge-question-surfacer.d.ts +105 -30
  227. package/dist/merge-question-surfacer.d.ts.map +1 -1
  228. package/dist/merge-question-surfacer.js +202 -91
  229. package/dist/merge-question-surfacer.js.map +1 -1
  230. package/dist/merge-question-tick.d.ts +64 -0
  231. package/dist/merge-question-tick.d.ts.map +1 -0
  232. package/dist/merge-question-tick.js +96 -0
  233. package/dist/merge-question-tick.js.map +1 -0
  234. package/dist/mint-adr.d.ts.map +1 -1
  235. package/dist/mint-adr.js +20 -2
  236. package/dist/mint-adr.js.map +1 -1
  237. package/dist/needs-attention.d.ts +72 -2
  238. package/dist/needs-attention.d.ts.map +1 -1
  239. package/dist/needs-attention.js +275 -38
  240. package/dist/needs-attention.js.map +1 -1
  241. package/dist/phase-recorder.d.ts +168 -0
  242. package/dist/phase-recorder.d.ts.map +1 -0
  243. package/dist/phase-recorder.js +218 -0
  244. package/dist/phase-recorder.js.map +1 -0
  245. package/dist/phase.d.ts +94 -0
  246. package/dist/phase.d.ts.map +1 -0
  247. package/dist/phase.js +148 -0
  248. package/dist/phase.js.map +1 -0
  249. package/dist/pi-harness.d.ts.map +1 -1
  250. package/dist/pi-harness.js +19 -6
  251. package/dist/pi-harness.js.map +1 -1
  252. package/dist/prepare.d.ts.map +1 -1
  253. package/dist/prepare.js +4 -0
  254. package/dist/prepare.js.map +1 -1
  255. package/dist/prompt.d.ts +4 -0
  256. package/dist/prompt.d.ts.map +1 -1
  257. package/dist/prompt.js +48 -1
  258. package/dist/prompt.js.map +1 -1
  259. package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
  260. package/dist/protocol/WORK-CONTRACT.md +1 -1
  261. package/dist/reap-branches.d.ts +48 -0
  262. package/dist/reap-branches.d.ts.map +1 -1
  263. package/dist/reap-branches.js +64 -0
  264. package/dist/reap-branches.js.map +1 -1
  265. package/dist/ref-write.d.ts +198 -0
  266. package/dist/ref-write.d.ts.map +1 -0
  267. package/dist/ref-write.js +79 -0
  268. package/dist/ref-write.js.map +1 -0
  269. package/dist/repo-config.js +8 -8
  270. package/dist/repo-config.js.map +1 -1
  271. package/dist/repo-mirror.d.ts +36 -0
  272. package/dist/repo-mirror.d.ts.map +1 -1
  273. package/dist/repo-mirror.js +91 -1
  274. package/dist/repo-mirror.js.map +1 -1
  275. package/dist/review-verdict.d.ts +1 -1
  276. package/dist/review-verdict.js +1 -1
  277. package/dist/run.d.ts +7 -1
  278. package/dist/run.d.ts.map +1 -1
  279. package/dist/run.js +28 -4
  280. package/dist/run.js.map +1 -1
  281. package/dist/scan.d.ts +19 -1
  282. package/dist/scan.d.ts.map +1 -1
  283. package/dist/scan.js +21 -8
  284. package/dist/scan.js.map +1 -1
  285. package/dist/sidecar.d.ts +14 -0
  286. package/dist/sidecar.d.ts.map +1 -1
  287. package/dist/sidecar.js +21 -0
  288. package/dist/sidecar.js.map +1 -1
  289. package/dist/single-job-warning.d.ts +63 -0
  290. package/dist/single-job-warning.d.ts.map +1 -0
  291. package/dist/single-job-warning.js +110 -0
  292. package/dist/single-job-warning.js.map +1 -0
  293. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
  294. package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
  295. package/dist/spec-complete.d.ts +13 -6
  296. package/dist/spec-complete.d.ts.map +1 -1
  297. package/dist/spec-complete.js +23 -11
  298. package/dist/spec-complete.js.map +1 -1
  299. package/dist/start.d.ts.map +1 -1
  300. package/dist/start.js +47 -14
  301. package/dist/start.js.map +1 -1
  302. package/dist/status.d.ts +12 -0
  303. package/dist/status.d.ts.map +1 -1
  304. package/dist/status.js +11 -3
  305. package/dist/status.js.map +1 -1
  306. package/dist/tasking.d.ts +246 -1
  307. package/dist/tasking.d.ts.map +1 -1
  308. package/dist/tasking.js +452 -358
  309. package/dist/tasking.js.map +1 -1
  310. package/dist/triage-persist.d.ts.map +1 -1
  311. package/dist/triage-persist.js +21 -2
  312. package/dist/triage-persist.js.map +1 -1
  313. package/dist/verify-workflow-template.d.ts.map +1 -1
  314. package/dist/verify-workflow-template.js +8 -4
  315. package/dist/verify-workflow-template.js.map +1 -1
  316. package/dist/verify.d.ts.map +1 -1
  317. package/dist/verify.js +4 -0
  318. package/dist/verify.js.map +1 -1
  319. package/dist/watch-session.d.ts +42 -13
  320. package/dist/watch-session.d.ts.map +1 -1
  321. package/dist/watch-session.js +52 -23
  322. package/dist/watch-session.js.map +1 -1
  323. package/dist/work-layout.d.ts +10 -4
  324. package/dist/work-layout.d.ts.map +1 -1
  325. package/dist/work-layout.js +11 -3
  326. package/dist/work-layout.js.map +1 -1
  327. package/dist/workspace.d.ts +65 -4
  328. package/dist/workspace.d.ts.map +1 -1
  329. package/dist/workspace.js +159 -11
  330. package/dist/workspace.js.map +1 -1
  331. package/package.json +1 -1
  332. package/src/advance-ci-template.ts +80 -81
  333. package/src/advance-drivers.ts +45 -6
  334. package/src/advance-isolated.ts +3 -5
  335. package/src/advance-lifecycle-template.ts +375 -538
  336. package/src/advance-loop-driver.ts +3 -5
  337. package/src/advance-treeless-publish.ts +61 -0
  338. package/src/advance.ts +145 -15
  339. package/src/advancing-lock.ts +36 -1
  340. package/src/agent-env.ts +174 -0
  341. package/src/agent-stop.ts +81 -0
  342. package/src/apply-merge-action.ts +401 -154
  343. package/src/apply-stuck-action.ts +2 -2
  344. package/src/ci-agent-result.ts +421 -0
  345. package/src/ci-handoff-apply.ts +737 -0
  346. package/src/ci-handoff-format.ts +926 -0
  347. package/src/ci-handoff-lfs.ts +519 -0
  348. package/src/ci-handoff.ts +392 -0
  349. package/src/ci-lock-outputs.ts +273 -0
  350. package/src/ci-phase-build.ts +1328 -0
  351. package/src/ci-phase-driver.ts +548 -0
  352. package/src/ci-phase-intake.ts +861 -0
  353. package/src/ci-phase-tasking.ts +1255 -0
  354. package/src/ci-phase-treeless.ts +2028 -0
  355. package/src/ci-read-token.ts +90 -0
  356. package/src/cli.ts +591 -29
  357. package/src/close-job-template.ts +52 -15
  358. package/src/close-job.ts +77 -12
  359. package/src/complete.ts +19 -1
  360. package/src/config.ts +43 -48
  361. package/src/continue-branch.ts +121 -16
  362. package/src/crashed-run-save.ts +162 -0
  363. package/src/cwd-section.ts +10 -0
  364. package/src/decision-engine.ts +10 -0
  365. package/src/do-config.ts +2 -57
  366. package/src/do.ts +170 -28
  367. package/src/dorfl-item-template.ts +556 -0
  368. package/src/env-config.ts +7 -5
  369. package/src/frontmatter.ts +155 -2
  370. package/src/gc.ts +27 -2
  371. package/src/git.ts +83 -5
  372. package/src/harness.ts +58 -6
  373. package/src/index.ts +50 -1
  374. package/src/install-ci-action-pins.ts +209 -0
  375. package/src/install-ci-actions-pr-creation.ts +186 -0
  376. package/src/install-ci-capabilities/dorfl-item.ts +33 -0
  377. package/src/install-ci-core.ts +388 -233
  378. package/src/install-ci-github.ts +149 -1
  379. package/src/install-ci.ts +83 -37
  380. package/src/intake-marker.ts +6 -1
  381. package/src/intake-triage.ts +7 -3
  382. package/src/intake-trigger-template.ts +126 -327
  383. package/src/intake.ts +725 -544
  384. package/src/integration-core.ts +975 -523
  385. package/src/integrator.ts +28 -0
  386. package/src/isolation.ts +29 -10
  387. package/src/item-lock.ts +308 -92
  388. package/src/land-question-residue.ts +122 -0
  389. package/src/lifecycle-gather.ts +82 -10
  390. package/src/merge-answer.ts +82 -0
  391. package/src/merge-question-surfacer.ts +320 -112
  392. package/src/merge-question-tick.ts +182 -0
  393. package/src/mint-adr.ts +28 -2
  394. package/src/needs-attention.ts +350 -42
  395. package/src/phase-recorder.ts +350 -0
  396. package/src/phase.ts +177 -0
  397. package/src/pi-harness.ts +25 -5
  398. package/src/prepare.ts +4 -0
  399. package/src/prompt.ts +68 -1
  400. package/src/reap-branches.ts +107 -0
  401. package/src/ref-write.ts +324 -0
  402. package/src/repo-config.ts +8 -8
  403. package/src/repo-mirror.ts +107 -1
  404. package/src/review-verdict.ts +1 -1
  405. package/src/run.ts +47 -6
  406. package/src/scan.ts +37 -7
  407. package/src/sidecar.ts +36 -0
  408. package/src/single-job-warning.ts +135 -0
  409. package/src/spec-complete.ts +36 -14
  410. package/src/start.ts +55 -16
  411. package/src/status.ts +29 -3
  412. package/src/tasking.ts +593 -378
  413. package/src/triage-persist.ts +24 -2
  414. package/src/verify-workflow-template.ts +8 -4
  415. package/src/verify.ts +4 -0
  416. package/src/watch-session.ts +86 -28
  417. package/src/work-layout.ts +11 -3
  418. package/src/workspace.ts +210 -14
@@ -0,0 +1,146 @@
1
+ /**
2
+ * **Git LFS objects in the CI handoff** (spec `ci-agent-job-without-write-token`,
3
+ * ADR `ci-agent-job-holds-no-write-token` decision 6, task
4
+ * `ci-split-handoff-lfs-objects`).
5
+ *
6
+ * The agent job commits LFS-tracked paths as pointers (its checkout runs
7
+ * `git lfs install --local`), with the objects under `.git/lfs/objects/`. It
8
+ * holds no write token, so it cannot upload them: the handoff carries them as
9
+ * `lfs/<oid>`, and the apply job pushes them to the arbiter's LFS store BEFORE
10
+ * any ref, so a ref never lands pointing at a missing object.
11
+ *
12
+ * Pointers are found by scanning the blobs the new commits add or change (a
13
+ * blob that parses as a pointer counts, whatever `.gitattributes` says), never
14
+ * by trusting `git lfs ls-files`. Accepted consequence: a legitimate text file
15
+ * that happens to parse as a pointer is treated as one, and its object must be
16
+ * present or the handoff is rejected.
17
+ *
18
+ * - agent side: {@link writeLfsObjects} copies every referenced object from
19
+ * the local store into `lfs/<oid>`, including one the agent copied from
20
+ * elsewhere in the repository;
21
+ * - apply side: {@link checkLfsObjects} runs the strict parser's verdicts
22
+ * against the artifact's objects (present, a regular file, exactly `size`
23
+ * bytes, hashing to `oid`, none unreferenced, within the size limit), and
24
+ * {@link pushLfsObjects} stages the validated objects into the apply
25
+ * checkout's own store (re-hashing every byte it copies) and pushes them with
26
+ * `git lfs push --object-id <arbiter> <oids>` from that checkout of the
27
+ * trusted base, so `.lfsconfig` and the endpoint come from `main` (the
28
+ * bundle cannot change `.lfsconfig`: it is a protected path).
29
+ *
30
+ * Content addressing means a hostile object can only be itself: it cannot
31
+ * overwrite another object.
32
+ */
33
+ /** git-lfs's blob size cutoff: a larger blob is never read as a pointer. */
34
+ export declare const LFS_POINTER_MAX_BYTES = 1024;
35
+ /** A parsed LFS pointer. */
36
+ export interface LfsPointer {
37
+ /** The sha256 of the object, 64 lower-case hex. */
38
+ oid: string;
39
+ /** The object's size in bytes. */
40
+ size: number;
41
+ }
42
+ /**
43
+ * Parse a blob as a spec v1 LFS pointer, STRICTLY: at most
44
+ * {@link LFS_POINTER_MAX_BYTES} bytes, exactly
45
+ * `version https://git-lfs.github.com/spec/v1`, `oid sha256:<64 hex>`,
46
+ * `size <n>` (no leading zero, a safe integer), each ending in `\n`, in that
47
+ * order, nothing else. `undefined` when the blob is not such a pointer.
48
+ */
49
+ export declare function parseLfsPointer(content: Buffer): LfsPointer | undefined;
50
+ /**
51
+ * Whether git-lfs could read a blob as a pointer (see {@link POINTER_LIKE}). A
52
+ * blob that looks like one but does not parse strictly is a MALFORMED pointer,
53
+ * which the apply job rejects rather than treating as an ordinary file.
54
+ */
55
+ export declare function looksLikeLfsPointer(content: Buffer): boolean;
56
+ /** A regular-file blob a new commit adds or changes, with one path it has. */
57
+ export interface ChangedBlob {
58
+ oid: string;
59
+ path: string;
60
+ }
61
+ /** A pointer found in the new commits, with a path that carries it. */
62
+ export interface FoundPointer extends LfsPointer {
63
+ path: string;
64
+ }
65
+ /** What {@link scanLfsPointers} found. */
66
+ export interface LfsScan {
67
+ pointers: FoundPointer[];
68
+ /** Paths of blobs that look like pointers but do not parse strictly. */
69
+ malformed: string[];
70
+ }
71
+ /** Whether a tree entry mode is a regular file (LFS applies to nothing else). */
72
+ export declare function isRegularFileMode(mode: string): boolean;
73
+ /**
74
+ * Scan `blobs` (read in `cwd`) for LFS pointers: every blob of at most
75
+ * {@link LFS_POINTER_MAX_BYTES} bytes is read, in one `cat-file --batch`.
76
+ */
77
+ export declare function scanLfsPointers(params: {
78
+ cwd: string;
79
+ blobs: readonly ChangedBlob[];
80
+ env?: NodeJS.ProcessEnv;
81
+ }): LfsScan;
82
+ /**
83
+ * The regular-file blobs `commits` add or change against their first parent
84
+ * (a root commit against the empty tree). Deletions carry no blob.
85
+ */
86
+ export declare function changedFileBlobs(cwd: string, commits: readonly string[], env?: NodeJS.ProcessEnv): ChangedBlob[];
87
+ /**
88
+ * The repository's local LFS object directory: `<storage>/objects`, where the
89
+ * storage is `lfs.storage` (relative to the git directory) or `<git dir>/lfs`.
90
+ */
91
+ export declare function localLfsObjectsDir(repo: string, env?: NodeJS.ProcessEnv): string;
92
+ /** Where the store keeps `oid`: `<objects>/<oid[0:2]>/<oid[2:4]>/<oid>`. */
93
+ export declare function lfsObjectPath(objectsDir: string, oid: string): string;
94
+ /**
95
+ * Copy into `<dir>/lfs/<oid>` the object of every pointer the commits
96
+ * `<ref> ^<baseSha>` add or change, from `repo`'s local store. An object the
97
+ * store lacks is left out: the apply job then rejects the handoff naming it
98
+ * (that is also the fate of a text file that parses as a pointer). Refuses
99
+ * ({@link HandoffRejected} `size`) objects over the LFS limit together.
100
+ * Returns the oids left out.
101
+ */
102
+ export declare function writeLfsObjects(params: {
103
+ dir: string;
104
+ repo: string;
105
+ ref: string;
106
+ baseSha: string;
107
+ env?: NodeJS.ProcessEnv;
108
+ }): {
109
+ missing: string[];
110
+ };
111
+ /** An object file of the artifact (`lfs/<oid>`, already a named regular file). */
112
+ export interface LfsArtifactObject {
113
+ oid: string;
114
+ path: string;
115
+ size: number;
116
+ }
117
+ /**
118
+ * Check the artifact's LFS objects against the pointers the new commits carry
119
+ * (decision 6). Rejects ({@link HandoffRejected}): a blob that looks like a
120
+ * pointer but does not parse strictly (`lfs-pointer`); two pointers giving one
121
+ * oid different sizes, or an object whose size or hash differs from its
122
+ * pointer (`lfs-mismatch`); a pointer whose object is not in `lfs/`
123
+ * (`lfs-missing`); an object no pointer references (`lfs-extra`); a pointer, or
124
+ * all of them together, over the LFS limit (`size`). Returns the referenced
125
+ * objects, sorted by oid.
126
+ */
127
+ export declare function checkLfsObjects(scan: LfsScan, objects: readonly LfsArtifactObject[]): LfsArtifactObject[];
128
+ /**
129
+ * Stage the validated objects into `cwd`'s own LFS store and push them to
130
+ * `arbiter` with `git lfs push --object-id`, BEFORE any ref is pushed. `cwd`
131
+ * is the apply job's checkout of the trusted base, so `.lfsconfig` and the
132
+ * endpoint come from `main`. Every byte staged is hashed again as it is copied
133
+ * (a file that changed since validation rejects, `lfs-mismatch`); an object
134
+ * the store already holds is not copied. The upload itself is the write seam
135
+ * `refWrite.pushLfsObjects`. Returns the push's status and stderr.
136
+ */
137
+ export declare function pushLfsObjects(params: {
138
+ cwd: string;
139
+ arbiter: string;
140
+ objects: readonly LfsArtifactObject[];
141
+ env?: NodeJS.ProcessEnv;
142
+ }): Promise<{
143
+ status: number;
144
+ stderr: string;
145
+ }>;
146
+ //# sourceMappingURL=ci-handoff-lfs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-handoff-lfs.d.ts","sourceRoot":"","sources":["../src/ci-handoff-lfs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAyBH,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAsB1C,4BAA4B;AAC5B,MAAM,WAAW,UAAU;IAC1B,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAOvE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK5D;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC/C,IAAI,EAAE,MAAM,CAAC;CACb;AAED,0CAA0C;AAC1C,MAAM,WAAW,OAAO;IACvB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAoDV;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACrB,WAAW,EAAE,CAgCf;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACrB,MAAM,CAgBR;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAMD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC,CAuCtB;AAMD,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAyCD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC9B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACnC,iBAAiB,EAAE,CAyErB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,CAY5C"}
@@ -0,0 +1,397 @@
1
+ /**
2
+ * **Git LFS objects in the CI handoff** (spec `ci-agent-job-without-write-token`,
3
+ * ADR `ci-agent-job-holds-no-write-token` decision 6, task
4
+ * `ci-split-handoff-lfs-objects`).
5
+ *
6
+ * The agent job commits LFS-tracked paths as pointers (its checkout runs
7
+ * `git lfs install --local`), with the objects under `.git/lfs/objects/`. It
8
+ * holds no write token, so it cannot upload them: the handoff carries them as
9
+ * `lfs/<oid>`, and the apply job pushes them to the arbiter's LFS store BEFORE
10
+ * any ref, so a ref never lands pointing at a missing object.
11
+ *
12
+ * Pointers are found by scanning the blobs the new commits add or change (a
13
+ * blob that parses as a pointer counts, whatever `.gitattributes` says), never
14
+ * by trusting `git lfs ls-files`. Accepted consequence: a legitimate text file
15
+ * that happens to parse as a pointer is treated as one, and its object must be
16
+ * present or the handoff is rejected.
17
+ *
18
+ * - agent side: {@link writeLfsObjects} copies every referenced object from
19
+ * the local store into `lfs/<oid>`, including one the agent copied from
20
+ * elsewhere in the repository;
21
+ * - apply side: {@link checkLfsObjects} runs the strict parser's verdicts
22
+ * against the artifact's objects (present, a regular file, exactly `size`
23
+ * bytes, hashing to `oid`, none unreferenced, within the size limit), and
24
+ * {@link pushLfsObjects} stages the validated objects into the apply
25
+ * checkout's own store (re-hashing every byte it copies) and pushes them with
26
+ * `git lfs push --object-id <arbiter> <oids>` from that checkout of the
27
+ * trusted base, so `.lfsconfig` and the endpoint come from `main` (the
28
+ * bundle cannot change `.lfsconfig`: it is a protected path).
29
+ *
30
+ * Content addressing means a hostile object can only be itself: it cannot
31
+ * overwrite another object.
32
+ */
33
+ import { createHash } from 'node:crypto';
34
+ import { closeSync, constants as fsConstants, copyFileSync, existsSync, fstatSync, mkdirSync, openSync, readSync, renameSync, rmSync, statSync, writeSync, } from 'node:fs';
35
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
36
+ import { handoffByteLimits, rejectHandoff as reject, } from './ci-handoff-format.js';
37
+ import { git, gitBytes, run } from './git.js';
38
+ import { refWrite } from './ref-write.js';
39
+ /** git-lfs's blob size cutoff: a larger blob is never read as a pointer. */
40
+ export const LFS_POINTER_MAX_BYTES = 1024;
41
+ /**
42
+ * The directory of the artifact that holds the objects (`lfs/<oid>`): the
43
+ * same name as `HANDOFF_LFS_DIR` in `ci-handoff.ts`, which imports this module.
44
+ */
45
+ const LFS_ARTIFACT_DIR = 'lfs';
46
+ /** The one pointer form accepted: spec v1, keys in order, nothing else. */
47
+ const STRICT_POINTER = /^version https:\/\/git-lfs\.github\.com\/spec\/v1\noid sha256:([0-9a-f]{64})\nsize (0|[1-9][0-9]*)\n$/;
48
+ /**
49
+ * What git-lfs itself could read as a pointer: a first key `version` naming an
50
+ * LFS spec (current or the legacy `hawser` one), after leading whitespace.
51
+ */
52
+ const POINTER_LIKE = /^[ \t\r\n]*version https:\/\/(?:git-lfs|hawser)\.github\.com\/spec\//;
53
+ const MODE_FILE = '100644';
54
+ const MODE_EXEC = '100755';
55
+ /**
56
+ * Parse a blob as a spec v1 LFS pointer, STRICTLY: at most
57
+ * {@link LFS_POINTER_MAX_BYTES} bytes, exactly
58
+ * `version https://git-lfs.github.com/spec/v1`, `oid sha256:<64 hex>`,
59
+ * `size <n>` (no leading zero, a safe integer), each ending in `\n`, in that
60
+ * order, nothing else. `undefined` when the blob is not such a pointer.
61
+ */
62
+ export function parseLfsPointer(content) {
63
+ if (content.length > LFS_POINTER_MAX_BYTES)
64
+ return undefined;
65
+ const m = STRICT_POINTER.exec(content.toString('latin1'));
66
+ if (m === null)
67
+ return undefined;
68
+ const size = Number(m[2]);
69
+ if (!Number.isSafeInteger(size))
70
+ return undefined;
71
+ return { oid: m[1], size };
72
+ }
73
+ /**
74
+ * Whether git-lfs could read a blob as a pointer (see {@link POINTER_LIKE}). A
75
+ * blob that looks like one but does not parse strictly is a MALFORMED pointer,
76
+ * which the apply job rejects rather than treating as an ordinary file.
77
+ */
78
+ export function looksLikeLfsPointer(content) {
79
+ if (content.length === 0 || content.length > LFS_POINTER_MAX_BYTES) {
80
+ return false;
81
+ }
82
+ return POINTER_LIKE.test(content.toString('latin1'));
83
+ }
84
+ /** Whether a tree entry mode is a regular file (LFS applies to nothing else). */
85
+ export function isRegularFileMode(mode) {
86
+ return mode === MODE_FILE || mode === MODE_EXEC;
87
+ }
88
+ /**
89
+ * Scan `blobs` (read in `cwd`) for LFS pointers: every blob of at most
90
+ * {@link LFS_POINTER_MAX_BYTES} bytes is read, in one `cat-file --batch`.
91
+ */
92
+ export function scanLfsPointers(params) {
93
+ const { cwd, env } = params;
94
+ const pathOf = new Map();
95
+ for (const b of params.blobs) {
96
+ if (!pathOf.has(b.oid))
97
+ pathOf.set(b.oid, b.path);
98
+ }
99
+ if (pathOf.size === 0)
100
+ return { pointers: [], malformed: [] };
101
+ const small = [];
102
+ const checks = git(['cat-file', '--batch-check=%(objectname) %(objecttype) %(objectsize)'], cwd, { env, input: [...pathOf.keys()].join('\n') + '\n' })
103
+ .trim()
104
+ .split('\n');
105
+ for (const line of checks) {
106
+ const [oid, type, size] = line.split(' ');
107
+ const n = Number(size);
108
+ if (type === 'blob' && n > 0 && n <= LFS_POINTER_MAX_BYTES)
109
+ small.push(oid);
110
+ }
111
+ if (small.length === 0)
112
+ return { pointers: [], malformed: [] };
113
+ const out = gitBytes(['cat-file', '--batch'], cwd, {
114
+ env,
115
+ input: small.join('\n') + '\n',
116
+ });
117
+ const pointers = [];
118
+ const malformed = [];
119
+ let at = 0;
120
+ for (let i = 0; i < small.length; i++) {
121
+ const eol = out.indexOf(0x0a, at);
122
+ if (eol === -1)
123
+ throw new Error('git cat-file --batch: truncated output');
124
+ const [oid, type, size] = out
125
+ .subarray(at, eol)
126
+ .toString('latin1')
127
+ .split(' ');
128
+ const n = Number(size);
129
+ if (type !== 'blob' || !Number.isSafeInteger(n)) {
130
+ throw new Error(`git cat-file --batch: unexpected header for ${oid}`);
131
+ }
132
+ const content = out.subarray(eol + 1, eol + 1 + n);
133
+ at = eol + 1 + n + 1;
134
+ const path = pathOf.get(oid) ?? oid;
135
+ const pointer = parseLfsPointer(content);
136
+ if (pointer !== undefined) {
137
+ pointers.push({ ...pointer, path });
138
+ }
139
+ else if (looksLikeLfsPointer(content)) {
140
+ malformed.push(path);
141
+ }
142
+ }
143
+ return { pointers, malformed };
144
+ }
145
+ /**
146
+ * The regular-file blobs `commits` add or change against their first parent
147
+ * (a root commit against the empty tree). Deletions carry no blob.
148
+ */
149
+ export function changedFileBlobs(cwd, commits, env) {
150
+ if (commits.length === 0)
151
+ return [];
152
+ const out = git([
153
+ '-c',
154
+ 'diff.renames=false',
155
+ 'diff-tree',
156
+ '--stdin',
157
+ '--root',
158
+ '-r',
159
+ '-z',
160
+ '--raw',
161
+ '--no-renames',
162
+ '--no-commit-id',
163
+ ], cwd, { env, input: commits.join('\n') + '\n' });
164
+ const fields = out.split('\0');
165
+ const blobs = [];
166
+ for (let i = 0; i + 1 < fields.length; i += 2) {
167
+ const meta = fields[i];
168
+ if (!meta.startsWith(':')) {
169
+ // `--stdin` may echo a commit id line; skip it.
170
+ i -= 1;
171
+ continue;
172
+ }
173
+ const [, newMode, , newOid, status] = meta.slice(1).split(' ');
174
+ if (status === 'D' || !isRegularFileMode(newMode))
175
+ continue;
176
+ blobs.push({ oid: newOid, path: fields[i + 1] });
177
+ }
178
+ return blobs;
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // The local object store
182
+ // ---------------------------------------------------------------------------
183
+ /**
184
+ * The repository's local LFS object directory: `<storage>/objects`, where the
185
+ * storage is `lfs.storage` (relative to the git directory) or `<git dir>/lfs`.
186
+ */
187
+ export function localLfsObjectsDir(repo, env) {
188
+ const common = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], repo, { env }).trim();
189
+ const custom = run('git', ['config', '--get', 'lfs.storage'], repo, {
190
+ env,
191
+ }).stdout.trim();
192
+ const storage = custom === ''
193
+ ? join(common, 'lfs')
194
+ : isAbsolute(custom)
195
+ ? custom
196
+ : resolve(common, custom);
197
+ return join(storage, 'objects');
198
+ }
199
+ /** Where the store keeps `oid`: `<objects>/<oid[0:2]>/<oid[2:4]>/<oid>`. */
200
+ export function lfsObjectPath(objectsDir, oid) {
201
+ return join(objectsDir, oid.slice(0, 2), oid.slice(2, 4), oid);
202
+ }
203
+ // ---------------------------------------------------------------------------
204
+ // Agent side
205
+ // ---------------------------------------------------------------------------
206
+ /**
207
+ * Copy into `<dir>/lfs/<oid>` the object of every pointer the commits
208
+ * `<ref> ^<baseSha>` add or change, from `repo`'s local store. An object the
209
+ * store lacks is left out: the apply job then rejects the handoff naming it
210
+ * (that is also the fate of a text file that parses as a pointer). Refuses
211
+ * ({@link HandoffRejected} `size`) objects over the LFS limit together.
212
+ * Returns the oids left out.
213
+ */
214
+ export function writeLfsObjects(params) {
215
+ const { dir, repo, env } = params;
216
+ const commits = git(['rev-list', params.ref, `^${params.baseSha}`], repo, {
217
+ env,
218
+ })
219
+ .split('\n')
220
+ .filter((l) => l !== '');
221
+ const { pointers } = scanLfsPointers({
222
+ cwd: repo,
223
+ blobs: changedFileBlobs(repo, commits, env),
224
+ env,
225
+ });
226
+ const oids = [...new Set(pointers.map((p) => p.oid))].sort();
227
+ if (oids.length === 0)
228
+ return { missing: [] };
229
+ const store = localLfsObjectsDir(repo, env);
230
+ const present = [];
231
+ const missing = [];
232
+ let total = 0;
233
+ for (const oid of oids) {
234
+ const src = lfsObjectPath(store, oid);
235
+ if (!existsSync(src) || !statSync(src).isFile()) {
236
+ missing.push(oid);
237
+ continue;
238
+ }
239
+ total += statSync(src).size;
240
+ present.push(oid);
241
+ }
242
+ const limit = handoffByteLimits().lfsBytes;
243
+ if (total > limit) {
244
+ reject('size', `the LFS objects are over ${limit} bytes`);
245
+ }
246
+ if (present.length > 0) {
247
+ mkdirSync(join(dir, LFS_ARTIFACT_DIR), { recursive: true });
248
+ for (const oid of present) {
249
+ copyFileSync(lfsObjectPath(store, oid), join(dir, LFS_ARTIFACT_DIR, oid));
250
+ }
251
+ }
252
+ return { missing };
253
+ }
254
+ function q(path) {
255
+ return JSON.stringify(path.slice(0, 300));
256
+ }
257
+ /**
258
+ * Stream a regular file (refusing a symlink at open time) through sha256,
259
+ * reading at most `max + 1` bytes; optionally copy every byte read to `outFd`.
260
+ */
261
+ function hashFile(path, max, outFd) {
262
+ let fd;
263
+ try {
264
+ fd = openSync(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
265
+ }
266
+ catch {
267
+ reject('symlink', `${q(path)} could not be opened as a regular file`);
268
+ }
269
+ try {
270
+ if (!fstatSync(fd).isFile()) {
271
+ reject('layout', `${q(path)} is not a regular file`);
272
+ }
273
+ const hash = createHash('sha256');
274
+ const buf = Buffer.alloc(1024 * 1024);
275
+ let size = 0;
276
+ while (size <= max) {
277
+ const n = readSync(fd, buf, 0, buf.length, null);
278
+ if (n === 0)
279
+ break;
280
+ hash.update(buf.subarray(0, n));
281
+ if (outFd !== undefined)
282
+ writeSync(outFd, buf, 0, n);
283
+ size += n;
284
+ }
285
+ return { sha256: hash.digest('hex'), size };
286
+ }
287
+ finally {
288
+ closeSync(fd);
289
+ }
290
+ }
291
+ /**
292
+ * Check the artifact's LFS objects against the pointers the new commits carry
293
+ * (decision 6). Rejects ({@link HandoffRejected}): a blob that looks like a
294
+ * pointer but does not parse strictly (`lfs-pointer`); two pointers giving one
295
+ * oid different sizes, or an object whose size or hash differs from its
296
+ * pointer (`lfs-mismatch`); a pointer whose object is not in `lfs/`
297
+ * (`lfs-missing`); an object no pointer references (`lfs-extra`); a pointer, or
298
+ * all of them together, over the LFS limit (`size`). Returns the referenced
299
+ * objects, sorted by oid.
300
+ */
301
+ export function checkLfsObjects(scan, objects) {
302
+ if (scan.malformed.length > 0) {
303
+ reject('lfs-pointer', `the file ${q(scan.malformed[0])} looks like a Git LFS pointer but is not a ` +
304
+ 'strict spec v1 pointer (version, oid sha256:<64 hex>, size, in order, nothing else)');
305
+ }
306
+ const limit = handoffByteLimits().lfsBytes;
307
+ const want = new Map();
308
+ let total = 0;
309
+ for (const p of scan.pointers) {
310
+ if (p.size > limit) {
311
+ reject('size', `the LFS pointer ${q(p.path)} names a ${p.size}-byte object, over the ` +
312
+ `LFS limit of ${limit} bytes`);
313
+ }
314
+ const seen = want.get(p.oid);
315
+ if (seen !== undefined) {
316
+ if (seen.size !== p.size) {
317
+ reject('lfs-mismatch', `the LFS pointers ${q(seen.path)} and ${q(p.path)} give the object ` +
318
+ `${p.oid} different sizes`);
319
+ }
320
+ continue;
321
+ }
322
+ want.set(p.oid, p);
323
+ total += p.size;
324
+ }
325
+ if (total > limit) {
326
+ reject('size', `the LFS objects are over ${limit} bytes`);
327
+ }
328
+ const byOid = new Map(objects.map((o) => [o.oid, o]));
329
+ for (const o of objects) {
330
+ if (!want.has(o.oid)) {
331
+ reject('lfs-extra', `lfs/${o.oid} is referenced by no LFS pointer of the new commits`);
332
+ }
333
+ }
334
+ const referenced = [];
335
+ for (const [oid, p] of [...want].sort(([a], [b]) => a.localeCompare(b))) {
336
+ const obj = byOid.get(oid);
337
+ if (obj === undefined) {
338
+ reject('lfs-missing', `the LFS object ${oid} of ${q(p.path)} is not in lfs/ (a text file ` +
339
+ 'that parses as an LFS pointer counts as one)');
340
+ }
341
+ const got = hashFile(obj.path, p.size);
342
+ if (got.size !== p.size) {
343
+ reject('lfs-mismatch', `lfs/${oid} is ${got.size > p.size ? 'over ' : ''}${got.size} bytes, but ` +
344
+ `its pointer ${q(p.path)} says ${p.size} bytes`);
345
+ }
346
+ if (got.sha256 !== oid) {
347
+ reject('lfs-mismatch', `lfs/${oid} does not hash to its oid (its sha256 hash is ${got.sha256})`);
348
+ }
349
+ referenced.push(obj);
350
+ }
351
+ return referenced;
352
+ }
353
+ /**
354
+ * Stage the validated objects into `cwd`'s own LFS store and push them to
355
+ * `arbiter` with `git lfs push --object-id`, BEFORE any ref is pushed. `cwd`
356
+ * is the apply job's checkout of the trusted base, so `.lfsconfig` and the
357
+ * endpoint come from `main`. Every byte staged is hashed again as it is copied
358
+ * (a file that changed since validation rejects, `lfs-mismatch`); an object
359
+ * the store already holds is not copied. The upload itself is the write seam
360
+ * `refWrite.pushLfsObjects`. Returns the push's status and stderr.
361
+ */
362
+ export async function pushLfsObjects(params) {
363
+ const { cwd, arbiter, objects, env } = params;
364
+ if (objects.length === 0)
365
+ return { status: 0, stderr: '' };
366
+ const store = localLfsObjectsDir(cwd, env);
367
+ for (const o of objects)
368
+ stageObject(store, o);
369
+ const r = await refWrite.pushLfsObjects({
370
+ arbiter,
371
+ oids: objects.map((o) => o.oid),
372
+ cwd,
373
+ env,
374
+ });
375
+ return { status: r.status, stderr: r.stderr };
376
+ }
377
+ function stageObject(store, o) {
378
+ const dest = lfsObjectPath(store, o.oid);
379
+ if (existsSync(dest))
380
+ return;
381
+ mkdirSync(dirname(dest), { recursive: true });
382
+ const tmp = `${dest}.dorfl-${process.pid}.tmp`;
383
+ const out = openSync(tmp, 'w', 0o644);
384
+ let got;
385
+ try {
386
+ got = hashFile(o.path, o.size, out);
387
+ }
388
+ finally {
389
+ closeSync(out);
390
+ }
391
+ if (got.size !== o.size || got.sha256 !== o.oid) {
392
+ rmSync(tmp, { force: true });
393
+ reject('lfs-mismatch', `lfs/${o.oid} changed after it was validated`);
394
+ }
395
+ renameSync(tmp, dest);
396
+ }
397
+ //# sourceMappingURL=ci-handoff-lfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-handoff-lfs.js","sourceRoot":"","sources":["../src/ci-handoff-lfs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * **The CI handoff artifact on disk**: the writer (agent job) and the safe
3
+ * reader (apply job). The record format and its intent table are
4
+ * `ci-handoff-format.ts`.
5
+ *
6
+ * The artifact directory holds exactly:
7
+ *
8
+ * - `handoff.json`: the record;
9
+ * - `work.bundle`: for code-carrying intents only, `git bundle create
10
+ * work.bundle <work-branch> ^<baseSha>`, exactly one ref;
11
+ * - `lfs/<oid>`: the object of every LFS pointer the bundle's new commits
12
+ * add or change (`ci-handoff-lfs.ts`; the reader here checks only their
13
+ * names and sizes, the apply validation checks them against the pointers).
14
+ *
15
+ * The reader treats the directory as HOSTILE (the agent controls everything in
16
+ * its job): it must sit under `$RUNNER_TEMP`, only those names are read, a
17
+ * symlink or any other entry rejects the whole handoff, files are opened with
18
+ * `O_NOFOLLOW`, and every size limit is checked before anything is parsed. The
19
+ * bundle header is parsed here without running git on hostile bytes; the
20
+ * bundle's CONTENT (history, protected paths) is task
21
+ * `ci-split-apply-rejects-hostile-bundle`.
22
+ */
23
+ import { type HandoffRecord, type HandoffRung, type HandoffTrust, type IntentRow } from './ci-handoff-format.js';
24
+ /** The record file of the artifact. */
25
+ export declare const HANDOFF_JSON = "handoff.json";
26
+ /** The git bundle of the work branch (code-carrying intents only). */
27
+ export declare const HANDOFF_BUNDLE = "work.bundle";
28
+ /** The directory of LFS objects, one file per oid. */
29
+ export declare const HANDOFF_LFS_DIR = "lfs";
30
+ /** What a bundle header says: its one ref, the ref's tip and the prerequisites. */
31
+ export interface BundleHeader {
32
+ ref: string;
33
+ tip: string;
34
+ prerequisites: string[];
35
+ }
36
+ /**
37
+ * Parse the header of a git bundle (v2, or v3 with only `@object-format`)
38
+ * without running git, and require exactly one ref.
39
+ */
40
+ export declare function parseBundleHeader(head: Buffer): BundleHeader;
41
+ /** What {@link writeHandoff} needs to also write `work.bundle`. */
42
+ export interface HandoffBundleSource {
43
+ /** The repository (the agent job's checkout). */
44
+ repo: string;
45
+ /** The work branch name; the bundle's one ref is `refs/heads/<workBranch>`. */
46
+ workBranch: string;
47
+ /** The trusted base: the bundle holds `<workBranch> ^<baseSha>`. */
48
+ baseSha: string;
49
+ }
50
+ /**
51
+ * Write the handoff into `dir` (created, and required to be empty): validate
52
+ * the record exactly as the reader will, then write `handoff.json` and, for a
53
+ * code-carrying intent, `work.bundle` with exactly one ref, and `lfs/<oid>` for
54
+ * the object of every LFS pointer its new commits add or change (from the
55
+ * local store; one the store lacks is left out and returned in `lfsMissing`,
56
+ * and the apply job then rejects the handoff naming it). The record's item is
57
+ * written in its canonical form.
58
+ */
59
+ export declare function writeHandoff(params: {
60
+ dir: string;
61
+ rung: HandoffRung;
62
+ record: HandoffRecord;
63
+ bundle?: HandoffBundleSource;
64
+ }): {
65
+ lfsMissing: string[];
66
+ };
67
+ /** An LFS object file of the artifact (checked against pointers elsewhere). */
68
+ export interface HandoffLfsObject {
69
+ oid: string;
70
+ path: string;
71
+ size: number;
72
+ }
73
+ /** A read, validated handoff. */
74
+ export interface ReadHandoff {
75
+ record: HandoffRecord;
76
+ /** The intent table row the record was validated against. */
77
+ row: IntentRow;
78
+ /** `work.bundle` with its parsed header, when the artifact carries one. */
79
+ bundle?: {
80
+ path: string;
81
+ size: number;
82
+ } & BundleHeader;
83
+ lfsObjects: HandoffLfsObject[];
84
+ }
85
+ /**
86
+ * Read and validate the handoff the agent job uploaded. `dir` is where the
87
+ * artifact was downloaded: a real directory strictly inside `runnerTemp`
88
+ * (`$RUNNER_TEMP`). Rejects ({@link HandoffRejected}) a symlink anywhere, any
89
+ * entry but `handoff.json`, `work.bundle` and `lfs/<64-hex oid>`, any size over
90
+ * the byte limits (`handoffByteLimits`), a record that fails `validateHandoffRecord`, a bundle
91
+ * the intent forbids or lacks, `lfs/` without a bundle, and a bundle whose
92
+ * header is malformed or carries other than one ref.
93
+ */
94
+ export declare function readHandoff(params: {
95
+ dir: string;
96
+ runnerTemp: string;
97
+ trust: HandoffTrust;
98
+ }): ReadHandoff;
99
+ //# sourceMappingURL=ci-handoff.d.ts.map