@ebrindley/build 0.0.0-stage → 1.42.1-rc28
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.
- package/.build-project/providers/catalog.json +13951 -0
- package/.build-project/providers/pricing.json +1978 -0
- package/.build-project/providers/registry.json +4103 -0
- package/.github/SECURITY.md +75 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +661 -0
- package/CODE_OF_CONDUCT.md +44 -0
- package/CONTRIBUTING.md +24 -0
- package/INSTALL.md +623 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/THIRD_PARTY_NOTICES.txt +26 -0
- package/dist/ai-judge-UE5UOYOO.js +191 -0
- package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
- package/dist/allowlist-utils-MZZKKXRD.js +31 -0
- package/dist/apply-completion-mode-EM37UZHB.js +110 -0
- package/dist/artifact-migrator-WPACNSEP.js +276 -0
- package/dist/ask-2OZ4JFQR.js +176 -0
- package/dist/auth-6UKBTJUQ.js +185 -0
- package/dist/auth-H5KJL6HL.js +165 -0
- package/dist/auth-LVJRJDMU.js +12 -0
- package/dist/auth-NHZOCNRR.js +94 -0
- package/dist/auth-PBXHWXGW.js +95 -0
- package/dist/auth-T7Y5DCAH.js +91 -0
- package/dist/auth-TVVKCHQW.js +13 -0
- package/dist/auth-UHB2UCSS.js +97 -0
- package/dist/auth-VACA3VUL.js +98 -0
- package/dist/auth-VVLC77S3.js +44 -0
- package/dist/auth-liveness-XO4AGDDD.js +53 -0
- package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
- package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
- package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
- package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
- package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
- package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
- package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
- package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
- package/dist/autoloop-success-C32QVWOP.js +93 -0
- package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
- package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
- package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
- package/dist/backend-YUTFDZXZ.js +30 -0
- package/dist/background-executor-Z3ODTMD7.js +260 -0
- package/dist/backlog-75QPEAMW.js +403 -0
- package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
- package/dist/backup-active-store-XHWLN34M.js +16 -0
- package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
- package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
- package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
- package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
- package/dist/build-gate-7NKWPY62.js +68 -0
- package/dist/build-root-S7DINODY.js +18 -0
- package/dist/change-summary-R6CB6VXJ.js +50 -0
- package/dist/chunk-24MNKKOM.js +173 -0
- package/dist/chunk-2H6VZFIO.js +235 -0
- package/dist/chunk-2JIX3EKV.js +39 -0
- package/dist/chunk-2KO7HVZM.js +106 -0
- package/dist/chunk-2L36U25O.js +100 -0
- package/dist/chunk-2NMNFUQA.js +475 -0
- package/dist/chunk-2OUAEH67.js +83 -0
- package/dist/chunk-2VLJUXJU.js +115 -0
- package/dist/chunk-2VUEDIUM.js +77 -0
- package/dist/chunk-2Y2IIHPI.js +430 -0
- package/dist/chunk-2YRG3LHZ.js +220 -0
- package/dist/chunk-2Z5Q3SUY.js +65 -0
- package/dist/chunk-32IYXIE5.js +873 -0
- package/dist/chunk-33IGPPUI.js +292 -0
- package/dist/chunk-352PCJLK.js +283 -0
- package/dist/chunk-35J4NE6S.js +226 -0
- package/dist/chunk-3C7WBIVK.js +1016 -0
- package/dist/chunk-3H3EY6U7.js +1225 -0
- package/dist/chunk-3H4MWYIC.js +198 -0
- package/dist/chunk-3HHWQR3N.js +2479 -0
- package/dist/chunk-3JKNRYEE.js +14 -0
- package/dist/chunk-3LECCYZY.js +128 -0
- package/dist/chunk-3LOY6YZI.js +852 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3NOTLVLG.js +3803 -0
- package/dist/chunk-3OPP2OZB.js +1480 -0
- package/dist/chunk-3P5IN5E7.js +9 -0
- package/dist/chunk-3PL22IYZ.js +20 -0
- package/dist/chunk-3REDAJUJ.js +14 -0
- package/dist/chunk-3S2R4XJI.js +203 -0
- package/dist/chunk-3UBZI5DW.js +61 -0
- package/dist/chunk-3VV5OBGG.js +308 -0
- package/dist/chunk-3WY5TU2O.js +823 -0
- package/dist/chunk-43OKHTW4.js +82 -0
- package/dist/chunk-45KUJBDI.js +112 -0
- package/dist/chunk-47X5LM7B.js +224 -0
- package/dist/chunk-4B6FQJ77.js +268 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4DJL2VXA.js +24 -0
- package/dist/chunk-4EBSXZ73.js +48 -0
- package/dist/chunk-4QKP6I4C.js +294 -0
- package/dist/chunk-4U6XK2DG.js +300 -0
- package/dist/chunk-4UMRUFBD.js +132 -0
- package/dist/chunk-4US4SYJ4.js +569 -0
- package/dist/chunk-4ZPLWEZO.js +154 -0
- package/dist/chunk-56WPYDJH.js +624 -0
- package/dist/chunk-57WRCJJW.js +50 -0
- package/dist/chunk-5AW4RMNG.js +24 -0
- package/dist/chunk-5EQN56OZ.js +155 -0
- package/dist/chunk-5F43TUIN.js +38 -0
- package/dist/chunk-5FKHIKTS.js +115 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5KQETTNS.js +152 -0
- package/dist/chunk-5PDVN2KO.js +498 -0
- package/dist/chunk-5UBIFVRD.js +509 -0
- package/dist/chunk-5YDE3Q62.js +46 -0
- package/dist/chunk-626AJVMU.js +302 -0
- package/dist/chunk-633XZ7UF.js +3583 -0
- package/dist/chunk-63DHNHGA.js +236 -0
- package/dist/chunk-666656AR.js +568 -0
- package/dist/chunk-67YRJPJO.js +19 -0
- package/dist/chunk-6CAU7VBA.js +20 -0
- package/dist/chunk-6CB3KJRY.js +21 -0
- package/dist/chunk-6DBMALXL.js +29 -0
- package/dist/chunk-6E25C4IA.js +269 -0
- package/dist/chunk-6FIBNIQU.js +73 -0
- package/dist/chunk-6G57FYBV.js +189 -0
- package/dist/chunk-6KZKRTG3.js +4885 -0
- package/dist/chunk-6LAH2UV2.js +525 -0
- package/dist/chunk-6PZY2TTU.js +921 -0
- package/dist/chunk-6T6NHXK4.js +230 -0
- package/dist/chunk-6TBEX4R4.js +432 -0
- package/dist/chunk-6TRD7TEE.js +911 -0
- package/dist/chunk-6XCP2RAI.js +47 -0
- package/dist/chunk-726G6WDL.js +351 -0
- package/dist/chunk-75EMT7BF.js +616 -0
- package/dist/chunk-762ACDZJ.js +4696 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DJ6SQH3.js +510 -0
- package/dist/chunk-7HPCIOMA.js +1383 -0
- package/dist/chunk-7HYBSRPZ.js +17503 -0
- package/dist/chunk-7KBEOPVB.js +73 -0
- package/dist/chunk-7LHJDJB3.js +2222 -0
- package/dist/chunk-7NVJMH52.js +306 -0
- package/dist/chunk-7RMARDVH.js +33 -0
- package/dist/chunk-7SEWU4QG.js +36 -0
- package/dist/chunk-7TUTZTI2.js +911 -0
- package/dist/chunk-7VBDFPUM.js +340 -0
- package/dist/chunk-7VRJHTGZ.js +6406 -0
- package/dist/chunk-7YAOUUKK.js +493 -0
- package/dist/chunk-AAAODV76.js +860 -0
- package/dist/chunk-AB4HUJVQ.js +339 -0
- package/dist/chunk-AEQWV746.js +611 -0
- package/dist/chunk-AHU3FKPA.js +11639 -0
- package/dist/chunk-AISTV5XU.js +226 -0
- package/dist/chunk-AJBTKGJZ.js +48 -0
- package/dist/chunk-AJJNW7XT.js +248 -0
- package/dist/chunk-AJN5Y5YZ.js +160 -0
- package/dist/chunk-ALDTFNZ3.js +150 -0
- package/dist/chunk-ARARVIZK.js +281 -0
- package/dist/chunk-ATBM6AW5.js +53 -0
- package/dist/chunk-ATKEKM3E.js +206 -0
- package/dist/chunk-AUJLW6JZ.js +92 -0
- package/dist/chunk-AY3MHEFM.js +24 -0
- package/dist/chunk-B7PYTV72.js +236 -0
- package/dist/chunk-B7YRSULE.js +1423 -0
- package/dist/chunk-BAWJNMWF.js +339 -0
- package/dist/chunk-BBPIILO2.js +88 -0
- package/dist/chunk-BD3PGW6A.js +957 -0
- package/dist/chunk-BDPYFAB4.js +44 -0
- package/dist/chunk-BIUV23HH.js +729 -0
- package/dist/chunk-BQ3VAGXH.js +243 -0
- package/dist/chunk-BQJOGAWT.js +31 -0
- package/dist/chunk-BT7EG7CO.js +68 -0
- package/dist/chunk-BV3XQQLU.js +27 -0
- package/dist/chunk-BY6ODHNZ.js +78 -0
- package/dist/chunk-BZRLSZNV.js +79 -0
- package/dist/chunk-C3DH3NCY.js +2773 -0
- package/dist/chunk-C3KCMAJP.js +756 -0
- package/dist/chunk-C7AGUJ5K.js +267 -0
- package/dist/chunk-CBSWKQCX.js +342 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFCVYHSW.js +373 -0
- package/dist/chunk-CHA2HDE5.js +126 -0
- package/dist/chunk-CIHWPGS5.js +146 -0
- package/dist/chunk-CJBSZX3F.js +127 -0
- package/dist/chunk-CLA6QIJM.js +43 -0
- package/dist/chunk-CPSVQR2I.js +365 -0
- package/dist/chunk-CRPRVNQ4.js +254 -0
- package/dist/chunk-CSIE4Y7C.js +1093 -0
- package/dist/chunk-CSOPSYDY.js +142 -0
- package/dist/chunk-CSXLLEGB.js +205 -0
- package/dist/chunk-CTDLERWU.js +293 -0
- package/dist/chunk-CULBXMSK.js +110 -0
- package/dist/chunk-D2M2Y5ID.js +21 -0
- package/dist/chunk-D37WY7SH.js +1060 -0
- package/dist/chunk-D4VJFCGZ.js +89 -0
- package/dist/chunk-D6WTZMZB.js +48 -0
- package/dist/chunk-DBHOWDUH.js +976 -0
- package/dist/chunk-DCACX7BV.js +19364 -0
- package/dist/chunk-DFHX4XYA.js +128 -0
- package/dist/chunk-DIKBRRKE.js +176 -0
- package/dist/chunk-DJG5RIOR.js +9 -0
- package/dist/chunk-DNIOIISW.js +616 -0
- package/dist/chunk-DOA5G7IA.js +315 -0
- package/dist/chunk-DP7FKP6W.js +855 -0
- package/dist/chunk-DS2JZ677.js +79 -0
- package/dist/chunk-DSRLVYBS.js +82 -0
- package/dist/chunk-EAEVKNJA.js +209 -0
- package/dist/chunk-EEUESN3T.js +575 -0
- package/dist/chunk-EJ4LUU5Y.js +73 -0
- package/dist/chunk-EJIWTPSZ.js +1063 -0
- package/dist/chunk-EJVCDTAN.js +116 -0
- package/dist/chunk-EKLUBDBQ.js +627 -0
- package/dist/chunk-EN62WLFD.js +849 -0
- package/dist/chunk-ENYRCL43.js +26 -0
- package/dist/chunk-EP22ZMFO.js +273 -0
- package/dist/chunk-EP4SVAVI.js +87 -0
- package/dist/chunk-EPAMVGI3.js +1562 -0
- package/dist/chunk-EPRMH6HO.js +24 -0
- package/dist/chunk-ES6NWJ36.js +4057 -0
- package/dist/chunk-ETA2K4KI.js +30 -0
- package/dist/chunk-ETAD4NKD.js +31 -0
- package/dist/chunk-EUWIIDQ7.js +93 -0
- package/dist/chunk-EVE3OD2W.js +141 -0
- package/dist/chunk-EXA26FFC.js +459 -0
- package/dist/chunk-EXSPAA35.js +244 -0
- package/dist/chunk-F55YABKV.js +258 -0
- package/dist/chunk-F5DNGU5W.js +27 -0
- package/dist/chunk-F622MJYV.js +103 -0
- package/dist/chunk-F6NIQD74.js +5111 -0
- package/dist/chunk-FAWJTC34.js +43 -0
- package/dist/chunk-FAZBRHGG.js +47 -0
- package/dist/chunk-FIEUSGNJ.js +39 -0
- package/dist/chunk-FKCG7K6H.js +1146 -0
- package/dist/chunk-FOVVXV45.js +229 -0
- package/dist/chunk-FRBPKBFE.js +30 -0
- package/dist/chunk-FSA5BPFI.js +268 -0
- package/dist/chunk-FSJ5TTZ3.js +29 -0
- package/dist/chunk-FT7SGLXT.js +193 -0
- package/dist/chunk-FUUVZZUB.js +62 -0
- package/dist/chunk-FYMRVUNZ.js +1697 -0
- package/dist/chunk-FYTAA5FG.js +162 -0
- package/dist/chunk-G3VB2UCZ.js +3892 -0
- package/dist/chunk-G6OIMVXH.js +405 -0
- package/dist/chunk-G7PQCXVH.js +228 -0
- package/dist/chunk-GAB6UW52.js +1087 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GC4CRMTS.js +642 -0
- package/dist/chunk-GFHSIWHL.js +5297 -0
- package/dist/chunk-GI5M67YA.js +735 -0
- package/dist/chunk-GIJWHUWF.js +191 -0
- package/dist/chunk-GO6DRECI.js +1 -0
- package/dist/chunk-GOFVBTMN.js +314 -0
- package/dist/chunk-GQIP72ZJ.js +13 -0
- package/dist/chunk-GQXLXRFA.js +28 -0
- package/dist/chunk-GT6HFKJB.js +48 -0
- package/dist/chunk-GVJGSSKM.js +174 -0
- package/dist/chunk-GWHGTG37.js +36 -0
- package/dist/chunk-GZAJHLKO.js +18 -0
- package/dist/chunk-GZK62I3N.js +314 -0
- package/dist/chunk-H3HGMISG.js +483 -0
- package/dist/chunk-H3ISZJMJ.js +403 -0
- package/dist/chunk-H4HXVWEC.js +9 -0
- package/dist/chunk-H6MJGKAW.js +120 -0
- package/dist/chunk-HBTTGVR5.js +353 -0
- package/dist/chunk-HBZ322WZ.js +35 -0
- package/dist/chunk-HEP3ILP4.js +34 -0
- package/dist/chunk-HGFXODKO.js +106 -0
- package/dist/chunk-HHWDTB6S.js +59 -0
- package/dist/chunk-HKO45NLZ.js +286 -0
- package/dist/chunk-HLVTEA6A.js +89 -0
- package/dist/chunk-HS3SY6HN.js +160 -0
- package/dist/chunk-HT5OCGKN.js +899 -0
- package/dist/chunk-HXIIEMGF.js +78 -0
- package/dist/chunk-I56TH7VM.js +46 -0
- package/dist/chunk-I6FN3R5D.js +352 -0
- package/dist/chunk-I7SCBHJN.js +655 -0
- package/dist/chunk-IJM4GKAT.js +529 -0
- package/dist/chunk-IMEYU36R.js +344 -0
- package/dist/chunk-INJ3SS6A.js +936 -0
- package/dist/chunk-IUKOPCCE.js +441 -0
- package/dist/chunk-IVEPZQJO.js +33 -0
- package/dist/chunk-IVZEONFC.js +43 -0
- package/dist/chunk-IWBHRSG7.js +936 -0
- package/dist/chunk-IYY3HQAD.js +484 -0
- package/dist/chunk-J2ICMYDA.js +48 -0
- package/dist/chunk-J2QCFLUN.js +505 -0
- package/dist/chunk-J5HM6V5P.js +1039 -0
- package/dist/chunk-J6NZX6QH.js +398 -0
- package/dist/chunk-JBBB2Y47.js +432 -0
- package/dist/chunk-JDCZQZU7.js +42 -0
- package/dist/chunk-JEDNXGSS.js +169 -0
- package/dist/chunk-JHP7OT6K.js +89 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JNMWYMC6.js +38 -0
- package/dist/chunk-JNSTWNNW.js +25 -0
- package/dist/chunk-JOSK6GEU.js +9564 -0
- package/dist/chunk-JQUMSIGC.js +100 -0
- package/dist/chunk-JVHGMURQ.js +1106 -0
- package/dist/chunk-JYXS47FQ.js +951 -0
- package/dist/chunk-K2ZMW2Z5.js +2147 -0
- package/dist/chunk-K7FAFORM.js +14 -0
- package/dist/chunk-KBJXX3ON.js +333 -0
- package/dist/chunk-KHFKVQWY.js +486 -0
- package/dist/chunk-KHMEI3DQ.js +173 -0
- package/dist/chunk-KHW3UD7B.js +69 -0
- package/dist/chunk-KKQKWAMF.js +365 -0
- package/dist/chunk-KOKXJV2U.js +455 -0
- package/dist/chunk-KQ4NGOJI.js +14 -0
- package/dist/chunk-KQYQOXMS.js +342 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KUEWV2JH.js +593 -0
- package/dist/chunk-KXYDV3HP.js +1523 -0
- package/dist/chunk-KZ4JMGZ5.js +96 -0
- package/dist/chunk-KZWGBUIO.js +2659 -0
- package/dist/chunk-L2CBG5IX.js +1892 -0
- package/dist/chunk-L2S235CU.js +314 -0
- package/dist/chunk-L32WD57F.js +98 -0
- package/dist/chunk-LDJCKAJP.js +123 -0
- package/dist/chunk-LDWNBEN7.js +541 -0
- package/dist/chunk-LJVH57G2.js +241 -0
- package/dist/chunk-LPQ7NZEN.js +209 -0
- package/dist/chunk-LRGH3CCV.js +148 -0
- package/dist/chunk-LULOGMMM.js +1133 -0
- package/dist/chunk-LWGKN2GY.js +8284 -0
- package/dist/chunk-LX4F24ZH.js +157 -0
- package/dist/chunk-M4BUEXXF.js +839 -0
- package/dist/chunk-M6UHOC4I.js +553 -0
- package/dist/chunk-MCBNWZNO.js +657 -0
- package/dist/chunk-MEMSY6VY.js +223 -0
- package/dist/chunk-MHLEZQQI.js +145 -0
- package/dist/chunk-MJ6WNSIX.js +192 -0
- package/dist/chunk-MKASIKWZ.js +442 -0
- package/dist/chunk-MNOVBYFU.js +60 -0
- package/dist/chunk-MO2BYAAF.js +2896 -0
- package/dist/chunk-MP6P7Y2A.js +180 -0
- package/dist/chunk-MQ4TFJAG.js +3365 -0
- package/dist/chunk-MT2WAIR3.js +131 -0
- package/dist/chunk-MTU7VTNN.js +1122 -0
- package/dist/chunk-MULMXX4V.js +150 -0
- package/dist/chunk-MW2DWWVA.js +147 -0
- package/dist/chunk-MY3ZPUN4.js +151 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N3Q2JNPV.js +249 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-NAINGKHF.js +35 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NHNPEZS4.js +1408 -0
- package/dist/chunk-NHTIXJCR.js +17 -0
- package/dist/chunk-NKLPMBNV.js +1154 -0
- package/dist/chunk-NMVZ5YES.js +199 -0
- package/dist/chunk-NRFRY3GE.js +256 -0
- package/dist/chunk-NTD6CSI6.js +1266 -0
- package/dist/chunk-NUO7B3SH.js +318 -0
- package/dist/chunk-NVOHIJKG.js +1319 -0
- package/dist/chunk-NXH3RX7O.js +523 -0
- package/dist/chunk-NXZ3DURF.js +257 -0
- package/dist/chunk-O26L6F7F.js +209 -0
- package/dist/chunk-O5RRIUEP.js +124 -0
- package/dist/chunk-O5X6BKAO.js +10 -0
- package/dist/chunk-O6ZQUVTR.js +31 -0
- package/dist/chunk-O7FE75QZ.js +306 -0
- package/dist/chunk-OC7Z6AJQ.js +81 -0
- package/dist/chunk-OH7IEZ63.js +307 -0
- package/dist/chunk-OI734GRS.js +115 -0
- package/dist/chunk-OJBH62DK.js +684 -0
- package/dist/chunk-OKJLVSTJ.js +247 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ONHAMBN6.js +12 -0
- package/dist/chunk-ONYPLIVE.js +753 -0
- package/dist/chunk-OSK7QWPD.js +637 -0
- package/dist/chunk-OTXLMJKP.js +438 -0
- package/dist/chunk-OV5PKQMA.js +213 -0
- package/dist/chunk-OXK5HA4B.js +31 -0
- package/dist/chunk-OYDWFQA4.js +272 -0
- package/dist/chunk-OZAUKWHB.js +22 -0
- package/dist/chunk-P5P2EKGH.js +94 -0
- package/dist/chunk-P6SSWAKY.js +29 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-P7RVCHQV.js +197 -0
- package/dist/chunk-PEBWDYIW.js +726 -0
- package/dist/chunk-PFCXFO7A.js +9193 -0
- package/dist/chunk-PFLDWMX6.js +168 -0
- package/dist/chunk-PHZGLQSK.js +15883 -0
- package/dist/chunk-PNFTPNSR.js +20 -0
- package/dist/chunk-PPHXX4W7.js +175 -0
- package/dist/chunk-PPZESPJT.js +262 -0
- package/dist/chunk-PRDHI2L5.js +296 -0
- package/dist/chunk-PSTKWHHN.js +32 -0
- package/dist/chunk-PV53ODOT.js +555 -0
- package/dist/chunk-PVJBTI7P.js +24 -0
- package/dist/chunk-PXKERBKX.js +100 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3DHEZA5.js +85 -0
- package/dist/chunk-Q5RZDVQK.js +204 -0
- package/dist/chunk-Q64IR2Z2.js +4933 -0
- package/dist/chunk-Q6Z5UGPL.js +398 -0
- package/dist/chunk-QAMAV4DT.js +627 -0
- package/dist/chunk-QBWF4444.js +112 -0
- package/dist/chunk-QDKYT4CI.js +22805 -0
- package/dist/chunk-QFLBRWCJ.js +126 -0
- package/dist/chunk-QILAQACE.js +107 -0
- package/dist/chunk-QJ6OEH4K.js +3683 -0
- package/dist/chunk-QK3NLYTV.js +320 -0
- package/dist/chunk-QOFLUS24.js +535 -0
- package/dist/chunk-QOIASNEJ.js +98 -0
- package/dist/chunk-QP4UTSOV.js +3587 -0
- package/dist/chunk-QP5ST7RI.js +904 -0
- package/dist/chunk-QS5AGRZN.js +167 -0
- package/dist/chunk-QTAOU53N.js +107 -0
- package/dist/chunk-QTCLJHLZ.js +351 -0
- package/dist/chunk-QTOLTG4L.js +119 -0
- package/dist/chunk-QWDV4OSL.js +142 -0
- package/dist/chunk-QWMQ74KD.js +508 -0
- package/dist/chunk-QX6LOMMH.js +158 -0
- package/dist/chunk-QXBKIHY7.js +238 -0
- package/dist/chunk-QXBKTJT6.js +270 -0
- package/dist/chunk-QYQOVOLR.js +196 -0
- package/dist/chunk-R2CKS6WT.js +73 -0
- package/dist/chunk-R2TIYG7U.js +68 -0
- package/dist/chunk-R2W3LESH.js +465 -0
- package/dist/chunk-R5EXHMUN.js +100 -0
- package/dist/chunk-RBBTBCHX.js +289 -0
- package/dist/chunk-RBKLOUXA.js +121 -0
- package/dist/chunk-RCP2Y6DQ.js +86 -0
- package/dist/chunk-RCYEJSD5.js +260 -0
- package/dist/chunk-RFDVSI4N.js +134 -0
- package/dist/chunk-RGJU63FV.js +1717 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RIMARPCF.js +152 -0
- package/dist/chunk-RJ6RSNT6.js +42 -0
- package/dist/chunk-RNI2GUIF.js +131 -0
- package/dist/chunk-RPHUWJ3K.js +264 -0
- package/dist/chunk-RPIMAMV7.js +361 -0
- package/dist/chunk-RQFX6YRN.js +49 -0
- package/dist/chunk-RRC5BL7B.js +285 -0
- package/dist/chunk-RSYTSBOO.js +79 -0
- package/dist/chunk-RWQQIKIC.js +35 -0
- package/dist/chunk-RZUBOZH2.js +373 -0
- package/dist/chunk-S2E632FQ.js +380 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S2XIMQJQ.js +681 -0
- package/dist/chunk-S47ISY27.js +183 -0
- package/dist/chunk-S6I4KISU.js +142 -0
- package/dist/chunk-S6TAFQI5.js +2403 -0
- package/dist/chunk-SBBNT5ZJ.js +27 -0
- package/dist/chunk-SDJV2DTQ.js +491 -0
- package/dist/chunk-SDPIFNLD.js +33 -0
- package/dist/chunk-SF6YK3JI.js +835 -0
- package/dist/chunk-SK3ZFONO.js +976 -0
- package/dist/chunk-SKTFRFQ7.js +1775 -0
- package/dist/chunk-SNGM452K.js +2002 -0
- package/dist/chunk-STW7OFOA.js +581 -0
- package/dist/chunk-SU6UFUQY.js +1351 -0
- package/dist/chunk-SVJM3LJA.js +27 -0
- package/dist/chunk-SVUNZOPG.js +129 -0
- package/dist/chunk-T5XLUYV3.js +34 -0
- package/dist/chunk-T7E5JS6N.js +57 -0
- package/dist/chunk-TA3N5MMO.js +450 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TDEMDRN2.js +1639 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TGCVL74Y.js +28 -0
- package/dist/chunk-TGXXYDL4.js +1012 -0
- package/dist/chunk-TIKTYUJO.js +296 -0
- package/dist/chunk-TIZAJD4M.js +6029 -0
- package/dist/chunk-TLSEZZCG.js +5549 -0
- package/dist/chunk-TMBDRHAX.js +19 -0
- package/dist/chunk-TRURQUBP.js +1217 -0
- package/dist/chunk-TSQ256N5.js +152 -0
- package/dist/chunk-TUQMMFEQ.js +2244 -0
- package/dist/chunk-U424MJLC.js +81 -0
- package/dist/chunk-U4DRS6ZW.js +11 -0
- package/dist/chunk-U5KS22IN.js +204 -0
- package/dist/chunk-UAPHFJPN.js +673 -0
- package/dist/chunk-UB7LQITD.js +1040 -0
- package/dist/chunk-UBVRHGAZ.js +237 -0
- package/dist/chunk-UDNNAQN5.js +98 -0
- package/dist/chunk-UJP3XM54.js +342 -0
- package/dist/chunk-UJYDH2MW.js +305 -0
- package/dist/chunk-UKMPASIC.js +399 -0
- package/dist/chunk-UKUL3EHK.js +763 -0
- package/dist/chunk-UPHU64NB.js +1327 -0
- package/dist/chunk-URMSZXAD.js +43 -0
- package/dist/chunk-US3RT6CY.js +525 -0
- package/dist/chunk-UTDEOPKV.js +30 -0
- package/dist/chunk-UUUDKV7N.js +13 -0
- package/dist/chunk-UVKWU3SM.js +39 -0
- package/dist/chunk-V2TVPO3X.js +521 -0
- package/dist/chunk-V3KLVK7B.js +398 -0
- package/dist/chunk-V3YFFHDR.js +17 -0
- package/dist/chunk-VAG6GUBR.js +1162 -0
- package/dist/chunk-VBENAHDA.js +1814 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VCGSWN5M.js +1300 -0
- package/dist/chunk-VE2O2I7M.js +115 -0
- package/dist/chunk-VIFHYTI5.js +31 -0
- package/dist/chunk-VJER3FUS.js +5080 -0
- package/dist/chunk-VLMDM56Q.js +268 -0
- package/dist/chunk-VMFZEVVX.js +99 -0
- package/dist/chunk-VNDPQVUZ.js +1853 -0
- package/dist/chunk-VSPJRLGA.js +4783 -0
- package/dist/chunk-VSWC5ZXI.js +103 -0
- package/dist/chunk-VYI4DSZP.js +2017 -0
- package/dist/chunk-W4BAIQ6L.js +1212 -0
- package/dist/chunk-W7VQAL2K.js +2203 -0
- package/dist/chunk-WEI3ZDZH.js +33 -0
- package/dist/chunk-WEIUS5NT.js +26 -0
- package/dist/chunk-WEW7UFWE.js +546 -0
- package/dist/chunk-WGQAXLNA.js +384 -0
- package/dist/chunk-WJAL4JL3.js +206 -0
- package/dist/chunk-WKFOJ5ZG.js +141 -0
- package/dist/chunk-WMWQEFLN.js +2177 -0
- package/dist/chunk-WNECKKO2.js +154 -0
- package/dist/chunk-WPKLYOAM.js +195 -0
- package/dist/chunk-WSH37YQN.js +243 -0
- package/dist/chunk-WV3KQDPE.js +84 -0
- package/dist/chunk-WZFC4V4U.js +389 -0
- package/dist/chunk-XCOIBNET.js +13 -0
- package/dist/chunk-XDM65LBX.js +53 -0
- package/dist/chunk-XGU6PBO5.js +645 -0
- package/dist/chunk-XHOI4V62.js +43 -0
- package/dist/chunk-XIBVFMLQ.js +6858 -0
- package/dist/chunk-XJ2E7YEJ.js +117 -0
- package/dist/chunk-XON4ZCYX.js +69 -0
- package/dist/chunk-XQPEWJPI.js +127 -0
- package/dist/chunk-XSJTIFUY.js +31 -0
- package/dist/chunk-XT7OVWM3.js +64 -0
- package/dist/chunk-XTXYIISX.js +1104 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XWIZ6B5S.js +109 -0
- package/dist/chunk-XWLIKOBX.js +240 -0
- package/dist/chunk-XZOXEIN4.js +156 -0
- package/dist/chunk-XZRS7I7W.js +3673 -0
- package/dist/chunk-Y4ZDDHB6.js +44 -0
- package/dist/chunk-Y733RB7T.js +194 -0
- package/dist/chunk-YBHUB4XR.js +14931 -0
- package/dist/chunk-YKC75XK4.js +147 -0
- package/dist/chunk-YKMXFVK7.js +27 -0
- package/dist/chunk-YO5BAJ55.js +225 -0
- package/dist/chunk-YPDCLXNZ.js +568 -0
- package/dist/chunk-YSQPGVFK.js +116 -0
- package/dist/chunk-YTVVTIWJ.js +716 -0
- package/dist/chunk-YVCMDVBB.js +698 -0
- package/dist/chunk-YYTOA6DP.js +114 -0
- package/dist/chunk-Z5WGNULV.js +88 -0
- package/dist/chunk-Z6NGVVXG.js +66 -0
- package/dist/chunk-Z7CDQNH3.js +20 -0
- package/dist/chunk-ZH4G3FU5.js +187 -0
- package/dist/chunk-ZJB6SQPA.js +257 -0
- package/dist/chunk-ZL6E4HMY.js +42 -0
- package/dist/chunk-ZMLOC3DW.js +62 -0
- package/dist/chunk-ZORW64QH.js +101 -0
- package/dist/chunk-ZVVD2XGV.js +1369 -0
- package/dist/cli-utils-ER33ODC5.js +31 -0
- package/dist/cli-validation-6CHPTANV.js +146 -0
- package/dist/commit-utils-3STAY7C7.js +49 -0
- package/dist/compatibility-notice-IHNXR2JR.js +21 -0
- package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
- package/dist/compete-results-XUM3PJHS.js +318 -0
- package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
- package/dist/config-4HHE4OIK.js +238 -0
- package/dist/config-D6YAZASH.js +10 -0
- package/dist/config-MIPRDYYV.js +46 -0
- package/dist/config-audit-GA5DCUAI.js +371 -0
- package/dist/config-explain-4LMO556H.js +71 -0
- package/dist/config-loader-RGP634PC.js +51 -0
- package/dist/config-manager-J2UTO635.js +114 -0
- package/dist/config-merger-TVUJBZTS.js +14 -0
- package/dist/config-validator-H4ZVIE56.js +122 -0
- package/dist/cost-HSIV5U2Y.js +85 -0
- package/dist/data-migration-state-OAUL2WPQ.js +35 -0
- package/dist/db-migrator-NUOW5NKR.js +34 -0
- package/dist/default-paths-UILB62BN.js +49 -0
- package/dist/discovery-V6GYUJUC.js +53 -0
- package/dist/doctor-BBSMCDWE.js +271 -0
- package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
- package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
- package/dist/doctor-purpose-6YJOXPBE.js +30 -0
- package/dist/domain-analyzer-S35ABUNA.js +48 -0
- package/dist/ensure-initialized-Q6PEU55O.js +78 -0
- package/dist/entry.js +922 -0
- package/dist/error-parser-core-MZFMFANN.js +70 -0
- package/dist/evaluation-context-GQBYDAEV.js +63 -0
- package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
- package/dist/execution-data-writer-YCG5UH37.js +72 -0
- package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
- package/dist/execution-preflight-DASJWWQF.js +223 -0
- package/dist/execution-roles-KNSNNB2D.js +112 -0
- package/dist/exit-code-error-QIY5DOKF.js +18 -0
- package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
- package/dist/factory-YCYCB7AE.js +209 -0
- package/dist/file-utils-ZQYNBQBW.js +45 -0
- package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
- package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
- package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
- package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
- package/dist/flywheel-executor-JWU43KAY.js +437 -0
- package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
- package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
- package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
- package/dist/flywheel-preflight-III2C7TH.js +163 -0
- package/dist/flywheel-resume-2O45MS7Y.js +31 -0
- package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
- package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
- package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
- package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
- package/dist/git-message-style-KCXGMTDT.js +104 -0
- package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
- package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
- package/dist/identity-XLQJ4YJF.js +25 -0
- package/dist/index-query-6NUY4FKX.js +37 -0
- package/dist/index.js +85588 -0
- package/dist/indexer-DL54CM3K.js +44 -0
- package/dist/init-OUZ72344.js +245 -0
- package/dist/instructions-import-offer-NSYXSITC.js +363 -0
- package/dist/instructions-run-NBII7W36.js +114 -0
- package/dist/interrupted-work-E4TLKJLE.js +43 -0
- package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
- package/dist/judge-scoring.json +64 -0
- package/dist/lab-mode-YXQ2Y3NG.js +44 -0
- package/dist/lab-utils-5QAMDJR5.js +29 -0
- package/dist/linux-host-class-YDWQX65O.js +31 -0
- package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
- package/dist/local-ci-config-VZ4WWDT5.js +48 -0
- package/dist/local-executor-DCMDDRX2.js +280 -0
- package/dist/logger-5KNKS3O4.js +154 -0
- package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
- package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
- package/dist/merge-batch-RTEEMVDB.js +313 -0
- package/dist/merge-batch-members-VIWGVRHP.js +24 -0
- package/dist/merge-queue-TIIUSOY6.js +126 -0
- package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
- package/dist/metadata-UKSMWM6J.js +29 -0
- package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
- package/dist/model-invocations-db-XTUPPQ34.js +70 -0
- package/dist/model-resolver-JFZ2MHOJ.js +39 -0
- package/dist/model-resolver-SCQ57KAQ.js +217 -0
- package/dist/model-roles-D3OR4NAH.js +110 -0
- package/dist/monitor-F4SIU5G2.js +96 -0
- package/dist/nested-worktrees-2WUWQ75N.js +23 -0
- package/dist/next-S3IRLVER.js +96 -0
- package/dist/next-UKPFRXOO.js +148 -0
- package/dist/opportunistic-TL4S26VX.js +86 -0
- package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
- package/dist/package-HJR4QJT4.js +10 -0
- package/dist/parse-git-status-7PIYFPS2.js +21 -0
- package/dist/path-security-5WJSY56G.js +41 -0
- package/dist/paths-JUI7C4KU.js +45 -0
- package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
- package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
- package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
- package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
- package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
- package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
- package/dist/preflight-PJHGTJNZ.js +12 -0
- package/dist/process-registry-B4ZUCN7T.js +28 -0
- package/dist/process-scanner-TU3QHWVB.js +23 -0
- package/dist/processes-6FHVGT72.js +28 -0
- package/dist/processes-json-fast-YWTMUWXB.js +72 -0
- package/dist/profile-HWTC56SG.js +209 -0
- package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
- package/dist/protected-branches-N36LDBFY.js +71 -0
- package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
- package/dist/provider-matrix-2KO2V5OM.js +214 -0
- package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
- package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
- package/dist/provider-registry-WCZ65QHC.js +136 -0
- package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
- package/dist/prune-engine-EOQVWEP6.js +448 -0
- package/dist/publication-turn-S42RZTQF.js +44 -0
- package/dist/quality-gate-JQMVCKOO.js +72 -0
- package/dist/quickstart-3O6FRCFB.js +105 -0
- package/dist/readiness-GW6ABRHB.js +139 -0
- package/dist/readiness-KFZCXDTT.js +215 -0
- package/dist/registry-7XG376SY.js +217 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-YG6MQZDW.js +28 -0
- package/dist/resolution-engine-FFEK7HQ2.js +187 -0
- package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
- package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
- package/dist/routing-events-AW6SRZ6G.js +13 -0
- package/dist/routing-history-store-FMDDQL6W.js +39 -0
- package/dist/routing-planner-C6OHO3EY.js +221 -0
- package/dist/runner-KHDNQ5KU.js +1194 -0
- package/dist/runner-PIS2BW7Y.js +1801 -0
- package/dist/safety-E3JJVSFL.js +67 -0
- package/dist/safety-stanza-HE6GXP4G.js +49 -0
- package/dist/sandbox-7LBZC5PE.js +72 -0
- package/dist/sandbox-ASWCDYZL.js +80 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-JI22TBEB.js +17 -0
- package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
- package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
- package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
- package/dist/shared-utils-34HJZ6JX.js +40 -0
- package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
- package/dist/sprint-N7JFTEGL.js +403 -0
- package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
- package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
- package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
- package/dist/sprint-manager-MU2ZUWN2.js +140 -0
- package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
- package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
- package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
- package/dist/storage-access-I3V5LER7.js +31 -0
- package/dist/store-XVG64ZR2.js +25 -0
- package/dist/task-classifier-MN2DYE5R.js +558 -0
- package/dist/task-list-fast-7AT6LTTA.js +112 -0
- package/dist/task-manager-LHPBBRRE.js +136 -0
- package/dist/task-report-OLPAWBTK.js +64 -0
- package/dist/task-type-detector-6RG3YNZF.js +27 -0
- package/dist/task-type-resolver-EQHOUIHT.js +64 -0
- package/dist/telemetry-5VYSK772.js +212 -0
- package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
- package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
- package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
- package/dist/transports-NREZQTJK.js +106 -0
- package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
- package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
- package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
- package/dist/user-agents-QLIGPLP2.js +30 -0
- package/dist/variant-execution-I4ZZUAWY.js +319 -0
- package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
- package/dist/variant-request-input-PL37N3AN.js +11 -0
- package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
- package/dist/variant-worker-COB5YTJX.js +4517 -0
- package/dist/variant-worker.js +26 -0
- package/dist/variants-XGDSXYG3.js +267 -0
- package/dist/verification-command-YSPU4VBH.js +66 -0
- package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
- package/dist/verify-commands-LOHMOFKC.js +65 -0
- package/dist/warning-JSKSFKBT.js +13 -0
- package/dist/work-edge-K65NHVWY.js +50 -0
- package/dist/work-item-writer-KY5ZWZWW.js +30 -0
- package/dist/worker-JNXXEIT6.js +1504 -0
- package/dist/worker.js +21 -0
- package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
- package/dist/worktree-environment-WTVABAQW.js +64 -0
- package/dist/worktree-metrics-VZRQI26T.js +66 -0
- package/dist/writeback-TQFQBJNU.js +50 -0
- package/docs/CLI_REFERENCE.md +8287 -0
- package/docs/PROVIDER_SETUP.md +1737 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2209 -0
- package/docs/getting-started/QUICK_START.md +260 -0
- package/docs/getting-started/README.md +198 -0
- package/docs/getting-started/SETUP.md +25 -0
- package/docs/reference/AGENT_CONTEXT.md +84 -0
- package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/SECURITY.md +291 -0
- package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
- package/integrations/bb-plugin-build/README.md +391 -0
- package/integrations/bb-plugin-build/app.css +431 -0
- package/integrations/bb-plugin-build/app.tsx +6210 -0
- package/integrations/bb-plugin-build/package.json +45 -0
- package/integrations/bb-plugin-build/server.ts +488 -0
- package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
- package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
- package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-build/src/contract.ts +718 -0
- package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
- package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
- package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
- package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-build/src/model.ts +1318 -0
- package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
- package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
- package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
- package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
- package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
- package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
- package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-build/src/project-target.ts +45 -0
- package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
- package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
- package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
- package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
- package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
- package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
- package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
- package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
- package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
- package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
- package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
- package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
- package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
- package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
- package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
- package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
- package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
- package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
- package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
- package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
- package/package.json +306 -4
- package/schemas/README.md +80 -0
- package/schemas/build.config.schema.json +2110 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +257 -0
- package/schemas/provider-config.schema.json +179 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +146 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/setup-git-hooks.mjs +39 -0
- package/scripts/sync.sh +2871 -0
- package/scripts/write-node-launcher.sh +138 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.build-project/README.md +41 -0
- package/templates/.build-project/agents/README.md +50 -0
- package/templates/.build-project/agents/architect.md +10 -0
- package/templates/.build-project/agents/code-reviewer.md +10 -0
- package/templates/.build-project/agents/debugger.md +10 -0
- package/templates/.build-project/agents/documenter.md +10 -0
- package/templates/.build-project/agents/test-automator.md +10 -0
- package/templates/.build-project/config/build.config.jsonc +64 -0
- package/templates/.build-project/config/project-context.template.json +6 -0
- package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.build-project/config/task-type-aliases.yaml +29 -0
- package/templates/.build-project/gitignore.template +70 -0
- package/templates/.build-project/task-types/analysis.yaml +38 -0
- package/templates/.build-project/task-types/architecture.yaml +35 -0
- package/templates/.build-project/task-types/doc.yaml +34 -0
- package/templates/.build-project/task-types/feature.yaml +22 -0
- package/templates/.build-project/task-types/general.yaml +4 -0
- package/templates/.build-project/task-types/security.yaml +38 -0
- package/templates/AGENTS.template.md +85 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
- package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
- package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
- package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
- package/templates/ci/incident-intake.mjs +787 -0
- package/templates/ci/incidents.yml.template +103 -0
- package/templates/ci/node.yml.template +17 -0
- package/templates/ci/python.yml.template +17 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
- package/templates/guard-kit/build-guard.yml.template +189 -0
- package/templates/guard-kit/check.mjs.template +932 -0
- package/templates/guard-kit/config.json.template +7 -0
- package/templates/guard-kit/policy.json.template +1 -0
- package/templates/profiles/README.md +57 -0
- package/templates/profiles/frontier-claude.json +23 -0
- package/templates/profiles/frontier-codex.json +23 -0
|
@@ -0,0 +1,1814 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acquireFencedFileLock
|
|
3
|
+
} from "./chunk-QYQOVOLR.js";
|
|
4
|
+
import {
|
|
5
|
+
abandonHostPublicationGenerationJournal,
|
|
6
|
+
attachOutstandingStatusPublicationMac,
|
|
7
|
+
beginHostPublicationGenerationJournal,
|
|
8
|
+
recoverHostPublicationGeneration,
|
|
9
|
+
syncHostPublicationGeneration
|
|
10
|
+
} from "./chunk-4U6XK2DG.js";
|
|
11
|
+
import {
|
|
12
|
+
parseProjectDependencySetupCommand,
|
|
13
|
+
parseTomlSections,
|
|
14
|
+
readComposerScripts,
|
|
15
|
+
tomlStrings
|
|
16
|
+
} from "./chunk-AHU3FKPA.js";
|
|
17
|
+
import {
|
|
18
|
+
repoRootStateKey
|
|
19
|
+
} from "./chunk-BV3XQQLU.js";
|
|
20
|
+
import {
|
|
21
|
+
processOwnershipState,
|
|
22
|
+
readProcessIdentity
|
|
23
|
+
} from "./chunk-MT2WAIR3.js";
|
|
24
|
+
|
|
25
|
+
// src/core/ci/local-ci-state.ts
|
|
26
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
27
|
+
import { access, link, mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
|
|
28
|
+
import path4 from "path";
|
|
29
|
+
import { z } from "zod";
|
|
30
|
+
|
|
31
|
+
// src/core/ci/local-ci-entrypoint-fingerprint.ts
|
|
32
|
+
import { createHash } from "crypto";
|
|
33
|
+
import { lstatSync, readFileSync as readFileSync3, readlinkSync, realpathSync } from "fs";
|
|
34
|
+
import * as path3 from "path";
|
|
35
|
+
|
|
36
|
+
// src/core/ci/local-ci-composer-scripts.ts
|
|
37
|
+
import { readFileSync } from "fs";
|
|
38
|
+
import * as path from "path";
|
|
39
|
+
var ALIAS_PREFIX = "\0scripts-aliases\0";
|
|
40
|
+
var TARGET_SEPARATOR = "\n";
|
|
41
|
+
function composerAliasKey(alias) {
|
|
42
|
+
return `${ALIAS_PREFIX}${alias}`;
|
|
43
|
+
}
|
|
44
|
+
function readComposerScriptTable(root) {
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(readFileSync(path.join(root, "composer.json"), "utf8"));
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
52
|
+
const table = { ...readComposerScripts(root) };
|
|
53
|
+
const aliases = parsed["scripts-aliases"];
|
|
54
|
+
if (aliases === void 0) return table;
|
|
55
|
+
if (!aliases || typeof aliases !== "object" || Array.isArray(aliases)) return null;
|
|
56
|
+
const targets = {};
|
|
57
|
+
for (const [target, names] of Object.entries(aliases)) {
|
|
58
|
+
if (!Array.isArray(names) || names.some((name) => typeof name !== "string")) return null;
|
|
59
|
+
for (const alias of names) targets[alias] = [...targets[alias] ?? [], target];
|
|
60
|
+
}
|
|
61
|
+
for (const [alias, aliasTargets] of Object.entries(targets)) {
|
|
62
|
+
table[composerAliasKey(alias)] = aliasTargets.sort().join(TARGET_SEPARATOR);
|
|
63
|
+
}
|
|
64
|
+
return table;
|
|
65
|
+
}
|
|
66
|
+
function composerRelatedScripts(name, scripts) {
|
|
67
|
+
const aliasKey = composerAliasKey(name);
|
|
68
|
+
const targets = scripts[aliasKey]?.split(TARGET_SEPARATOR) ?? [];
|
|
69
|
+
if (scripts[name] === void 0 && targets.length === 0) return null;
|
|
70
|
+
return [aliasKey, ...targets, "pre-command-run"];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/core/ci/local-ci-pyproject-scripts.ts
|
|
74
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
75
|
+
import * as path2 from "path";
|
|
76
|
+
var OWNERS_BY_HEAD = {
|
|
77
|
+
uv: /* @__PURE__ */ new Set(["project"]),
|
|
78
|
+
poetry: /* @__PURE__ */ new Set(["project", "poetry"]),
|
|
79
|
+
pdm: /* @__PURE__ */ new Set(["project", "pdm"]),
|
|
80
|
+
hatch: /* @__PURE__ */ new Set(["project", "hatch"])
|
|
81
|
+
};
|
|
82
|
+
var SCRIPT_KEYS = /* @__PURE__ */ new Set(["scripts", "gui-scripts", "console_scripts"]);
|
|
83
|
+
function classifyScriptsTable(keys) {
|
|
84
|
+
if (!keys.some((key) => SCRIPT_KEYS.has(key))) return void 0;
|
|
85
|
+
const [first, second, third, fourth] = keys;
|
|
86
|
+
if (first === "project" && keys.length === 2 && second && SCRIPT_KEYS.has(second)) {
|
|
87
|
+
return { owner: "project", subtable: false };
|
|
88
|
+
}
|
|
89
|
+
if (first === "tool" && second === "poetry" && third === "scripts" && keys.length === 3) {
|
|
90
|
+
return { owner: "poetry", subtable: false };
|
|
91
|
+
}
|
|
92
|
+
if (first === "tool" && second === "pdm" && third === "scripts") {
|
|
93
|
+
if (keys.length === 3) return { owner: "pdm", subtable: false };
|
|
94
|
+
if (keys.length === 4 && fourth) return { owner: "pdm", subtable: true };
|
|
95
|
+
}
|
|
96
|
+
if (first === "tool" && second === "hatch" && third === "envs" && keys[4] === "scripts") {
|
|
97
|
+
if (keys.length === 5) return { owner: "hatch", subtable: false };
|
|
98
|
+
}
|
|
99
|
+
return "unmodeled";
|
|
100
|
+
}
|
|
101
|
+
function readPyprojectScripts(root, head) {
|
|
102
|
+
const owners = OWNERS_BY_HEAD[head];
|
|
103
|
+
if (!owners) return null;
|
|
104
|
+
let text;
|
|
105
|
+
try {
|
|
106
|
+
text = readFileSync2(path2.join(root, "pyproject.toml"), "utf8");
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
if (/"""|'''/.test(text)) return null;
|
|
111
|
+
let unread = false;
|
|
112
|
+
const sections = parseTomlSections(text, () => {
|
|
113
|
+
unread = true;
|
|
114
|
+
});
|
|
115
|
+
if (unread) return null;
|
|
116
|
+
const definitions = {};
|
|
117
|
+
const define = (name, definition) => {
|
|
118
|
+
definitions[name] = [...definitions[name] ?? [], definition];
|
|
119
|
+
};
|
|
120
|
+
for (const section of sections) {
|
|
121
|
+
const table = classifyScriptsTable(section.keys);
|
|
122
|
+
if (table === "unmodeled") return null;
|
|
123
|
+
if (table) {
|
|
124
|
+
if (!owners.has(table.owner)) continue;
|
|
125
|
+
const header = section.keys.join(".");
|
|
126
|
+
if (table.subtable) {
|
|
127
|
+
const entries = section.entries.map((entry) => `${entry.key}=${entry.value}`).join(";");
|
|
128
|
+
define(section.keys[3] ?? "", `[${header}] ${entries}`);
|
|
129
|
+
} else {
|
|
130
|
+
for (const entry of section.entries) {
|
|
131
|
+
if (entry.key.includes(".")) return null;
|
|
132
|
+
define(entry.key, `${header}.${entry.key}=${entry.value}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const mentionsScripts = section.entries.some(
|
|
138
|
+
(entry) => [...section.keys, ...entry.key.split(".")].some((key) => SCRIPT_KEYS.has(key))
|
|
139
|
+
);
|
|
140
|
+
if (mentionsScripts) return null;
|
|
141
|
+
}
|
|
142
|
+
return Object.fromEntries(
|
|
143
|
+
Object.entries(definitions).map(([name, lines]) => [name, lines.sort().join("\n")])
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function pyprojectScriptReferences(body, scripts, head) {
|
|
147
|
+
const names = [];
|
|
148
|
+
for (const value of tomlStrings(body)) {
|
|
149
|
+
const words = value.trim().split(/\s+/);
|
|
150
|
+
if ((head === "hatch" || head === "pdm") && words[0] === head && words[1] === "run") {
|
|
151
|
+
const target = (words[2] ?? "").replace(/^['"]|['"]$/g, "");
|
|
152
|
+
if (!target || target.startsWith("-") || target.startsWith("+")) return null;
|
|
153
|
+
if (/[;&|\n`]|\$\(/.test(value)) return null;
|
|
154
|
+
names.push(head === "hatch" ? target.split(":").pop() ?? target : target);
|
|
155
|
+
} else {
|
|
156
|
+
names.push(words[0] ?? "");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return names.filter((name) => Object.hasOwn(scripts, name));
|
|
160
|
+
}
|
|
161
|
+
function pyprojectAutomaticScripts(head, name) {
|
|
162
|
+
return head === "pdm" ? [`pre_${name}`, `post_${name}`, "pre_script", "post_script", "pre_run", "post_run", "_"] : [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/core/ci/local-ci-manifest-pins.ts
|
|
166
|
+
var NODE_PACKAGE_MANAGER_MANIFESTS = ["package.json", ".npmrc"];
|
|
167
|
+
var verbs = (...names) => new Set(names);
|
|
168
|
+
var PYTHON_PYPROJECT = ["pyproject.toml"];
|
|
169
|
+
var GRADLE_BUILD_FILES = [
|
|
170
|
+
"build.gradle",
|
|
171
|
+
"build.gradle.kts",
|
|
172
|
+
"settings.gradle",
|
|
173
|
+
"settings.gradle.kts"
|
|
174
|
+
];
|
|
175
|
+
var COMPOSER_PASSIVE_OPTIONS = /* @__PURE__ */ new Set([
|
|
176
|
+
"-q",
|
|
177
|
+
"--quiet",
|
|
178
|
+
"-n",
|
|
179
|
+
"--no-interaction",
|
|
180
|
+
"--ansi",
|
|
181
|
+
"--no-ansi",
|
|
182
|
+
"--dev",
|
|
183
|
+
"--no-dev"
|
|
184
|
+
]);
|
|
185
|
+
var COMPOSER_RUN_VERBS = /* @__PURE__ */ new Set(["run", "run-script"]);
|
|
186
|
+
var COMPOSER_SETUP_VERBS = verbs("install");
|
|
187
|
+
function launcherVerb(tokens) {
|
|
188
|
+
for (let index = 1; index < tokens.length; index += 1) {
|
|
189
|
+
const token = tokens[index] ?? "";
|
|
190
|
+
if (token.startsWith("+")) continue;
|
|
191
|
+
return token.startsWith("-") ? null : token.toLowerCase();
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
function composerReference(tokens) {
|
|
196
|
+
if (isSetupInvocation(COMPOSER_SETUP_VERBS, tokens)) return null;
|
|
197
|
+
let verbIndex = 1;
|
|
198
|
+
while (COMPOSER_PASSIVE_OPTIONS.has((tokens[verbIndex] ?? "").toLowerCase())) verbIndex += 1;
|
|
199
|
+
const verb = (tokens[verbIndex] ?? "").toLowerCase();
|
|
200
|
+
if (!verb || verb.startsWith("-")) return void 0;
|
|
201
|
+
let name = verb;
|
|
202
|
+
let nameIndex = verbIndex;
|
|
203
|
+
if (COMPOSER_RUN_VERBS.has(verb)) {
|
|
204
|
+
nameIndex = verbIndex + 1;
|
|
205
|
+
while (COMPOSER_PASSIVE_OPTIONS.has((tokens[nameIndex] ?? "").toLowerCase())) nameIndex += 1;
|
|
206
|
+
name = tokens[nameIndex] ?? "";
|
|
207
|
+
if (!name || name.startsWith("-")) return void 0;
|
|
208
|
+
}
|
|
209
|
+
for (const token of tokens.slice(nameIndex + 1)) {
|
|
210
|
+
if (token === "--") break;
|
|
211
|
+
if (/^-d/.test(token) || token.startsWith("--working-dir")) return void 0;
|
|
212
|
+
}
|
|
213
|
+
return name.replace(/^['"]|['"]$/g, "");
|
|
214
|
+
}
|
|
215
|
+
function composerReferences(body, scripts) {
|
|
216
|
+
return [...body.matchAll(/(?:^|\s)@([\w:.-]+)/g)].map((match) => match[1] ?? "").filter((name) => Object.hasOwn(scripts, name));
|
|
217
|
+
}
|
|
218
|
+
var COMPOSER_SCRIPTS = {
|
|
219
|
+
read: readComposerScriptTable,
|
|
220
|
+
references: composerReferences,
|
|
221
|
+
related: composerRelatedScripts,
|
|
222
|
+
referenced: composerReference
|
|
223
|
+
};
|
|
224
|
+
var PYTHON_WRAPPER_PASSIVE_OPTIONS = {
|
|
225
|
+
uv: verbs("--frozen", "--locked", "--no-sync", "--offline", "-q", "--quiet", "--no-dev"),
|
|
226
|
+
poetry: verbs("-q", "--quiet", "-v", "--verbose"),
|
|
227
|
+
pdm: verbs("-q", "--quiet", "-v", "--verbose"),
|
|
228
|
+
hatch: verbs()
|
|
229
|
+
};
|
|
230
|
+
function pythonWrapperReference(head, tokens) {
|
|
231
|
+
if ((tokens[1] ?? "").toLowerCase() !== "run") return void 0;
|
|
232
|
+
const passive = PYTHON_WRAPPER_PASSIVE_OPTIONS[head] ?? verbs();
|
|
233
|
+
let index = 2;
|
|
234
|
+
while (passive.has((tokens[index] ?? "").toLowerCase())) index += 1;
|
|
235
|
+
const target = (tokens[index] ?? "").replace(/^['"]|['"]$/g, "");
|
|
236
|
+
if (!target || target.startsWith("-") || target.startsWith("+")) return void 0;
|
|
237
|
+
return head === "hatch" ? target.split(":").pop() ?? target : target;
|
|
238
|
+
}
|
|
239
|
+
function pythonScripts(head) {
|
|
240
|
+
return {
|
|
241
|
+
read: (root) => readPyprojectScripts(root, head),
|
|
242
|
+
references: (body, scripts) => pyprojectScriptReferences(body, scripts, head),
|
|
243
|
+
related: (name) => pyprojectAutomaticScripts(head, name),
|
|
244
|
+
referenced: (tokens) => pythonWrapperReference(head, tokens)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
var LAUNCHER_MANIFESTS = {
|
|
248
|
+
npm: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
249
|
+
pnpm: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
250
|
+
yarn: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
251
|
+
bun: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
252
|
+
npx: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
253
|
+
pnpx: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
254
|
+
bunx: { definitions: NODE_PACKAGE_MANAGER_MANIFESTS },
|
|
255
|
+
make: { definitions: ["Makefile", "GNUmakefile"] },
|
|
256
|
+
just: { definitions: ["justfile", ".justfile"] },
|
|
257
|
+
task: { definitions: ["Taskfile.yml", "Taskfile.yaml"] },
|
|
258
|
+
rake: { definitions: ["Rakefile"] },
|
|
259
|
+
tox: { definitions: ["tox.ini", "pyproject.toml", "setup.cfg"] },
|
|
260
|
+
nox: { definitions: ["noxfile.py"] },
|
|
261
|
+
// `run` names either a tool or a script the pyproject.toml defines; the script
|
|
262
|
+
// tables tell them apart. Every other verb stays pinned whole.
|
|
263
|
+
poetry: {
|
|
264
|
+
definitions: PYTHON_PYPROJECT,
|
|
265
|
+
setupVerbs: verbs("install"),
|
|
266
|
+
scriptTable: pythonScripts("poetry")
|
|
267
|
+
},
|
|
268
|
+
pdm: {
|
|
269
|
+
definitions: PYTHON_PYPROJECT,
|
|
270
|
+
setupVerbs: verbs("install", "sync"),
|
|
271
|
+
scriptTable: pythonScripts("pdm")
|
|
272
|
+
},
|
|
273
|
+
hatch: { definitions: PYTHON_PYPROJECT, scriptTable: pythonScripts("hatch") },
|
|
274
|
+
uv: {
|
|
275
|
+
definitions: PYTHON_PYPROJECT,
|
|
276
|
+
setupVerbs: verbs("sync"),
|
|
277
|
+
scriptTable: pythonScripts("uv")
|
|
278
|
+
},
|
|
279
|
+
pipenv: { definitions: ["Pipfile"], setupVerbs: verbs("install", "sync") },
|
|
280
|
+
composer: {
|
|
281
|
+
definitions: ["composer.json"],
|
|
282
|
+
setupVerbs: COMPOSER_SETUP_VERBS,
|
|
283
|
+
scriptTable: COMPOSER_SCRIPTS
|
|
284
|
+
},
|
|
285
|
+
cargo: {
|
|
286
|
+
definitions: [".cargo/config.toml"],
|
|
287
|
+
declarations: ["Cargo.toml"],
|
|
288
|
+
builtinVerbs: verbs("test", "build", "check", "clippy", "fmt", "fetch", "bench", "doc")
|
|
289
|
+
},
|
|
290
|
+
mvn: { definitions: ["pom.xml"] },
|
|
291
|
+
gradle: { definitions: GRADLE_BUILD_FILES },
|
|
292
|
+
gradlew: { definitions: GRADLE_BUILD_FILES },
|
|
293
|
+
swift: { definitions: ["Package.swift"] },
|
|
294
|
+
dart: {
|
|
295
|
+
definitions: [],
|
|
296
|
+
declarations: ["pubspec.yaml"],
|
|
297
|
+
builtinVerbs: verbs("test", "analyze", "format")
|
|
298
|
+
},
|
|
299
|
+
flutter: { definitions: ["pubspec.yaml"] },
|
|
300
|
+
deno: { definitions: ["deno.json", "deno.jsonc"] },
|
|
301
|
+
turbo: { definitions: ["turbo.json", "package.json"] },
|
|
302
|
+
nx: { definitions: ["nx.json", "package.json"] },
|
|
303
|
+
go: {
|
|
304
|
+
definitions: [],
|
|
305
|
+
declarations: ["go.mod"],
|
|
306
|
+
builtinVerbs: verbs("test", "build", "vet", "mod", "list")
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
function launcherManifests(head) {
|
|
310
|
+
return LAUNCHER_MANIFESTS[head];
|
|
311
|
+
}
|
|
312
|
+
function isSetupInvocation(setupVerbs, tokens) {
|
|
313
|
+
if (parseProjectDependencySetupCommand(tokens.join(" "))) return true;
|
|
314
|
+
const verb = launcherVerb(tokens);
|
|
315
|
+
return verb !== null && setupVerbs?.has(verb) === true;
|
|
316
|
+
}
|
|
317
|
+
function pinnedManifestFiles(manifests, tokens) {
|
|
318
|
+
if (isSetupInvocation(manifests.setupVerbs, tokens)) return [];
|
|
319
|
+
const verb = launcherVerb(tokens);
|
|
320
|
+
const builtin = verb !== null && manifests.builtinVerbs?.has(verb) === true;
|
|
321
|
+
return builtin ? manifests.definitions : [...manifests.definitions, ...manifests.declarations ?? []];
|
|
322
|
+
}
|
|
323
|
+
function referencedTableScript(head, tokens) {
|
|
324
|
+
const manifests = LAUNCHER_MANIFESTS[head];
|
|
325
|
+
const file = manifests?.definitions[0];
|
|
326
|
+
const reference = manifests?.scriptTable?.referenced(tokens);
|
|
327
|
+
if (!manifests?.scriptTable || !file || reference === null) return null;
|
|
328
|
+
return { file, name: reference };
|
|
329
|
+
}
|
|
330
|
+
var SCOPED_PIN_SEPARATOR = "\0";
|
|
331
|
+
function scopedPinKey(file, head, name) {
|
|
332
|
+
return [file, head, name].join(SCOPED_PIN_SEPARATOR);
|
|
333
|
+
}
|
|
334
|
+
function parseScopedPin(key) {
|
|
335
|
+
const [file, head, name, ...rest] = key.split(SCOPED_PIN_SEPARATOR);
|
|
336
|
+
return file && head && name !== void 0 && rest.length === 0 ? { file, head, name } : null;
|
|
337
|
+
}
|
|
338
|
+
function scriptTableValues(head, names, trustedRoot, candidateRoot) {
|
|
339
|
+
const table = LAUNCHER_MANIFESTS[head]?.scriptTable;
|
|
340
|
+
if (!table) return null;
|
|
341
|
+
const base = table.read(trustedRoot);
|
|
342
|
+
const candidate = table.read(candidateRoot);
|
|
343
|
+
if (!base || !candidate) return null;
|
|
344
|
+
const pending = [...names].map((name) => ({ name, related: true }));
|
|
345
|
+
const seen = /* @__PURE__ */ new Set();
|
|
346
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
347
|
+
const values = [];
|
|
348
|
+
const sides = [base, candidate];
|
|
349
|
+
while (pending.length > 0) {
|
|
350
|
+
const { name, related } = pending.pop() ?? { name: "", related: false };
|
|
351
|
+
const first = !seen.has(name);
|
|
352
|
+
if (!first && (!related || expanded.has(name))) continue;
|
|
353
|
+
if (first) {
|
|
354
|
+
seen.add(name);
|
|
355
|
+
values.push({ name, base: base[name], candidate: candidate[name] });
|
|
356
|
+
for (const side of sides) {
|
|
357
|
+
const body = side[name];
|
|
358
|
+
if (body !== void 0) {
|
|
359
|
+
const refs = table.references(body, side);
|
|
360
|
+
if (refs === null) return null;
|
|
361
|
+
pending.push(...refs.map((ref) => ({ name: ref, related: true })));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (!related) continue;
|
|
366
|
+
expanded.add(name);
|
|
367
|
+
for (const side of sides) {
|
|
368
|
+
const extra = table.related?.(name, side);
|
|
369
|
+
if (extra === null) return null;
|
|
370
|
+
pending.push(...(extra ?? []).map((ref) => ({ name: ref, related: false })));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return values.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
374
|
+
}
|
|
375
|
+
function scriptTableUnchanged(head, names, trustedRoot, candidateRoot) {
|
|
376
|
+
const values = scriptTableValues(head, names, trustedRoot, candidateRoot);
|
|
377
|
+
if (!values) return false;
|
|
378
|
+
return values.every((value) => value.base === value.candidate);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/core/ci/local-ci-entrypoint-fingerprint.ts
|
|
382
|
+
var ENTRYPOINT_FINGERPRINT_VERSION = 1;
|
|
383
|
+
var ENTRYPOINT_FINGERPRINT_PATTERN = /^v\d+:[a-f0-9]{64}$/;
|
|
384
|
+
var Unbindable = class extends Error {
|
|
385
|
+
};
|
|
386
|
+
var sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
387
|
+
var isMissing = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
388
|
+
function assertInsideRoot(root, absolute) {
|
|
389
|
+
const resolvedRoot = realpathSync(root);
|
|
390
|
+
const resolved = realpathSync(absolute);
|
|
391
|
+
if (resolved !== resolvedRoot && !resolved.startsWith(`${resolvedRoot}${path3.sep}`)) {
|
|
392
|
+
throw new Unbindable(`resolves outside the checkout: ${absolute}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function readSpelling(root, relativePath) {
|
|
396
|
+
const absolute = path3.join(root, relativePath);
|
|
397
|
+
let stat;
|
|
398
|
+
try {
|
|
399
|
+
stat = lstatSync(absolute);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
if (isMissing(error)) return { type: "missing", executable: false, digest: "", lines: [] };
|
|
402
|
+
throw new Unbindable(`unreadable: ${absolute}`);
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
assertInsideRoot(root, absolute);
|
|
406
|
+
const linkTarget = stat.isSymbolicLink() ? readlinkSync(absolute) : void 0;
|
|
407
|
+
const resolvedStat = linkTarget === void 0 ? stat : lstatSync(realpathSync(absolute));
|
|
408
|
+
const executable = (resolvedStat.mode & 73) !== 0;
|
|
409
|
+
if (!resolvedStat.isFile()) {
|
|
410
|
+
if (linkTarget !== void 0) throw new Unbindable(`link to a non-file: ${absolute}`);
|
|
411
|
+
return {
|
|
412
|
+
type: stat.isDirectory() ? "directory" : "other",
|
|
413
|
+
executable,
|
|
414
|
+
digest: "",
|
|
415
|
+
lines: []
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const bytes = readFileSync3(absolute);
|
|
419
|
+
const lines = bytes.toString("utf8").split("\n");
|
|
420
|
+
if (linkTarget === void 0) return { type: "file", executable, digest: sha256(bytes), lines };
|
|
421
|
+
return {
|
|
422
|
+
type: "symlink",
|
|
423
|
+
executable,
|
|
424
|
+
digest: sha256(`link:${linkTarget}\0${sha256(bytes)}`),
|
|
425
|
+
lines: [linkTarget, ...lines],
|
|
426
|
+
linkTarget
|
|
427
|
+
};
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (error instanceof Unbindable) throw error;
|
|
430
|
+
throw new Unbindable(`cannot resolve: ${absolute}`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function readSide(root, spellings) {
|
|
434
|
+
for (const spelling of spellings) {
|
|
435
|
+
const state = readSpelling(root, spelling);
|
|
436
|
+
if (state.type !== "missing") return state;
|
|
437
|
+
}
|
|
438
|
+
return { type: "missing", executable: false, digest: "", lines: [] };
|
|
439
|
+
}
|
|
440
|
+
function scriptSide(side, entries) {
|
|
441
|
+
const lines = entries.map(([name, value]) => JSON.stringify([name, value ?? null]));
|
|
442
|
+
return { ...side, digest: sha256(JSON.stringify([lines, side.linkTarget ?? null])), lines };
|
|
443
|
+
}
|
|
444
|
+
function lineDelta(base, candidate) {
|
|
445
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
446
|
+
for (const line of base) remaining.set(line, (remaining.get(line) ?? 0) + 1);
|
|
447
|
+
let additions = 0;
|
|
448
|
+
for (const line of candidate) {
|
|
449
|
+
const count = remaining.get(line) ?? 0;
|
|
450
|
+
if (count > 0) remaining.set(line, count - 1);
|
|
451
|
+
else additions += 1;
|
|
452
|
+
}
|
|
453
|
+
let deletions = 0;
|
|
454
|
+
for (const count of remaining.values()) deletions += count;
|
|
455
|
+
return { additions, deletions };
|
|
456
|
+
}
|
|
457
|
+
function scriptEntries(input, file) {
|
|
458
|
+
if (file === "package.json" && input.packageScripts) {
|
|
459
|
+
const { names, base: base2, candidate: candidate2 } = input.packageScripts;
|
|
460
|
+
const sorted = [...names].sort();
|
|
461
|
+
return {
|
|
462
|
+
base: sorted.map((name) => [name, base2[name]]),
|
|
463
|
+
candidate: sorted.map((name) => [name, candidate2[name]])
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const scoped = input.pins.scoped.get(file);
|
|
467
|
+
if (!scoped || input.pins.whole.has(file)) return void 0;
|
|
468
|
+
const base = [];
|
|
469
|
+
const candidate = [];
|
|
470
|
+
for (const head of [...new Set(scoped.map((script) => script.head))].sort()) {
|
|
471
|
+
const values = scriptTableValues(
|
|
472
|
+
head,
|
|
473
|
+
scoped.filter((script) => script.head === head).map((script) => script.name),
|
|
474
|
+
input.trustedRoot,
|
|
475
|
+
input.candidateRoot
|
|
476
|
+
);
|
|
477
|
+
if (!values) return void 0;
|
|
478
|
+
for (const value of values) {
|
|
479
|
+
base.push([`${head}:${value.name}`, value.base]);
|
|
480
|
+
candidate.push([`${head}:${value.name}`, value.candidate]);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return { base, candidate };
|
|
484
|
+
}
|
|
485
|
+
function computeEntrypointFingerprint(input) {
|
|
486
|
+
let paths;
|
|
487
|
+
try {
|
|
488
|
+
paths = [...input.redefined].sort().map((file) => {
|
|
489
|
+
const spellings = input.trackedPaths.get(file) ?? [];
|
|
490
|
+
if (spellings.length > 1) throw new Unbindable(`several spellings of ${file}`);
|
|
491
|
+
const baseFile = readSide(input.trustedRoot, spellings);
|
|
492
|
+
const candidateFile = readSide(input.candidateRoot, spellings);
|
|
493
|
+
if (baseFile.type === "missing" && candidateFile.type === "missing") {
|
|
494
|
+
throw new Unbindable(`neither side has ${file}`);
|
|
495
|
+
}
|
|
496
|
+
const entries = scriptEntries(input, file);
|
|
497
|
+
const base = entries ? scriptSide(baseFile, entries.base) : baseFile;
|
|
498
|
+
const candidate = entries ? scriptSide(candidateFile, entries.candidate) : candidateFile;
|
|
499
|
+
const side = (state) => ({
|
|
500
|
+
type: state.type,
|
|
501
|
+
executable: state.executable,
|
|
502
|
+
digest: state.digest
|
|
503
|
+
});
|
|
504
|
+
return {
|
|
505
|
+
path: file,
|
|
506
|
+
unit: entries ? "scripts" : "file",
|
|
507
|
+
base: side(base),
|
|
508
|
+
candidate: side(candidate),
|
|
509
|
+
...lineDelta(base.lines, candidate.lines)
|
|
510
|
+
};
|
|
511
|
+
});
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (error instanceof Unbindable) return void 0;
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
const scoped = [...input.pins.scoped.entries()].map(([file, scripts]) => ({ file, scripts: scripts.map((s) => `${s.head}:${s.name}`).sort() })).sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0);
|
|
517
|
+
const document = {
|
|
518
|
+
version: ENTRYPOINT_FINGERPRINT_VERSION,
|
|
519
|
+
commands: input.commands,
|
|
520
|
+
pins: { whole: [...input.pins.whole].sort(), scoped },
|
|
521
|
+
paths
|
|
522
|
+
};
|
|
523
|
+
return `v${ENTRYPOINT_FINGERPRINT_VERSION}:${sha256(JSON.stringify(document))}`;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/core/ci/local-ci-state.ts
|
|
527
|
+
function currentProcessIdentity() {
|
|
528
|
+
const identity = readProcessIdentity(process.pid);
|
|
529
|
+
if (!identity) throw new Error("Unable to establish local CI process identity.");
|
|
530
|
+
return identity;
|
|
531
|
+
}
|
|
532
|
+
var DEFAULT_LOCAL_CI_STATUS_CONTEXT = "Build Local CI";
|
|
533
|
+
var DEFAULT_LOCAL_CI_MAX_CONCURRENT = 2;
|
|
534
|
+
var LocalCiRunStatusSchema = z.enum([
|
|
535
|
+
"queued",
|
|
536
|
+
"running",
|
|
537
|
+
/**
|
|
538
|
+
* Validation passed; remote commit-status publication is not yet confirmed.
|
|
539
|
+
* Distinct from `succeeded_pending` (awaiting attestation preconditions).
|
|
540
|
+
*/
|
|
541
|
+
"publication_pending",
|
|
542
|
+
"succeeded_pending",
|
|
543
|
+
"succeeded",
|
|
544
|
+
"skipped",
|
|
545
|
+
"failed",
|
|
546
|
+
"superseded"
|
|
547
|
+
]);
|
|
548
|
+
var LocalCiLegacyRunStatusSchema = z.enum(["succeeded_manual", "merged", "merge_blocked"]);
|
|
549
|
+
var LocalCiPersistedRunStatusSchema = z.union([
|
|
550
|
+
LocalCiRunStatusSchema,
|
|
551
|
+
LocalCiLegacyRunStatusSchema
|
|
552
|
+
]);
|
|
553
|
+
function normalizeLocalCiRunStatus(status) {
|
|
554
|
+
switch (status) {
|
|
555
|
+
case "succeeded_manual":
|
|
556
|
+
case "merged":
|
|
557
|
+
case "merge_blocked":
|
|
558
|
+
return "succeeded";
|
|
559
|
+
default:
|
|
560
|
+
return status;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
var LocalCiPublicationTargetStatusSchema = z.enum(["succeeded", "succeeded_pending"]);
|
|
564
|
+
var LocalCiPersistedPublicationTargetStatusSchema = z.enum(["succeeded", "succeeded_pending", "succeeded_manual"]).transform(
|
|
565
|
+
(status) => status === "succeeded_manual" ? "succeeded" : status
|
|
566
|
+
);
|
|
567
|
+
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
568
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
569
|
+
"publication_pending",
|
|
570
|
+
"succeeded_pending",
|
|
571
|
+
"succeeded",
|
|
572
|
+
"skipped",
|
|
573
|
+
"failed",
|
|
574
|
+
"superseded"
|
|
575
|
+
]);
|
|
576
|
+
function isLocalCiActiveStatus(status) {
|
|
577
|
+
return ACTIVE_STATUSES.has(status);
|
|
578
|
+
}
|
|
579
|
+
function isLocalCiTerminalStatus(status) {
|
|
580
|
+
return TERMINAL_STATUSES.has(status);
|
|
581
|
+
}
|
|
582
|
+
var MAX_LOCAL_CI_COMMAND_OUTPUT_BYTES = 16 * 1024;
|
|
583
|
+
var MAX_LOCAL_CI_RETAINED_LOG_BYTES = 32 * 1024 * 1024;
|
|
584
|
+
var LocalCiCommandResultSchema = z.object({
|
|
585
|
+
command: z.string().min(1),
|
|
586
|
+
exitCode: z.number().int(),
|
|
587
|
+
output: z.string(),
|
|
588
|
+
/** True when output is a bounded tail; the retained log may contain more under its budget. */
|
|
589
|
+
outputTruncated: z.boolean().optional(),
|
|
590
|
+
/** Complete UTF-8 byte length before inline output bounding. */
|
|
591
|
+
outputBytes: z.number().int().nonnegative().optional(),
|
|
592
|
+
id: z.string().min(1).optional(),
|
|
593
|
+
phase: z.string().min(1).optional(),
|
|
594
|
+
durationMs: z.number().int().nonnegative().optional()
|
|
595
|
+
});
|
|
596
|
+
var LocalCiFailingTestSchema = z.object({
|
|
597
|
+
/** Test file path when the identifier carried one (vitest/pytest/go path forms). */
|
|
598
|
+
file: z.string().min(1).optional(),
|
|
599
|
+
/** Failing test identifier (suite/test path, or raw name when no file parsed). */
|
|
600
|
+
test: z.string().min(1),
|
|
601
|
+
/** Verification phase of the failing step (e.g. 'verify'). */
|
|
602
|
+
phase: z.string().min(1)
|
|
603
|
+
});
|
|
604
|
+
var WorkerClaimPayloadSchema = z.object({
|
|
605
|
+
pid: z.number().int().positive(),
|
|
606
|
+
pidIdentity: z.string().min(1).optional(),
|
|
607
|
+
token: z.string().uuid(),
|
|
608
|
+
claimedAt: z.iso.datetime(),
|
|
609
|
+
/** Deterministic fingerprint of the authorized record generation; optional for legacy. */
|
|
610
|
+
generationKey: z.string().min(1).optional()
|
|
611
|
+
});
|
|
612
|
+
var CROSS_PROCESS_FS_LOCK_STALE_MS = 18e4;
|
|
613
|
+
var CROSS_PROCESS_FS_LOCK_UPDATE_MS = 1e4;
|
|
614
|
+
var PROPER_LOCKFILE_STALE_MIN_MS = 2e3;
|
|
615
|
+
var PROPER_LOCKFILE_UPDATE_MIN_MS = 1e3;
|
|
616
|
+
function resolveCrossProcessFsLockPolicy() {
|
|
617
|
+
const stale = Math.max(CROSS_PROCESS_FS_LOCK_STALE_MS, PROPER_LOCKFILE_STALE_MIN_MS);
|
|
618
|
+
const configuredUpdate = CROSS_PROCESS_FS_LOCK_UPDATE_MS;
|
|
619
|
+
const update = Math.max(Math.min(configuredUpdate, stale / 2), PROPER_LOCKFILE_UPDATE_MIN_MS);
|
|
620
|
+
return { stale, update };
|
|
621
|
+
}
|
|
622
|
+
var CROSS_PROCESS_FS_LOCK_OPTIONS = {
|
|
623
|
+
...resolveCrossProcessFsLockPolicy(),
|
|
624
|
+
realpath: false,
|
|
625
|
+
retries: {
|
|
626
|
+
retries: 400,
|
|
627
|
+
minTimeout: 15,
|
|
628
|
+
maxTimeout: 100,
|
|
629
|
+
factor: 1.2
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
function boundLocalCiCommandOutput(result) {
|
|
633
|
+
const complete = Buffer.from(result.output, "utf8");
|
|
634
|
+
if (complete.byteLength <= MAX_LOCAL_CI_COMMAND_OUTPUT_BYTES) {
|
|
635
|
+
return result;
|
|
636
|
+
}
|
|
637
|
+
let start = complete.byteLength - MAX_LOCAL_CI_COMMAND_OUTPUT_BYTES;
|
|
638
|
+
while (start < complete.byteLength && (complete[start] & 192) === 128) {
|
|
639
|
+
start += 1;
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
...result,
|
|
643
|
+
output: complete.subarray(start).toString("utf8"),
|
|
644
|
+
outputTruncated: true,
|
|
645
|
+
outputBytes: complete.byteLength
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function utf8Head(input, maxBytes) {
|
|
649
|
+
if (input.byteLength <= maxBytes) return input;
|
|
650
|
+
let end = Math.max(0, maxBytes);
|
|
651
|
+
while (end > 0 && end < input.byteLength && (input[end] & 192) === 128) {
|
|
652
|
+
end -= 1;
|
|
653
|
+
}
|
|
654
|
+
return input.subarray(0, end);
|
|
655
|
+
}
|
|
656
|
+
function utf8Tail(input, maxBytes) {
|
|
657
|
+
if (input.byteLength <= maxBytes) return input;
|
|
658
|
+
let start = input.byteLength - Math.max(0, maxBytes);
|
|
659
|
+
while (start < input.byteLength && (input[start] & 192) === 128) {
|
|
660
|
+
start += 1;
|
|
661
|
+
}
|
|
662
|
+
return input.subarray(start);
|
|
663
|
+
}
|
|
664
|
+
function renderBoundedLocalCiLogEntry(input) {
|
|
665
|
+
if (input.maxBytes <= 0) return "";
|
|
666
|
+
const completeOutput = Buffer.from(input.output, "utf8");
|
|
667
|
+
const complete = Buffer.from(
|
|
668
|
+
`${input.header}
|
|
669
|
+
${input.output}${input.output.endsWith("\n") ? "" : "\n"}`,
|
|
670
|
+
"utf8"
|
|
671
|
+
);
|
|
672
|
+
if (complete.byteLength <= input.maxBytes) return complete.toString("utf8");
|
|
673
|
+
const marker = Buffer.from(
|
|
674
|
+
`
|
|
675
|
+
[local-ci] transcript truncated: captured=${completeOutput.byteLength} bytes
|
|
676
|
+
`,
|
|
677
|
+
"utf8"
|
|
678
|
+
);
|
|
679
|
+
const header = Buffer.from(`${input.header}
|
|
680
|
+
`, "utf8");
|
|
681
|
+
const outputBudget = input.maxBytes - header.byteLength - marker.byteLength;
|
|
682
|
+
if (outputBudget <= 0) {
|
|
683
|
+
return utf8Head(Buffer.concat([header, marker]), input.maxBytes).toString("utf8");
|
|
684
|
+
}
|
|
685
|
+
const headBudget = Math.floor(outputBudget / 2);
|
|
686
|
+
const tailBudget = outputBudget - headBudget;
|
|
687
|
+
return Buffer.concat([
|
|
688
|
+
header,
|
|
689
|
+
utf8Head(completeOutput, headBudget),
|
|
690
|
+
marker,
|
|
691
|
+
utf8Tail(completeOutput, tailBudget)
|
|
692
|
+
]).toString("utf8");
|
|
693
|
+
}
|
|
694
|
+
var LOCAL_CI_REPOSITORY_IDENTITY_MAX = 256;
|
|
695
|
+
function parseLocalCiRepositoryIdentity(value) {
|
|
696
|
+
const trimmed = value.trim();
|
|
697
|
+
if (!trimmed || trimmed.length > LOCAL_CI_REPOSITORY_IDENTITY_MAX) return null;
|
|
698
|
+
if (/\s/.test(trimmed) || trimmed.includes("\\") || trimmed.includes("\0")) return null;
|
|
699
|
+
if (trimmed.startsWith("/") || trimmed.endsWith("/")) return null;
|
|
700
|
+
if (trimmed.includes("//")) return null;
|
|
701
|
+
const segments = trimmed.split("/");
|
|
702
|
+
if (segments.length < 2 || segments.length > 24) return null;
|
|
703
|
+
for (const segment of segments) {
|
|
704
|
+
if (!segment || segment === "." || segment === "..") return null;
|
|
705
|
+
if (!/^[A-Za-z0-9._~+-]+$/.test(segment)) return null;
|
|
706
|
+
}
|
|
707
|
+
return trimmed;
|
|
708
|
+
}
|
|
709
|
+
var LocalCiRepositoryIdentitySchema = z.string().refine((value) => parseLocalCiRepositoryIdentity(value) !== null, {
|
|
710
|
+
message: "Invalid local CI repository identity"
|
|
711
|
+
});
|
|
712
|
+
var OutstandingRemoteStatusPublicationSchema = z.object({
|
|
713
|
+
repository: LocalCiRepositoryIdentitySchema,
|
|
714
|
+
sha: z.string().regex(/^[a-f0-9]{40}$/i),
|
|
715
|
+
context: z.string().min(1).max(100),
|
|
716
|
+
state: z.enum(["failure", "success", "pending"]),
|
|
717
|
+
description: z.string().min(1).max(140),
|
|
718
|
+
/**
|
|
719
|
+
* Local status to apply once remote publication is confirmed. Required for
|
|
720
|
+
* success/pending remote states; omitted for failure (record is already failed).
|
|
721
|
+
* Legacy `succeeded_manual` normalizes to `succeeded` at read time.
|
|
722
|
+
*/
|
|
723
|
+
targetStatus: LocalCiPersistedPublicationTargetStatusSchema.optional(),
|
|
724
|
+
/**
|
|
725
|
+
* Host-issued generation bound outside candidate-writable state.
|
|
726
|
+
* Optional on disk so legacy records still parse; the publisher fails closed
|
|
727
|
+
* when this is missing or does not match the host generation file.
|
|
728
|
+
*/
|
|
729
|
+
generation: z.string().uuid().optional(),
|
|
730
|
+
/**
|
|
731
|
+
* Host-keyed HMAC over repository/sha/context/state/description/targetStatus
|
|
732
|
+
* and generation. Optional on disk so legacy records still parse; the
|
|
733
|
+
* publisher fails closed when this is missing or invalid and never publishes
|
|
734
|
+
* success.
|
|
735
|
+
*/
|
|
736
|
+
mac: z.string().regex(/^[a-f0-9]{64}$/i).optional()
|
|
737
|
+
}).superRefine((value, ctx) => {
|
|
738
|
+
if (value.state === "failure" && value.targetStatus !== void 0) {
|
|
739
|
+
ctx.addIssue({
|
|
740
|
+
code: "custom",
|
|
741
|
+
message: "Failure status publications must not carry a success targetStatus.",
|
|
742
|
+
path: ["targetStatus"]
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
if (value.state !== "failure" && value.targetStatus === void 0) {
|
|
746
|
+
ctx.addIssue({
|
|
747
|
+
code: "custom",
|
|
748
|
+
message: "Success-path status publications require targetStatus.",
|
|
749
|
+
path: ["targetStatus"]
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
var FailurePublicationLeaseSchema = z.object({
|
|
754
|
+
/** Unique lease token; required for post-publish clear/readback authorization. */
|
|
755
|
+
token: z.string().uuid(),
|
|
756
|
+
/** Process that holds the in-flight remote publication. */
|
|
757
|
+
ownerPid: z.number().int().positive(),
|
|
758
|
+
ownerPidIdentity: z.string().min(1).optional(),
|
|
759
|
+
/**
|
|
760
|
+
* Fingerprint of the record generation authorized for this remote write
|
|
761
|
+
* (same identity fields as record CAS, frozen at lease acquire).
|
|
762
|
+
*/
|
|
763
|
+
generationKey: z.string().min(1),
|
|
764
|
+
claimedAt: z.iso.datetime()
|
|
765
|
+
});
|
|
766
|
+
var LocalCiRunRecordSchema = z.object({
|
|
767
|
+
schemaVersion: z.literal(1),
|
|
768
|
+
repoRoot: z.string().min(1),
|
|
769
|
+
repository: LocalCiRepositoryIdentitySchema,
|
|
770
|
+
sha: z.string().regex(/^[a-f0-9]{40}$/i),
|
|
771
|
+
branch: z.string().min(1),
|
|
772
|
+
remote: z.string().min(1),
|
|
773
|
+
statusContext: z.string().min(1).max(100),
|
|
774
|
+
maxConcurrent: z.number().int().min(1).max(8),
|
|
775
|
+
status: LocalCiPersistedRunStatusSchema,
|
|
776
|
+
commands: z.array(LocalCiCommandResultSchema),
|
|
777
|
+
/** Immutable branch-submission order; legacy records fall back to createdAt. */
|
|
778
|
+
submittedAt: z.iso.datetime().optional(),
|
|
779
|
+
createdAt: z.iso.datetime(),
|
|
780
|
+
updatedAt: z.iso.datetime(),
|
|
781
|
+
startedAt: z.iso.datetime().optional(),
|
|
782
|
+
queueWaitMs: z.number().int().nonnegative().optional(),
|
|
783
|
+
pid: z.number().int().positive().optional(),
|
|
784
|
+
/** OS start identity paired with pid so PID reuse is not mistaken for the worker. */
|
|
785
|
+
pidIdentity: z.string().min(1).optional(),
|
|
786
|
+
worktreePath: z.string().min(1).optional(),
|
|
787
|
+
/** Count of automatic unresolved-trusted-base recovery requeues. */
|
|
788
|
+
recoveryAttempts: z.number().int().nonnegative().optional(),
|
|
789
|
+
error: z.string().min(1).optional(),
|
|
790
|
+
trustedBaseRef: z.string().min(1).optional(),
|
|
791
|
+
trustedBaseSha: z.string().regex(/^[a-f0-9]{40}$/i).optional(),
|
|
792
|
+
/** Open PR identity observed when the exact head/base pair was admitted. */
|
|
793
|
+
pullRequestNumber: z.number().int().positive().optional(),
|
|
794
|
+
/** Pre-validation hold: an intentional entrypoint change still needs exact-SHA approval. */
|
|
795
|
+
entrypointApprovalPending: z.boolean().optional(),
|
|
796
|
+
/** Fingerprint of the redefined entrypoint definitions (local-ci-entrypoint-fingerprint.ts), for status and holds. */
|
|
797
|
+
entrypointFingerprint: z.string().regex(ENTRYPOINT_FINGERPRINT_PATTERN).optional(),
|
|
798
|
+
/**
|
|
799
|
+
* Authority-selected validation profile id (`full` or trusted-base profile).
|
|
800
|
+
* Optional for legacy records; missing means pre-profile runs (treat as full).
|
|
801
|
+
* Telemetry / runtime propagation only — never changes pass/fail by itself.
|
|
802
|
+
*/
|
|
803
|
+
validationProfile: z.string().min(1).max(64).optional(),
|
|
804
|
+
/**
|
|
805
|
+
* Human-readable reason for {@link validationProfile}. Optional for legacy.
|
|
806
|
+
*/
|
|
807
|
+
validationProfileReason: z.string().min(1).max(500).optional(),
|
|
808
|
+
/**
|
|
809
|
+
* Immediate failed predecessor whose successful release-gate slices may be
|
|
810
|
+
* reused once. Submission assigns this only on the first same-branch
|
|
811
|
+
* successor; the worker revalidates every retry invariant before use.
|
|
812
|
+
*/
|
|
813
|
+
retryOfSha: z.string().regex(/^[a-f0-9]{40}$/i).optional(),
|
|
814
|
+
/** Successor SHA that atomically claimed this failed run's one retry slot. */
|
|
815
|
+
retryClaimedBySha: z.string().regex(/^[a-f0-9]{40}$/i).optional(),
|
|
816
|
+
/** Durable provenance for an accepted or attempted release-gate slice retry. */
|
|
817
|
+
gateRetry: z.object({
|
|
818
|
+
sourceSha: z.string().regex(/^[a-f0-9]{40}$/i),
|
|
819
|
+
manifestDigest: z.string().regex(/^[a-f0-9]{64}$/),
|
|
820
|
+
rerunLabels: z.array(z.string().min(1).max(120)).min(1).max(200),
|
|
821
|
+
reusedLabels: z.array(z.string().min(1).max(120)).min(1).max(200),
|
|
822
|
+
applied: z.boolean().optional()
|
|
823
|
+
}).optional(),
|
|
824
|
+
/**
|
|
825
|
+
* Structured failing tests parsed from a failed test-phase command's captured
|
|
826
|
+
* output. Telemetry only — never consulted by pass/fail decisions.
|
|
827
|
+
*/
|
|
828
|
+
failingTests: z.array(LocalCiFailingTestSchema).optional(),
|
|
829
|
+
/** True when failingTests hit the retention cap and dropped further entries. */
|
|
830
|
+
failingTestsTruncated: z.boolean().optional(),
|
|
831
|
+
/**
|
|
832
|
+
* Set when the run failed while materializing (slot, worktree, executed tree)
|
|
833
|
+
* before any candidate command ran. Excluded from the consecutive-failure halt.
|
|
834
|
+
*/
|
|
835
|
+
failureClass: z.literal("infrastructure").optional(),
|
|
836
|
+
/**
|
|
837
|
+
* Historical cross-machine verdict reuse provenance. The verdict cache was
|
|
838
|
+
* removed and nothing writes this field; it stays accepted so retained run
|
|
839
|
+
* records that carry it still parse.
|
|
840
|
+
*/
|
|
841
|
+
verdictReuse: z.object({
|
|
842
|
+
/** Verdict ref that authorized the skip. */
|
|
843
|
+
ref: z.string().min(1).max(300),
|
|
844
|
+
/** Executed tree the verdict attests to (field name retained). */
|
|
845
|
+
mergeTree: z.string().min(1).max(64),
|
|
846
|
+
/** Machine that originally produced the verdict (provenance only). */
|
|
847
|
+
producer: z.string().min(1).max(32),
|
|
848
|
+
/** Completion time of the ORIGINAL validating run. */
|
|
849
|
+
originallyCompletedAt: z.string().min(1).max(64)
|
|
850
|
+
}).optional(),
|
|
851
|
+
/**
|
|
852
|
+
* Outstanding remote status publication (failure or success-path). Present
|
|
853
|
+
* until exact-context readback confirms the remote state; blocks terminal
|
|
854
|
+
* retention deletion. Success-path records use status `publication_pending`
|
|
855
|
+
* until publication is confirmed.
|
|
856
|
+
*/
|
|
857
|
+
outstandingRemoteStatusPublication: OutstandingRemoteStatusPublicationSchema.optional(),
|
|
858
|
+
/**
|
|
859
|
+
* In-flight remote status publication lease (historical field name). Blocks
|
|
860
|
+
* fresh-generation establishment while the exact owner process is alive;
|
|
861
|
+
* recoverable when the owner is dead or the PID has been reused.
|
|
862
|
+
*/
|
|
863
|
+
failurePublicationLease: FailurePublicationLeaseSchema.optional()
|
|
864
|
+
}).transform((record) => ({
|
|
865
|
+
...record,
|
|
866
|
+
status: normalizeLocalCiRunStatus(record.status)
|
|
867
|
+
}));
|
|
868
|
+
function hasOutstandingRemoteStatusPublication(record) {
|
|
869
|
+
return record.outstandingRemoteStatusPublication !== void 0;
|
|
870
|
+
}
|
|
871
|
+
function isPublicationPending(record) {
|
|
872
|
+
return record.status === "publication_pending";
|
|
873
|
+
}
|
|
874
|
+
function outstandingStatusPublicationMatchesRecord(record, obligation) {
|
|
875
|
+
return obligation.repository === record.repository && obligation.sha.toLowerCase() === record.sha.toLowerCase();
|
|
876
|
+
}
|
|
877
|
+
function hasLiveFailurePublicationLease(record) {
|
|
878
|
+
const lease = record.failurePublicationLease;
|
|
879
|
+
if (!lease) return false;
|
|
880
|
+
return processOwnershipState(lease.ownerPid, lease.ownerPidIdentity, lease.claimedAt) !== "reclaimable";
|
|
881
|
+
}
|
|
882
|
+
function resolveFailurePublicationClaimedAt(currentUpdatedAt, candidateClaimedAt) {
|
|
883
|
+
const currentMs = Date.parse(currentUpdatedAt);
|
|
884
|
+
const candidateMs = Date.parse(candidateClaimedAt);
|
|
885
|
+
if (!Number.isFinite(currentMs)) {
|
|
886
|
+
return Number.isFinite(candidateMs) ? new Date(candidateMs).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
887
|
+
}
|
|
888
|
+
const floorMs = currentMs + 1;
|
|
889
|
+
if (!Number.isFinite(candidateMs)) {
|
|
890
|
+
return new Date(floorMs).toISOString();
|
|
891
|
+
}
|
|
892
|
+
return new Date(Math.max(candidateMs, floorMs)).toISOString();
|
|
893
|
+
}
|
|
894
|
+
function createFailurePublicationLease(current, claimedAt) {
|
|
895
|
+
const resolvedClaimedAt = resolveFailurePublicationClaimedAt(
|
|
896
|
+
current.updatedAt,
|
|
897
|
+
claimedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
898
|
+
);
|
|
899
|
+
const postAcquisitionGeneration = {
|
|
900
|
+
...current,
|
|
901
|
+
updatedAt: resolvedClaimedAt,
|
|
902
|
+
// Lease attached below; field is not part of the generation fingerprint.
|
|
903
|
+
failurePublicationLease: void 0
|
|
904
|
+
};
|
|
905
|
+
const lease = FailurePublicationLeaseSchema.parse({
|
|
906
|
+
token: randomUUID(),
|
|
907
|
+
ownerPid: process.pid,
|
|
908
|
+
ownerPidIdentity: currentProcessIdentity(),
|
|
909
|
+
generationKey: localCiRecordGenerationKey(postAcquisitionGeneration),
|
|
910
|
+
claimedAt: resolvedClaimedAt
|
|
911
|
+
});
|
|
912
|
+
const leased = {
|
|
913
|
+
...postAcquisitionGeneration,
|
|
914
|
+
failurePublicationLease: lease
|
|
915
|
+
};
|
|
916
|
+
return { lease, leased };
|
|
917
|
+
}
|
|
918
|
+
function matchesFailurePublicationLease(record, lease) {
|
|
919
|
+
const current = record.failurePublicationLease;
|
|
920
|
+
if (!current) return false;
|
|
921
|
+
if (current.token !== lease.token || current.generationKey !== lease.generationKey || current.ownerPid !== lease.ownerPid || current.ownerPidIdentity !== lease.ownerPidIdentity || current.claimedAt !== lease.claimedAt) {
|
|
922
|
+
return false;
|
|
923
|
+
}
|
|
924
|
+
return localCiRecordGenerationKey(record) === lease.generationKey;
|
|
925
|
+
}
|
|
926
|
+
function isSameLocalCiRecordGeneration(current, expected) {
|
|
927
|
+
return localCiRecordGenerationKey(current) === localCiRecordGenerationKey(expected);
|
|
928
|
+
}
|
|
929
|
+
function localCiRecordGenerationKey(record) {
|
|
930
|
+
const obligation = record.outstandingRemoteStatusPublication;
|
|
931
|
+
const material = [
|
|
932
|
+
record.repository,
|
|
933
|
+
record.sha.toLowerCase(),
|
|
934
|
+
record.status,
|
|
935
|
+
record.submittedAt ?? record.createdAt,
|
|
936
|
+
record.createdAt,
|
|
937
|
+
record.updatedAt,
|
|
938
|
+
obligation ? [
|
|
939
|
+
obligation.repository,
|
|
940
|
+
obligation.sha.toLowerCase(),
|
|
941
|
+
obligation.context,
|
|
942
|
+
obligation.state,
|
|
943
|
+
obligation.description,
|
|
944
|
+
obligation.targetStatus ?? "",
|
|
945
|
+
obligation.mac ?? ""
|
|
946
|
+
].join("\0") : ""
|
|
947
|
+
].join("\0");
|
|
948
|
+
return createHash2("sha256").update(material).digest("hex");
|
|
949
|
+
}
|
|
950
|
+
function createOutstandingStatusPublication(record, input) {
|
|
951
|
+
if (input.state === "failure" && input.targetStatus !== void 0) {
|
|
952
|
+
throw new Error("Failure status publications must not carry a success targetStatus.");
|
|
953
|
+
}
|
|
954
|
+
if (input.state !== "failure" && input.targetStatus === void 0) {
|
|
955
|
+
throw new Error("Success-path status publications require targetStatus.");
|
|
956
|
+
}
|
|
957
|
+
const parsed = OutstandingRemoteStatusPublicationSchema.parse({
|
|
958
|
+
repository: record.repository,
|
|
959
|
+
sha: record.sha.toLowerCase(),
|
|
960
|
+
context: record.statusContext,
|
|
961
|
+
state: input.state,
|
|
962
|
+
description: input.description.slice(0, 140),
|
|
963
|
+
...input.targetStatus !== void 0 ? { targetStatus: input.targetStatus } : {}
|
|
964
|
+
});
|
|
965
|
+
return OutstandingRemoteStatusPublicationSchema.parse(
|
|
966
|
+
attachOutstandingStatusPublicationMac(parsed)
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
function createOutstandingFailureStatusPublication(record, description) {
|
|
970
|
+
return createOutstandingStatusPublication(record, {
|
|
971
|
+
state: "failure",
|
|
972
|
+
description
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
var DEFAULT_LOCAL_CI_MAX_TERMINAL_RECORDS = 50;
|
|
976
|
+
function resolveMaxTerminalRecords(value) {
|
|
977
|
+
if (value !== void 0 && Number.isInteger(value) && value >= 0) {
|
|
978
|
+
return value;
|
|
979
|
+
}
|
|
980
|
+
return DEFAULT_LOCAL_CI_MAX_TERMINAL_RECORDS;
|
|
981
|
+
}
|
|
982
|
+
function compareRecordsNewestFirst(a, b) {
|
|
983
|
+
const byCreated = b.createdAt.localeCompare(a.createdAt);
|
|
984
|
+
if (byCreated !== 0) return byCreated;
|
|
985
|
+
return b.sha.localeCompare(a.sha);
|
|
986
|
+
}
|
|
987
|
+
function submissionTime(record) {
|
|
988
|
+
return record.submittedAt ?? record.createdAt;
|
|
989
|
+
}
|
|
990
|
+
function compareSubmissionsNewestFirst(a, b) {
|
|
991
|
+
const bySubmitted = submissionTime(b).localeCompare(submissionTime(a));
|
|
992
|
+
if (bySubmitted !== 0) return bySubmitted;
|
|
993
|
+
return b.sha.localeCompare(a.sha);
|
|
994
|
+
}
|
|
995
|
+
function createQueuedRun(input) {
|
|
996
|
+
const timestamp = (input.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
997
|
+
const repository = parseLocalCiRepositoryIdentity(input.repository);
|
|
998
|
+
if (!repository) {
|
|
999
|
+
throw new Error(`Invalid local CI repository identity: ${input.repository}`);
|
|
1000
|
+
}
|
|
1001
|
+
return LocalCiRunRecordSchema.parse({
|
|
1002
|
+
schemaVersion: 1,
|
|
1003
|
+
repoRoot: path4.resolve(input.repoRoot),
|
|
1004
|
+
repository,
|
|
1005
|
+
sha: input.sha.toLowerCase(),
|
|
1006
|
+
branch: input.branch,
|
|
1007
|
+
remote: input.remote,
|
|
1008
|
+
statusContext: input.statusContext ?? DEFAULT_LOCAL_CI_STATUS_CONTEXT,
|
|
1009
|
+
maxConcurrent: input.maxConcurrent ?? DEFAULT_LOCAL_CI_MAX_CONCURRENT,
|
|
1010
|
+
retryOfSha: input.retryOfSha?.toLowerCase(),
|
|
1011
|
+
status: "queued",
|
|
1012
|
+
commands: [],
|
|
1013
|
+
submittedAt: timestamp,
|
|
1014
|
+
createdAt: timestamp,
|
|
1015
|
+
updatedAt: timestamp
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
function repoStateKey(repository) {
|
|
1019
|
+
const identity = parseLocalCiRepositoryIdentity(repository) ?? repository.trim();
|
|
1020
|
+
const readable = identity.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
1021
|
+
const digest = createHash2("sha256").update(identity).digest("hex").slice(0, 12);
|
|
1022
|
+
return `${readable || "repository"}-${digest}`;
|
|
1023
|
+
}
|
|
1024
|
+
function stateReadError(filePath, cause) {
|
|
1025
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1026
|
+
return new Error(`Invalid local CI state at ${filePath}: ${detail}`, { cause });
|
|
1027
|
+
}
|
|
1028
|
+
function isNodeErrorCode(error, code) {
|
|
1029
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
1030
|
+
}
|
|
1031
|
+
function isPathUnderRoot(root, candidatePath) {
|
|
1032
|
+
const resolvedRoot = path4.resolve(root);
|
|
1033
|
+
const resolvedCandidate = path4.resolve(candidatePath);
|
|
1034
|
+
const relative = path4.relative(resolvedRoot, resolvedCandidate);
|
|
1035
|
+
return relative !== "" && !relative.startsWith("..") && !path4.isAbsolute(relative);
|
|
1036
|
+
}
|
|
1037
|
+
var LocalCiStateStore = class {
|
|
1038
|
+
constructor(root, hooks = {}) {
|
|
1039
|
+
this.root = root;
|
|
1040
|
+
this.hooks = hooks;
|
|
1041
|
+
}
|
|
1042
|
+
root;
|
|
1043
|
+
hooks;
|
|
1044
|
+
repositoryDirectory(repository) {
|
|
1045
|
+
return path4.join(this.root, repoStateKey(repository));
|
|
1046
|
+
}
|
|
1047
|
+
filePath(repository, sha) {
|
|
1048
|
+
return path4.join(this.repositoryDirectory(repository), `${sha.toLowerCase()}.json`);
|
|
1049
|
+
}
|
|
1050
|
+
logPath(repository, sha) {
|
|
1051
|
+
return path4.join(this.repositoryDirectory(repository), `${sha.toLowerCase()}.log`);
|
|
1052
|
+
}
|
|
1053
|
+
workerClaimPath(repository, sha) {
|
|
1054
|
+
return path4.join(this.repositoryDirectory(repository), `${sha.toLowerCase()}.worker.lock`);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Synthetic proper-lockfile target for worker-claim mutations (never created).
|
|
1058
|
+
* Distinct from the claim payload path and from the record JSON path.
|
|
1059
|
+
*/
|
|
1060
|
+
workerClaimMutexTarget(repository, sha) {
|
|
1061
|
+
return path4.join(
|
|
1062
|
+
this.repositoryDirectory(repository),
|
|
1063
|
+
`${sha.toLowerCase()}.worker.claim.mutex`
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Serialize claim create/stale-reclaim, transfer, and release for one SHA.
|
|
1068
|
+
*
|
|
1069
|
+
* Lock order (mandatory, never reverse):
|
|
1070
|
+
* 1. record mutation lock (when proving authorized generation)
|
|
1071
|
+
* 2. claim mutation lock
|
|
1072
|
+
*
|
|
1073
|
+
* claimWorker nests claim under record. transfer/release take claim only and
|
|
1074
|
+
* must never acquire the record lock while holding claim.
|
|
1075
|
+
*
|
|
1076
|
+
* Uses fenced proper-lockfile ownership (owner token) so a resumed predecessor
|
|
1077
|
+
* after stale reclaim cannot unlock/remove a successor's lock path.
|
|
1078
|
+
*/
|
|
1079
|
+
async withWorkerClaimMutationLock(repository, sha, fn) {
|
|
1080
|
+
const lockTarget = this.workerClaimMutexTarget(repository, sha);
|
|
1081
|
+
await mkdir(path4.dirname(lockTarget), { recursive: true, mode: 448 });
|
|
1082
|
+
const held = await acquireFencedFileLock(lockTarget, {
|
|
1083
|
+
...this.resolveFsLockOptions(),
|
|
1084
|
+
description: "Local CI worker claim lock"
|
|
1085
|
+
});
|
|
1086
|
+
try {
|
|
1087
|
+
if (this.hooks.afterClaimLockAcquired) {
|
|
1088
|
+
await this.hooks.afterClaimLockAcquired();
|
|
1089
|
+
}
|
|
1090
|
+
await held.assertOwned();
|
|
1091
|
+
return await fn(held);
|
|
1092
|
+
} finally {
|
|
1093
|
+
await held();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Run `fn` while holding the exclusive per-record mutation lock.
|
|
1098
|
+
* Non-reentrant for the same lock class.
|
|
1099
|
+
*
|
|
1100
|
+
* Locks the stable record JSON path without creating it (`realpath: false`).
|
|
1101
|
+
* May nest the claim mutation lock inside (record → claim only).
|
|
1102
|
+
*
|
|
1103
|
+
* Fenced with an owner token: assert ownership before state-changing writes and
|
|
1104
|
+
* across await boundaries; release is a no-op if superseded.
|
|
1105
|
+
*/
|
|
1106
|
+
async withRecordMutationLock(repository, sha, fn) {
|
|
1107
|
+
const lockTarget = this.filePath(repository, sha);
|
|
1108
|
+
await mkdir(path4.dirname(lockTarget), { recursive: true, mode: 448 });
|
|
1109
|
+
const held = await acquireFencedFileLock(lockTarget, {
|
|
1110
|
+
...this.resolveFsLockOptions(),
|
|
1111
|
+
description: "Local CI record lock"
|
|
1112
|
+
});
|
|
1113
|
+
try {
|
|
1114
|
+
if (this.hooks.afterRecordLockAcquired) {
|
|
1115
|
+
await this.hooks.afterRecordLockAcquired();
|
|
1116
|
+
}
|
|
1117
|
+
await held.assertOwned();
|
|
1118
|
+
return await fn(held);
|
|
1119
|
+
} finally {
|
|
1120
|
+
await held();
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
/** Shared lock options; test hooks may override floors-respecting timings. */
|
|
1124
|
+
resolveFsLockOptions() {
|
|
1125
|
+
const overrides = this.hooks.lockOptions;
|
|
1126
|
+
if (!overrides) return CROSS_PROCESS_FS_LOCK_OPTIONS;
|
|
1127
|
+
const base = resolveCrossProcessFsLockPolicy();
|
|
1128
|
+
const stale = Math.max(overrides.stale ?? base.stale, PROPER_LOCKFILE_STALE_MIN_MS);
|
|
1129
|
+
const configuredUpdate = overrides.update ?? base.update;
|
|
1130
|
+
const update = Math.max(Math.min(configuredUpdate, stale / 2), PROPER_LOCKFILE_UPDATE_MIN_MS);
|
|
1131
|
+
return {
|
|
1132
|
+
...CROSS_PROCESS_FS_LOCK_OPTIONS,
|
|
1133
|
+
stale,
|
|
1134
|
+
update
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
async readUnlocked(repository, sha) {
|
|
1138
|
+
const filePath = this.filePath(repository, sha);
|
|
1139
|
+
try {
|
|
1140
|
+
const raw = await readFile(filePath, "utf8");
|
|
1141
|
+
return LocalCiRunRecordSchema.parse(JSON.parse(raw));
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
throw stateReadError(filePath, error);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Write a run record under an already-held record mutation fence.
|
|
1148
|
+
*
|
|
1149
|
+
* Awaits (mkdir, temp write) may outlive a predecessor's stale window; re-assert
|
|
1150
|
+
* the fence immediately before the final rename that publishes the record so a
|
|
1151
|
+
* superseded holder cannot overwrite successor state.
|
|
1152
|
+
*/
|
|
1153
|
+
async writeRecordUnlocked(record, fence) {
|
|
1154
|
+
const parsed = LocalCiRunRecordSchema.parse(record);
|
|
1155
|
+
const filePath = this.filePath(parsed.repository, parsed.sha);
|
|
1156
|
+
await mkdir(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
1157
|
+
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
1158
|
+
await writeFile(temporary, `${JSON.stringify(parsed, null, 2)}
|
|
1159
|
+
`, {
|
|
1160
|
+
encoding: "utf8",
|
|
1161
|
+
mode: 384
|
|
1162
|
+
});
|
|
1163
|
+
const attemptId = beginHostPublicationGenerationJournal(parsed);
|
|
1164
|
+
try {
|
|
1165
|
+
await fence.assertOwned();
|
|
1166
|
+
await rename(temporary, filePath);
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
abandonHostPublicationGenerationJournal(parsed, attemptId);
|
|
1169
|
+
await unlink(temporary).catch(() => void 0);
|
|
1170
|
+
throw error;
|
|
1171
|
+
}
|
|
1172
|
+
syncHostPublicationGeneration(parsed, attemptId);
|
|
1173
|
+
return parsed;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Acquire exclusive worker ownership for the authorized record generation.
|
|
1177
|
+
*
|
|
1178
|
+
* Lock order: record mutation lock, then claim mutation lock (never reverse).
|
|
1179
|
+
* Under the record lock, re-read the on-disk record and require
|
|
1180
|
+
* {@link isSameLocalCiRecordGeneration} with `authorized`. If the disk
|
|
1181
|
+
* generation differs, return null without touching the claim (prevents a
|
|
1182
|
+
* stale launcher from superseding a fresher claim after the fact).
|
|
1183
|
+
*
|
|
1184
|
+
* Same-generation and legacy claims coalesce unless their owner is reclaimable.
|
|
1185
|
+
* A different authorized generation may supersede an old claim even if its owner lives.
|
|
1186
|
+
*/
|
|
1187
|
+
async claimWorker(authorized) {
|
|
1188
|
+
const repository = authorized.repository;
|
|
1189
|
+
const normalizedSha = authorized.sha.toLowerCase();
|
|
1190
|
+
const generationKey = localCiRecordGenerationKey(authorized);
|
|
1191
|
+
return this.withRecordMutationLock(repository, normalizedSha, async (recordFence) => {
|
|
1192
|
+
await recordFence.assertOwned();
|
|
1193
|
+
let onDisk;
|
|
1194
|
+
try {
|
|
1195
|
+
onDisk = await this.readUnlocked(repository, normalizedSha);
|
|
1196
|
+
} catch {
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
await recordFence.assertOwned();
|
|
1200
|
+
if (!isSameLocalCiRecordGeneration(onDisk, authorized)) {
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
return this.withWorkerClaimMutationLock(repository, normalizedSha, async (claimFence) => {
|
|
1204
|
+
await claimFence.assertOwned();
|
|
1205
|
+
const filePath = this.workerClaimPath(repository, normalizedSha);
|
|
1206
|
+
await mkdir(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
1207
|
+
await claimFence.assertOwned();
|
|
1208
|
+
try {
|
|
1209
|
+
const current = WorkerClaimPayloadSchema.parse(
|
|
1210
|
+
JSON.parse(await readFile(filePath, "utf8"))
|
|
1211
|
+
);
|
|
1212
|
+
await claimFence.assertOwned();
|
|
1213
|
+
const currentGeneration = current.generationKey;
|
|
1214
|
+
if (currentGeneration === generationKey) {
|
|
1215
|
+
if (processOwnershipState(current.pid, current.pidIdentity, current.claimedAt) !== "reclaimable") {
|
|
1216
|
+
return null;
|
|
1217
|
+
}
|
|
1218
|
+
await claimFence.assertOwned();
|
|
1219
|
+
await unlink(filePath).catch((unlinkError) => {
|
|
1220
|
+
if (!isNodeErrorCode(unlinkError, "ENOENT")) throw unlinkError;
|
|
1221
|
+
});
|
|
1222
|
+
} else if (currentGeneration === void 0) {
|
|
1223
|
+
if (processOwnershipState(current.pid, current.pidIdentity, current.claimedAt) !== "reclaimable") {
|
|
1224
|
+
return null;
|
|
1225
|
+
}
|
|
1226
|
+
await claimFence.assertOwned();
|
|
1227
|
+
await unlink(filePath).catch((unlinkError) => {
|
|
1228
|
+
if (!isNodeErrorCode(unlinkError, "ENOENT")) throw unlinkError;
|
|
1229
|
+
});
|
|
1230
|
+
} else {
|
|
1231
|
+
await claimFence.assertOwned();
|
|
1232
|
+
await unlink(filePath).catch((unlinkError) => {
|
|
1233
|
+
if (!isNodeErrorCode(unlinkError, "ENOENT")) throw unlinkError;
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
} catch (readError) {
|
|
1237
|
+
if (isNodeErrorCode(readError, "ENOENT")) {
|
|
1238
|
+
} else {
|
|
1239
|
+
await claimFence.assertOwned();
|
|
1240
|
+
await unlink(filePath).catch((unlinkError) => {
|
|
1241
|
+
if (!isNodeErrorCode(unlinkError, "ENOENT")) throw unlinkError;
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const claim = WorkerClaimPayloadSchema.parse({
|
|
1246
|
+
pid: process.pid,
|
|
1247
|
+
pidIdentity: currentProcessIdentity(),
|
|
1248
|
+
token: randomUUID(),
|
|
1249
|
+
claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1250
|
+
generationKey
|
|
1251
|
+
});
|
|
1252
|
+
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
1253
|
+
await writeFile(temporary, `${JSON.stringify(claim)}
|
|
1254
|
+
`, {
|
|
1255
|
+
encoding: "utf8",
|
|
1256
|
+
mode: 384
|
|
1257
|
+
});
|
|
1258
|
+
try {
|
|
1259
|
+
await claimFence.assertOwned();
|
|
1260
|
+
await link(temporary, filePath);
|
|
1261
|
+
await unlink(temporary).catch(() => void 0);
|
|
1262
|
+
return {
|
|
1263
|
+
...claim,
|
|
1264
|
+
generationKey,
|
|
1265
|
+
filePath,
|
|
1266
|
+
repository,
|
|
1267
|
+
sha: normalizedSha
|
|
1268
|
+
};
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
await unlink(temporary).catch(() => void 0);
|
|
1271
|
+
if (isNodeErrorCode(error, "EEXIST")) return null;
|
|
1272
|
+
throw error;
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Transfer claim ownership to a spawned worker PID. Token + generation must
|
|
1279
|
+
* still match under the claim mutex; a superseded claim cannot transfer.
|
|
1280
|
+
*/
|
|
1281
|
+
async transferWorkerClaim(claim, pid, pidIdentity) {
|
|
1282
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
1283
|
+
throw new Error("transferWorkerClaim requires a positive integer pid.");
|
|
1284
|
+
}
|
|
1285
|
+
if (pidIdentity !== void 0 && pidIdentity.trim().length === 0) {
|
|
1286
|
+
throw new Error("transferWorkerClaim requires a process identity.");
|
|
1287
|
+
}
|
|
1288
|
+
return this.withWorkerClaimMutationLock(claim.repository, claim.sha, async (fence) => {
|
|
1289
|
+
await fence.assertOwned();
|
|
1290
|
+
let current;
|
|
1291
|
+
try {
|
|
1292
|
+
current = WorkerClaimPayloadSchema.parse(
|
|
1293
|
+
JSON.parse(await readFile(claim.filePath, "utf8"))
|
|
1294
|
+
);
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
throw new Error(
|
|
1297
|
+
`Local CI worker claim missing during transfer at ${claim.filePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1298
|
+
{ cause: error }
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
await fence.assertOwned();
|
|
1302
|
+
if (current.token !== claim.token) {
|
|
1303
|
+
throw new Error("Local CI worker claim ownership changed.");
|
|
1304
|
+
}
|
|
1305
|
+
if (current.generationKey !== claim.generationKey) {
|
|
1306
|
+
throw new Error("Local CI worker claim generation changed.");
|
|
1307
|
+
}
|
|
1308
|
+
const transferred = WorkerClaimPayloadSchema.parse({
|
|
1309
|
+
...current,
|
|
1310
|
+
pid,
|
|
1311
|
+
pidIdentity: pidIdentity ?? readProcessIdentity(pid),
|
|
1312
|
+
generationKey: claim.generationKey
|
|
1313
|
+
});
|
|
1314
|
+
const temporary = `${claim.filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
1315
|
+
await writeFile(temporary, `${JSON.stringify(transferred)}
|
|
1316
|
+
`, {
|
|
1317
|
+
encoding: "utf8",
|
|
1318
|
+
mode: 384
|
|
1319
|
+
});
|
|
1320
|
+
await fence.assertOwned();
|
|
1321
|
+
try {
|
|
1322
|
+
await rename(temporary, claim.filePath);
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
await unlink(temporary).catch(() => void 0);
|
|
1325
|
+
throw error;
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
...transferred,
|
|
1329
|
+
generationKey: claim.generationKey,
|
|
1330
|
+
filePath: claim.filePath,
|
|
1331
|
+
repository: claim.repository,
|
|
1332
|
+
sha: claim.sha
|
|
1333
|
+
};
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Release a claim only when on-disk owner pid (and optional generation) still
|
|
1338
|
+
* match. Serialized under the claim mutex so release cannot unlink a
|
|
1339
|
+
* generation-superseded successor.
|
|
1340
|
+
*
|
|
1341
|
+
* When `generationKey` is supplied, require exact equality with the on-disk
|
|
1342
|
+
* claim's generationKey (including refusing when the on-disk key is undefined
|
|
1343
|
+
* legacy) so new cleanup cannot delete a legacy claim on PID reuse.
|
|
1344
|
+
*/
|
|
1345
|
+
async releaseWorkerClaim(repository, sha, pid, generationKey) {
|
|
1346
|
+
const normalizedSha = sha.toLowerCase();
|
|
1347
|
+
await this.withWorkerClaimMutationLock(repository, normalizedSha, async (fence) => {
|
|
1348
|
+
await fence.assertOwned();
|
|
1349
|
+
const filePath = this.workerClaimPath(repository, normalizedSha);
|
|
1350
|
+
try {
|
|
1351
|
+
const current = WorkerClaimPayloadSchema.parse(
|
|
1352
|
+
JSON.parse(await readFile(filePath, "utf8"))
|
|
1353
|
+
);
|
|
1354
|
+
await fence.assertOwned();
|
|
1355
|
+
if (current.pid !== pid) return;
|
|
1356
|
+
if (generationKey !== void 0 && current.generationKey !== generationKey) {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
await fence.assertOwned();
|
|
1360
|
+
await unlink(filePath);
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
if (!isNodeErrorCode(error, "ENOENT")) throw error;
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
rootMappingPath(repoRoot) {
|
|
1367
|
+
return path4.join(this.root, "roots", `${repoRootStateKey(repoRoot)}.json`);
|
|
1368
|
+
}
|
|
1369
|
+
async registerRepositoryRoot(repository, repoRoot) {
|
|
1370
|
+
const filePath = this.rootMappingPath(repoRoot);
|
|
1371
|
+
await mkdir(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
1372
|
+
await writeFile(
|
|
1373
|
+
filePath,
|
|
1374
|
+
`${JSON.stringify({ repository, repoRoot: path4.resolve(repoRoot) }, null, 2)}
|
|
1375
|
+
`,
|
|
1376
|
+
{ encoding: "utf8", mode: 384 }
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
async repositoryForRoot(repoRoot) {
|
|
1380
|
+
const filePath = this.rootMappingPath(repoRoot);
|
|
1381
|
+
try {
|
|
1382
|
+
const value = z.object({
|
|
1383
|
+
repository: LocalCiRepositoryIdentitySchema,
|
|
1384
|
+
repoRoot: z.string().min(1)
|
|
1385
|
+
}).parse(JSON.parse(await readFile(filePath, "utf8")));
|
|
1386
|
+
return path4.resolve(value.repoRoot) === path4.resolve(repoRoot) ? value.repository : null;
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
if (isNodeErrorCode(error, "ENOENT")) return null;
|
|
1389
|
+
throw stateReadError(filePath, error);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
async create(record) {
|
|
1393
|
+
const parsed = LocalCiRunRecordSchema.parse(record);
|
|
1394
|
+
const filePath = this.filePath(parsed.repository, parsed.sha);
|
|
1395
|
+
return this.withRecordMutationLock(parsed.repository, parsed.sha, async (fence) => {
|
|
1396
|
+
await fence.assertOwned();
|
|
1397
|
+
await mkdir(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
1398
|
+
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
1399
|
+
await writeFile(temporary, `${JSON.stringify(parsed, null, 2)}
|
|
1400
|
+
`, {
|
|
1401
|
+
encoding: "utf8",
|
|
1402
|
+
mode: 384
|
|
1403
|
+
});
|
|
1404
|
+
try {
|
|
1405
|
+
await fence.assertOwned();
|
|
1406
|
+
await access(filePath);
|
|
1407
|
+
await unlink(temporary).catch(() => void 0);
|
|
1408
|
+
return {
|
|
1409
|
+
created: false,
|
|
1410
|
+
record: await this.readUnlocked(parsed.repository, parsed.sha),
|
|
1411
|
+
filePath
|
|
1412
|
+
};
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
if (!isNodeErrorCode(error, "ENOENT")) {
|
|
1415
|
+
await unlink(temporary).catch(() => void 0);
|
|
1416
|
+
throw error;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
const attemptId = beginHostPublicationGenerationJournal(parsed);
|
|
1420
|
+
try {
|
|
1421
|
+
await fence.assertOwned();
|
|
1422
|
+
await link(temporary, filePath);
|
|
1423
|
+
await unlink(temporary).catch(() => void 0);
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
abandonHostPublicationGenerationJournal(parsed, attemptId);
|
|
1426
|
+
await unlink(temporary).catch(() => void 0);
|
|
1427
|
+
if (!isNodeErrorCode(error, "EEXIST")) throw error;
|
|
1428
|
+
return {
|
|
1429
|
+
created: false,
|
|
1430
|
+
record: await this.readUnlocked(parsed.repository, parsed.sha),
|
|
1431
|
+
filePath
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
syncHostPublicationGeneration(parsed, attemptId);
|
|
1435
|
+
return { created: true, record: parsed, filePath };
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
async read(repository, sha) {
|
|
1439
|
+
return this.readUnlocked(repository, sha);
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Read the durable record and apply any leftover host-generation journal
|
|
1443
|
+
* under the per-record mutation lock so a concurrent writer cannot activate
|
|
1444
|
+
* a newer obligation that this reader then retires.
|
|
1445
|
+
*/
|
|
1446
|
+
async readAndRecover(repository, sha) {
|
|
1447
|
+
return this.withRecordMutationLock(repository, sha, async (fence) => {
|
|
1448
|
+
await fence.assertOwned();
|
|
1449
|
+
const current = await this.readUnlocked(repository, sha);
|
|
1450
|
+
await fence.assertOwned();
|
|
1451
|
+
recoverHostPublicationGeneration(current);
|
|
1452
|
+
return current;
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
async save(record) {
|
|
1456
|
+
const parsed = LocalCiRunRecordSchema.parse(record);
|
|
1457
|
+
return this.withRecordMutationLock(parsed.repository, parsed.sha, async (fence) => {
|
|
1458
|
+
await fence.assertOwned();
|
|
1459
|
+
return this.writeRecordUnlocked(parsed, fence);
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Conditional save (CAS): write `next` only when the on-disk record still
|
|
1464
|
+
* matches `expected` generation (status, createdAt, updatedAt, obligation).
|
|
1465
|
+
* Cross-process safe under the per-record mutation lock.
|
|
1466
|
+
*/
|
|
1467
|
+
async compareAndSave(expected, next) {
|
|
1468
|
+
const expectedParsed = LocalCiRunRecordSchema.parse(expected);
|
|
1469
|
+
const nextParsed = LocalCiRunRecordSchema.parse(next);
|
|
1470
|
+
if (expectedParsed.repository !== nextParsed.repository || expectedParsed.sha.toLowerCase() !== nextParsed.sha.toLowerCase()) {
|
|
1471
|
+
throw new Error("compareAndSave requires expected and next to share repository/sha.");
|
|
1472
|
+
}
|
|
1473
|
+
if (this.hooks.beforeCompareAndSave) {
|
|
1474
|
+
await this.hooks.beforeCompareAndSave(expectedParsed, nextParsed);
|
|
1475
|
+
}
|
|
1476
|
+
return this.withRecordMutationLock(
|
|
1477
|
+
expectedParsed.repository,
|
|
1478
|
+
expectedParsed.sha,
|
|
1479
|
+
async (fence) => {
|
|
1480
|
+
await fence.assertOwned();
|
|
1481
|
+
return this.compareAndWriteUnlocked(expectedParsed, nextParsed, fence);
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* CAS write under an already-held record mutation fence.
|
|
1487
|
+
*
|
|
1488
|
+
* Re-asserts ownership after the generation read and immediately before the
|
|
1489
|
+
* final record rename (via {@link writeRecordUnlocked}). A predecessor that
|
|
1490
|
+
* suspends after its first assertion and resumes after reclaim must fail closed
|
|
1491
|
+
* rather than overwrite successor state.
|
|
1492
|
+
*/
|
|
1493
|
+
async compareAndWriteUnlocked(expected, next, fence) {
|
|
1494
|
+
await fence.assertOwned();
|
|
1495
|
+
let current;
|
|
1496
|
+
try {
|
|
1497
|
+
current = await this.readUnlocked(expected.repository, expected.sha);
|
|
1498
|
+
} catch {
|
|
1499
|
+
return { applied: false, record: expected };
|
|
1500
|
+
}
|
|
1501
|
+
await fence.assertOwned();
|
|
1502
|
+
if (!isSameLocalCiRecordGeneration(current, expected)) {
|
|
1503
|
+
return { applied: false, record: current };
|
|
1504
|
+
}
|
|
1505
|
+
if (this.hooks.afterCompareAndWriteRead) {
|
|
1506
|
+
await this.hooks.afterCompareAndWriteRead(current);
|
|
1507
|
+
await fence.assertOwned();
|
|
1508
|
+
}
|
|
1509
|
+
const saved = await this.writeRecordUnlocked(next, fence);
|
|
1510
|
+
return { applied: true, record: saved };
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Hold the per-record mutation lock for a multi-step exclusive section
|
|
1514
|
+
* (e.g. remote failure publish + readback + clear). Nested save/compareAndSave
|
|
1515
|
+
* /deleteRetentionCandidates deadlock — use only the unlocked helpers provided
|
|
1516
|
+
* to `fn`.
|
|
1517
|
+
*
|
|
1518
|
+
* Callers that await (remote publish/readback) must re-assert fence ownership
|
|
1519
|
+
* before any further state change. Fail closed if ownership is lost.
|
|
1520
|
+
*/
|
|
1521
|
+
async withExclusiveRecordAccess(repository, sha, fn) {
|
|
1522
|
+
return this.withRecordMutationLock(repository, sha, async (fence) => {
|
|
1523
|
+
await fence.assertOwned();
|
|
1524
|
+
return fn({
|
|
1525
|
+
read: async () => {
|
|
1526
|
+
await fence.assertOwned();
|
|
1527
|
+
const record = await this.readUnlocked(repository, sha);
|
|
1528
|
+
await fence.assertOwned();
|
|
1529
|
+
return record;
|
|
1530
|
+
},
|
|
1531
|
+
writeIfSameGeneration: async (expected, next) => {
|
|
1532
|
+
await fence.assertOwned();
|
|
1533
|
+
return this.compareAndWriteUnlocked(expected, next, fence);
|
|
1534
|
+
},
|
|
1535
|
+
assertOwned: () => fence.assertOwned()
|
|
1536
|
+
});
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Persist a spawned worker PID only when the on-disk generation still matches
|
|
1541
|
+
* `expected` (the generation that authorized the spawn). Used after requeue CAS
|
|
1542
|
+
* so a concurrent same-SHA resubmit is never overwritten with a stale PID/save.
|
|
1543
|
+
*/
|
|
1544
|
+
async assignWorkerPid(expected, pid, pidIdentity) {
|
|
1545
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
1546
|
+
throw new Error("assignWorkerPid requires a positive integer pid.");
|
|
1547
|
+
}
|
|
1548
|
+
if (pidIdentity.trim().length === 0) {
|
|
1549
|
+
throw new Error("assignWorkerPid requires a process identity.");
|
|
1550
|
+
}
|
|
1551
|
+
const next = {
|
|
1552
|
+
...expected,
|
|
1553
|
+
pid,
|
|
1554
|
+
pidIdentity,
|
|
1555
|
+
status: "queued",
|
|
1556
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1557
|
+
};
|
|
1558
|
+
return this.compareAndSave(expected, next);
|
|
1559
|
+
}
|
|
1560
|
+
async list(repository) {
|
|
1561
|
+
const directory = this.repositoryDirectory(repository);
|
|
1562
|
+
let entries;
|
|
1563
|
+
try {
|
|
1564
|
+
const { readdir } = await import("fs/promises");
|
|
1565
|
+
entries = await readdir(directory);
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
if (isNodeErrorCode(error, "ENOENT")) return [];
|
|
1568
|
+
throw error;
|
|
1569
|
+
}
|
|
1570
|
+
const records = [];
|
|
1571
|
+
for (const entry of entries.filter((value) => value.endsWith(".json")).sort()) {
|
|
1572
|
+
const sha = entry.slice(0, -".json".length);
|
|
1573
|
+
if (!/^[a-f0-9]{40}$/i.test(sha)) continue;
|
|
1574
|
+
try {
|
|
1575
|
+
records.push(await this.read(repository, sha));
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
if (error instanceof Error && isNodeErrorCode(error.cause, "ENOENT")) continue;
|
|
1578
|
+
throw error;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
1582
|
+
}
|
|
1583
|
+
async listQueuedForBranch(repository, branch) {
|
|
1584
|
+
const records = await this.list(repository);
|
|
1585
|
+
return records.filter((record) => record.branch === branch && record.status === "queued").sort(compareSubmissionsNewestFirst);
|
|
1586
|
+
}
|
|
1587
|
+
async supersedeOlderQueued(input) {
|
|
1588
|
+
const newerSha = input.newerSha.toLowerCase();
|
|
1589
|
+
const updatedAt = (input.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1590
|
+
const queued = await this.listQueuedForBranch(input.repository, input.branch);
|
|
1591
|
+
const newerRecord = queued.find((record) => record.sha === newerSha);
|
|
1592
|
+
const superseded = [];
|
|
1593
|
+
for (const candidate of queued) {
|
|
1594
|
+
if (candidate.sha === newerSha) continue;
|
|
1595
|
+
if (newerRecord) {
|
|
1596
|
+
const ordering = compareSubmissionsNewestFirst(candidate, newerRecord);
|
|
1597
|
+
if (ordering <= 0) continue;
|
|
1598
|
+
}
|
|
1599
|
+
let current;
|
|
1600
|
+
try {
|
|
1601
|
+
current = await this.read(input.repository, candidate.sha);
|
|
1602
|
+
} catch {
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if (current.status !== "queued") continue;
|
|
1606
|
+
if (current.branch !== input.branch) continue;
|
|
1607
|
+
const next = {
|
|
1608
|
+
...current,
|
|
1609
|
+
status: "superseded",
|
|
1610
|
+
updatedAt
|
|
1611
|
+
};
|
|
1612
|
+
const saved = await this.save(next);
|
|
1613
|
+
superseded.push(saved);
|
|
1614
|
+
}
|
|
1615
|
+
return superseded;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Count consecutive terminal `failed` runs for a branch, newest submission
|
|
1619
|
+
* first. A success-family terminal run (validation passed) resets the count;
|
|
1620
|
+
* active runs, `superseded`/`skipped` runs and infrastructure-classified
|
|
1621
|
+
* failures carry no validation verdict and are ignored. Derived from on-disk records on every call so the count cannot
|
|
1622
|
+
* drift; the terminal retention window (newest-first, obligations held)
|
|
1623
|
+
* always preserves enough history for this small threshold.
|
|
1624
|
+
*/
|
|
1625
|
+
async countConsecutiveTerminalFailures(repository, branch) {
|
|
1626
|
+
const records = await this.list(repository);
|
|
1627
|
+
const sameBranch = records.filter((record) => record.branch === branch).sort(compareSubmissionsNewestFirst);
|
|
1628
|
+
const failedShas = [];
|
|
1629
|
+
for (const record of sameBranch) {
|
|
1630
|
+
if (isLocalCiActiveStatus(record.status)) continue;
|
|
1631
|
+
if (record.status === "superseded" || record.status === "skipped") continue;
|
|
1632
|
+
if (record.status === "failed" && record.failureClass === "infrastructure") continue;
|
|
1633
|
+
if (record.status === "failed") {
|
|
1634
|
+
failedShas.push(record.sha);
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
break;
|
|
1638
|
+
}
|
|
1639
|
+
return { count: failedShas.length, failedShas };
|
|
1640
|
+
}
|
|
1641
|
+
async hasNewerSubmission(input) {
|
|
1642
|
+
const sha = input.sha.toLowerCase();
|
|
1643
|
+
const submittedAt = input.submittedAt ?? input.createdAt;
|
|
1644
|
+
const records = await this.list(input.repository);
|
|
1645
|
+
const sameBranch = records.filter((record) => record.branch === input.branch);
|
|
1646
|
+
for (const record of sameBranch) {
|
|
1647
|
+
const candidateSubmittedAt = submissionTime(record);
|
|
1648
|
+
if (record.sha === sha && candidateSubmittedAt === submittedAt) continue;
|
|
1649
|
+
if (candidateSubmittedAt > submittedAt) return true;
|
|
1650
|
+
if (candidateSubmittedAt === submittedAt && record.sha !== sha) {
|
|
1651
|
+
if (record.sha.localeCompare(sha) > 0) return true;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
async toRetentionCandidate(record) {
|
|
1657
|
+
const recordPath = this.filePath(record.repository, record.sha);
|
|
1658
|
+
const logPath = this.logPath(record.repository, record.sha);
|
|
1659
|
+
let logExists = false;
|
|
1660
|
+
try {
|
|
1661
|
+
await access(logPath);
|
|
1662
|
+
logExists = true;
|
|
1663
|
+
} catch {
|
|
1664
|
+
logExists = false;
|
|
1665
|
+
}
|
|
1666
|
+
return { record, recordPath, logPath, logExists };
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Select terminal records to preserve vs delete under a global terminal cap.
|
|
1670
|
+
*
|
|
1671
|
+
* Active (queued/running) records are always preserved and never count toward
|
|
1672
|
+
* the terminal cap. Records with an outstanding remote-status publication or
|
|
1673
|
+
* retained-worktree cleanup obligation are always preserved and never count
|
|
1674
|
+
* toward the terminal cap.
|
|
1675
|
+
* Among remaining terminal records, keep the newest globally up to
|
|
1676
|
+
* `maxTerminalRecords` (default {@link DEFAULT_LOCAL_CI_MAX_TERMINAL_RECORDS});
|
|
1677
|
+
* older free terminals are deletable regardless of historical branch count.
|
|
1678
|
+
*/
|
|
1679
|
+
async selectTerminalRetention(repository, options) {
|
|
1680
|
+
const records = await this.list(repository);
|
|
1681
|
+
if (records.length === 0) {
|
|
1682
|
+
return { preserve: [], deletable: [] };
|
|
1683
|
+
}
|
|
1684
|
+
const activeRecords = records.filter((record) => isLocalCiActiveStatus(record.status));
|
|
1685
|
+
const terminalRecords = records.filter((record) => isLocalCiTerminalStatus(record.status)).sort(compareRecordsNewestFirst);
|
|
1686
|
+
const obligationHeld = terminalRecords.filter(
|
|
1687
|
+
(record) => hasOutstandingRemoteStatusPublication(record) || record.worktreePath !== void 0
|
|
1688
|
+
);
|
|
1689
|
+
const freeTerminals = terminalRecords.filter(
|
|
1690
|
+
(record) => !hasOutstandingRemoteStatusPublication(record) && record.worktreePath === void 0
|
|
1691
|
+
);
|
|
1692
|
+
const maxTerminal = resolveMaxTerminalRecords(options?.maxTerminalRecords);
|
|
1693
|
+
const preserveTerminals = [...obligationHeld, ...freeTerminals.slice(0, maxTerminal)];
|
|
1694
|
+
const deletableRecords = freeTerminals.slice(maxTerminal);
|
|
1695
|
+
const preserveRecords = [...activeRecords, ...preserveTerminals];
|
|
1696
|
+
const [preserve, deletable] = await Promise.all([
|
|
1697
|
+
Promise.all(preserveRecords.map((record) => this.toRetentionCandidate(record))),
|
|
1698
|
+
Promise.all(deletableRecords.map((record) => this.toRetentionCandidate(record)))
|
|
1699
|
+
]);
|
|
1700
|
+
return { preserve, deletable };
|
|
1701
|
+
}
|
|
1702
|
+
async deleteRetentionCandidates(candidates) {
|
|
1703
|
+
const deletedRecords = [];
|
|
1704
|
+
const deletedLogs = [];
|
|
1705
|
+
const rootResolved = path4.resolve(this.root);
|
|
1706
|
+
for (const candidate of candidates) {
|
|
1707
|
+
const selected = candidate.record;
|
|
1708
|
+
const recordPath = this.filePath(selected.repository, selected.sha);
|
|
1709
|
+
const logPath = this.logPath(selected.repository, selected.sha);
|
|
1710
|
+
if (!isPathUnderRoot(rootResolved, recordPath) || !isPathUnderRoot(rootResolved, logPath)) {
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
const deleted = await this.withRecordMutationLock(
|
|
1714
|
+
selected.repository,
|
|
1715
|
+
selected.sha,
|
|
1716
|
+
async (fence) => {
|
|
1717
|
+
await fence.assertOwned();
|
|
1718
|
+
let current;
|
|
1719
|
+
try {
|
|
1720
|
+
current = await this.readUnlocked(selected.repository, selected.sha);
|
|
1721
|
+
} catch {
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1724
|
+
await fence.assertOwned();
|
|
1725
|
+
if (isLocalCiActiveStatus(current.status)) return false;
|
|
1726
|
+
if (!isLocalCiTerminalStatus(current.status)) return false;
|
|
1727
|
+
if (hasOutstandingRemoteStatusPublication(current)) return false;
|
|
1728
|
+
if (current.worktreePath !== void 0) return false;
|
|
1729
|
+
if (current.repository !== selected.repository || current.sha !== selected.sha || current.status !== selected.status || current.updatedAt !== selected.updatedAt || current.createdAt !== selected.createdAt) {
|
|
1730
|
+
return false;
|
|
1731
|
+
}
|
|
1732
|
+
if (this.hooks.afterRetentionDeleteCheck) {
|
|
1733
|
+
await this.hooks.afterRetentionDeleteCheck(current);
|
|
1734
|
+
await fence.assertOwned();
|
|
1735
|
+
try {
|
|
1736
|
+
current = await this.readUnlocked(selected.repository, selected.sha);
|
|
1737
|
+
} catch {
|
|
1738
|
+
return false;
|
|
1739
|
+
}
|
|
1740
|
+
await fence.assertOwned();
|
|
1741
|
+
if (isLocalCiActiveStatus(current.status)) return false;
|
|
1742
|
+
if (!isLocalCiTerminalStatus(current.status)) return false;
|
|
1743
|
+
if (hasOutstandingRemoteStatusPublication(current)) return false;
|
|
1744
|
+
if (current.worktreePath !== void 0) return false;
|
|
1745
|
+
if (current.repository !== selected.repository || current.sha !== selected.sha || current.status !== selected.status || current.updatedAt !== selected.updatedAt || current.createdAt !== selected.createdAt) {
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
await fence.assertOwned();
|
|
1750
|
+
try {
|
|
1751
|
+
await unlink(recordPath);
|
|
1752
|
+
deletedRecords.push(recordPath);
|
|
1753
|
+
} catch (error) {
|
|
1754
|
+
if (!isNodeErrorCode(error, "ENOENT")) throw error;
|
|
1755
|
+
}
|
|
1756
|
+
await fence.assertOwned();
|
|
1757
|
+
try {
|
|
1758
|
+
await unlink(logPath);
|
|
1759
|
+
deletedLogs.push(logPath);
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (!isNodeErrorCode(error, "ENOENT")) throw error;
|
|
1762
|
+
}
|
|
1763
|
+
return true;
|
|
1764
|
+
}
|
|
1765
|
+
);
|
|
1766
|
+
void deleted;
|
|
1767
|
+
}
|
|
1768
|
+
return { deletedRecords, deletedLogs };
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
function selectCommandsForLocalCi(input) {
|
|
1772
|
+
const candidates = input.explicit?.length ? input.explicit : input.resolved.map((entry) => entry.command);
|
|
1773
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1774
|
+
const commands = [];
|
|
1775
|
+
for (const candidate of candidates) {
|
|
1776
|
+
const command = candidate.trim();
|
|
1777
|
+
if (!command || seen.has(command)) continue;
|
|
1778
|
+
seen.add(command);
|
|
1779
|
+
commands.push(command);
|
|
1780
|
+
}
|
|
1781
|
+
return commands;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
export {
|
|
1785
|
+
NODE_PACKAGE_MANAGER_MANIFESTS,
|
|
1786
|
+
launcherManifests,
|
|
1787
|
+
pinnedManifestFiles,
|
|
1788
|
+
referencedTableScript,
|
|
1789
|
+
scopedPinKey,
|
|
1790
|
+
parseScopedPin,
|
|
1791
|
+
scriptTableUnchanged,
|
|
1792
|
+
ENTRYPOINT_FINGERPRINT_PATTERN,
|
|
1793
|
+
computeEntrypointFingerprint,
|
|
1794
|
+
DEFAULT_LOCAL_CI_STATUS_CONTEXT,
|
|
1795
|
+
DEFAULT_LOCAL_CI_MAX_CONCURRENT,
|
|
1796
|
+
MAX_LOCAL_CI_RETAINED_LOG_BYTES,
|
|
1797
|
+
boundLocalCiCommandOutput,
|
|
1798
|
+
renderBoundedLocalCiLogEntry,
|
|
1799
|
+
parseLocalCiRepositoryIdentity,
|
|
1800
|
+
hasOutstandingRemoteStatusPublication,
|
|
1801
|
+
isPublicationPending,
|
|
1802
|
+
outstandingStatusPublicationMatchesRecord,
|
|
1803
|
+
hasLiveFailurePublicationLease,
|
|
1804
|
+
createFailurePublicationLease,
|
|
1805
|
+
matchesFailurePublicationLease,
|
|
1806
|
+
createOutstandingStatusPublication,
|
|
1807
|
+
createOutstandingFailureStatusPublication,
|
|
1808
|
+
DEFAULT_LOCAL_CI_MAX_TERMINAL_RECORDS,
|
|
1809
|
+
createQueuedRun,
|
|
1810
|
+
repoStateKey,
|
|
1811
|
+
LocalCiStateStore,
|
|
1812
|
+
selectCommandsForLocalCi
|
|
1813
|
+
};
|
|
1814
|
+
//# sourceMappingURL=chunk-VBENAHDA.js.map
|