@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,3583 @@
|
|
|
1
|
+
import {
|
|
2
|
+
globalProcessLimiter
|
|
3
|
+
} from "./chunk-SF6YK3JI.js";
|
|
4
|
+
import {
|
|
5
|
+
classifyAndWrapExecaError,
|
|
6
|
+
exitCodeToSignal,
|
|
7
|
+
formatTerminationRemedy,
|
|
8
|
+
hasSignal,
|
|
9
|
+
redactProviderArgv,
|
|
10
|
+
resolveTerminationExitCode
|
|
11
|
+
} from "./chunk-JBBB2Y47.js";
|
|
12
|
+
import {
|
|
13
|
+
evaluateCpuTimeProgressStreak,
|
|
14
|
+
readCpuTimeSeconds,
|
|
15
|
+
resolveIdleTimeoutsMs
|
|
16
|
+
} from "./chunk-WJAL4JL3.js";
|
|
17
|
+
import {
|
|
18
|
+
providerExecutionTransportStreamOptions,
|
|
19
|
+
sanitizeDiagnosticOutput,
|
|
20
|
+
sanitizeDiagnosticText
|
|
21
|
+
} from "./chunk-RBKLOUXA.js";
|
|
22
|
+
import {
|
|
23
|
+
TelemetryLogger
|
|
24
|
+
} from "./chunk-PFCXFO7A.js";
|
|
25
|
+
import {
|
|
26
|
+
requestProcessExit
|
|
27
|
+
} from "./chunk-GC4CRMTS.js";
|
|
28
|
+
import {
|
|
29
|
+
createLogger
|
|
30
|
+
} from "./chunk-3OPP2OZB.js";
|
|
31
|
+
import {
|
|
32
|
+
detectSandboxBoundaryAttestation,
|
|
33
|
+
wrapWithSandbox
|
|
34
|
+
} from "./chunk-B7YRSULE.js";
|
|
35
|
+
import {
|
|
36
|
+
BUILD_TEMP_MARKER_FILENAME,
|
|
37
|
+
markBuildTempDirSync
|
|
38
|
+
} from "./chunk-D6WTZMZB.js";
|
|
39
|
+
import {
|
|
40
|
+
chunkToText,
|
|
41
|
+
createLineBufferedStreamProcessor,
|
|
42
|
+
processOutput,
|
|
43
|
+
selectBestErrorSummary
|
|
44
|
+
} from "./chunk-TUQMMFEQ.js";
|
|
45
|
+
import {
|
|
46
|
+
isSignalableProcessGroupId,
|
|
47
|
+
signalProcessGroup
|
|
48
|
+
} from "./chunk-6CB3KJRY.js";
|
|
49
|
+
import {
|
|
50
|
+
safeRmDir
|
|
51
|
+
} from "./chunk-ALDTFNZ3.js";
|
|
52
|
+
import {
|
|
53
|
+
normalizeEngineId
|
|
54
|
+
} from "./chunk-KKQKWAMF.js";
|
|
55
|
+
import {
|
|
56
|
+
authorizeLinuxHostClassWrites
|
|
57
|
+
} from "./chunk-V2TVPO3X.js";
|
|
58
|
+
import {
|
|
59
|
+
ProviderExecutionError
|
|
60
|
+
} from "./chunk-RRC5BL7B.js";
|
|
61
|
+
import {
|
|
62
|
+
formatHostTimeoutTerminationDetail,
|
|
63
|
+
isHostTimeoutAbortReason
|
|
64
|
+
} from "./chunk-M6UHOC4I.js";
|
|
65
|
+
import {
|
|
66
|
+
createCleanChildEnv,
|
|
67
|
+
isSandboxAuthorityEnvVar,
|
|
68
|
+
stripClaudeSessionEnv
|
|
69
|
+
} from "./chunk-WGQAXLNA.js";
|
|
70
|
+
import {
|
|
71
|
+
DEFAULT_TIMEOUT_MS
|
|
72
|
+
} from "./chunk-I56TH7VM.js";
|
|
73
|
+
import {
|
|
74
|
+
BUILD_PROCESS_SLOT_WAIT_MS,
|
|
75
|
+
BUILD_SUBPROCESS_PID_WAIT_MS,
|
|
76
|
+
BUILD_VARIANT_WORKER,
|
|
77
|
+
emitWarning,
|
|
78
|
+
isDebugMode,
|
|
79
|
+
isPathDebug,
|
|
80
|
+
isTimeoutDebug,
|
|
81
|
+
readEnv
|
|
82
|
+
} from "./chunk-7VRJHTGZ.js";
|
|
83
|
+
import {
|
|
84
|
+
sanitizeError
|
|
85
|
+
} from "./chunk-CPSVQR2I.js";
|
|
86
|
+
|
|
87
|
+
// src/core/execution/boundary-telemetry.ts
|
|
88
|
+
function emitBoundaryTriggered(event) {
|
|
89
|
+
const { trigger, workloadClass, effectiveCeilingMs, elapsedMs, source } = event;
|
|
90
|
+
const severity = trigger === "ceiling" ? "ERROR" /* ERROR */ : "WARN" /* WARN */;
|
|
91
|
+
emitWarning(
|
|
92
|
+
"EB-001",
|
|
93
|
+
{
|
|
94
|
+
taskId: event.taskId ?? "unknown",
|
|
95
|
+
subsystem: "execution",
|
|
96
|
+
executionPhase: "execution"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
severity,
|
|
100
|
+
skipConsole: true,
|
|
101
|
+
metadata: {
|
|
102
|
+
...event.metadata,
|
|
103
|
+
trigger,
|
|
104
|
+
workloadClass,
|
|
105
|
+
effectiveCeilingMs,
|
|
106
|
+
elapsedMs,
|
|
107
|
+
source,
|
|
108
|
+
...event.idleDurationMs !== void 0 ? { idleDurationMs: event.idleDurationMs } : {},
|
|
109
|
+
...event.stagnantChecks !== void 0 ? { stagnantChecks: event.stagnantChecks } : {},
|
|
110
|
+
type: "execution_boundary_triggered"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
const level = trigger === "ceiling" ? "warn" : "debug";
|
|
115
|
+
const msg = `[ExecutionBoundary] ${trigger} fired for ${workloadClass} after ${elapsedMs}ms (ceiling: ${effectiveCeilingMs}ms, source: ${source})`;
|
|
116
|
+
if (level === "warn") {
|
|
117
|
+
console.warn(msg);
|
|
118
|
+
} else if (isDebugMode() || process.env.DEBUG) {
|
|
119
|
+
console.debug(msg);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/core/process-lifecycle-context.ts
|
|
124
|
+
var processLifecycleManager;
|
|
125
|
+
function setProcessLifecycleManager(manager) {
|
|
126
|
+
processLifecycleManager = manager;
|
|
127
|
+
}
|
|
128
|
+
function getProcessLifecycleManager() {
|
|
129
|
+
return processLifecycleManager;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/core/providers/shared/raw-chunk-observers.ts
|
|
133
|
+
function isNonEmptyRawChunk(chunk) {
|
|
134
|
+
if (typeof chunk === "string") {
|
|
135
|
+
return chunk.length > 0;
|
|
136
|
+
}
|
|
137
|
+
return chunk.byteLength > 0;
|
|
138
|
+
}
|
|
139
|
+
function rawChunkToBuffer(chunk) {
|
|
140
|
+
if (typeof chunk === "string") {
|
|
141
|
+
if (chunk.length === 0) {
|
|
142
|
+
return Buffer.alloc(0);
|
|
143
|
+
}
|
|
144
|
+
return Buffer.from(chunk, "utf8");
|
|
145
|
+
}
|
|
146
|
+
if (chunk.byteLength === 0) {
|
|
147
|
+
return Buffer.alloc(0);
|
|
148
|
+
}
|
|
149
|
+
return Buffer.from(chunk);
|
|
150
|
+
}
|
|
151
|
+
function composeRawChunkObservers(...observers) {
|
|
152
|
+
const active2 = observers.filter((o) => typeof o === "function");
|
|
153
|
+
if (active2.length === 0) return void 0;
|
|
154
|
+
if (active2.length === 1) return active2[0];
|
|
155
|
+
return (chunk) => {
|
|
156
|
+
for (const observer of active2) {
|
|
157
|
+
try {
|
|
158
|
+
observer(chunk);
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/core/providers/shared/tmux-completion-signal.ts
|
|
166
|
+
var TMUX_COMPLETION_SIGNAL_GRACE_MS = 1500;
|
|
167
|
+
var DEFAULT_MAX_PENDING_LINE_BYTES = 1024 * 1024;
|
|
168
|
+
function defaultShouldCheckPendingLine(pendingLine) {
|
|
169
|
+
return pendingLine.trimEnd().endsWith("}");
|
|
170
|
+
}
|
|
171
|
+
function createLineDelimitedTmuxCompletionSignal(lineConcludes, opts) {
|
|
172
|
+
let pendingLine = "";
|
|
173
|
+
let concluded = false;
|
|
174
|
+
const maxPendingLineBytes = opts?.maxPendingLineBytes ?? DEFAULT_MAX_PENDING_LINE_BYTES;
|
|
175
|
+
const shouldCheckPendingLine = opts?.shouldCheckPendingLine ?? defaultShouldCheckPendingLine;
|
|
176
|
+
const lineConcludesSafely = (line) => {
|
|
177
|
+
try {
|
|
178
|
+
return lineConcludes(line);
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return (chunk, stream) => {
|
|
184
|
+
if (concluded) return true;
|
|
185
|
+
if (stream !== "stdout" || !chunk) return false;
|
|
186
|
+
const combined = pendingLine + chunk;
|
|
187
|
+
const lines = combined.split(/\r?\n/);
|
|
188
|
+
pendingLine = lines.pop() ?? "";
|
|
189
|
+
if (pendingLine.length > maxPendingLineBytes) {
|
|
190
|
+
pendingLine = pendingLine.slice(-maxPendingLineBytes);
|
|
191
|
+
}
|
|
192
|
+
for (const line of lines) {
|
|
193
|
+
if (lineConcludesSafely(line)) {
|
|
194
|
+
concluded = true;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (shouldCheckPendingLine(pendingLine) && lineConcludesSafely(pendingLine)) {
|
|
199
|
+
concluded = true;
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/core/providers/shared/variant-launch.ts
|
|
207
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
208
|
+
var active = new AsyncLocalStorage();
|
|
209
|
+
function withVariantInvocation(invocation, run) {
|
|
210
|
+
return active.run(invocation, run);
|
|
211
|
+
}
|
|
212
|
+
function activeVariantServiceTier() {
|
|
213
|
+
return active.getStore()?.resolution.resolved.serviceTier;
|
|
214
|
+
}
|
|
215
|
+
function prepareVariantCommand(command, model, effort) {
|
|
216
|
+
active.getStore()?.prepare({
|
|
217
|
+
...model ? { model } : {},
|
|
218
|
+
...effort ? { effort } : {}
|
|
219
|
+
});
|
|
220
|
+
return command;
|
|
221
|
+
}
|
|
222
|
+
function observeVariantProcessSpawn(process2) {
|
|
223
|
+
const invocation = active.getStore();
|
|
224
|
+
if (invocation) process2.once("spawn", () => invocation.submitted());
|
|
225
|
+
}
|
|
226
|
+
function recordVariantTmuxLaunch() {
|
|
227
|
+
active.getStore()?.submitted();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/core/providers/shared/base-runner.ts
|
|
231
|
+
import { createHash } from "crypto";
|
|
232
|
+
import os3 from "os";
|
|
233
|
+
import path2 from "path";
|
|
234
|
+
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
235
|
+
|
|
236
|
+
// src/core/providers/shared/file-backed-stdout.ts
|
|
237
|
+
import * as fs from "fs";
|
|
238
|
+
import { StringDecoder } from "string_decoder";
|
|
239
|
+
var FILE_BACKED_STDOUT_DEFAULT_MAX_BYTES = 256 * 1024 * 1024;
|
|
240
|
+
var FILE_BACKED_STDOUT_MIN_FREE_BYTES = 8 * 1024 * 1024;
|
|
241
|
+
var DEFAULT_POLL_INTERVAL_MS = 25;
|
|
242
|
+
var TRUTHY_OVERRIDES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
243
|
+
var FALSY_OVERRIDES = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
|
|
244
|
+
var FILE_BACKED_STDOUT_ENV_NAME = "BUILD_PROVIDER_FILE_BACKED_STDOUT";
|
|
245
|
+
function resolveFileBackedStdoutOverride(env = process.env) {
|
|
246
|
+
const raw = env[FILE_BACKED_STDOUT_ENV_NAME];
|
|
247
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
248
|
+
const lower = raw.toLowerCase();
|
|
249
|
+
if (TRUTHY_OVERRIDES.has(lower)) return true;
|
|
250
|
+
if (FALSY_OVERRIDES.has(lower)) return false;
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Invalid boolean value for ${FILE_BACKED_STDOUT_ENV_NAME}: "${raw}". Expected one of: 1, true, yes, on, 0, false, no, off`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
var MAX_SLICE_BYTES = 1024 * 1024;
|
|
256
|
+
var SETTLE_ATTEMPTS = 5;
|
|
257
|
+
function spoolFilesystemFreeBytes(dir) {
|
|
258
|
+
if (typeof fs.statfsSync !== "function") return void 0;
|
|
259
|
+
try {
|
|
260
|
+
const stats = fs.statfsSync(dir);
|
|
261
|
+
const available = Number(stats.bavail) * Number(stats.bsize);
|
|
262
|
+
return Number.isFinite(available) && available >= 0 ? available : void 0;
|
|
263
|
+
} catch {
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function createStdoutSpoolTailer(options) {
|
|
268
|
+
const pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
269
|
+
const decoder = new StringDecoder("utf8");
|
|
270
|
+
let offset = 0;
|
|
271
|
+
let polling = false;
|
|
272
|
+
let timer;
|
|
273
|
+
let inflight;
|
|
274
|
+
let failure;
|
|
275
|
+
const recordFailure = (candidate) => {
|
|
276
|
+
if (failure) return;
|
|
277
|
+
failure = candidate;
|
|
278
|
+
polling = false;
|
|
279
|
+
options.onFailure(candidate);
|
|
280
|
+
};
|
|
281
|
+
const asMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
282
|
+
const pump = async () => {
|
|
283
|
+
for (; ; ) {
|
|
284
|
+
if (failure) return;
|
|
285
|
+
const size = await options.stream.statSize();
|
|
286
|
+
if (size > options.maxBytes) {
|
|
287
|
+
recordFailure({
|
|
288
|
+
reason: "output_ceiling_exceeded",
|
|
289
|
+
message: `provider stdout exceeded the ${options.maxBytes} byte capture ceiling (${size} bytes spooled)`
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (size <= offset) return;
|
|
294
|
+
const bytes = await options.stream.readAt(offset, Math.min(size - offset, MAX_SLICE_BYTES));
|
|
295
|
+
if (bytes.length === 0) return;
|
|
296
|
+
offset += bytes.length;
|
|
297
|
+
try {
|
|
298
|
+
options.onBytes(bytes, decoder.write(bytes));
|
|
299
|
+
} catch (error) {
|
|
300
|
+
recordFailure({
|
|
301
|
+
reason: "stdout_spool_consume_failed",
|
|
302
|
+
message: `processing spooled provider stdout failed: ${asMessage(error)}`
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const tick = () => {
|
|
309
|
+
if (!polling || inflight) return;
|
|
310
|
+
inflight = pump().catch((error) => {
|
|
311
|
+
recordFailure({
|
|
312
|
+
reason: "stdout_spool_read_failed",
|
|
313
|
+
message: `reading spooled provider stdout failed: ${asMessage(error)}`
|
|
314
|
+
});
|
|
315
|
+
}).finally(() => {
|
|
316
|
+
inflight = void 0;
|
|
317
|
+
if (polling) {
|
|
318
|
+
timer = setTimeout(tick, pollIntervalMs);
|
|
319
|
+
timer.unref?.();
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
};
|
|
323
|
+
const readToEnd = async () => {
|
|
324
|
+
polling = false;
|
|
325
|
+
if (timer) {
|
|
326
|
+
clearTimeout(timer);
|
|
327
|
+
timer = void 0;
|
|
328
|
+
}
|
|
329
|
+
if (inflight) {
|
|
330
|
+
try {
|
|
331
|
+
await inflight;
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (failure) return;
|
|
336
|
+
try {
|
|
337
|
+
await pump();
|
|
338
|
+
} catch (error) {
|
|
339
|
+
recordFailure({
|
|
340
|
+
reason: "stdout_spool_read_failed",
|
|
341
|
+
message: `final drain of spooled provider stdout failed: ${asMessage(error)}`
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
let flushed = false;
|
|
346
|
+
const flushDecoder = () => {
|
|
347
|
+
if (flushed) return;
|
|
348
|
+
flushed = true;
|
|
349
|
+
const tail = decoder.end();
|
|
350
|
+
if (!tail || failure) return;
|
|
351
|
+
try {
|
|
352
|
+
options.onBytes(void 0, tail);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
recordFailure({
|
|
355
|
+
reason: "stdout_spool_consume_failed",
|
|
356
|
+
message: `flushing spooled provider stdout failed: ${asMessage(error)}`
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
bytesObserved: () => offset,
|
|
362
|
+
failure: () => failure,
|
|
363
|
+
start() {
|
|
364
|
+
if (polling) return;
|
|
365
|
+
polling = true;
|
|
366
|
+
tick();
|
|
367
|
+
},
|
|
368
|
+
drain: readToEnd,
|
|
369
|
+
async finish() {
|
|
370
|
+
let size;
|
|
371
|
+
for (let attempt = 0; attempt < SETTLE_ATTEMPTS; attempt += 1) {
|
|
372
|
+
await readToEnd();
|
|
373
|
+
if (failure) break;
|
|
374
|
+
try {
|
|
375
|
+
size = await options.stream.statSize();
|
|
376
|
+
} catch (error) {
|
|
377
|
+
recordFailure({
|
|
378
|
+
reason: "stdout_spool_read_failed",
|
|
379
|
+
message: `measuring the spooled provider stdout failed: ${asMessage(error)}`
|
|
380
|
+
});
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
if (size === offset) break;
|
|
384
|
+
}
|
|
385
|
+
if (!failure && size !== void 0 && size !== offset) {
|
|
386
|
+
recordFailure({
|
|
387
|
+
reason: "stdout_spool_read_failed",
|
|
388
|
+
message: `provider stdout spool never settled: ${size} bytes on disk but ${offset} read after ${SETTLE_ATTEMPTS} passes`
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
flushDecoder();
|
|
392
|
+
return size;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/core/providers/shared/private-spool.ts
|
|
398
|
+
import * as fs2 from "fs";
|
|
399
|
+
import * as os from "os";
|
|
400
|
+
import * as path from "path";
|
|
401
|
+
var PrivateSpoolError = class extends Error {
|
|
402
|
+
constructor(message) {
|
|
403
|
+
super(message);
|
|
404
|
+
this.name = "PrivateSpoolError";
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var EXCL_RDWR_FLAGS = fs2.constants.O_RDWR | fs2.constants.O_CREAT | fs2.constants.O_EXCL | (fs2.constants.O_NOFOLLOW ?? 0);
|
|
408
|
+
function writeAllToFd(fd, buffer, createError) {
|
|
409
|
+
return new Promise((resolve, reject) => {
|
|
410
|
+
let offset = 0;
|
|
411
|
+
const writeNext = () => {
|
|
412
|
+
if (offset >= buffer.length) {
|
|
413
|
+
resolve();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
fs2.write(fd, buffer, offset, buffer.length - offset, null, (error, written) => {
|
|
417
|
+
if (error) {
|
|
418
|
+
reject(error);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (typeof written !== "number" || written <= 0) {
|
|
422
|
+
reject(createError("write made no progress"));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
offset += written;
|
|
426
|
+
writeNext();
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
writeNext();
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function fsyncFd(fd) {
|
|
433
|
+
return new Promise((resolve, reject) => {
|
|
434
|
+
fs2.fsync(fd, (error) => {
|
|
435
|
+
if (error) reject(error);
|
|
436
|
+
else resolve();
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function closeFd(fd) {
|
|
441
|
+
return new Promise((resolve, reject) => {
|
|
442
|
+
fs2.close(fd, (error) => {
|
|
443
|
+
if (error) reject(error);
|
|
444
|
+
else resolve();
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function fstatSize(fd) {
|
|
449
|
+
return new Promise((resolve, reject) => {
|
|
450
|
+
fs2.fstat(fd, (error, stats) => {
|
|
451
|
+
if (error) reject(error);
|
|
452
|
+
else resolve(stats.size);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function readAtFd(fd, position, length) {
|
|
457
|
+
if (length <= 0) return Promise.resolve(Buffer.alloc(0));
|
|
458
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
459
|
+
return new Promise((resolve, reject) => {
|
|
460
|
+
fs2.read(fd, buffer, 0, length, position, (error, bytesRead) => {
|
|
461
|
+
if (error) {
|
|
462
|
+
reject(error);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
resolve(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function cleanupPartiallyInitializedSpool(dir, leaves) {
|
|
470
|
+
for (const leaf of leaves) {
|
|
471
|
+
try {
|
|
472
|
+
fs2.unlinkSync(leaf);
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
fs2.rmdirSync(dir);
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function createPrivateSpool(options) {
|
|
482
|
+
const errorLabel = options.errorLabel ?? "private spool";
|
|
483
|
+
const createError = options.createError ?? ((message) => new PrivateSpoolError(message));
|
|
484
|
+
const fail = (message) => createError(`${errorLabel} ${message}`);
|
|
485
|
+
if (options.streams.length === 0) {
|
|
486
|
+
throw fail("requires at least one stream");
|
|
487
|
+
}
|
|
488
|
+
const label = (options.label ?? "spool").replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 40);
|
|
489
|
+
const dir = fs2.mkdtempSync(path.join(os.tmpdir(), `build-${label}-`));
|
|
490
|
+
fs2.chmodSync(dir, 448);
|
|
491
|
+
markBuildTempDirSync(dir);
|
|
492
|
+
const states = /* @__PURE__ */ new Map();
|
|
493
|
+
const leafPaths = [path.join(dir, BUILD_TEMP_MARKER_FILENAME)];
|
|
494
|
+
try {
|
|
495
|
+
for (const name of options.streams) {
|
|
496
|
+
if (states.has(name)) {
|
|
497
|
+
throw fail(`duplicate stream '${name}'`);
|
|
498
|
+
}
|
|
499
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name) || name === "." || name === "..") {
|
|
500
|
+
throw fail(`invalid stream name '${name}'`);
|
|
501
|
+
}
|
|
502
|
+
const leaf = path.join(dir, `${name}.spool`);
|
|
503
|
+
leafPaths.push(leaf);
|
|
504
|
+
const fd = fs2.openSync(leaf, EXCL_RDWR_FLAGS, 384);
|
|
505
|
+
fs2.fchmodSync(fd, 384);
|
|
506
|
+
states.set(name, { name, path: leaf, fd, sawEnqueuedWrite: false });
|
|
507
|
+
}
|
|
508
|
+
} catch (error) {
|
|
509
|
+
for (const state of states.values()) {
|
|
510
|
+
if (state.fd !== null) {
|
|
511
|
+
try {
|
|
512
|
+
fs2.closeSync(state.fd);
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
cleanupPartiallyInitializedSpool(dir, leafPaths);
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
520
|
+
let writeChain = Promise.resolve();
|
|
521
|
+
let writeError;
|
|
522
|
+
let finalized = false;
|
|
523
|
+
let disposed = false;
|
|
524
|
+
const closeHeldFds = async () => {
|
|
525
|
+
const fds = [];
|
|
526
|
+
for (const state of states.values()) {
|
|
527
|
+
if (state.fd !== null) {
|
|
528
|
+
fds.push(state.fd);
|
|
529
|
+
state.fd = null;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
for (const fd of fds) {
|
|
533
|
+
try {
|
|
534
|
+
await closeFd(fd);
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const makeStream = (state) => ({
|
|
540
|
+
get name() {
|
|
541
|
+
return state.name;
|
|
542
|
+
},
|
|
543
|
+
get path() {
|
|
544
|
+
return state.path;
|
|
545
|
+
},
|
|
546
|
+
get sawEnqueuedWrite() {
|
|
547
|
+
return state.sawEnqueuedWrite;
|
|
548
|
+
},
|
|
549
|
+
enqueue(chunk) {
|
|
550
|
+
if (disposed || finalized || state.fd === null) return;
|
|
551
|
+
if (!isNonEmptyRawChunk(chunk)) return;
|
|
552
|
+
const targetFd = state.fd;
|
|
553
|
+
const buffer = rawChunkToBuffer(chunk);
|
|
554
|
+
if (buffer.length === 0) return;
|
|
555
|
+
state.sawEnqueuedWrite = true;
|
|
556
|
+
writeChain = writeChain.then(async () => {
|
|
557
|
+
if (disposed) return;
|
|
558
|
+
await writeAllToFd(targetFd, buffer, fail);
|
|
559
|
+
}).catch((error) => {
|
|
560
|
+
if (!writeError) {
|
|
561
|
+
writeError = error instanceof Error ? error : fail(String(error));
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
},
|
|
565
|
+
requireOpenFd() {
|
|
566
|
+
if (disposed) throw fail("already disposed");
|
|
567
|
+
if (state.fd === null) throw fail(`${state.name} FD not available`);
|
|
568
|
+
return state.fd;
|
|
569
|
+
},
|
|
570
|
+
requireFinalizedFd() {
|
|
571
|
+
if (disposed) throw fail("already disposed");
|
|
572
|
+
if (!finalized || state.fd === null) {
|
|
573
|
+
throw fail(`${state.name} FD not available (finalize first)`);
|
|
574
|
+
}
|
|
575
|
+
return state.fd;
|
|
576
|
+
},
|
|
577
|
+
async statSize() {
|
|
578
|
+
if (disposed) throw fail("already disposed");
|
|
579
|
+
if (state.fd === null) throw fail(`${state.name} FD not available`);
|
|
580
|
+
return fstatSize(state.fd);
|
|
581
|
+
},
|
|
582
|
+
async readAt(position, length) {
|
|
583
|
+
if (disposed) throw fail("already disposed");
|
|
584
|
+
if (state.fd === null) throw fail(`${state.name} FD not available`);
|
|
585
|
+
return readAtFd(state.fd, position, length);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
const streams = /* @__PURE__ */ new Map();
|
|
589
|
+
for (const state of states.values()) {
|
|
590
|
+
streams.set(state.name, makeStream(state));
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
get dir() {
|
|
594
|
+
return dir;
|
|
595
|
+
},
|
|
596
|
+
stream(name) {
|
|
597
|
+
const stream = streams.get(name);
|
|
598
|
+
if (!stream) {
|
|
599
|
+
throw fail(`unknown stream '${name}'`);
|
|
600
|
+
}
|
|
601
|
+
return stream;
|
|
602
|
+
},
|
|
603
|
+
async finalize() {
|
|
604
|
+
if (disposed) throw fail("already disposed");
|
|
605
|
+
if (finalized) return;
|
|
606
|
+
await writeChain;
|
|
607
|
+
if (writeError) {
|
|
608
|
+
await closeHeldFds();
|
|
609
|
+
throw fail(`write failed: ${writeError.message}`);
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
for (const state of states.values()) {
|
|
613
|
+
if (state.fd !== null) {
|
|
614
|
+
await fsyncFd(state.fd);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
await closeHeldFds();
|
|
619
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
620
|
+
throw fail(`fsync failed: ${message}`);
|
|
621
|
+
}
|
|
622
|
+
finalized = true;
|
|
623
|
+
},
|
|
624
|
+
async dispose() {
|
|
625
|
+
if (disposed) return;
|
|
626
|
+
disposed = true;
|
|
627
|
+
try {
|
|
628
|
+
await writeChain;
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
631
|
+
await closeHeldFds();
|
|
632
|
+
try {
|
|
633
|
+
await safeRmDir(dir, {
|
|
634
|
+
protectedPaths: [os.tmpdir()],
|
|
635
|
+
managedRoots: [os.tmpdir()]
|
|
636
|
+
});
|
|
637
|
+
} catch {
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/core/providers/shared/process-signals.ts
|
|
644
|
+
var BUILD_PROCESS_GROUP_OWNER_ENV = "BUILD_PROCESS_GROUP_OWNER";
|
|
645
|
+
var BUILD_PROVIDER_PROCESS_GROUP_MARKER_PREFIX = "__BUILD_PROVIDER_PROCESS_GROUP_PID__:";
|
|
646
|
+
var BUILD_PROVIDER_PROCESS_GROUP_CLEARED_MARKER_PREFIX = "__BUILD_PROVIDER_PROCESS_GROUP_CLEARED__:";
|
|
647
|
+
function isReportableProviderProcessGroupPid(pid) {
|
|
648
|
+
return isSignalableProcessGroupId(pid);
|
|
649
|
+
}
|
|
650
|
+
function shouldEmitWorkerProcessGroupMarker(pid) {
|
|
651
|
+
return readEnv(BUILD_VARIANT_WORKER).value === true && process.platform !== "win32" && isReportableProviderProcessGroupPid(pid);
|
|
652
|
+
}
|
|
653
|
+
function isProviderProcessGroupMarkerLine(line) {
|
|
654
|
+
return line.startsWith(BUILD_PROVIDER_PROCESS_GROUP_MARKER_PREFIX) || line.startsWith(BUILD_PROVIDER_PROCESS_GROUP_CLEARED_MARKER_PREFIX);
|
|
655
|
+
}
|
|
656
|
+
function resolveProviderProcessGroupOwnership() {
|
|
657
|
+
return {
|
|
658
|
+
owner: "runner",
|
|
659
|
+
detach: process.platform !== "win32"
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function emitProviderProcessGroupMarker(pid) {
|
|
663
|
+
if (!shouldEmitWorkerProcessGroupMarker(pid)) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
process.stdout.write(`${BUILD_PROVIDER_PROCESS_GROUP_MARKER_PREFIX}${pid}
|
|
668
|
+
`);
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function emitProviderProcessGroupClearedMarker(pid) {
|
|
673
|
+
if (!shouldEmitWorkerProcessGroupMarker(pid)) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
try {
|
|
677
|
+
process.stdout.write(`${BUILD_PROVIDER_PROCESS_GROUP_CLEARED_MARKER_PREFIX}${pid}
|
|
678
|
+
`);
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function isNoSuchProcess(error) {
|
|
683
|
+
return error?.code === "ESRCH";
|
|
684
|
+
}
|
|
685
|
+
async function ensureDetachedProviderProcessGroupGone(pid, timeoutMs = 2e3) {
|
|
686
|
+
if (process.platform === "win32" || !isReportableProviderProcessGroupPid(pid)) {
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
signalProcessGroup(pid, 0);
|
|
691
|
+
} catch (error) {
|
|
692
|
+
return isNoSuchProcess(error);
|
|
693
|
+
}
|
|
694
|
+
try {
|
|
695
|
+
signalProcessGroup(pid, "SIGKILL");
|
|
696
|
+
} catch (error) {
|
|
697
|
+
return isNoSuchProcess(error);
|
|
698
|
+
}
|
|
699
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
700
|
+
while (Date.now() < deadline) {
|
|
701
|
+
try {
|
|
702
|
+
signalProcessGroup(pid, 0);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
return isNoSuchProcess(error);
|
|
705
|
+
}
|
|
706
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
707
|
+
}
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
function sendSignalToSubprocessTree(subprocess, signal) {
|
|
711
|
+
let delivered = false;
|
|
712
|
+
if (process.platform !== "win32" && isSignalableProcessGroupId(subprocess.pid)) {
|
|
713
|
+
try {
|
|
714
|
+
signalProcessGroup(subprocess.pid, signal);
|
|
715
|
+
delivered = true;
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
subprocess.kill(signal);
|
|
721
|
+
delivered = true;
|
|
722
|
+
} catch {
|
|
723
|
+
}
|
|
724
|
+
return delivered;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/core/providers/shared/process-slot-reservation.ts
|
|
728
|
+
import { execa } from "execa";
|
|
729
|
+
var DEFAULT_PROCESS_SLOT_WAIT_MS = 3e4;
|
|
730
|
+
var PROCESS_SLOT_POLL_MS = 100;
|
|
731
|
+
function releaseProcessSlotReservation(reservation) {
|
|
732
|
+
if (reservation !== void 0) {
|
|
733
|
+
globalProcessLimiter.cancelReservation(reservation);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function getProcessSlotWaitBudgetMs() {
|
|
737
|
+
const raw = readEnv(BUILD_PROCESS_SLOT_WAIT_MS).value ?? DEFAULT_PROCESS_SLOT_WAIT_MS;
|
|
738
|
+
return Math.max(0, Math.min(3e5, raw));
|
|
739
|
+
}
|
|
740
|
+
function tryReserveProcessSlot() {
|
|
741
|
+
if (typeof globalProcessLimiter.reserve === "function") {
|
|
742
|
+
const reservation = globalProcessLimiter.reserve();
|
|
743
|
+
if (reservation !== null) {
|
|
744
|
+
return reservation;
|
|
745
|
+
}
|
|
746
|
+
} else {
|
|
747
|
+
const canSpawn = typeof globalProcessLimiter.canSpawn === "function" ? globalProcessLimiter.canSpawn() : true;
|
|
748
|
+
if (canSpawn) {
|
|
749
|
+
return void 0;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
function processSlotAbortError(limiterDescription) {
|
|
755
|
+
return new Error(`Process slot wait aborted before spawn: ${limiterDescription}`);
|
|
756
|
+
}
|
|
757
|
+
function throwIfReservationAborted(reservation, limiterDescription, abortSignal) {
|
|
758
|
+
if (!abortSignal?.aborted) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
releaseProcessSlotReservation(reservation);
|
|
762
|
+
throw processSlotAbortError(limiterDescription);
|
|
763
|
+
}
|
|
764
|
+
function acquireProcessSlot(limiterDescription, abortSignal) {
|
|
765
|
+
const immediateReservation = tryReserveProcessSlot();
|
|
766
|
+
if (immediateReservation !== null) {
|
|
767
|
+
throwIfReservationAborted(immediateReservation, limiterDescription, abortSignal);
|
|
768
|
+
return immediateReservation;
|
|
769
|
+
}
|
|
770
|
+
return waitForProcessSlot(limiterDescription, abortSignal);
|
|
771
|
+
}
|
|
772
|
+
async function waitForProcessSlot(limiterDescription, abortSignal) {
|
|
773
|
+
const waitBudgetMs = getProcessSlotWaitBudgetMs();
|
|
774
|
+
const deadline = Date.now() + waitBudgetMs;
|
|
775
|
+
while (true) {
|
|
776
|
+
const reservation = tryReserveProcessSlot();
|
|
777
|
+
if (reservation !== null) {
|
|
778
|
+
throwIfReservationAborted(reservation, limiterDescription, abortSignal);
|
|
779
|
+
return reservation;
|
|
780
|
+
}
|
|
781
|
+
if (abortSignal?.aborted) {
|
|
782
|
+
throw processSlotAbortError(limiterDescription);
|
|
783
|
+
}
|
|
784
|
+
const remainingMs = deadline - Date.now();
|
|
785
|
+
if (remainingMs <= 0 || waitBudgetMs === 0) {
|
|
786
|
+
throw new Error(
|
|
787
|
+
`Process limit reached before spawn after waiting ${waitBudgetMs}ms: ${limiterDescription}. Wait for existing provider processes to complete or increase BUILD_MAX_PROCESSES.`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
await new Promise((resolve, reject) => {
|
|
791
|
+
const timer = setTimeout(
|
|
792
|
+
() => {
|
|
793
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
794
|
+
resolve();
|
|
795
|
+
},
|
|
796
|
+
Math.min(PROCESS_SLOT_POLL_MS, remainingMs)
|
|
797
|
+
);
|
|
798
|
+
const onAbort = () => {
|
|
799
|
+
clearTimeout(timer);
|
|
800
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
801
|
+
reject(processSlotAbortError(limiterDescription));
|
|
802
|
+
};
|
|
803
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
804
|
+
});
|
|
805
|
+
if (abortSignal?.aborted) {
|
|
806
|
+
throw processSlotAbortError(limiterDescription);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function spawnProviderSubprocess(command, args, options, reservation) {
|
|
811
|
+
try {
|
|
812
|
+
return execa(command, args, options);
|
|
813
|
+
} catch (spawnErr) {
|
|
814
|
+
releaseProcessSlotReservation(reservation);
|
|
815
|
+
throw spawnErr;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/core/providers/shared/sandbox-bypass-guard.ts
|
|
820
|
+
import os2 from "os";
|
|
821
|
+
var PROVIDER_INTERNAL_SANDBOX_BYPASS_FLAGS = /* @__PURE__ */ new Set([
|
|
822
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
823
|
+
"--dangerously-skip-permissions",
|
|
824
|
+
"--yolo",
|
|
825
|
+
"--allow-all-tools",
|
|
826
|
+
"--always-approve",
|
|
827
|
+
"--trust-all-tools"
|
|
828
|
+
]);
|
|
829
|
+
var PROVIDER_INTERNAL_SANDBOX_BYPASS_OPTION_VALUES = /* @__PURE__ */ new Map([
|
|
830
|
+
["--permission-mode", /* @__PURE__ */ new Set(["acceptEdits", "auto", "dontAsk", "bypassPermissions"])],
|
|
831
|
+
["--sandbox", /* @__PURE__ */ new Set(["disabled"])]
|
|
832
|
+
]);
|
|
833
|
+
function parseBooleanLikeFlagValue(value) {
|
|
834
|
+
const normalized = value.trim().toLowerCase();
|
|
835
|
+
return normalized === "true" || normalized === "1" || normalized === "yes";
|
|
836
|
+
}
|
|
837
|
+
function hasAcceptedPolicyBoundaryProof(sandboxed) {
|
|
838
|
+
const { boundaryProof, policyProof } = sandboxed;
|
|
839
|
+
if (boundaryProof === "wrap_applied" && policyProof === "wrap_applied") {
|
|
840
|
+
return true;
|
|
841
|
+
}
|
|
842
|
+
if ((boundaryProof === "inherited_outer" || boundaryProof === "container") && policyProof === "capability_probe") {
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
if (os2.platform() === "linux" && (boundaryProof === "linux_isolated" || boundaryProof === "linux_acknowledged") && policyProof === "linux_host_class") {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
var HOST_CLASS_BYPASS_PROVENANCES = /* @__PURE__ */ new Set([
|
|
851
|
+
"linux_isolated",
|
|
852
|
+
"linux_acknowledged"
|
|
853
|
+
]);
|
|
854
|
+
function formatHostClassBypassNotice(providerName, provenance) {
|
|
855
|
+
if (!HOST_CLASS_BYPASS_PROVENANCES.has(provenance)) return void 0;
|
|
856
|
+
return `Info: ${providerName} internal sandbox is bypassed for this run because the Linux host class provides the boundary (provenance=${provenance}).`;
|
|
857
|
+
}
|
|
858
|
+
function emitHostClassBypassNotice(providerName, providerId, options = {}) {
|
|
859
|
+
const { provenance } = detectSandboxBoundaryAttestation(providerName, providerId, options);
|
|
860
|
+
const notice = formatHostClassBypassNotice(providerName, provenance);
|
|
861
|
+
if (!notice) return;
|
|
862
|
+
console.warn(notice);
|
|
863
|
+
}
|
|
864
|
+
function findProviderInternalSandboxBypass(args) {
|
|
865
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
866
|
+
const arg = args[index];
|
|
867
|
+
if (!arg) continue;
|
|
868
|
+
if (PROVIDER_INTERNAL_SANDBOX_BYPASS_FLAGS.has(arg)) return arg;
|
|
869
|
+
for (const flag of PROVIDER_INTERNAL_SANDBOX_BYPASS_FLAGS) {
|
|
870
|
+
if (arg.startsWith(`${flag}=`)) {
|
|
871
|
+
const value = arg.slice(flag.length + 1);
|
|
872
|
+
if (parseBooleanLikeFlagValue(value)) return flag;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const equalsIndex = arg.indexOf("=");
|
|
876
|
+
if (equalsIndex > 0) {
|
|
877
|
+
const option = arg.slice(0, equalsIndex);
|
|
878
|
+
const value = arg.slice(equalsIndex + 1);
|
|
879
|
+
const bypassValues2 = PROVIDER_INTERNAL_SANDBOX_BYPASS_OPTION_VALUES.get(option);
|
|
880
|
+
if (bypassValues2?.has(value)) return `${option}=${value}`;
|
|
881
|
+
}
|
|
882
|
+
const bypassValues = PROVIDER_INTERNAL_SANDBOX_BYPASS_OPTION_VALUES.get(arg);
|
|
883
|
+
const next = args[index + 1];
|
|
884
|
+
if (next && bypassValues?.has(next)) return `${arg} ${next}`;
|
|
885
|
+
}
|
|
886
|
+
return void 0;
|
|
887
|
+
}
|
|
888
|
+
function assertProviderBypassHasBoundaryProof(metadata, args, sandboxed, explicitBypass) {
|
|
889
|
+
const bypassFlag = explicitBypass ?? findProviderInternalSandboxBypass(args);
|
|
890
|
+
if (!bypassFlag || hasAcceptedPolicyBoundaryProof(sandboxed)) return;
|
|
891
|
+
if (sandboxed.boundaryProof) {
|
|
892
|
+
throw new ProviderExecutionError(
|
|
893
|
+
`[${metadata.name}] Provider internal sandbox bypass (${bypassFlag}) requires a sandbox policy boundary for this exact invocation, but the final provider command only has security boundary proof (${sandboxed.boundaryProof}) [reason=sandbox_capability_denied]`,
|
|
894
|
+
{
|
|
895
|
+
retryable: false,
|
|
896
|
+
reason: "sandbox_capability_denied",
|
|
897
|
+
providerName: metadata.name
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
if (os2.platform() === "linux") {
|
|
902
|
+
authorizeLinuxHostClassWrites();
|
|
903
|
+
}
|
|
904
|
+
throw new ProviderExecutionError(
|
|
905
|
+
`[${metadata.name}] Provider internal sandbox bypass (${bypassFlag}) requires a sandbox boundary for this exact invocation, but the final provider command has no boundary proof [reason=sandbox_boundary_required]`,
|
|
906
|
+
{
|
|
907
|
+
retryable: false,
|
|
908
|
+
reason: "sandbox_boundary_required",
|
|
909
|
+
providerName: metadata.name
|
|
910
|
+
}
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/core/providers/shared/sandbox-path-denial.ts
|
|
915
|
+
var SANDBOX_PATH_DENIED_REASON = "sandbox_path_denied";
|
|
916
|
+
var SEATBELT_BOUNDARY_PROOFS = /* @__PURE__ */ new Set([
|
|
917
|
+
"wrap_applied",
|
|
918
|
+
"inherited_outer"
|
|
919
|
+
]);
|
|
920
|
+
var MAX_SCANNED_STDERR_CHARS = 16384;
|
|
921
|
+
var MAX_SCANNED_LINE_CHARS = 2048;
|
|
922
|
+
var MAX_REPORTED_PATH_CHARS = 512;
|
|
923
|
+
var PATH = String.raw`(?:\/|~\/)`;
|
|
924
|
+
var NODE_FILE_OPS = "open|mkdir|mkdtemp|rename|unlink|rmdir|rm|copyfile|symlink|link|scandir|opendir|access|stat|lstat|utime";
|
|
925
|
+
var DENIAL_PATTERNS = [
|
|
926
|
+
// node: EPERM: operation not permitted, open '/path'
|
|
927
|
+
new RegExp(
|
|
928
|
+
String.raw`\bEPERM: operation not permitted, (?:${NODE_FILE_OPS}) '(${PATH}[^'\n]+)'`,
|
|
929
|
+
"i"
|
|
930
|
+
),
|
|
931
|
+
// python: [Errno 1] Operation not permitted: '/path'
|
|
932
|
+
new RegExp(String.raw`operation not permitted: '(${PATH}[^'\n]+)'`, "i"),
|
|
933
|
+
// zsh: <script>:<line>: operation not permitted: /path
|
|
934
|
+
new RegExp(String.raw`operation not permitted: (${PATH}.*?)\s*$`, "i"),
|
|
935
|
+
// bash, dash, go: /path: Operation not permitted
|
|
936
|
+
new RegExp(String.raw`(${PATH}[^\n:']*): operation not permitted\b`, "i")
|
|
937
|
+
];
|
|
938
|
+
var NON_PATH_EPERM = /sandbox_apply|(?:^|:\s)(?:ch(?:own|mod|grp|flags)|kill|set[ug]id|ptrace)(?::|\s+[/~])|\b(?:l?ch(?:own|mod|flags)|kill|set[ug]id)\s*\(/i;
|
|
939
|
+
var TRACE_TAIL = /^(?:\s+at\s|\s+(?:errno|code|syscall|path|dest):\s|\}|Node\.js v|Bun v)/;
|
|
940
|
+
function detectSandboxPathDenial(stderr) {
|
|
941
|
+
const lines = stderr.slice(-MAX_SCANNED_STDERR_CHARS).split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
942
|
+
let end = lines.length - 1;
|
|
943
|
+
while (end >= 0 && TRACE_TAIL.test(lines[end] ?? "")) end--;
|
|
944
|
+
const last = lines[end];
|
|
945
|
+
if (!last || last.length > MAX_SCANNED_LINE_CHARS) return void 0;
|
|
946
|
+
let start = end;
|
|
947
|
+
while (start > 0 && /^\s/.test(lines[start - 1] ?? "")) start--;
|
|
948
|
+
const block = lines.slice(start).filter((line) => !/^\s+at\s/.test(line));
|
|
949
|
+
if (block.some((line) => NON_PATH_EPERM.test(line))) return void 0;
|
|
950
|
+
for (const pattern of DENIAL_PATTERNS) {
|
|
951
|
+
const path3 = pattern.exec(last)?.[1]?.trim();
|
|
952
|
+
if (path3) return { path: path3 };
|
|
953
|
+
}
|
|
954
|
+
return void 0;
|
|
955
|
+
}
|
|
956
|
+
function sandboxPathDenialError(metadata, sandbox, exit, maxPublicLength) {
|
|
957
|
+
const { exitCode } = exit;
|
|
958
|
+
if (sandbox?.sandboxed !== true || !SEATBELT_BOUNDARY_PROOFS.has(sandbox.boundaryProof)) {
|
|
959
|
+
return void 0;
|
|
960
|
+
}
|
|
961
|
+
if (exitCode === void 0 || exitCode === 0) return void 0;
|
|
962
|
+
if (exit.stdout.trim().length > 0 || (exit.stdoutBytes ?? 0) > 0) return void 0;
|
|
963
|
+
const denial = detectSandboxPathDenial(exit.stderr);
|
|
964
|
+
if (!denial) return void 0;
|
|
965
|
+
const path3 = sanitizeDiagnosticText(denial.path).slice(0, MAX_REPORTED_PATH_CHARS);
|
|
966
|
+
const providerId = normalizeEngineId(metadata.id ?? metadata.name);
|
|
967
|
+
return new ProviderExecutionError(
|
|
968
|
+
`${metadata.name} CLI exited with code ${exitCode} after reporting access denied to ${path3} while running inside the sandbox. If the command Build runs for ${metadata.name} is a wrapper or launcher that writes outside the run's workspace, point Build at the real binary with providers.${providerId}.cli.command in your user providers.yaml or BUILD_${providerId.toUpperCase()}_CLI_COMMAND.`,
|
|
969
|
+
{
|
|
970
|
+
retryable: false,
|
|
971
|
+
exitCode,
|
|
972
|
+
reason: SANDBOX_PATH_DENIED_REASON,
|
|
973
|
+
...providerExecutionTransportStreamOptions(exit.stdout, exit.stderr, { maxPublicLength })
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/core/memory/guardian.ts
|
|
979
|
+
import { randomBytes } from "crypto";
|
|
980
|
+
import { totalmem } from "os";
|
|
981
|
+
import { getHeapStatistics } from "v8";
|
|
982
|
+
|
|
983
|
+
// src/core/memory/provider-profiles.ts
|
|
984
|
+
function cloneProfile(profile) {
|
|
985
|
+
return {
|
|
986
|
+
...profile,
|
|
987
|
+
gracefulDegradation: { ...profile.gracefulDegradation }
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function cloneProfiles(profiles) {
|
|
991
|
+
return Object.fromEntries(
|
|
992
|
+
Object.entries(profiles).map(([providerId, profile]) => [providerId, cloneProfile(profile)])
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
var DEFAULT_PROFILES = {
|
|
996
|
+
default: {
|
|
997
|
+
warnThreshold: 0.85,
|
|
998
|
+
criticalThreshold: 0.9,
|
|
999
|
+
emergencyThreshold: 0.95,
|
|
1000
|
+
gracefulDegradation: {
|
|
1001
|
+
enabled: true,
|
|
1002
|
+
reduceParallelism: true,
|
|
1003
|
+
slowPolling: true
|
|
1004
|
+
},
|
|
1005
|
+
maxProcesses: 10
|
|
1006
|
+
},
|
|
1007
|
+
claude: {
|
|
1008
|
+
warnThreshold: 0.8,
|
|
1009
|
+
criticalThreshold: 0.88,
|
|
1010
|
+
emergencyThreshold: 0.93,
|
|
1011
|
+
gracefulDegradation: {
|
|
1012
|
+
enabled: true,
|
|
1013
|
+
reduceParallelism: true,
|
|
1014
|
+
slowPolling: true
|
|
1015
|
+
},
|
|
1016
|
+
maxProcesses: 3
|
|
1017
|
+
},
|
|
1018
|
+
gemini: {
|
|
1019
|
+
warnThreshold: 0.85,
|
|
1020
|
+
criticalThreshold: 0.92,
|
|
1021
|
+
emergencyThreshold: 0.96,
|
|
1022
|
+
gracefulDegradation: {
|
|
1023
|
+
enabled: true,
|
|
1024
|
+
reduceParallelism: true,
|
|
1025
|
+
slowPolling: false
|
|
1026
|
+
},
|
|
1027
|
+
maxProcesses: 8
|
|
1028
|
+
},
|
|
1029
|
+
codex: {
|
|
1030
|
+
warnThreshold: 0.82,
|
|
1031
|
+
criticalThreshold: 0.89,
|
|
1032
|
+
emergencyThreshold: 0.94,
|
|
1033
|
+
gracefulDegradation: {
|
|
1034
|
+
enabled: true,
|
|
1035
|
+
reduceParallelism: true,
|
|
1036
|
+
slowPolling: true
|
|
1037
|
+
},
|
|
1038
|
+
maxProcesses: 2
|
|
1039
|
+
},
|
|
1040
|
+
cursor: {
|
|
1041
|
+
warnThreshold: 0.75,
|
|
1042
|
+
criticalThreshold: 0.85,
|
|
1043
|
+
emergencyThreshold: 0.92,
|
|
1044
|
+
gracefulDegradation: {
|
|
1045
|
+
enabled: true,
|
|
1046
|
+
reduceParallelism: true,
|
|
1047
|
+
slowPolling: true
|
|
1048
|
+
},
|
|
1049
|
+
maxProcesses: 1
|
|
1050
|
+
},
|
|
1051
|
+
copilot: {
|
|
1052
|
+
warnThreshold: 0.78,
|
|
1053
|
+
criticalThreshold: 0.86,
|
|
1054
|
+
emergencyThreshold: 0.92,
|
|
1055
|
+
gracefulDegradation: {
|
|
1056
|
+
enabled: true,
|
|
1057
|
+
reduceParallelism: true,
|
|
1058
|
+
slowPolling: true
|
|
1059
|
+
},
|
|
1060
|
+
maxProcesses: 3
|
|
1061
|
+
},
|
|
1062
|
+
opencode: {
|
|
1063
|
+
warnThreshold: 0.85,
|
|
1064
|
+
criticalThreshold: 0.9,
|
|
1065
|
+
emergencyThreshold: 0.95,
|
|
1066
|
+
gracefulDegradation: {
|
|
1067
|
+
enabled: true,
|
|
1068
|
+
reduceParallelism: true,
|
|
1069
|
+
slowPolling: true
|
|
1070
|
+
},
|
|
1071
|
+
maxProcesses: 3
|
|
1072
|
+
},
|
|
1073
|
+
grok: {
|
|
1074
|
+
warnThreshold: 0.85,
|
|
1075
|
+
criticalThreshold: 0.92,
|
|
1076
|
+
emergencyThreshold: 0.96,
|
|
1077
|
+
gracefulDegradation: {
|
|
1078
|
+
enabled: true,
|
|
1079
|
+
reduceParallelism: true,
|
|
1080
|
+
slowPolling: true
|
|
1081
|
+
},
|
|
1082
|
+
maxProcesses: 3
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
var MemoryProfilesManager = class _MemoryProfilesManager {
|
|
1086
|
+
static instance;
|
|
1087
|
+
profiles = cloneProfiles(DEFAULT_PROFILES);
|
|
1088
|
+
constructor() {
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Synchronous singleton construction. JavaScript module execution is single-threaded
|
|
1092
|
+
* here, so an event-loop-blocking spin lock is unnecessary and unsafe.
|
|
1093
|
+
*/
|
|
1094
|
+
static getInstance() {
|
|
1095
|
+
if (_MemoryProfilesManager.instance) {
|
|
1096
|
+
return _MemoryProfilesManager.instance;
|
|
1097
|
+
}
|
|
1098
|
+
_MemoryProfilesManager.instance = new _MemoryProfilesManager();
|
|
1099
|
+
return _MemoryProfilesManager.instance;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Replace profiles explicitly for an embedding or test harness.
|
|
1103
|
+
* Production startup does not call this from providers.yaml.
|
|
1104
|
+
*/
|
|
1105
|
+
loadProfiles(config) {
|
|
1106
|
+
this.profiles = {
|
|
1107
|
+
...cloneProfiles(DEFAULT_PROFILES),
|
|
1108
|
+
...cloneProfiles(config)
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Get profile for a specific provider
|
|
1113
|
+
* Falls back to 'default' if provider-specific profile not found
|
|
1114
|
+
*/
|
|
1115
|
+
getProfile(providerId) {
|
|
1116
|
+
return cloneProfile(this.profiles[providerId] || this.profiles.default);
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Get all loaded profiles
|
|
1120
|
+
*/
|
|
1121
|
+
getAllProfiles() {
|
|
1122
|
+
return cloneProfiles(this.profiles);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Check if a memory ratio requires warning
|
|
1126
|
+
*/
|
|
1127
|
+
shouldWarn(providerId, ratio) {
|
|
1128
|
+
const profile = this.getProfile(providerId);
|
|
1129
|
+
return ratio >= profile.warnThreshold;
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Check if memory ratio is at critical threshold
|
|
1133
|
+
*/
|
|
1134
|
+
isCritical(providerId, ratio) {
|
|
1135
|
+
const profile = this.getProfile(providerId);
|
|
1136
|
+
return ratio >= profile.criticalThreshold;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Check if memory ratio is at emergency threshold
|
|
1140
|
+
*/
|
|
1141
|
+
isEmergency(providerId, ratio) {
|
|
1142
|
+
const profile = this.getProfile(providerId);
|
|
1143
|
+
return ratio >= profile.emergencyThreshold;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Get graceful degradation strategy for provider
|
|
1147
|
+
*/
|
|
1148
|
+
getGracefulDegradationStrategy(providerId) {
|
|
1149
|
+
const profile = this.getProfile(providerId);
|
|
1150
|
+
return profile.gracefulDegradation;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Get max concurrent processes for provider
|
|
1154
|
+
*/
|
|
1155
|
+
getMaxProcesses(providerId) {
|
|
1156
|
+
const profile = this.getProfile(providerId);
|
|
1157
|
+
return profile.maxProcesses;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Reset to defaults (for testing)
|
|
1161
|
+
*/
|
|
1162
|
+
static resetForTesting() {
|
|
1163
|
+
if (_MemoryProfilesManager.instance) {
|
|
1164
|
+
_MemoryProfilesManager.instance.profiles = cloneProfiles(DEFAULT_PROFILES);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
function getMemoryProfilesManager() {
|
|
1169
|
+
return MemoryProfilesManager.getInstance();
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// src/core/memory/guardian.ts
|
|
1173
|
+
var MemoryGuardian = class _MemoryGuardian {
|
|
1174
|
+
static instance;
|
|
1175
|
+
trackedProcesses = /* @__PURE__ */ new Map();
|
|
1176
|
+
pids = [];
|
|
1177
|
+
// LIFO ordering, de-duplicated (legacy support)
|
|
1178
|
+
interval;
|
|
1179
|
+
isShuttingDown = false;
|
|
1180
|
+
isActive = false;
|
|
1181
|
+
exitHandlerRegistered = false;
|
|
1182
|
+
emergencyHandler;
|
|
1183
|
+
handleExit = () => {
|
|
1184
|
+
this.stop();
|
|
1185
|
+
};
|
|
1186
|
+
// CRITICAL FIX: Track active kill operations to prevent race conditions
|
|
1187
|
+
activeKillOperations = /* @__PURE__ */ new Set();
|
|
1188
|
+
// Adaptive intervals (ms)
|
|
1189
|
+
// Increased from 2s/1s/500ms to reduce overhead and false alarms
|
|
1190
|
+
currentInterval = 1e4;
|
|
1191
|
+
// Start slow (10s)
|
|
1192
|
+
NORMAL_INTERVAL = 1e4;
|
|
1193
|
+
// 10s when < warn threshold
|
|
1194
|
+
WARNING_INTERVAL = 5e3;
|
|
1195
|
+
// 5s when warn-critical
|
|
1196
|
+
CRITICAL_INTERVAL = 2e3;
|
|
1197
|
+
// 2s when > critical threshold
|
|
1198
|
+
// Kill strategy configuration
|
|
1199
|
+
// - Default: Warn at provider warn%, kill at critical%+ (hybrid approach)
|
|
1200
|
+
// - BUILD_MEMORY_KILL=0: Never kill (log-only mode)
|
|
1201
|
+
// - BUILD_MEMORY_KILL=1: Kill at all thresholds (aggressive mode)
|
|
1202
|
+
killMode = this.getKillMode();
|
|
1203
|
+
lastCheckTime = 0;
|
|
1204
|
+
lastWarnTime = 0;
|
|
1205
|
+
// Default thresholds (fallback if provider profiles not available)
|
|
1206
|
+
DEFAULT_WARN = 0.85;
|
|
1207
|
+
DEFAULT_CRIT = 0.9;
|
|
1208
|
+
DEFAULT_EMRG = 0.95;
|
|
1209
|
+
constructor() {
|
|
1210
|
+
}
|
|
1211
|
+
resolveRssLimitMb() {
|
|
1212
|
+
const explicitMb = parseInt(process.env.BUILD_RSS_LIMIT_MB || "", 10);
|
|
1213
|
+
if (!Number.isNaN(explicitMb) && explicitMb > 0) {
|
|
1214
|
+
return explicitMb;
|
|
1215
|
+
}
|
|
1216
|
+
const pctRaw = process.env.BUILD_RSS_LIMIT_PCT;
|
|
1217
|
+
if (!pctRaw) return null;
|
|
1218
|
+
const pct = Number(pctRaw);
|
|
1219
|
+
if (!Number.isFinite(pct) || pct <= 0) return null;
|
|
1220
|
+
const totalMb = totalmem() / (1024 * 1024);
|
|
1221
|
+
const derived = Math.floor(totalMb * pct / 100);
|
|
1222
|
+
return derived > 0 ? derived : null;
|
|
1223
|
+
}
|
|
1224
|
+
static getInstance() {
|
|
1225
|
+
if (!_MemoryGuardian.instance) {
|
|
1226
|
+
_MemoryGuardian.instance = new _MemoryGuardian();
|
|
1227
|
+
}
|
|
1228
|
+
return _MemoryGuardian.instance;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Determine kill mode from environment variable
|
|
1232
|
+
* - '0' or 'false': Never kill (log-only)
|
|
1233
|
+
* - '1' or 'true': Kill at all thresholds (aggressive)
|
|
1234
|
+
* - undefined or other: Hybrid (warn only at warn-crit%, kill at crit%+)
|
|
1235
|
+
*/
|
|
1236
|
+
getKillMode() {
|
|
1237
|
+
const env = process.env.BUILD_MEMORY_KILL;
|
|
1238
|
+
if (env === "0" || env === "false") {
|
|
1239
|
+
return "never";
|
|
1240
|
+
}
|
|
1241
|
+
if (env === "1" || env === "true") {
|
|
1242
|
+
return "always";
|
|
1243
|
+
}
|
|
1244
|
+
return "hybrid";
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Get memory thresholds for a provider
|
|
1248
|
+
*/
|
|
1249
|
+
getThresholds(provider) {
|
|
1250
|
+
try {
|
|
1251
|
+
const profilesManager = getMemoryProfilesManager();
|
|
1252
|
+
const profile = profilesManager.getProfile(provider || "default");
|
|
1253
|
+
return {
|
|
1254
|
+
warn: profile.warnThreshold,
|
|
1255
|
+
crit: profile.criticalThreshold,
|
|
1256
|
+
emrg: profile.emergencyThreshold
|
|
1257
|
+
};
|
|
1258
|
+
} catch {
|
|
1259
|
+
return {
|
|
1260
|
+
warn: this.DEFAULT_WARN,
|
|
1261
|
+
crit: this.DEFAULT_CRIT,
|
|
1262
|
+
emrg: this.DEFAULT_EMRG
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Phase 1: Evaluate a single process memory sample and return a decision.
|
|
1268
|
+
* PLM calls this instead of using hard-coded thresholds.
|
|
1269
|
+
*
|
|
1270
|
+
* Policy:
|
|
1271
|
+
* - If no RSS limit configured (BUILD_RSS_LIMIT_MB/PCT), warn-only (never kill)
|
|
1272
|
+
* - If RSS limit configured but killMode='never', warn-only
|
|
1273
|
+
* - If RSS limit configured and kill enabled, kill at/above limit
|
|
1274
|
+
* - Runaway (2x limit) gets immediate kill (graceMs=0)
|
|
1275
|
+
*/
|
|
1276
|
+
evaluateProcessSample(sample) {
|
|
1277
|
+
const rssLimitMB = this.resolveRssLimitMb();
|
|
1278
|
+
const killEnabled = rssLimitMB !== null && this.killMode !== "never";
|
|
1279
|
+
const rssMB = sample.rssMB;
|
|
1280
|
+
const details = {
|
|
1281
|
+
processId: sample.processId,
|
|
1282
|
+
pid: sample.pid,
|
|
1283
|
+
rssMB,
|
|
1284
|
+
rssLimitMB,
|
|
1285
|
+
provider: sample.provider ?? null,
|
|
1286
|
+
command: sample.command ?? null,
|
|
1287
|
+
workloadClass: sample.workloadClass,
|
|
1288
|
+
killMode: this.killMode
|
|
1289
|
+
};
|
|
1290
|
+
if (!rssLimitMB) {
|
|
1291
|
+
if (rssMB >= 1e3) {
|
|
1292
|
+
return {
|
|
1293
|
+
action: "warn",
|
|
1294
|
+
reason: "high_rss_no_cap_configured",
|
|
1295
|
+
details
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
return { action: "none" };
|
|
1299
|
+
}
|
|
1300
|
+
const warnAtMB = Math.floor(rssLimitMB * 0.8);
|
|
1301
|
+
const runawayAtMB = rssLimitMB * 2;
|
|
1302
|
+
if (rssMB >= runawayAtMB) {
|
|
1303
|
+
if (!killEnabled) {
|
|
1304
|
+
return { action: "warn", reason: "runaway_rss_would_kill_but_disabled", details };
|
|
1305
|
+
}
|
|
1306
|
+
return {
|
|
1307
|
+
action: "kill",
|
|
1308
|
+
reason: "runaway",
|
|
1309
|
+
graceMs: 0,
|
|
1310
|
+
// immediate
|
|
1311
|
+
details
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
if (rssMB >= rssLimitMB) {
|
|
1315
|
+
if (!killEnabled) {
|
|
1316
|
+
return { action: "warn", reason: "over_cap_would_kill_but_disabled", details };
|
|
1317
|
+
}
|
|
1318
|
+
return {
|
|
1319
|
+
action: "kill",
|
|
1320
|
+
reason: "over_cap",
|
|
1321
|
+
graceMs: 5e3,
|
|
1322
|
+
// SIGTERM -> wait -> SIGKILL
|
|
1323
|
+
details
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
if (rssMB >= warnAtMB) {
|
|
1327
|
+
return { action: "warn", reason: "approaching_rss_cap", details };
|
|
1328
|
+
}
|
|
1329
|
+
return { action: "none" };
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Start memory monitoring with adaptive intervals
|
|
1333
|
+
*/
|
|
1334
|
+
start() {
|
|
1335
|
+
if (this.isActive) return;
|
|
1336
|
+
this.isActive = true;
|
|
1337
|
+
this.scheduleCheck();
|
|
1338
|
+
if (!this.exitHandlerRegistered) {
|
|
1339
|
+
process.on("exit", this.handleExit);
|
|
1340
|
+
this.exitHandlerRegistered = true;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Stop monitoring and cleanup
|
|
1345
|
+
*/
|
|
1346
|
+
stop() {
|
|
1347
|
+
this.isActive = false;
|
|
1348
|
+
if (this.interval) {
|
|
1349
|
+
clearTimeout(this.interval);
|
|
1350
|
+
this.interval = void 0;
|
|
1351
|
+
}
|
|
1352
|
+
if (this.exitHandlerRegistered) {
|
|
1353
|
+
process.off("exit", this.handleExit);
|
|
1354
|
+
this.exitHandlerRegistered = false;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Schedule next check with adaptive interval
|
|
1359
|
+
*/
|
|
1360
|
+
scheduleCheck() {
|
|
1361
|
+
if (!this.isActive) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
this.interval = setTimeout(() => {
|
|
1365
|
+
this.check();
|
|
1366
|
+
if (this.isActive) {
|
|
1367
|
+
this.scheduleCheck();
|
|
1368
|
+
}
|
|
1369
|
+
}, this.currentInterval);
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Ultra-lightweight memory check
|
|
1373
|
+
* Target: <1ms execution time
|
|
1374
|
+
*/
|
|
1375
|
+
check() {
|
|
1376
|
+
const now = Date.now();
|
|
1377
|
+
if (now - this.lastCheckTime < 100) return;
|
|
1378
|
+
this.lastCheckTime = now;
|
|
1379
|
+
const { heapUsed, rss } = process.memoryUsage();
|
|
1380
|
+
const { heap_size_limit } = getHeapStatistics();
|
|
1381
|
+
let ratio = heapUsed / heap_size_limit;
|
|
1382
|
+
const rssLimitMb = this.resolveRssLimitMb();
|
|
1383
|
+
if (typeof rssLimitMb === "number" && rssLimitMb > 0) {
|
|
1384
|
+
const rssRatio = rss / (rssLimitMb * 1024 * 1024);
|
|
1385
|
+
if (rssRatio > ratio) ratio = rssRatio;
|
|
1386
|
+
}
|
|
1387
|
+
const currentProvider = this.getMostCommonProvider();
|
|
1388
|
+
const thresholds = this.getThresholds(currentProvider);
|
|
1389
|
+
if (ratio < thresholds.warn) {
|
|
1390
|
+
this.currentInterval = this.NORMAL_INTERVAL;
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (ratio >= thresholds.emrg) {
|
|
1394
|
+
this.emergencyKill(ratio, thresholds);
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
if (ratio >= thresholds.crit) {
|
|
1398
|
+
this.currentInterval = this.CRITICAL_INTERVAL;
|
|
1399
|
+
if (!this.isShuttingDown) {
|
|
1400
|
+
this.gracefulKill(ratio, thresholds);
|
|
1401
|
+
}
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
this.currentInterval = this.WARNING_INTERVAL;
|
|
1405
|
+
this.warn(ratio, currentProvider);
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Track a process for memory management
|
|
1409
|
+
*
|
|
1410
|
+
* CRITICAL FIX: Ensure atomic updates to both data structures to prevent
|
|
1411
|
+
* inconsistent state between legacy array and new Map tracking systems.
|
|
1412
|
+
*/
|
|
1413
|
+
track(pid, provider) {
|
|
1414
|
+
if (!pid || pid <= 0) {
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
const trackingInfo = {
|
|
1418
|
+
pid,
|
|
1419
|
+
provider,
|
|
1420
|
+
startedAt: Date.now()
|
|
1421
|
+
};
|
|
1422
|
+
if (!this.pids.includes(pid)) {
|
|
1423
|
+
this.pids.push(pid);
|
|
1424
|
+
}
|
|
1425
|
+
this.trackedProcesses.set(pid, trackingInfo);
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Untrack a process
|
|
1429
|
+
*/
|
|
1430
|
+
untrack(pid) {
|
|
1431
|
+
const idx = this.pids.indexOf(pid);
|
|
1432
|
+
if (idx !== -1) {
|
|
1433
|
+
this.pids.splice(idx, 1);
|
|
1434
|
+
}
|
|
1435
|
+
this.trackedProcesses.delete(pid);
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Get most common provider from tracked processes
|
|
1439
|
+
*/
|
|
1440
|
+
getMostCommonProvider() {
|
|
1441
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1442
|
+
for (const tracking of this.trackedProcesses.values()) {
|
|
1443
|
+
if (tracking.provider) {
|
|
1444
|
+
providers.set(tracking.provider, (providers.get(tracking.provider) || 0) + 1);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
let maxProvider;
|
|
1448
|
+
let maxCount = 0;
|
|
1449
|
+
for (const [provider, count] of providers) {
|
|
1450
|
+
if (count > maxCount) {
|
|
1451
|
+
maxProvider = provider;
|
|
1452
|
+
maxCount = count;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return maxProvider;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Warn (minimal logging, max once per 10s)
|
|
1459
|
+
*/
|
|
1460
|
+
warn(ratio, provider) {
|
|
1461
|
+
const now = Date.now();
|
|
1462
|
+
if (now - this.lastWarnTime < 1e4) return;
|
|
1463
|
+
this.lastWarnTime = now;
|
|
1464
|
+
const pct = Math.floor(ratio * 100);
|
|
1465
|
+
const processCount = this.trackedProcesses.size || this.pids.length;
|
|
1466
|
+
emitWarning(
|
|
1467
|
+
"PERF-002",
|
|
1468
|
+
{
|
|
1469
|
+
subsystem: "memory-guardian",
|
|
1470
|
+
provider,
|
|
1471
|
+
executionPhase: "execution"
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
severity: pct >= 85 ? "WARN" /* WARN */ : "INFO" /* INFO */,
|
|
1475
|
+
metadata: {
|
|
1476
|
+
percentage: pct,
|
|
1477
|
+
processCount,
|
|
1478
|
+
killMode: this.killMode,
|
|
1479
|
+
threshold: "warn"
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
);
|
|
1483
|
+
if (this.killMode === "never") {
|
|
1484
|
+
emitWarning(
|
|
1485
|
+
"CFG-002",
|
|
1486
|
+
{
|
|
1487
|
+
subsystem: "memory-guardian",
|
|
1488
|
+
executionPhase: "execution"
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
severity: "INFO" /* INFO */,
|
|
1492
|
+
metadata: {
|
|
1493
|
+
setting: "BUILD_MEMORY_KILL",
|
|
1494
|
+
value: "0",
|
|
1495
|
+
message: "Log-only mode: Set BUILD_MEMORY_KILL=1 to enable process termination"
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
} else if (this.killMode === "hybrid") {
|
|
1500
|
+
const profile = this.getThresholds(provider);
|
|
1501
|
+
emitWarning(
|
|
1502
|
+
"PERF-002",
|
|
1503
|
+
{
|
|
1504
|
+
subsystem: "memory-guardian",
|
|
1505
|
+
provider,
|
|
1506
|
+
executionPhase: "execution"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
severity: "INFO" /* INFO */,
|
|
1510
|
+
metadata: {
|
|
1511
|
+
killThreshold: Math.floor(profile.crit * 100),
|
|
1512
|
+
currentUsage: pct,
|
|
1513
|
+
message: `Hybrid mode: Will kill processes at ${Math.floor(profile.crit * 100)}%+ (currently ${pct}%)`
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Graceful kill - newest processes first
|
|
1521
|
+
* Kill 50% of processes to free memory quickly
|
|
1522
|
+
* Respects kill mode: never (log only), hybrid (kill at critical%+), always (kill at all thresholds)
|
|
1523
|
+
*
|
|
1524
|
+
* CRITICAL FIX: Track active kill operations to prevent race conditions and ensure
|
|
1525
|
+
* proper state synchronization during concurrent kill operations.
|
|
1526
|
+
*/
|
|
1527
|
+
gracefulKill(ratio, _thresholds = this.getThresholds()) {
|
|
1528
|
+
const pct = Math.floor(ratio * 100);
|
|
1529
|
+
const operationId = `graceful-${Date.now()}-${randomBytes(8).toString("hex")}`;
|
|
1530
|
+
if (this.killMode === "never") {
|
|
1531
|
+
const allPids2 = this.getTerminationPids();
|
|
1532
|
+
emitWarning(
|
|
1533
|
+
"PERF-002",
|
|
1534
|
+
{
|
|
1535
|
+
subsystem: "memory-guardian",
|
|
1536
|
+
executionPhase: "execution"
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
severity: "ERROR" /* ERROR */,
|
|
1540
|
+
metadata: {
|
|
1541
|
+
percentage: pct,
|
|
1542
|
+
threshold: "critical",
|
|
1543
|
+
killMode: "never",
|
|
1544
|
+
wouldKillCount: Math.max(1, Math.floor(allPids2.length / 2)),
|
|
1545
|
+
trackedPids: allPids2,
|
|
1546
|
+
message: `CRITICAL: ${pct}% memory - would kill processes (log-only mode)`
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
emitWarning(
|
|
1551
|
+
"CFG-002",
|
|
1552
|
+
{
|
|
1553
|
+
subsystem: "memory-guardian",
|
|
1554
|
+
executionPhase: "execution"
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
severity: "ERROR" /* ERROR */,
|
|
1558
|
+
metadata: {
|
|
1559
|
+
setting: "BUILD_MEMORY_KILL",
|
|
1560
|
+
value: "0",
|
|
1561
|
+
message: "Set BUILD_MEMORY_KILL=1 or remove BUILD_MEMORY_KILL=0 to enable process termination"
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
this.activeKillOperations.add(operationId);
|
|
1568
|
+
this.isShuttingDown = true;
|
|
1569
|
+
const allPids = this.getTerminationPids();
|
|
1570
|
+
const killCount = Math.max(1, Math.floor(allPids.length / 2));
|
|
1571
|
+
emitWarning(
|
|
1572
|
+
"PERF-002",
|
|
1573
|
+
{
|
|
1574
|
+
subsystem: "memory-guardian",
|
|
1575
|
+
executionPhase: "execution"
|
|
1576
|
+
},
|
|
1577
|
+
{
|
|
1578
|
+
severity: "ERROR" /* ERROR */,
|
|
1579
|
+
metadata: {
|
|
1580
|
+
percentage: pct,
|
|
1581
|
+
threshold: "critical",
|
|
1582
|
+
killMode: this.killMode,
|
|
1583
|
+
killCount,
|
|
1584
|
+
totalProcesses: allPids.length,
|
|
1585
|
+
operationId,
|
|
1586
|
+
message: `CRITICAL: ${pct}% memory - killing newest processes (${this.killMode} mode)`
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
);
|
|
1590
|
+
const killedPids = [];
|
|
1591
|
+
for (let i = 0; i < killCount && allPids.length > 0; i++) {
|
|
1592
|
+
const pid = allPids.pop();
|
|
1593
|
+
try {
|
|
1594
|
+
process.kill(pid, "SIGTERM");
|
|
1595
|
+
killedPids.push(pid);
|
|
1596
|
+
} catch {
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
emitWarning(
|
|
1600
|
+
"PERF-002",
|
|
1601
|
+
{
|
|
1602
|
+
subsystem: "memory-guardian",
|
|
1603
|
+
executionPhase: "cleanup"
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
severity: "ERROR" /* ERROR */,
|
|
1607
|
+
metadata: {
|
|
1608
|
+
killedCount: killedPids.length,
|
|
1609
|
+
killedPids,
|
|
1610
|
+
operationId,
|
|
1611
|
+
message: `Killed ${killedPids.length} processes: ${killedPids.join(", ")}`
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
);
|
|
1615
|
+
setTimeout(() => {
|
|
1616
|
+
try {
|
|
1617
|
+
for (const pid of killedPids) {
|
|
1618
|
+
try {
|
|
1619
|
+
process.kill(pid, 0);
|
|
1620
|
+
try {
|
|
1621
|
+
process.kill(pid, "SIGKILL");
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1624
|
+
} catch {
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
} finally {
|
|
1628
|
+
this.activeKillOperations.delete(operationId);
|
|
1629
|
+
if (this.activeKillOperations.size === 0) {
|
|
1630
|
+
this.isShuttingDown = false;
|
|
1631
|
+
}
|
|
1632
|
+
emitWarning(
|
|
1633
|
+
"PERF-002",
|
|
1634
|
+
{
|
|
1635
|
+
subsystem: "memory-guardian",
|
|
1636
|
+
executionPhase: "cleanup"
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
severity: "INFO" /* INFO */,
|
|
1640
|
+
metadata: {
|
|
1641
|
+
operationId,
|
|
1642
|
+
message: "Graceful kill operation completed"
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
}, 3e3);
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Emergency kill - all processes immediately
|
|
1651
|
+
* Last resort at emergency% heap usage
|
|
1652
|
+
* Respects kill mode: never (log only), hybrid/always (kill all processes)
|
|
1653
|
+
*
|
|
1654
|
+
* CRITICAL FIX: Add proper error handling and cleanup to prevent
|
|
1655
|
+
* inconsistent state during emergency termination.
|
|
1656
|
+
*/
|
|
1657
|
+
emergencyKill(ratio, _thresholds = this.getThresholds()) {
|
|
1658
|
+
const pct = Math.floor(ratio * 100);
|
|
1659
|
+
const operationId = `emergency-${Date.now()}-${randomBytes(8).toString("hex")}`;
|
|
1660
|
+
this.activeKillOperations.add(operationId);
|
|
1661
|
+
try {
|
|
1662
|
+
if (this.killMode === "never") {
|
|
1663
|
+
const allPids2 = this.getTerminationPids();
|
|
1664
|
+
emitWarning(
|
|
1665
|
+
"PERF-002",
|
|
1666
|
+
{
|
|
1667
|
+
subsystem: "memory-guardian",
|
|
1668
|
+
executionPhase: "execution"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
severity: "ERROR" /* ERROR */,
|
|
1672
|
+
metadata: {
|
|
1673
|
+
percentage: pct,
|
|
1674
|
+
threshold: "emergency",
|
|
1675
|
+
killMode: "never",
|
|
1676
|
+
wouldKillCount: allPids2.length,
|
|
1677
|
+
trackedPids: allPids2,
|
|
1678
|
+
operationId,
|
|
1679
|
+
message: `EMERGENCY: ${pct}% memory - would kill ALL processes (log-only mode)`
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
);
|
|
1683
|
+
emitWarning(
|
|
1684
|
+
"CFG-002",
|
|
1685
|
+
{
|
|
1686
|
+
subsystem: "memory-guardian",
|
|
1687
|
+
executionPhase: "execution"
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
severity: "ERROR" /* ERROR */,
|
|
1691
|
+
metadata: {
|
|
1692
|
+
setting: "BUILD_MEMORY_KILL",
|
|
1693
|
+
value: "0",
|
|
1694
|
+
criticalWarning: "SYSTEM AT RISK OF OOM CRASH - Manual intervention strongly recommended",
|
|
1695
|
+
message: "Set BUILD_MEMORY_KILL=1 or remove BUILD_MEMORY_KILL=0 to enable process termination"
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
);
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
const limiterOwned = this.getTrackedPids().length === 0;
|
|
1702
|
+
const allPids = this.getTerminationPids();
|
|
1703
|
+
if (allPids.length === 0) {
|
|
1704
|
+
emitWarning(
|
|
1705
|
+
"PERF-002",
|
|
1706
|
+
{
|
|
1707
|
+
subsystem: "memory-guardian",
|
|
1708
|
+
executionPhase: "execution"
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
severity: "ERROR" /* ERROR */,
|
|
1712
|
+
metadata: {
|
|
1713
|
+
percentage: pct,
|
|
1714
|
+
threshold: "emergency",
|
|
1715
|
+
processCount: 0,
|
|
1716
|
+
operationId,
|
|
1717
|
+
action: "skipping forced exit to prevent data loss",
|
|
1718
|
+
message: `EMERGENCY: ${pct}% memory - no tracked child processes`
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
);
|
|
1722
|
+
this.currentInterval = this.CRITICAL_INTERVAL;
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
emitWarning(
|
|
1726
|
+
"PERF-002",
|
|
1727
|
+
{
|
|
1728
|
+
subsystem: "memory-guardian",
|
|
1729
|
+
executionPhase: "execution"
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
severity: "ERROR" /* ERROR */,
|
|
1733
|
+
metadata: {
|
|
1734
|
+
percentage: pct,
|
|
1735
|
+
threshold: "emergency",
|
|
1736
|
+
killMode: this.killMode,
|
|
1737
|
+
killCount: allPids.length,
|
|
1738
|
+
operationId,
|
|
1739
|
+
message: `EMERGENCY: ${pct}% memory - killing ALL ${allPids.length} processes (${this.killMode} mode)`
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
const killedCount = allPids.length;
|
|
1744
|
+
const successfulKills = [];
|
|
1745
|
+
const failedKills = [];
|
|
1746
|
+
for (const pid of allPids) {
|
|
1747
|
+
try {
|
|
1748
|
+
if (limiterOwned && process.platform !== "win32") {
|
|
1749
|
+
try {
|
|
1750
|
+
signalProcessGroup(pid, "SIGKILL");
|
|
1751
|
+
} catch {
|
|
1752
|
+
process.kill(pid, "SIGKILL");
|
|
1753
|
+
}
|
|
1754
|
+
} else {
|
|
1755
|
+
process.kill(pid, "SIGKILL");
|
|
1756
|
+
}
|
|
1757
|
+
successfulKills.push(pid);
|
|
1758
|
+
} catch (error) {
|
|
1759
|
+
failedKills.push({ pid, error });
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
emitWarning(
|
|
1763
|
+
"PERF-002",
|
|
1764
|
+
{
|
|
1765
|
+
subsystem: "memory-guardian",
|
|
1766
|
+
executionPhase: "cleanup"
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
severity: "ERROR" /* ERROR */,
|
|
1770
|
+
metadata: {
|
|
1771
|
+
successfulKills: successfulKills.length,
|
|
1772
|
+
totalAttempted: killedCount,
|
|
1773
|
+
killedPids: successfulKills,
|
|
1774
|
+
operationId,
|
|
1775
|
+
message: `Force-killed ${successfulKills.length}/${killedCount} processes to prevent crash`
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
);
|
|
1779
|
+
if (failedKills.length > 0) {
|
|
1780
|
+
emitWarning(
|
|
1781
|
+
"PERF-002",
|
|
1782
|
+
{
|
|
1783
|
+
subsystem: "memory-guardian",
|
|
1784
|
+
executionPhase: "cleanup"
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
severity: "ERROR" /* ERROR */,
|
|
1788
|
+
metadata: {
|
|
1789
|
+
failedCount: failedKills.length,
|
|
1790
|
+
failedPids: failedKills.map((f) => f.pid),
|
|
1791
|
+
operationId,
|
|
1792
|
+
message: `Failed to kill ${failedKills.length} processes: ${failedKills.map((f) => f.pid).join(", ")}`
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
this.pids.length = 0;
|
|
1798
|
+
this.trackedProcesses.clear();
|
|
1799
|
+
this.stop();
|
|
1800
|
+
const handler = this.emergencyHandler;
|
|
1801
|
+
if (handler) {
|
|
1802
|
+
try {
|
|
1803
|
+
handler({ pct, killedCount: successfulKills.length, pids: successfulKills });
|
|
1804
|
+
} catch (handlerError) {
|
|
1805
|
+
emitWarning(
|
|
1806
|
+
"PERF-002",
|
|
1807
|
+
{
|
|
1808
|
+
subsystem: "memory-guardian",
|
|
1809
|
+
executionPhase: "cleanup"
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
severity: "ERROR" /* ERROR */,
|
|
1813
|
+
metadata: {
|
|
1814
|
+
error: String(handlerError),
|
|
1815
|
+
operationId,
|
|
1816
|
+
message: `Emergency handler failed: ${handlerError}`
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
);
|
|
1820
|
+
requestProcessExit(1);
|
|
1821
|
+
}
|
|
1822
|
+
} else {
|
|
1823
|
+
requestProcessExit(1);
|
|
1824
|
+
}
|
|
1825
|
+
} finally {
|
|
1826
|
+
this.activeKillOperations.delete(operationId);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Get current memory status (for debugging)
|
|
1831
|
+
*/
|
|
1832
|
+
getStatus() {
|
|
1833
|
+
const usage = process.memoryUsage();
|
|
1834
|
+
const { heap_size_limit } = getHeapStatistics();
|
|
1835
|
+
return {
|
|
1836
|
+
heapUsedMB: Math.round(usage.heapUsed / 1024 / 1024),
|
|
1837
|
+
heapTotalMB: Math.round(heap_size_limit / 1024 / 1024),
|
|
1838
|
+
heapUsedPercent: Math.round(usage.heapUsed / heap_size_limit * 100),
|
|
1839
|
+
processCount: Math.max(this.trackedProcesses.size, this.pids.length),
|
|
1840
|
+
interval: this.currentInterval,
|
|
1841
|
+
mostCommonProvider: this.getMostCommonProvider()
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Get tracked PIDs (for testing)
|
|
1846
|
+
*/
|
|
1847
|
+
getTrackedPids() {
|
|
1848
|
+
return [
|
|
1849
|
+
...this.trackedProcesses.keys(),
|
|
1850
|
+
...this.pids.filter((p) => !this.trackedProcesses.has(p))
|
|
1851
|
+
];
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* One ownership policy for both termination stages. Preserve direct tracking
|
|
1855
|
+
* precedence; competition parents fall back to the workers they registered
|
|
1856
|
+
* with the process limiter. Return oldest first for newest-first shedding.
|
|
1857
|
+
*/
|
|
1858
|
+
getTerminationPids() {
|
|
1859
|
+
const direct = this.getTrackedPids();
|
|
1860
|
+
if (direct.length > 0) return direct;
|
|
1861
|
+
return globalProcessLimiter.getTrackedProcesses().filter(
|
|
1862
|
+
(tracked) => tracked.pid !== void 0 && Number.isInteger(tracked.pid) && tracked.pid > 0
|
|
1863
|
+
).sort((a, b) => b.ageMs - a.ageMs).map((tracked) => tracked.pid);
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Check if log-only mode is enabled
|
|
1867
|
+
*/
|
|
1868
|
+
isLogOnly() {
|
|
1869
|
+
return this.killMode === "never";
|
|
1870
|
+
}
|
|
1871
|
+
/**
|
|
1872
|
+
* Get current kill mode
|
|
1873
|
+
*/
|
|
1874
|
+
getKillModeStatus() {
|
|
1875
|
+
return this.killMode;
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Register an emergency handler. If set, the guardian will call this instead of exiting.
|
|
1879
|
+
*/
|
|
1880
|
+
setEmergencyHandler(handler) {
|
|
1881
|
+
this.emergencyHandler = handler;
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Reset instance (for testing only)
|
|
1885
|
+
*/
|
|
1886
|
+
static resetForTesting() {
|
|
1887
|
+
if (_MemoryGuardian.instance) {
|
|
1888
|
+
if (_MemoryGuardian.instance.exitHandlerRegistered) {
|
|
1889
|
+
process.off("exit", _MemoryGuardian.instance.handleExit);
|
|
1890
|
+
_MemoryGuardian.instance.exitHandlerRegistered = false;
|
|
1891
|
+
}
|
|
1892
|
+
_MemoryGuardian.instance.stop();
|
|
1893
|
+
_MemoryGuardian.instance.pids.length = 0;
|
|
1894
|
+
_MemoryGuardian.instance.trackedProcesses.clear();
|
|
1895
|
+
_MemoryGuardian.instance.isShuttingDown = false;
|
|
1896
|
+
_MemoryGuardian.instance.lastCheckTime = 0;
|
|
1897
|
+
_MemoryGuardian.instance.lastWarnTime = 0;
|
|
1898
|
+
_MemoryGuardian.instance.currentInterval = 1e4;
|
|
1899
|
+
}
|
|
1900
|
+
_MemoryGuardian.instance = void 0;
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
// src/core/providers/shared/subprocess-tracking.ts
|
|
1905
|
+
var logger = createLogger("ProviderRunner");
|
|
1906
|
+
function trackSubprocess(subprocess) {
|
|
1907
|
+
if (!subprocess) {
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
const guardian = MemoryGuardian.getInstance();
|
|
1911
|
+
const trackedPid = typeof subprocess.pid === "number" && subprocess.pid > 0 ? subprocess.pid : null;
|
|
1912
|
+
if (trackedPid) {
|
|
1913
|
+
guardian.track(trackedPid);
|
|
1914
|
+
}
|
|
1915
|
+
return trackedPid;
|
|
1916
|
+
}
|
|
1917
|
+
var subprocessFallbackTracking = /* @__PURE__ */ new WeakMap();
|
|
1918
|
+
var fallbackSubprocesses = /* @__PURE__ */ new Map();
|
|
1919
|
+
var fallbackCleanupRegistered = false;
|
|
1920
|
+
function getFallbackTrackedSubprocesses() {
|
|
1921
|
+
return Array.from(fallbackSubprocesses.values());
|
|
1922
|
+
}
|
|
1923
|
+
function killAllFallbackTrackedSubprocesses(signal = "SIGTERM") {
|
|
1924
|
+
let killed = 0;
|
|
1925
|
+
for (const subprocess of getFallbackTrackedSubprocesses()) {
|
|
1926
|
+
if (killFallbackSubprocess(subprocess, signal)) {
|
|
1927
|
+
killed += 1;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
return killed;
|
|
1931
|
+
}
|
|
1932
|
+
var fallbackExitHandler;
|
|
1933
|
+
function registerFallbackCleanup() {
|
|
1934
|
+
if (fallbackCleanupRegistered) {
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
fallbackCleanupRegistered = true;
|
|
1938
|
+
fallbackExitHandler = () => {
|
|
1939
|
+
killAllFallbackTrackedSubprocesses("SIGTERM");
|
|
1940
|
+
killAllFallbackTrackedSubprocesses("SIGKILL");
|
|
1941
|
+
};
|
|
1942
|
+
process.removeListener("exit", fallbackExitHandler);
|
|
1943
|
+
process.on("exit", fallbackExitHandler);
|
|
1944
|
+
}
|
|
1945
|
+
function killFallbackSubprocess(subprocess, signal = "SIGTERM") {
|
|
1946
|
+
try {
|
|
1947
|
+
return sendSignalToSubprocessTree(subprocess, signal);
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
logger.warn(`[BaseRunner] Failed to kill fallback subprocess: ${error}`);
|
|
1950
|
+
return false;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
async function trackSubprocessAsync(subprocess) {
|
|
1954
|
+
if (!subprocess) {
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
const guardian = MemoryGuardian.getInstance();
|
|
1958
|
+
let trackedPid = null;
|
|
1959
|
+
const maxPidWaitMs = Math.max(250, readEnv(BUILD_SUBPROCESS_PID_WAIT_MS).value ?? 2e3);
|
|
1960
|
+
await new Promise((resolve) => {
|
|
1961
|
+
let settled = false;
|
|
1962
|
+
let pollInterval;
|
|
1963
|
+
const finish = () => {
|
|
1964
|
+
if (settled) return;
|
|
1965
|
+
settled = true;
|
|
1966
|
+
if (pollInterval) {
|
|
1967
|
+
clearInterval(pollInterval);
|
|
1968
|
+
}
|
|
1969
|
+
resolve();
|
|
1970
|
+
};
|
|
1971
|
+
const exitListener = () => {
|
|
1972
|
+
finish();
|
|
1973
|
+
};
|
|
1974
|
+
subprocess.once?.("exit", exitListener);
|
|
1975
|
+
subprocess.once?.("close", exitListener);
|
|
1976
|
+
if (typeof subprocess.pid === "number" && subprocess.pid > 0) {
|
|
1977
|
+
trackedPid = subprocess.pid;
|
|
1978
|
+
guardian.track(subprocess.pid);
|
|
1979
|
+
finish();
|
|
1980
|
+
} else {
|
|
1981
|
+
const PID_POLL_INTERVAL_MS = 25;
|
|
1982
|
+
const startTime = Date.now();
|
|
1983
|
+
pollInterval = setInterval(() => {
|
|
1984
|
+
if (typeof subprocess.pid === "number" && subprocess.pid > 0) {
|
|
1985
|
+
trackedPid = subprocess.pid;
|
|
1986
|
+
guardian.track(subprocess.pid);
|
|
1987
|
+
finish();
|
|
1988
|
+
} else if (Date.now() - startTime >= maxPidWaitMs) {
|
|
1989
|
+
if (!subprocess.killed && (subprocess.exitCode ?? null) === null) {
|
|
1990
|
+
const subprocessId = /* @__PURE__ */ Symbol("subprocess-fallback");
|
|
1991
|
+
subprocessFallbackTracking.set(subprocess, subprocessId);
|
|
1992
|
+
fallbackSubprocesses.set(subprocessId, subprocess);
|
|
1993
|
+
registerFallbackCleanup();
|
|
1994
|
+
logger.warn(
|
|
1995
|
+
`[BaseRunner] PID not available after ${maxPidWaitMs}ms - using fallback tracking by subprocess reference`,
|
|
1996
|
+
{ maxWaitMs: maxPidWaitMs }
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
finish();
|
|
2000
|
+
}
|
|
2001
|
+
}, PID_POLL_INTERVAL_MS);
|
|
2002
|
+
pollInterval.unref?.();
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
return trackedPid;
|
|
2006
|
+
}
|
|
2007
|
+
function untrackSubprocess(trackedPid, subprocess) {
|
|
2008
|
+
if (trackedPid) {
|
|
2009
|
+
const guardian = MemoryGuardian.getInstance();
|
|
2010
|
+
guardian.untrack(trackedPid);
|
|
2011
|
+
} else if (subprocess) {
|
|
2012
|
+
const subprocessId = subprocessFallbackTracking.get(subprocess);
|
|
2013
|
+
if (subprocessId) {
|
|
2014
|
+
fallbackSubprocesses.delete(subprocessId);
|
|
2015
|
+
subprocessFallbackTracking.delete(subprocess);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// src/core/providers/shared/base-runner.ts
|
|
2021
|
+
var logger2 = createLogger("ProviderRunner");
|
|
2022
|
+
var PROVIDER_TERMINATION_GRACE_MS = 2e3;
|
|
2023
|
+
var PROVIDER_ARGV_PAYLOAD_REDACTION = "[argv-redacted]";
|
|
2024
|
+
var PROVIDER_SINGLE_ARG_MAX_SAFE_BYTES = 128 * 1024;
|
|
2025
|
+
var PROVIDER_PROCESS_ARG_ENV_MAX_SAFE_BYTES = 256 * 1024;
|
|
2026
|
+
function computePromptFingerprint(prompt) {
|
|
2027
|
+
return createHash("sha256").update(prompt).digest("hex");
|
|
2028
|
+
}
|
|
2029
|
+
var DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_MS;
|
|
2030
|
+
var DEFAULT_STREAM_CAPTURE_BYTES = 1024 * 1024;
|
|
2031
|
+
function throwIfAbortAlreadyRequested(abortSignal, providerName) {
|
|
2032
|
+
if (!abortSignal?.aborted) {
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
const hostTimeout = isHostTimeoutAbortReason(abortSignal.reason);
|
|
2036
|
+
const detail = hostTimeout ? formatHostTimeoutTerminationDetail(abortSignal.reason) : void 0;
|
|
2037
|
+
throw new ProviderExecutionError(
|
|
2038
|
+
`[${providerName}] Provider execution aborted before subprocess start${detail ? ` (${detail})` : ""}`,
|
|
2039
|
+
{
|
|
2040
|
+
retryable: false,
|
|
2041
|
+
reason: hostTimeout ? "timeout" : "abort",
|
|
2042
|
+
terminationSource: "abortSignal",
|
|
2043
|
+
providerName
|
|
2044
|
+
}
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
var BoundedOutputBuffer = class {
|
|
2048
|
+
chunks = [];
|
|
2049
|
+
bytes = 0;
|
|
2050
|
+
append(chunk, maxBytes) {
|
|
2051
|
+
if (!chunk) {
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
if (!Number.isFinite(maxBytes) || maxBytes <= 0) {
|
|
2055
|
+
this.clear();
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
const boundedMaxBytes = Math.floor(maxBytes);
|
|
2059
|
+
if (boundedMaxBytes <= 0) {
|
|
2060
|
+
this.clear();
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const nextChunk = chunk;
|
|
2064
|
+
const nextChunkBytes = Buffer.byteLength(nextChunk, "utf8");
|
|
2065
|
+
if (nextChunkBytes >= boundedMaxBytes) {
|
|
2066
|
+
const tail = trimUtf8Tail(nextChunk, boundedMaxBytes);
|
|
2067
|
+
this.chunks.length = 0;
|
|
2068
|
+
this.bytes = 0;
|
|
2069
|
+
if (tail.text) {
|
|
2070
|
+
this.chunks.push(tail.text);
|
|
2071
|
+
this.bytes = tail.bytes;
|
|
2072
|
+
}
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
this.chunks.push(nextChunk);
|
|
2076
|
+
this.bytes += nextChunkBytes;
|
|
2077
|
+
while (this.bytes > boundedMaxBytes && this.chunks.length > 0) {
|
|
2078
|
+
const overflowBytes = this.bytes - boundedMaxBytes;
|
|
2079
|
+
const first = this.chunks[0] ?? "";
|
|
2080
|
+
const firstBytes = Buffer.byteLength(first, "utf8");
|
|
2081
|
+
if (firstBytes <= overflowBytes) {
|
|
2082
|
+
this.chunks.shift();
|
|
2083
|
+
this.bytes -= firstBytes;
|
|
2084
|
+
continue;
|
|
2085
|
+
}
|
|
2086
|
+
const trimmed = trimUtf8Tail(first, firstBytes - overflowBytes);
|
|
2087
|
+
if (trimmed.text) {
|
|
2088
|
+
this.chunks[0] = trimmed.text;
|
|
2089
|
+
this.bytes = this.bytes - firstBytes + trimmed.bytes;
|
|
2090
|
+
} else {
|
|
2091
|
+
this.chunks.shift();
|
|
2092
|
+
this.bytes -= firstBytes;
|
|
2093
|
+
}
|
|
2094
|
+
break;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
toString() {
|
|
2098
|
+
return this.chunks.join("");
|
|
2099
|
+
}
|
|
2100
|
+
clear() {
|
|
2101
|
+
this.chunks.length = 0;
|
|
2102
|
+
this.bytes = 0;
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
function trimUtf8Tail(text, maxBytes) {
|
|
2106
|
+
if (!Number.isFinite(maxBytes) || maxBytes <= 0) {
|
|
2107
|
+
return { text: "", bytes: 0 };
|
|
2108
|
+
}
|
|
2109
|
+
const buffer = Buffer.from(text, "utf8");
|
|
2110
|
+
if (buffer.length <= maxBytes) {
|
|
2111
|
+
return { text, bytes: buffer.length };
|
|
2112
|
+
}
|
|
2113
|
+
let start = buffer.length - Math.floor(maxBytes);
|
|
2114
|
+
while (start < buffer.length && ((buffer[start] ?? 0) & 192) === 128) {
|
|
2115
|
+
start += 1;
|
|
2116
|
+
}
|
|
2117
|
+
const tail = buffer.subarray(start);
|
|
2118
|
+
return { text: tail.toString("utf8"), bytes: tail.length };
|
|
2119
|
+
}
|
|
2120
|
+
function sanitizeExecutionEnv(env, options) {
|
|
2121
|
+
if (!env) {
|
|
2122
|
+
if (isDebugMode()) {
|
|
2123
|
+
console.warn("[BaseRunner] executeProviderCommand fallbacked to marker-stripped process.env");
|
|
2124
|
+
}
|
|
2125
|
+
return createCleanChildEnv();
|
|
2126
|
+
}
|
|
2127
|
+
const sanitized = stripClaudeSessionEnv(env);
|
|
2128
|
+
for (const key of Object.keys(sanitized)) {
|
|
2129
|
+
if (isSandboxAuthorityEnvVar(key)) {
|
|
2130
|
+
delete sanitized[key];
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
if (options?.preserveClaudeBackendFlags === true) {
|
|
2134
|
+
if (env.CLAUDE_CODE_USE_BEDROCK !== void 0) {
|
|
2135
|
+
sanitized.CLAUDE_CODE_USE_BEDROCK = env.CLAUDE_CODE_USE_BEDROCK;
|
|
2136
|
+
}
|
|
2137
|
+
if (env.CLAUDE_CODE_USE_VERTEX !== void 0) {
|
|
2138
|
+
sanitized.CLAUDE_CODE_USE_VERTEX = env.CLAUDE_CODE_USE_VERTEX;
|
|
2139
|
+
}
|
|
2140
|
+
if (env.CLAUDE_CODE_USE_FOUNDRY !== void 0) {
|
|
2141
|
+
sanitized.CLAUDE_CODE_USE_FOUNDRY = env.CLAUDE_CODE_USE_FOUNDRY;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
return sanitized;
|
|
2145
|
+
}
|
|
2146
|
+
function resolveCommandName(command) {
|
|
2147
|
+
const posix = path2.posix.basename(command);
|
|
2148
|
+
const win32 = path2.win32.basename(command);
|
|
2149
|
+
const basename = posix.length <= win32.length ? posix : win32;
|
|
2150
|
+
return basename.replace(/\.(exe|cmd|bat|ps1)$/i, "").toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
2151
|
+
}
|
|
2152
|
+
function redactProviderArgvForRunnerDiagnostics(args) {
|
|
2153
|
+
return redactProviderArgv(args).map((arg) => {
|
|
2154
|
+
if (arg === "[REDACTED]") return arg;
|
|
2155
|
+
if (/[\r\n]/.test(arg)) return PROVIDER_ARGV_PAYLOAD_REDACTION;
|
|
2156
|
+
if (/\s/.test(arg)) return PROVIDER_ARGV_PAYLOAD_REDACTION;
|
|
2157
|
+
if (arg.length > 200) return PROVIDER_ARGV_PAYLOAD_REDACTION;
|
|
2158
|
+
return arg;
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
function formatRedactedCommandSignature(command, args) {
|
|
2162
|
+
const [redactedCommand = command] = redactProviderArgv([command]);
|
|
2163
|
+
return [redactedCommand, ...redactProviderArgvForRunnerDiagnostics(args)].join(" ").trim();
|
|
2164
|
+
}
|
|
2165
|
+
function logPlmVerificationFailure(details) {
|
|
2166
|
+
logger2.warn(`[BaseRunner] PLM verification failed for ${details.provider}`, {
|
|
2167
|
+
provider: details.provider,
|
|
2168
|
+
command: details.command,
|
|
2169
|
+
argCount: details.args.length,
|
|
2170
|
+
exitCode: details.code,
|
|
2171
|
+
signal: details.signal,
|
|
2172
|
+
hasExited: details.hasExited,
|
|
2173
|
+
stderr: sanitizeDiagnosticOutput(details.stderr, 2e3)
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
function hasPlmExitEvidence(details) {
|
|
2177
|
+
return details.hasExited || details.code !== null || details.signal !== null;
|
|
2178
|
+
}
|
|
2179
|
+
var TERMINATION_PRIORITY = {
|
|
2180
|
+
abort: 100,
|
|
2181
|
+
hard_timeout: 90,
|
|
2182
|
+
timeout: 80,
|
|
2183
|
+
plm_timeout: 70,
|
|
2184
|
+
plm_abort: 60,
|
|
2185
|
+
plm_cancelled: 50,
|
|
2186
|
+
idle_timeout: 40,
|
|
2187
|
+
unknown: 0
|
|
2188
|
+
};
|
|
2189
|
+
function createProviderTerminationController(params) {
|
|
2190
|
+
const { subprocess, startTime, timeout, providerName, command } = params;
|
|
2191
|
+
const processState = {
|
|
2192
|
+
hasExited: false,
|
|
2193
|
+
exitCode: null,
|
|
2194
|
+
exitSignal: null,
|
|
2195
|
+
exitAt: null,
|
|
2196
|
+
sigtermSentAt: null,
|
|
2197
|
+
sigkillSentAt: null
|
|
2198
|
+
};
|
|
2199
|
+
const terminationState = {
|
|
2200
|
+
attempts: [],
|
|
2201
|
+
getPrimaryRecord() {
|
|
2202
|
+
const activeAttempts = this.attempts.filter((attempt) => attempt.requestedAfterExit !== true);
|
|
2203
|
+
if (activeAttempts.length === 0) return null;
|
|
2204
|
+
const priorityFor = (reason) => TERMINATION_PRIORITY[reason] ?? 0;
|
|
2205
|
+
return activeAttempts.reduce((best, current) => {
|
|
2206
|
+
const bestPriority = priorityFor(best.reason);
|
|
2207
|
+
const currentPriority = priorityFor(current.reason);
|
|
2208
|
+
return currentPriority > bestPriority ? current : best;
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
let hardTimeoutTimer;
|
|
2213
|
+
let terminationForceKillTimer;
|
|
2214
|
+
let stdioClosed = false;
|
|
2215
|
+
const exitWaiters = /* @__PURE__ */ new Set();
|
|
2216
|
+
const clearTerminationTimer = () => {
|
|
2217
|
+
if (hardTimeoutTimer) {
|
|
2218
|
+
clearTimeout(hardTimeoutTimer);
|
|
2219
|
+
hardTimeoutTimer = void 0;
|
|
2220
|
+
}
|
|
2221
|
+
if (terminationForceKillTimer) {
|
|
2222
|
+
clearTimeout(terminationForceKillTimer);
|
|
2223
|
+
terminationForceKillTimer = void 0;
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
const markExited = (code, signal) => {
|
|
2227
|
+
if (processState.hasExited) return;
|
|
2228
|
+
processState.hasExited = true;
|
|
2229
|
+
processState.exitCode = code;
|
|
2230
|
+
processState.exitSignal = signal;
|
|
2231
|
+
processState.exitAt = Date.now();
|
|
2232
|
+
for (const resolve of exitWaiters) {
|
|
2233
|
+
resolve();
|
|
2234
|
+
}
|
|
2235
|
+
exitWaiters.clear();
|
|
2236
|
+
};
|
|
2237
|
+
const waitForExitOrTimeout = async (timeoutMs) => {
|
|
2238
|
+
if (processState.hasExited || timeoutMs <= 0) return;
|
|
2239
|
+
await new Promise((resolve) => {
|
|
2240
|
+
let timer;
|
|
2241
|
+
const finish = () => {
|
|
2242
|
+
if (timer) {
|
|
2243
|
+
clearTimeout(timer);
|
|
2244
|
+
}
|
|
2245
|
+
exitWaiters.delete(finish);
|
|
2246
|
+
resolve();
|
|
2247
|
+
};
|
|
2248
|
+
exitWaiters.add(finish);
|
|
2249
|
+
timer = setTimeout(finish, timeoutMs);
|
|
2250
|
+
timer.unref?.();
|
|
2251
|
+
});
|
|
2252
|
+
};
|
|
2253
|
+
subprocess.on("exit", (code, signal) => {
|
|
2254
|
+
markExited(code, signal ?? null);
|
|
2255
|
+
});
|
|
2256
|
+
subprocess.on("close", (code, signal) => {
|
|
2257
|
+
markExited(code, signal ?? null);
|
|
2258
|
+
stdioClosed = true;
|
|
2259
|
+
clearTerminationTimer();
|
|
2260
|
+
});
|
|
2261
|
+
const requestTermination = (reason, source, detail, boundaryEvent) => {
|
|
2262
|
+
const record = {
|
|
2263
|
+
reason,
|
|
2264
|
+
source,
|
|
2265
|
+
detail,
|
|
2266
|
+
requestedAt: Date.now(),
|
|
2267
|
+
requestedAfterExit: processState.hasExited
|
|
2268
|
+
};
|
|
2269
|
+
terminationState.attempts.push(record);
|
|
2270
|
+
if (processState.hasExited) {
|
|
2271
|
+
if (!stdioClosed && processState.sigkillSentAt === null) {
|
|
2272
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGKILL")) {
|
|
2273
|
+
processState.sigkillSentAt = Date.now();
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
return true;
|
|
2277
|
+
}
|
|
2278
|
+
const isFirstRequest = terminationState.attempts.length === 1;
|
|
2279
|
+
if (!isFirstRequest) {
|
|
2280
|
+
return false;
|
|
2281
|
+
}
|
|
2282
|
+
if (reason === "idle_timeout") {
|
|
2283
|
+
emitBoundaryTriggered({
|
|
2284
|
+
trigger: "idle",
|
|
2285
|
+
workloadClass: "agent-run",
|
|
2286
|
+
effectiveCeilingMs: timeout,
|
|
2287
|
+
elapsedMs: Math.max(0, Date.now() - startTime),
|
|
2288
|
+
source: "base-runner",
|
|
2289
|
+
idleDurationMs: boundaryEvent?.idleDurationMs,
|
|
2290
|
+
metadata: {
|
|
2291
|
+
provider: providerName,
|
|
2292
|
+
command
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
} else if (reason === "hard_timeout" || reason === "timeout" || reason === "plm_timeout") {
|
|
2296
|
+
emitBoundaryTriggered({
|
|
2297
|
+
trigger: "ceiling",
|
|
2298
|
+
workloadClass: "agent-run",
|
|
2299
|
+
effectiveCeilingMs: timeout,
|
|
2300
|
+
elapsedMs: Math.max(0, Date.now() - startTime),
|
|
2301
|
+
source: "base-runner",
|
|
2302
|
+
metadata: {
|
|
2303
|
+
provider: providerName,
|
|
2304
|
+
command
|
|
2305
|
+
}
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
if (processState.sigtermSentAt === null) {
|
|
2309
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGTERM")) {
|
|
2310
|
+
processState.sigtermSentAt = Date.now();
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
terminationForceKillTimer = setTimeout(() => {
|
|
2314
|
+
if (stdioClosed) return;
|
|
2315
|
+
if (processState.sigkillSentAt !== null) return;
|
|
2316
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGKILL")) {
|
|
2317
|
+
processState.sigkillSentAt = Date.now();
|
|
2318
|
+
}
|
|
2319
|
+
}, PROVIDER_TERMINATION_GRACE_MS);
|
|
2320
|
+
terminationForceKillTimer.unref?.();
|
|
2321
|
+
return true;
|
|
2322
|
+
};
|
|
2323
|
+
if (Number.isFinite(timeout) && timeout > 0) {
|
|
2324
|
+
hardTimeoutTimer = setTimeout(() => {
|
|
2325
|
+
requestTermination(
|
|
2326
|
+
"timeout",
|
|
2327
|
+
"base-runner:hard-timeout",
|
|
2328
|
+
`timeout after ${Math.round(timeout / 1e3)}s`
|
|
2329
|
+
);
|
|
2330
|
+
}, timeout);
|
|
2331
|
+
hardTimeoutTimer.unref?.();
|
|
2332
|
+
}
|
|
2333
|
+
return {
|
|
2334
|
+
processState,
|
|
2335
|
+
terminationState,
|
|
2336
|
+
requestTermination,
|
|
2337
|
+
markExited,
|
|
2338
|
+
clearTerminationTimer,
|
|
2339
|
+
waitForExitOrTimeout
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
async function drainProviderTerminationGrace(terminationController) {
|
|
2343
|
+
const { processState } = terminationController;
|
|
2344
|
+
if (terminationController.terminationState.getPrimaryRecord() === null) return;
|
|
2345
|
+
if (processState.hasExited) return;
|
|
2346
|
+
if (processState.sigtermSentAt === null) return;
|
|
2347
|
+
if (processState.sigkillSentAt !== null) return;
|
|
2348
|
+
const elapsedMs = Date.now() - processState.sigtermSentAt;
|
|
2349
|
+
const remainingMs = Math.max(0, PROVIDER_TERMINATION_GRACE_MS - elapsedMs);
|
|
2350
|
+
if (remainingMs > 0) {
|
|
2351
|
+
await terminationController.waitForExitOrTimeout(remainingMs);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
function trackProviderSubprocess(subprocess, mode) {
|
|
2355
|
+
return mode === "async" ? trackSubprocessAsync(subprocess) : trackSubprocess(subprocess);
|
|
2356
|
+
}
|
|
2357
|
+
function registerProviderProcessLimiter(params) {
|
|
2358
|
+
const {
|
|
2359
|
+
subprocess,
|
|
2360
|
+
limiterDescription,
|
|
2361
|
+
timeout,
|
|
2362
|
+
reservation,
|
|
2363
|
+
requestTermination,
|
|
2364
|
+
trackedPid,
|
|
2365
|
+
onRegistrationFailure
|
|
2366
|
+
} = params;
|
|
2367
|
+
try {
|
|
2368
|
+
return globalProcessLimiter.track(
|
|
2369
|
+
subprocess,
|
|
2370
|
+
limiterDescription,
|
|
2371
|
+
timeout,
|
|
2372
|
+
(request) => {
|
|
2373
|
+
const detail = `timeout after ${Math.round(request.timeoutMs / 1e3)}s`;
|
|
2374
|
+
requestTermination(request.reason, request.source, detail);
|
|
2375
|
+
},
|
|
2376
|
+
reservation
|
|
2377
|
+
);
|
|
2378
|
+
} catch (err) {
|
|
2379
|
+
sendSignalToSubprocessTree(subprocess, "SIGKILL");
|
|
2380
|
+
untrackSubprocess(trackedPid, subprocess);
|
|
2381
|
+
try {
|
|
2382
|
+
onRegistrationFailure?.();
|
|
2383
|
+
} catch {
|
|
2384
|
+
}
|
|
2385
|
+
throw err;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
async function registerProviderProcessLifecycle(params) {
|
|
2389
|
+
const {
|
|
2390
|
+
subprocess,
|
|
2391
|
+
completionPromise,
|
|
2392
|
+
processState,
|
|
2393
|
+
exitStatus,
|
|
2394
|
+
requestTermination,
|
|
2395
|
+
activityState,
|
|
2396
|
+
stderrChunks,
|
|
2397
|
+
providerName,
|
|
2398
|
+
command,
|
|
2399
|
+
args,
|
|
2400
|
+
workingDir,
|
|
2401
|
+
ownsProcessGroup
|
|
2402
|
+
} = params;
|
|
2403
|
+
try {
|
|
2404
|
+
const plm = getProcessLifecycleManager();
|
|
2405
|
+
if (!plm) {
|
|
2406
|
+
return void 0;
|
|
2407
|
+
}
|
|
2408
|
+
return await plm.registerProcessWithOptions({
|
|
2409
|
+
completionPromise,
|
|
2410
|
+
terminateFn: async (request) => {
|
|
2411
|
+
const mappedReason = request.reason === "timeout" ? "hard_timeout" : `plm_${request.reason}`;
|
|
2412
|
+
const detail = `PLM requested termination: ${request.reason}`;
|
|
2413
|
+
requestTermination(mappedReason, request.source, detail);
|
|
2414
|
+
},
|
|
2415
|
+
verificationFn: async () => {
|
|
2416
|
+
const code = processState.exitCode ?? exitStatus.code ?? subprocess.exitCode ?? null;
|
|
2417
|
+
const signal = processState.exitSignal ?? exitStatus.signal ?? subprocess?.signal ?? null;
|
|
2418
|
+
const verified = hasPlmExitEvidence({
|
|
2419
|
+
code,
|
|
2420
|
+
signal: signal == null ? null : String(signal),
|
|
2421
|
+
hasExited: processState.hasExited
|
|
2422
|
+
});
|
|
2423
|
+
if (!verified) {
|
|
2424
|
+
logPlmVerificationFailure({
|
|
2425
|
+
provider: providerName,
|
|
2426
|
+
command,
|
|
2427
|
+
args,
|
|
2428
|
+
code,
|
|
2429
|
+
signal: signal == null ? null : String(signal),
|
|
2430
|
+
hasExited: processState.hasExited,
|
|
2431
|
+
stderr: stderrChunks.join("").trim().slice(0, 2e3)
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
return verified;
|
|
2435
|
+
},
|
|
2436
|
+
processType: "local",
|
|
2437
|
+
timeoutMs: 0,
|
|
2438
|
+
metadata: {
|
|
2439
|
+
provider: providerName,
|
|
2440
|
+
command,
|
|
2441
|
+
args,
|
|
2442
|
+
cwd: workingDir,
|
|
2443
|
+
startTime: Date.now()
|
|
2444
|
+
},
|
|
2445
|
+
progressCallback: async () => ({
|
|
2446
|
+
filesChanged: 0,
|
|
2447
|
+
outputLines: activityState.outputLines,
|
|
2448
|
+
lastActivityTimestamp: activityState.lastActivityAt
|
|
2449
|
+
}),
|
|
2450
|
+
childProcess: subprocess,
|
|
2451
|
+
ownsProcessGroup
|
|
2452
|
+
});
|
|
2453
|
+
} catch (err) {
|
|
2454
|
+
logger2.warn(`[BaseRunner] ProcessLifecycleManager registration failed: ${err}`, {
|
|
2455
|
+
provider: providerName
|
|
2456
|
+
});
|
|
2457
|
+
return void 0;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
function startProviderIdleMonitor(params) {
|
|
2461
|
+
const {
|
|
2462
|
+
subprocess,
|
|
2463
|
+
activityState,
|
|
2464
|
+
terminationState,
|
|
2465
|
+
requestTermination,
|
|
2466
|
+
command,
|
|
2467
|
+
timeout,
|
|
2468
|
+
providerName,
|
|
2469
|
+
variantLabel,
|
|
2470
|
+
commandSignature,
|
|
2471
|
+
stderrChunks,
|
|
2472
|
+
onActivity,
|
|
2473
|
+
onIdleTermination,
|
|
2474
|
+
idleTimeouts
|
|
2475
|
+
} = params;
|
|
2476
|
+
if (!idleTimeouts) {
|
|
2477
|
+
return void 0;
|
|
2478
|
+
}
|
|
2479
|
+
const { warnMs, killMs } = idleTimeouts;
|
|
2480
|
+
const idlePollMs = Math.max(250, Math.min(3e4, Math.floor(warnMs / 2)));
|
|
2481
|
+
if (isDebugMode() || isTimeoutDebug()) {
|
|
2482
|
+
logger2.debug(
|
|
2483
|
+
`[BaseRunner] Idle config (${providerName}) variant=${variantLabel} timeout=${timeout}ms warn=${warnMs}ms kill=${killMs}ms poll=${idlePollMs}ms command=${commandSignature}`,
|
|
2484
|
+
{
|
|
2485
|
+
provider: providerName,
|
|
2486
|
+
variantLabel,
|
|
2487
|
+
timeout,
|
|
2488
|
+
warnMs,
|
|
2489
|
+
killMs,
|
|
2490
|
+
idlePollMs,
|
|
2491
|
+
command: commandSignature
|
|
2492
|
+
}
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
let idleWarned = false;
|
|
2496
|
+
let lastCpuTimeSeconds;
|
|
2497
|
+
const idleMonitorInterval = setInterval(() => {
|
|
2498
|
+
if (terminationState.attempts.length > 0) return;
|
|
2499
|
+
const now = Date.now();
|
|
2500
|
+
const pid = subprocess?.pid;
|
|
2501
|
+
const currentCpuSeconds = typeof pid === "number" ? readCpuTimeSeconds(pid) : void 0;
|
|
2502
|
+
const cpuActivity = evaluateCpuTimeProgressStreak({
|
|
2503
|
+
currentCpuSeconds,
|
|
2504
|
+
previousCpuSeconds: lastCpuTimeSeconds,
|
|
2505
|
+
currentBusyPolls: activityState.consecutiveBusyCpuPolls
|
|
2506
|
+
});
|
|
2507
|
+
lastCpuTimeSeconds = currentCpuSeconds;
|
|
2508
|
+
activityState.consecutiveBusyCpuPolls = cpuActivity.nextBusyPolls;
|
|
2509
|
+
if (cpuActivity.shouldCountAsActivity) {
|
|
2510
|
+
activityState.lastActivityAt = now;
|
|
2511
|
+
onActivity?.({ source: "cpu", timestampMs: now });
|
|
2512
|
+
}
|
|
2513
|
+
const idleFor = now - activityState.lastActivityAt;
|
|
2514
|
+
if (!idleWarned && idleFor >= warnMs) {
|
|
2515
|
+
idleWarned = true;
|
|
2516
|
+
logger2.warn(
|
|
2517
|
+
`[BaseRunner] ${providerName} subprocess appears idle for ${Math.round(idleFor / 1e3)}s (warn=${Math.round(warnMs / 1e3)}s kill=${Math.round(killMs / 1e3)}s) - ${command}`,
|
|
2518
|
+
{
|
|
2519
|
+
provider: providerName,
|
|
2520
|
+
command,
|
|
2521
|
+
idleForMs: Math.round(idleFor / 1e3) * 1e3,
|
|
2522
|
+
warnMs,
|
|
2523
|
+
killMs
|
|
2524
|
+
}
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
if (idleFor >= killMs) {
|
|
2528
|
+
const detail = `idle timeout after ${Math.round(killMs / 1e3)}s with no stdout/stderr/CPU activity`;
|
|
2529
|
+
const recentStderr = stderrChunks.join("").trim();
|
|
2530
|
+
if (recentStderr) {
|
|
2531
|
+
const sanitizedRecentStderr = sanitizeError(new Error(recentStderr.slice(-500))).message;
|
|
2532
|
+
logger2.warn(`[BaseRunner] Recent stderr before idle kill:
|
|
2533
|
+
${sanitizedRecentStderr}`, {
|
|
2534
|
+
provider: providerName,
|
|
2535
|
+
command,
|
|
2536
|
+
idleForMs: Math.round(idleFor / 1e3) * 1e3,
|
|
2537
|
+
warnMs,
|
|
2538
|
+
killMs
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
onIdleTermination?.();
|
|
2542
|
+
const won = requestTermination("idle_timeout", "base-runner:idle-monitor", detail, {
|
|
2543
|
+
idleDurationMs: Math.round(idleFor / 1e3) * 1e3
|
|
2544
|
+
});
|
|
2545
|
+
if (won) {
|
|
2546
|
+
logger2.warn(`[BaseRunner] Terminating idle subprocess (${providerName}): ${detail}`, {
|
|
2547
|
+
provider: providerName,
|
|
2548
|
+
command,
|
|
2549
|
+
idleForMs: Math.round(idleFor / 1e3) * 1e3,
|
|
2550
|
+
warnMs,
|
|
2551
|
+
killMs
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}, idlePollMs);
|
|
2556
|
+
idleMonitorInterval.unref?.();
|
|
2557
|
+
return idleMonitorInterval;
|
|
2558
|
+
}
|
|
2559
|
+
function startProviderTimeoutWatchdog(params) {
|
|
2560
|
+
const { processState, startTime, timeout, providerName } = params;
|
|
2561
|
+
return setInterval(() => {
|
|
2562
|
+
const elapsed = Date.now() - startTime;
|
|
2563
|
+
if (elapsed > timeout * 0.75 && !processState.hasExited) {
|
|
2564
|
+
logger2.debug(
|
|
2565
|
+
`[BaseRunner] Subprocess approaching timeout (${Math.round(elapsed / 1e3)}s / ${Math.round(timeout / 1e3)}s) - ${providerName}`,
|
|
2566
|
+
{
|
|
2567
|
+
provider: providerName,
|
|
2568
|
+
elapsedMs: Math.round(elapsed),
|
|
2569
|
+
timeoutMs: timeout
|
|
2570
|
+
}
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
}, 3e4);
|
|
2574
|
+
}
|
|
2575
|
+
function removeStreamDataListener(subprocess, streamName, listener) {
|
|
2576
|
+
try {
|
|
2577
|
+
const stream = subprocess?.[streamName];
|
|
2578
|
+
if (typeof stream?.removeListener === "function") {
|
|
2579
|
+
stream.removeListener("data", listener);
|
|
2580
|
+
} else if (typeof stream?.off === "function") {
|
|
2581
|
+
stream.off("data", listener);
|
|
2582
|
+
}
|
|
2583
|
+
} catch {
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
function cleanupProviderExecutionResources(params) {
|
|
2587
|
+
const {
|
|
2588
|
+
watchdogInterval,
|
|
2589
|
+
idleMonitorInterval,
|
|
2590
|
+
terminationController,
|
|
2591
|
+
abortSignal,
|
|
2592
|
+
abortListener,
|
|
2593
|
+
subprocess,
|
|
2594
|
+
stdoutDataListener,
|
|
2595
|
+
stderrListener,
|
|
2596
|
+
plmProcessId,
|
|
2597
|
+
limiterId,
|
|
2598
|
+
trackedPid,
|
|
2599
|
+
workingDir,
|
|
2600
|
+
providerName
|
|
2601
|
+
} = params;
|
|
2602
|
+
if (watchdogInterval) {
|
|
2603
|
+
clearInterval(watchdogInterval);
|
|
2604
|
+
}
|
|
2605
|
+
if (idleMonitorInterval) {
|
|
2606
|
+
clearInterval(idleMonitorInterval);
|
|
2607
|
+
}
|
|
2608
|
+
terminationController.clearTerminationTimer();
|
|
2609
|
+
if (abortSignal && abortListener) {
|
|
2610
|
+
abortSignal.removeEventListener("abort", abortListener);
|
|
2611
|
+
}
|
|
2612
|
+
removeStreamDataListener(subprocess, "stdout", stdoutDataListener);
|
|
2613
|
+
removeStreamDataListener(subprocess, "stderr", stderrListener);
|
|
2614
|
+
const cleanupErrors = [];
|
|
2615
|
+
const { processState } = terminationController;
|
|
2616
|
+
if (!processState.hasExited) {
|
|
2617
|
+
if (processState.sigtermSentAt === null) {
|
|
2618
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGTERM")) {
|
|
2619
|
+
processState.sigtermSentAt = Date.now();
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
if (processState.sigkillSentAt === null) {
|
|
2623
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGKILL")) {
|
|
2624
|
+
processState.sigkillSentAt = Date.now();
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
if (plmProcessId) {
|
|
2629
|
+
try {
|
|
2630
|
+
const plm = getProcessLifecycleManager();
|
|
2631
|
+
plm?.unregisterProcess(plmProcessId);
|
|
2632
|
+
} catch (error) {
|
|
2633
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
if (limiterId !== void 0) {
|
|
2637
|
+
try {
|
|
2638
|
+
globalProcessLimiter.untrack(limiterId);
|
|
2639
|
+
} catch (error) {
|
|
2640
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
try {
|
|
2644
|
+
untrackSubprocess(trackedPid, subprocess);
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2647
|
+
}
|
|
2648
|
+
if (cleanupErrors.length > 0) {
|
|
2649
|
+
try {
|
|
2650
|
+
const telemetryLogger = TelemetryLogger.getInstance({
|
|
2651
|
+
buildDir: resolveTelemetryBuildDir(workingDir)
|
|
2652
|
+
});
|
|
2653
|
+
telemetryLogger.logCostEvent("resource_cleanup_failed", {
|
|
2654
|
+
errors: cleanupErrors.map((e) => e.message),
|
|
2655
|
+
errorCount: cleanupErrors.length,
|
|
2656
|
+
limiterId: limiterId ?? null,
|
|
2657
|
+
pid: trackedPid ?? null,
|
|
2658
|
+
provider: providerName,
|
|
2659
|
+
timestamp: Date.now()
|
|
2660
|
+
});
|
|
2661
|
+
} catch (telemetryError) {
|
|
2662
|
+
logger2.warn(`Failed to log cleanup error to telemetry: ${telemetryError}`);
|
|
2663
|
+
}
|
|
2664
|
+
logger2.warn("Resource cleanup errors occurred:", {
|
|
2665
|
+
provider: providerName,
|
|
2666
|
+
errorCount: cleanupErrors.length,
|
|
2667
|
+
limiterId: limiterId ?? null,
|
|
2668
|
+
pid: trackedPid ?? null
|
|
2669
|
+
});
|
|
2670
|
+
if (process.env.NODE_ENV === "test") {
|
|
2671
|
+
logger2.error(
|
|
2672
|
+
`[BaseRunner] Resource cleanup failed: ${cleanupErrors.length} error(s) - ${cleanupErrors.map((e) => e.message).join("; ")}`,
|
|
2673
|
+
void 0,
|
|
2674
|
+
{
|
|
2675
|
+
provider: providerName,
|
|
2676
|
+
errorCount: cleanupErrors.length,
|
|
2677
|
+
limiterId: limiterId ?? null,
|
|
2678
|
+
pid: trackedPid ?? null
|
|
2679
|
+
}
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
function prepareCommand(command, args, workingDir, metadata, sandboxOptions) {
|
|
2685
|
+
if (sandboxOptions?.disableBuildSandbox === true) {
|
|
2686
|
+
return { command, args, sandboxed: false };
|
|
2687
|
+
}
|
|
2688
|
+
const result = wrapWithSandbox({
|
|
2689
|
+
workingDir,
|
|
2690
|
+
command,
|
|
2691
|
+
args,
|
|
2692
|
+
providerName: metadata.name,
|
|
2693
|
+
extraWritePaths: sandboxOptions?.extraWritePaths,
|
|
2694
|
+
readOnlyWorkingDir: sandboxOptions?.readOnlyWorkingDir,
|
|
2695
|
+
// This is the provider execution path, so it must not run write-capable
|
|
2696
|
+
// automation without an accepted boundary. Read-only runs still pass
|
|
2697
|
+
// readOnlyWorkingDir and stay exempt.
|
|
2698
|
+
requiresBoundaryForWrites: true,
|
|
2699
|
+
env: sandboxOptions?.env,
|
|
2700
|
+
// Prefer stable provider ids when available, otherwise derive a stable-ish id
|
|
2701
|
+
// from the command basename (e.g. "copilot", "cursor-agent").
|
|
2702
|
+
providerId: metadata.id ?? resolveCommandName(command)
|
|
2703
|
+
});
|
|
2704
|
+
if (result.sandboxed && isDebugMode()) {
|
|
2705
|
+
logger2.debug(`[BaseRunner] Sandboxed ${metadata.name} execution in ${workingDir}`, {
|
|
2706
|
+
provider: metadata.name,
|
|
2707
|
+
workingDir
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
return result;
|
|
2711
|
+
}
|
|
2712
|
+
function computeArgvBytes(command, args, env) {
|
|
2713
|
+
let argBytes = Buffer.byteLength(command, "utf8") + 1;
|
|
2714
|
+
let maxArgBytes = Buffer.byteLength(command, "utf8");
|
|
2715
|
+
for (const arg of args) {
|
|
2716
|
+
const bytes = Buffer.byteLength(arg, "utf8");
|
|
2717
|
+
argBytes += bytes + 1;
|
|
2718
|
+
maxArgBytes = Math.max(maxArgBytes, bytes);
|
|
2719
|
+
}
|
|
2720
|
+
let envBytes = 0;
|
|
2721
|
+
for (const [key, value] of Object.entries(env)) {
|
|
2722
|
+
if (value === void 0) continue;
|
|
2723
|
+
envBytes += Buffer.byteLength(key, "utf8") + 1 + Buffer.byteLength(value, "utf8") + 1;
|
|
2724
|
+
}
|
|
2725
|
+
return { argBytes, envBytes, totalBytes: argBytes + envBytes, maxArgBytes };
|
|
2726
|
+
}
|
|
2727
|
+
function throwIfProviderArgvTooLarge(command, args, env, metadata) {
|
|
2728
|
+
const { argBytes, envBytes, totalBytes, maxArgBytes } = computeArgvBytes(command, args, env);
|
|
2729
|
+
if (totalBytes <= PROVIDER_PROCESS_ARG_ENV_MAX_SAFE_BYTES && maxArgBytes <= PROVIDER_SINGLE_ARG_MAX_SAFE_BYTES) {
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
throw new ProviderExecutionError(
|
|
2733
|
+
`[${metadata.name}] Provider command argv/env is too large before spawn (argvBytes=${argBytes}, envBytes=${envBytes}, totalBytes=${totalBytes}, maxArgBytes=${maxArgBytes}). Use stdin or a prompt-file/config-file transport for large provider prompts or config payloads. [reason=argument_list_too_long]`,
|
|
2734
|
+
{
|
|
2735
|
+
retryable: false,
|
|
2736
|
+
reason: "argument_list_too_long",
|
|
2737
|
+
providerName: metadata.name
|
|
2738
|
+
}
|
|
2739
|
+
);
|
|
2740
|
+
}
|
|
2741
|
+
function resolveTelemetryBuildDir(workingDir) {
|
|
2742
|
+
return path2.join(path2.resolve(workingDir), ".build-project");
|
|
2743
|
+
}
|
|
2744
|
+
function resultIsOurCancellation(result) {
|
|
2745
|
+
if (result == null || typeof result !== "object") {
|
|
2746
|
+
return false;
|
|
2747
|
+
}
|
|
2748
|
+
const r = result;
|
|
2749
|
+
if (r.isCanceled === true || r.canceled === true) return true;
|
|
2750
|
+
if (r.isTerminated === true && (r.signal === "SIGTERM" || r.signal === "SIGKILL")) {
|
|
2751
|
+
return true;
|
|
2752
|
+
}
|
|
2753
|
+
if (r.signal === "SIGTERM" || r.signal === "SIGKILL") return true;
|
|
2754
|
+
return false;
|
|
2755
|
+
}
|
|
2756
|
+
function shouldPreserveTerminationPartialOutput(record) {
|
|
2757
|
+
return record.reason === "timeout" || record.reason === "hard_timeout" || record.reason === "plm_timeout" || record.reason === "idle_timeout" || record.reason === "abort" || // Spool failures kill the child, and what was captured before the failure is
|
|
2758
|
+
// the evidence for diagnosing it.
|
|
2759
|
+
record.reason === "output_ceiling_exceeded" || record.reason === "stdout_spool_read_failed" || record.reason === "stdout_spool_consume_failed";
|
|
2760
|
+
}
|
|
2761
|
+
async function executeProviderCommand(options) {
|
|
2762
|
+
return executeProviderCommandImpl(options, "immediate");
|
|
2763
|
+
}
|
|
2764
|
+
async function executeProviderCommandAsync(options) {
|
|
2765
|
+
return executeProviderCommandImpl(options, "async");
|
|
2766
|
+
}
|
|
2767
|
+
async function executeProviderCommandImpl(options, trackMode) {
|
|
2768
|
+
const {
|
|
2769
|
+
command,
|
|
2770
|
+
args,
|
|
2771
|
+
workingDir,
|
|
2772
|
+
env,
|
|
2773
|
+
preserveClaudeBackendFlags = false,
|
|
2774
|
+
input,
|
|
2775
|
+
stdinMode = "pipe",
|
|
2776
|
+
timeout = DEFAULT_TIMEOUT,
|
|
2777
|
+
maxBufferBytes,
|
|
2778
|
+
extraWritePaths,
|
|
2779
|
+
readOnlyWorkingDir,
|
|
2780
|
+
disableBuildSandbox,
|
|
2781
|
+
providerInternalSandboxBypass,
|
|
2782
|
+
abortSignal,
|
|
2783
|
+
beforeSpawn,
|
|
2784
|
+
metadata,
|
|
2785
|
+
onData,
|
|
2786
|
+
onErrorData,
|
|
2787
|
+
onStderrChunk,
|
|
2788
|
+
onStdoutChunk,
|
|
2789
|
+
onActivity,
|
|
2790
|
+
onIdleTermination,
|
|
2791
|
+
formatLine,
|
|
2792
|
+
passThroughUnformatted = false,
|
|
2793
|
+
streamOutput = false,
|
|
2794
|
+
variantLabel,
|
|
2795
|
+
idleWarnMs: optIdleWarnMs,
|
|
2796
|
+
idleKillMs: optIdleKillMs,
|
|
2797
|
+
fileBackedStdout = false,
|
|
2798
|
+
fileBackedStdoutMaxBytes
|
|
2799
|
+
} = options;
|
|
2800
|
+
const useStreamingOutput = streamOutput === true;
|
|
2801
|
+
const useFileBackedStdout = fileBackedStdout === true;
|
|
2802
|
+
const fileBackedMaxBytes = typeof fileBackedStdoutMaxBytes === "number" && Number.isFinite(fileBackedStdoutMaxBytes) && fileBackedStdoutMaxBytes > 0 ? Math.floor(fileBackedStdoutMaxBytes) : FILE_BACKED_STDOUT_DEFAULT_MAX_BYTES;
|
|
2803
|
+
throwIfAbortAlreadyRequested(abortSignal, metadata.name);
|
|
2804
|
+
const idleTimeouts = resolveIdleTimeoutsMs(command, args, timeout, {
|
|
2805
|
+
idleWarnMs: optIdleWarnMs,
|
|
2806
|
+
idleKillMs: optIdleKillMs
|
|
2807
|
+
});
|
|
2808
|
+
const startTime = Date.now();
|
|
2809
|
+
if (isTimeoutDebug()) {
|
|
2810
|
+
logger2.debug(
|
|
2811
|
+
`[BaseRunner] Starting ${metadata.name} with timeout ${timeout}ms (${Math.round(timeout / 6e4)}m)`,
|
|
2812
|
+
{
|
|
2813
|
+
provider: metadata.name,
|
|
2814
|
+
command,
|
|
2815
|
+
workingDir,
|
|
2816
|
+
timeout,
|
|
2817
|
+
variantLabel
|
|
2818
|
+
}
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2821
|
+
if (isPathDebug()) {
|
|
2822
|
+
logger2.debug(`[base-runner] Executing ${metadata.name} in cwd: ${workingDir}`, {
|
|
2823
|
+
provider: metadata.name,
|
|
2824
|
+
command,
|
|
2825
|
+
workingDir,
|
|
2826
|
+
timeout,
|
|
2827
|
+
variantLabel
|
|
2828
|
+
});
|
|
2829
|
+
if (workingDir.includes("build-worktrees-")) {
|
|
2830
|
+
const expectedUid = os3.userInfo().uid.toString();
|
|
2831
|
+
if (!workingDir.includes(`build-worktrees-${expectedUid}`)) {
|
|
2832
|
+
logger2.warn(`[base-runner] cwd UID mismatch - expected ${expectedUid} in: ${workingDir}`, {
|
|
2833
|
+
provider: metadata.name,
|
|
2834
|
+
command,
|
|
2835
|
+
workingDir,
|
|
2836
|
+
timeout,
|
|
2837
|
+
variantLabel
|
|
2838
|
+
});
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
const internalController = new AbortController();
|
|
2843
|
+
const sandboxed = prepareCommand(command, args, workingDir, metadata, {
|
|
2844
|
+
extraWritePaths,
|
|
2845
|
+
readOnlyWorkingDir,
|
|
2846
|
+
disableBuildSandbox,
|
|
2847
|
+
env
|
|
2848
|
+
});
|
|
2849
|
+
assertProviderBypassHasBoundaryProof(metadata, args, sandboxed, providerInternalSandboxBypass);
|
|
2850
|
+
const { command: sandboxedCommand, args: sandboxedArgs } = sandboxed;
|
|
2851
|
+
const childEnv = sanitizeExecutionEnv(env, { preserveClaudeBackendFlags });
|
|
2852
|
+
const processGroupOwnership = resolveProviderProcessGroupOwnership();
|
|
2853
|
+
childEnv[BUILD_PROCESS_GROUP_OWNER_ENV] = processGroupOwnership.owner;
|
|
2854
|
+
childEnv.PWD = path2.resolve(workingDir);
|
|
2855
|
+
throwIfProviderArgvTooLarge(sandboxedCommand, sandboxedArgs, childEnv, metadata);
|
|
2856
|
+
const commandSignature = formatRedactedCommandSignature(sandboxedCommand, sandboxedArgs);
|
|
2857
|
+
const resolvedVariantLabel = variantLabel ?? "unknown";
|
|
2858
|
+
const limiterDescription = `${metadata.name} provider command: ${formatRedactedCommandSignature(command, args)}`.trim();
|
|
2859
|
+
const reservationOrWait = acquireProcessSlot(limiterDescription, abortSignal);
|
|
2860
|
+
const reservation = reservationOrWait instanceof Promise ? await reservationOrWait : reservationOrWait;
|
|
2861
|
+
let stdoutSpool;
|
|
2862
|
+
let stdoutSpoolFd;
|
|
2863
|
+
try {
|
|
2864
|
+
throwIfAbortAlreadyRequested(abortSignal, metadata.name);
|
|
2865
|
+
if (useFileBackedStdout) {
|
|
2866
|
+
stdoutSpool = createPrivateSpool({
|
|
2867
|
+
label: `stdout-${metadata.name}`,
|
|
2868
|
+
streams: ["stdout"],
|
|
2869
|
+
errorLabel: "stdout spool"
|
|
2870
|
+
});
|
|
2871
|
+
const freeBytes = spoolFilesystemFreeBytes(stdoutSpool.dir);
|
|
2872
|
+
if (freeBytes !== void 0 && freeBytes < FILE_BACKED_STDOUT_MIN_FREE_BYTES) {
|
|
2873
|
+
throw new ProviderExecutionError(
|
|
2874
|
+
`${metadata.name} CLI not started: stdout spool filesystem has ${freeBytes} bytes free, below the ${FILE_BACKED_STDOUT_MIN_FREE_BYTES} byte floor.`,
|
|
2875
|
+
{
|
|
2876
|
+
retryable: false,
|
|
2877
|
+
reason: "stdout_spool_insufficient_space",
|
|
2878
|
+
providerName: metadata.name
|
|
2879
|
+
}
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
stdoutSpoolFd = stdoutSpool.stream("stdout").requireOpenFd();
|
|
2883
|
+
}
|
|
2884
|
+
beforeSpawn?.();
|
|
2885
|
+
} catch (error) {
|
|
2886
|
+
if (stdoutSpool) {
|
|
2887
|
+
await stdoutSpool.dispose().catch(() => {
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
releaseProcessSlotReservation(reservation);
|
|
2891
|
+
throw error;
|
|
2892
|
+
}
|
|
2893
|
+
const spawnOptions = {
|
|
2894
|
+
cwd: workingDir,
|
|
2895
|
+
env: childEnv,
|
|
2896
|
+
extendEnv: false,
|
|
2897
|
+
input,
|
|
2898
|
+
...stdinMode === "ignore" && input === void 0 ? { stdin: "ignore" } : {},
|
|
2899
|
+
// The child writes stdout straight to the spool inode. execa then reports
|
|
2900
|
+
// `subprocess.stdout` as null and `result.stdout` as undefined, so the
|
|
2901
|
+
// captured spool contents become the authoritative stdout below.
|
|
2902
|
+
//
|
|
2903
|
+
// The cast is deliberate: execa's types admit only the literals 3-9 for a
|
|
2904
|
+
// descriptor here, while the runtime passes any integer through to Node's
|
|
2905
|
+
// stdio, which shares that descriptor with the child. Verified on execa
|
|
2906
|
+
// 9.6.1 / Node 24 with descriptor 42: exit 0 and all 500,000 bytes landed on
|
|
2907
|
+
// the spool inode.
|
|
2908
|
+
...stdoutSpoolFd !== void 0 ? { stdout: stdoutSpoolFd } : {},
|
|
2909
|
+
cancelSignal: internalController.signal,
|
|
2910
|
+
all: false,
|
|
2911
|
+
reject: false,
|
|
2912
|
+
detached: processGroupOwnership.detach,
|
|
2913
|
+
timeout: 0,
|
|
2914
|
+
buffer: !useStreamingOutput,
|
|
2915
|
+
...!useStreamingOutput && typeof maxBufferBytes === "number" && Number.isFinite(maxBufferBytes) && maxBufferBytes > 0 ? { maxBuffer: Math.floor(maxBufferBytes) } : {}
|
|
2916
|
+
};
|
|
2917
|
+
const subprocess = spawnProviderSubprocess(
|
|
2918
|
+
sandboxedCommand,
|
|
2919
|
+
sandboxedArgs,
|
|
2920
|
+
spawnOptions,
|
|
2921
|
+
reservation
|
|
2922
|
+
);
|
|
2923
|
+
observeVariantProcessSpawn(subprocess);
|
|
2924
|
+
const providerProcessGroupPid = subprocess.pid;
|
|
2925
|
+
emitProviderProcessGroupMarker(providerProcessGroupPid);
|
|
2926
|
+
const terminationController = createProviderTerminationController({
|
|
2927
|
+
subprocess,
|
|
2928
|
+
startTime,
|
|
2929
|
+
timeout,
|
|
2930
|
+
providerName: metadata.name,
|
|
2931
|
+
command
|
|
2932
|
+
});
|
|
2933
|
+
const { processState, terminationState, requestTermination, markExited } = terminationController;
|
|
2934
|
+
const requestTerminationFromAbortSignal = (signal) => {
|
|
2935
|
+
if (isHostTimeoutAbortReason(signal.reason)) {
|
|
2936
|
+
requestTermination(
|
|
2937
|
+
"timeout",
|
|
2938
|
+
"abortSignal",
|
|
2939
|
+
formatHostTimeoutTerminationDetail(signal.reason)
|
|
2940
|
+
);
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
requestTermination("abort", "abortSignal");
|
|
2944
|
+
};
|
|
2945
|
+
let abortListener;
|
|
2946
|
+
if (abortSignal) {
|
|
2947
|
+
if (abortSignal.aborted) {
|
|
2948
|
+
internalController.abort();
|
|
2949
|
+
requestTerminationFromAbortSignal(abortSignal);
|
|
2950
|
+
} else {
|
|
2951
|
+
abortListener = () => {
|
|
2952
|
+
internalController.abort();
|
|
2953
|
+
requestTerminationFromAbortSignal(abortSignal);
|
|
2954
|
+
};
|
|
2955
|
+
abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
const activityState = {
|
|
2959
|
+
outputLines: 0,
|
|
2960
|
+
lastActivityAt: Date.now(),
|
|
2961
|
+
consecutiveBusyCpuPolls: 0
|
|
2962
|
+
};
|
|
2963
|
+
const stderrChunks = [];
|
|
2964
|
+
const stdoutChunks = [];
|
|
2965
|
+
const streamCaptureLimit = typeof maxBufferBytes === "number" && Number.isFinite(maxBufferBytes) && maxBufferBytes > 0 ? Math.floor(maxBufferBytes) : DEFAULT_STREAM_CAPTURE_BYTES;
|
|
2966
|
+
const capturedStdout = new BoundedOutputBuffer();
|
|
2967
|
+
const capturedStderr = new BoundedOutputBuffer();
|
|
2968
|
+
const resolveResultStdout = (result) => useFileBackedStdout ? capturedStdout.toString() || result.stdout : result.stdout;
|
|
2969
|
+
let streamingStdoutProcessor;
|
|
2970
|
+
let streamingStderrProcessor;
|
|
2971
|
+
let streamCallbackError;
|
|
2972
|
+
const captureStreamCallbackError = (error, streamName) => {
|
|
2973
|
+
if (streamCallbackError) {
|
|
2974
|
+
return;
|
|
2975
|
+
}
|
|
2976
|
+
streamCallbackError = error;
|
|
2977
|
+
logger2.error(
|
|
2978
|
+
`[BaseRunner] Streaming ${streamName} callback failed: ${String(error)}`,
|
|
2979
|
+
void 0,
|
|
2980
|
+
{
|
|
2981
|
+
provider: metadata.name,
|
|
2982
|
+
stream: streamName
|
|
2983
|
+
}
|
|
2984
|
+
);
|
|
2985
|
+
};
|
|
2986
|
+
if (useStreamingOutput) {
|
|
2987
|
+
streamingStdoutProcessor = createLineBufferedStreamProcessor((line) => {
|
|
2988
|
+
if (!onData || streamCallbackError) {
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
try {
|
|
2992
|
+
if (formatLine) {
|
|
2993
|
+
const formatted = formatLine(line);
|
|
2994
|
+
if (formatted) {
|
|
2995
|
+
onData(`${formatted}
|
|
2996
|
+
`);
|
|
2997
|
+
} else if (passThroughUnformatted) {
|
|
2998
|
+
onData(`${line}
|
|
2999
|
+
`);
|
|
3000
|
+
}
|
|
3001
|
+
} else {
|
|
3002
|
+
onData(`${line}
|
|
3003
|
+
`);
|
|
3004
|
+
}
|
|
3005
|
+
} catch (error) {
|
|
3006
|
+
captureStreamCallbackError(error, "stdout");
|
|
3007
|
+
}
|
|
3008
|
+
});
|
|
3009
|
+
streamingStderrProcessor = createLineBufferedStreamProcessor((line) => {
|
|
3010
|
+
if (!onErrorData || streamCallbackError) {
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
try {
|
|
3014
|
+
onErrorData(line);
|
|
3015
|
+
} catch (error) {
|
|
3016
|
+
captureStreamCallbackError(error, "stderr");
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
const bumpActivity = (chunk, source = "stdout") => {
|
|
3021
|
+
if (chunk == null) return;
|
|
3022
|
+
const now = Date.now();
|
|
3023
|
+
activityState.lastActivityAt = now;
|
|
3024
|
+
const text = chunkToText(chunk);
|
|
3025
|
+
if (text) {
|
|
3026
|
+
activityState.outputLines += text.split(/\r?\n/).filter(Boolean).length;
|
|
3027
|
+
onActivity?.({ source, timestampMs: now });
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
3030
|
+
const completionSignal = options.completionSignal;
|
|
3031
|
+
const completionSignalGraceMs = Math.max(
|
|
3032
|
+
0,
|
|
3033
|
+
options.completionSignalGraceMs ?? TMUX_COMPLETION_SIGNAL_GRACE_MS
|
|
3034
|
+
);
|
|
3035
|
+
let completionSignalGraceTimer;
|
|
3036
|
+
let completionSignalEarlyExit = false;
|
|
3037
|
+
const observeCompletionSignal = (text, stream) => {
|
|
3038
|
+
if (!completionSignal || completionSignalGraceTimer || processState.hasExited) return;
|
|
3039
|
+
let signalled = false;
|
|
3040
|
+
try {
|
|
3041
|
+
signalled = completionSignal(text, stream) === true;
|
|
3042
|
+
} catch {
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
if (!signalled) return;
|
|
3046
|
+
completionSignalGraceTimer = setTimeout(() => {
|
|
3047
|
+
if (processState.hasExited) return;
|
|
3048
|
+
if (terminationState.getPrimaryRecord() !== null) return;
|
|
3049
|
+
completionSignalEarlyExit = true;
|
|
3050
|
+
if (processState.sigtermSentAt === null) {
|
|
3051
|
+
if (sendSignalToSubprocessTree(subprocess, "SIGTERM")) {
|
|
3052
|
+
processState.sigtermSentAt = Date.now();
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
internalController.abort();
|
|
3056
|
+
}, completionSignalGraceMs);
|
|
3057
|
+
completionSignalGraceTimer.unref?.();
|
|
3058
|
+
};
|
|
3059
|
+
const deliverRawChunk = (observer, chunk) => {
|
|
3060
|
+
if (!observer) {
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
3063
|
+
let raw;
|
|
3064
|
+
if (Buffer.isBuffer(chunk)) {
|
|
3065
|
+
if (chunk.length === 0) return;
|
|
3066
|
+
raw = Buffer.from(chunk);
|
|
3067
|
+
} else if (chunk instanceof Uint8Array) {
|
|
3068
|
+
if (chunk.byteLength === 0) return;
|
|
3069
|
+
raw = Buffer.from(chunk);
|
|
3070
|
+
} else if (typeof chunk === "string") {
|
|
3071
|
+
if (chunk.length === 0) return;
|
|
3072
|
+
raw = chunk;
|
|
3073
|
+
} else {
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
try {
|
|
3077
|
+
observer(raw);
|
|
3078
|
+
} catch {
|
|
3079
|
+
}
|
|
3080
|
+
};
|
|
3081
|
+
let firstOutputFiredInExec = false;
|
|
3082
|
+
const ingestStdout = (raw, text) => {
|
|
3083
|
+
bumpActivity(text);
|
|
3084
|
+
if (!firstOutputFiredInExec && text) {
|
|
3085
|
+
firstOutputFiredInExec = true;
|
|
3086
|
+
options.onFirstOutput?.(Date.now());
|
|
3087
|
+
}
|
|
3088
|
+
if (raw !== void 0) {
|
|
3089
|
+
deliverRawChunk(onStdoutChunk, raw);
|
|
3090
|
+
}
|
|
3091
|
+
if (text) {
|
|
3092
|
+
observeCompletionSignal(text, "stdout");
|
|
3093
|
+
}
|
|
3094
|
+
if ((useStreamingOutput || completionSignal || useFileBackedStdout) && text) {
|
|
3095
|
+
capturedStdout.append(text, streamCaptureLimit);
|
|
3096
|
+
stdoutChunks.push(text);
|
|
3097
|
+
if (stdoutChunks.length > 5) {
|
|
3098
|
+
stdoutChunks.shift();
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
if (useStreamingOutput) {
|
|
3102
|
+
streamingStdoutProcessor?.push(text);
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
const stdoutDecoder = new StringDecoder2("utf8");
|
|
3106
|
+
const stderrDecoder = new StringDecoder2("utf8");
|
|
3107
|
+
let pipeDecodersFlushed = false;
|
|
3108
|
+
const decodePipeChunk = (decoder, chunk) => {
|
|
3109
|
+
if (Buffer.isBuffer(chunk)) return decoder.write(chunk);
|
|
3110
|
+
if (chunk instanceof Uint8Array) return decoder.write(Buffer.from(chunk));
|
|
3111
|
+
return chunkToText(chunk);
|
|
3112
|
+
};
|
|
3113
|
+
const stdoutDataListener = (chunk) => {
|
|
3114
|
+
ingestStdout(chunk, decodePipeChunk(stdoutDecoder, chunk));
|
|
3115
|
+
};
|
|
3116
|
+
let stdoutSpoolFailure;
|
|
3117
|
+
let stdoutSpoolTailer;
|
|
3118
|
+
if (stdoutSpool !== void 0) {
|
|
3119
|
+
stdoutSpoolTailer = createStdoutSpoolTailer({
|
|
3120
|
+
stream: stdoutSpool.stream("stdout"),
|
|
3121
|
+
maxBytes: fileBackedMaxBytes,
|
|
3122
|
+
onBytes: (bytes, text) => {
|
|
3123
|
+
ingestStdout(bytes, text);
|
|
3124
|
+
},
|
|
3125
|
+
onFailure: (failure) => {
|
|
3126
|
+
if (stdoutSpoolFailure) return;
|
|
3127
|
+
stdoutSpoolFailure = failure;
|
|
3128
|
+
requestTermination(failure.reason, "base-runner:stdout-spool", failure.message);
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3131
|
+
stdoutSpoolTailer.start();
|
|
3132
|
+
}
|
|
3133
|
+
if (typeof subprocess?.stdout?.on === "function") {
|
|
3134
|
+
subprocess.stdout.on("data", stdoutDataListener);
|
|
3135
|
+
} else if (stdoutSpool === void 0 && isDebugMode()) {
|
|
3136
|
+
console.warn("[base-runner] subprocess.stdout not available \u2014 diagnostics may be incomplete");
|
|
3137
|
+
}
|
|
3138
|
+
const ingestStderr = (raw, text) => {
|
|
3139
|
+
if (raw !== void 0) {
|
|
3140
|
+
deliverRawChunk(onStderrChunk, raw);
|
|
3141
|
+
}
|
|
3142
|
+
if (text) {
|
|
3143
|
+
bumpActivity(text, "stderr");
|
|
3144
|
+
if (useStreamingOutput || completionSignal) {
|
|
3145
|
+
capturedStderr.append(text, streamCaptureLimit);
|
|
3146
|
+
}
|
|
3147
|
+
stderrChunks.push(text);
|
|
3148
|
+
if (stderrChunks.length > 5) {
|
|
3149
|
+
stderrChunks.shift();
|
|
3150
|
+
}
|
|
3151
|
+
observeCompletionSignal(text, "stderr");
|
|
3152
|
+
}
|
|
3153
|
+
if (useStreamingOutput) {
|
|
3154
|
+
streamingStderrProcessor?.push(text);
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
const stderrListener = (chunk) => {
|
|
3158
|
+
ingestStderr(chunk, decodePipeChunk(stderrDecoder, chunk));
|
|
3159
|
+
};
|
|
3160
|
+
const flushPipeDecoders = () => {
|
|
3161
|
+
if (pipeDecodersFlushed) return;
|
|
3162
|
+
pipeDecodersFlushed = true;
|
|
3163
|
+
const stdoutRemainder = stdoutDecoder.end();
|
|
3164
|
+
if (stdoutRemainder) ingestStdout(void 0, stdoutRemainder);
|
|
3165
|
+
const stderrRemainder = stderrDecoder.end();
|
|
3166
|
+
if (stderrRemainder) ingestStderr(void 0, stderrRemainder);
|
|
3167
|
+
};
|
|
3168
|
+
if (typeof subprocess?.stderr?.on === "function") {
|
|
3169
|
+
subprocess.stderr.on("data", stderrListener);
|
|
3170
|
+
}
|
|
3171
|
+
const exitStatus = {
|
|
3172
|
+
code: null,
|
|
3173
|
+
signal: null
|
|
3174
|
+
};
|
|
3175
|
+
const completionPromise = (async () => {
|
|
3176
|
+
const result = await subprocess;
|
|
3177
|
+
exitStatus.code = result.exitCode ?? null;
|
|
3178
|
+
exitStatus.signal = result?.signal ?? null;
|
|
3179
|
+
if (!processState.hasExited) {
|
|
3180
|
+
markExited(exitStatus.code, exitStatus.signal);
|
|
3181
|
+
}
|
|
3182
|
+
return result;
|
|
3183
|
+
})();
|
|
3184
|
+
let enteredTry = false;
|
|
3185
|
+
completionPromise.catch((err) => {
|
|
3186
|
+
if (!enteredTry) {
|
|
3187
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3188
|
+
logger2.error(`[BaseRunner] Subprocess rejected before await: ${message}`, void 0, {
|
|
3189
|
+
provider: metadata.name
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
});
|
|
3193
|
+
let trackedPid = null;
|
|
3194
|
+
let limiterId;
|
|
3195
|
+
const registerLimiter = () => registerProviderProcessLimiter({
|
|
3196
|
+
subprocess,
|
|
3197
|
+
limiterDescription,
|
|
3198
|
+
timeout,
|
|
3199
|
+
reservation,
|
|
3200
|
+
requestTermination,
|
|
3201
|
+
trackedPid,
|
|
3202
|
+
onRegistrationFailure: () => {
|
|
3203
|
+
terminationController.clearTerminationTimer();
|
|
3204
|
+
if (abortSignal && abortListener) {
|
|
3205
|
+
abortSignal.removeEventListener("abort", abortListener);
|
|
3206
|
+
}
|
|
3207
|
+
removeStreamDataListener(subprocess, "stdout", stdoutDataListener);
|
|
3208
|
+
removeStreamDataListener(subprocess, "stderr", stderrListener);
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
try {
|
|
3212
|
+
if (trackMode === "async") {
|
|
3213
|
+
limiterId = registerLimiter();
|
|
3214
|
+
trackedPid = await trackProviderSubprocess(subprocess, "async");
|
|
3215
|
+
} else {
|
|
3216
|
+
trackedPid = trackProviderSubprocess(subprocess, "immediate");
|
|
3217
|
+
limiterId = registerLimiter();
|
|
3218
|
+
}
|
|
3219
|
+
} catch (error) {
|
|
3220
|
+
try {
|
|
3221
|
+
if (limiterId !== void 0) {
|
|
3222
|
+
globalProcessLimiter.untrack(limiterId);
|
|
3223
|
+
limiterId = void 0;
|
|
3224
|
+
} else if (reservation !== void 0) {
|
|
3225
|
+
globalProcessLimiter.cancelReservation(reservation);
|
|
3226
|
+
}
|
|
3227
|
+
} catch (cleanupError) {
|
|
3228
|
+
logger2.warn("[BaseRunner] Failed to release process limiter after tracking setup failure", {
|
|
3229
|
+
cleanupError: sanitizeError(
|
|
3230
|
+
cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError))
|
|
3231
|
+
).message
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
3234
|
+
sendSignalToSubprocessTree(subprocess, "SIGKILL");
|
|
3235
|
+
terminationController.clearTerminationTimer();
|
|
3236
|
+
if (abortSignal && abortListener) {
|
|
3237
|
+
abortSignal.removeEventListener("abort", abortListener);
|
|
3238
|
+
}
|
|
3239
|
+
removeStreamDataListener(subprocess, "stdout", stdoutDataListener);
|
|
3240
|
+
removeStreamDataListener(subprocess, "stderr", stderrListener);
|
|
3241
|
+
if (stdoutSpool) {
|
|
3242
|
+
await stdoutSpool.dispose().catch(() => {
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
untrackSubprocess(trackedPid, subprocess);
|
|
3246
|
+
throw error;
|
|
3247
|
+
}
|
|
3248
|
+
const plmProcessId = await registerProviderProcessLifecycle({
|
|
3249
|
+
subprocess,
|
|
3250
|
+
completionPromise,
|
|
3251
|
+
processState,
|
|
3252
|
+
exitStatus,
|
|
3253
|
+
requestTermination,
|
|
3254
|
+
activityState,
|
|
3255
|
+
stderrChunks,
|
|
3256
|
+
providerName: metadata.name,
|
|
3257
|
+
command,
|
|
3258
|
+
args,
|
|
3259
|
+
workingDir,
|
|
3260
|
+
ownsProcessGroup: processGroupOwnership.detach
|
|
3261
|
+
});
|
|
3262
|
+
let idleMonitorInterval;
|
|
3263
|
+
let watchdogInterval;
|
|
3264
|
+
enteredTry = true;
|
|
3265
|
+
try {
|
|
3266
|
+
idleMonitorInterval = startProviderIdleMonitor({
|
|
3267
|
+
subprocess,
|
|
3268
|
+
activityState,
|
|
3269
|
+
terminationState,
|
|
3270
|
+
requestTermination,
|
|
3271
|
+
command,
|
|
3272
|
+
timeout,
|
|
3273
|
+
providerName: metadata.name,
|
|
3274
|
+
variantLabel: resolvedVariantLabel,
|
|
3275
|
+
commandSignature,
|
|
3276
|
+
stderrChunks,
|
|
3277
|
+
onActivity,
|
|
3278
|
+
onIdleTermination,
|
|
3279
|
+
idleTimeouts
|
|
3280
|
+
});
|
|
3281
|
+
watchdogInterval = startProviderTimeoutWatchdog({
|
|
3282
|
+
processState,
|
|
3283
|
+
startTime,
|
|
3284
|
+
timeout,
|
|
3285
|
+
providerName: metadata.name
|
|
3286
|
+
});
|
|
3287
|
+
const result = await completionPromise;
|
|
3288
|
+
const failure = result;
|
|
3289
|
+
if (failure.failed && (failure.isMaxBuffer || failure.code === "ENOENT")) throw result;
|
|
3290
|
+
flushPipeDecoders();
|
|
3291
|
+
let stdoutSpoolBytes;
|
|
3292
|
+
let stdoutSpoolDiskExhausted;
|
|
3293
|
+
if (stdoutSpoolTailer && stdoutSpool) {
|
|
3294
|
+
await stdoutSpoolTailer.drain();
|
|
3295
|
+
stdoutSpoolFailure ??= stdoutSpoolTailer.failure();
|
|
3296
|
+
try {
|
|
3297
|
+
await stdoutSpool.finalize();
|
|
3298
|
+
stdoutSpoolBytes = await stdoutSpoolTailer.finish();
|
|
3299
|
+
stdoutSpoolFailure ??= stdoutSpoolTailer.failure();
|
|
3300
|
+
} catch (error) {
|
|
3301
|
+
stdoutSpoolFailure ??= {
|
|
3302
|
+
reason: "stdout_spool_read_failed",
|
|
3303
|
+
message: `finalizing the provider stdout spool failed: ${error instanceof Error ? error.message : String(error)}`
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
const freeBytes = spoolFilesystemFreeBytes(stdoutSpool.dir);
|
|
3307
|
+
if (freeBytes !== void 0 && freeBytes < FILE_BACKED_STDOUT_MIN_FREE_BYTES) {
|
|
3308
|
+
stdoutSpoolDiskExhausted = true;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
const spoolMeasuredBytes = stdoutSpoolBytes !== void 0 && stdoutSpoolBytes > 0 ? stdoutSpoolBytes : void 0;
|
|
3312
|
+
if (useStreamingOutput) {
|
|
3313
|
+
streamingStdoutProcessor?.flush();
|
|
3314
|
+
streamingStderrProcessor?.flush();
|
|
3315
|
+
if (streamCallbackError) {
|
|
3316
|
+
throw streamCallbackError;
|
|
3317
|
+
}
|
|
3318
|
+
} else {
|
|
3319
|
+
processOutput(
|
|
3320
|
+
resolveResultStdout(result),
|
|
3321
|
+
result.stderr,
|
|
3322
|
+
onData,
|
|
3323
|
+
onErrorData,
|
|
3324
|
+
formatLine,
|
|
3325
|
+
passThroughUnformatted
|
|
3326
|
+
);
|
|
3327
|
+
}
|
|
3328
|
+
if (stdoutSpoolFailure !== void 0) {
|
|
3329
|
+
throw new ProviderExecutionError(
|
|
3330
|
+
`${metadata.name} CLI stdout capture failed: ${stdoutSpoolFailure.message}`,
|
|
3331
|
+
{
|
|
3332
|
+
retryable: false,
|
|
3333
|
+
exitCode: result.exitCode ?? void 0,
|
|
3334
|
+
reason: stdoutSpoolFailure.reason,
|
|
3335
|
+
providerName: metadata.name,
|
|
3336
|
+
includeStdoutTail: true,
|
|
3337
|
+
...providerExecutionTransportStreamOptions(
|
|
3338
|
+
capturedStdout.toString(),
|
|
3339
|
+
result.stderr || capturedStderr.toString(),
|
|
3340
|
+
{ streamBytes: { stdout: spoolMeasuredBytes } }
|
|
3341
|
+
)
|
|
3342
|
+
}
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
if (completionSignalEarlyExit && terminationState.getPrimaryRecord() === null && resultIsOurCancellation(result)) {
|
|
3346
|
+
return {
|
|
3347
|
+
stdout: capturedStdout.toString() || stdoutChunks.join("") || resolveResultStdout(result) || "",
|
|
3348
|
+
stderr: capturedStderr.toString() || stderrChunks.join("") || result.stderr || "",
|
|
3349
|
+
exitCode: 0,
|
|
3350
|
+
...stdoutSpoolBytes !== void 0 ? { stdoutBytes: stdoutSpoolBytes } : {},
|
|
3351
|
+
...stdoutSpoolDiskExhausted ? { stdoutSpoolDiskExhausted: true } : {}
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
const successfulTermination = terminationState.getPrimaryRecord();
|
|
3355
|
+
if (result.exitCode === 0 && successfulTermination !== null) {
|
|
3356
|
+
const detailSuffix = successfulTermination.detail ? ` (${successfulTermination.detail})` : "";
|
|
3357
|
+
const preservePartialOutput = shouldPreserveTerminationPartialOutput(successfulTermination);
|
|
3358
|
+
const terminationStdout = resolveResultStdout(result) || (preservePartialOutput ? capturedStdout.toString() || stdoutChunks.join("") : "");
|
|
3359
|
+
const terminationStderr = result.stderr || (preservePartialOutput ? capturedStderr.toString() || stderrChunks.join("") : "");
|
|
3360
|
+
throw new ProviderExecutionError(
|
|
3361
|
+
`${metadata.name} CLI terminated (${successfulTermination.reason} via ${successfulTermination.source})${detailSuffix}.${formatTerminationRemedy(successfulTermination.reason, timeout)}`,
|
|
3362
|
+
{
|
|
3363
|
+
retryable: false,
|
|
3364
|
+
exitCode: resolveTerminationExitCode(
|
|
3365
|
+
successfulTermination.reason,
|
|
3366
|
+
result.exitCode ?? void 0
|
|
3367
|
+
),
|
|
3368
|
+
reason: successfulTermination.reason,
|
|
3369
|
+
signal: hasSignal(result) ? result.signal : void 0,
|
|
3370
|
+
terminationSource: successfulTermination.source,
|
|
3371
|
+
...providerExecutionTransportStreamOptions(terminationStdout, terminationStderr, {
|
|
3372
|
+
streamBytes: { stdout: spoolMeasuredBytes }
|
|
3373
|
+
})
|
|
3374
|
+
}
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
if (result.exitCode !== 0) {
|
|
3378
|
+
const bufferedStderr = stderrChunks.join("").trim();
|
|
3379
|
+
const bufferedStdout = useStreamingOutput || useFileBackedStdout ? stdoutChunks.join("").trim() : "";
|
|
3380
|
+
const rawErrorOutput = result.stderr?.trim() || bufferedStderr || bufferedStdout || resolveResultStdout(result)?.trim() || "no error output";
|
|
3381
|
+
const decoratedErrorOutput = rawErrorOutput === "no error output" ? `${rawErrorOutput} (command: ${commandSignature})` : rawErrorOutput;
|
|
3382
|
+
const selectedLine = selectBestErrorSummary(
|
|
3383
|
+
decoratedErrorOutput,
|
|
3384
|
+
bufferedStdout || resolveResultStdout(result)?.trim()
|
|
3385
|
+
) ?? "";
|
|
3386
|
+
const sanitizedSelectedLine = selectedLine ? sanitizeError(new Error(selectedLine)).message : selectedLine;
|
|
3387
|
+
const termination = terminationState.getPrimaryRecord();
|
|
3388
|
+
if (termination !== null) {
|
|
3389
|
+
const signal = hasSignal(result) ? result.signal : void 0;
|
|
3390
|
+
const detailSuffix = termination.detail ? ` (${termination.detail})` : "";
|
|
3391
|
+
const preservePartialOutput = shouldPreserveTerminationPartialOutput(termination);
|
|
3392
|
+
const terminationStdout = resolveResultStdout(result) || (preservePartialOutput ? capturedStdout.toString() || stdoutChunks.join("") : "");
|
|
3393
|
+
const terminationStderr = result.stderr || (preservePartialOutput ? capturedStderr.toString() || stderrChunks.join("") : "");
|
|
3394
|
+
throw new ProviderExecutionError(
|
|
3395
|
+
`${metadata.name} CLI terminated (${termination.reason} via ${termination.source})${detailSuffix}: ${sanitizedSelectedLine}${formatTerminationRemedy(termination.reason, timeout)}`,
|
|
3396
|
+
{
|
|
3397
|
+
retryable: false,
|
|
3398
|
+
exitCode: resolveTerminationExitCode(termination.reason, result.exitCode ?? void 0),
|
|
3399
|
+
reason: termination.reason,
|
|
3400
|
+
signal,
|
|
3401
|
+
terminationSource: termination.source,
|
|
3402
|
+
...providerExecutionTransportStreamOptions(
|
|
3403
|
+
terminationStdout,
|
|
3404
|
+
terminationStderr || bufferedStderr || "",
|
|
3405
|
+
{ streamBytes: { stdout: spoolMeasuredBytes } }
|
|
3406
|
+
)
|
|
3407
|
+
}
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
let terminationReason;
|
|
3411
|
+
let reason;
|
|
3412
|
+
if (result.exitCode === void 0) {
|
|
3413
|
+
if (hasSignal(result)) {
|
|
3414
|
+
const signal = result.signal;
|
|
3415
|
+
if (signal === "SIGTERM") {
|
|
3416
|
+
terminationReason = "killed by SIGTERM signal (timeout or manual termination)";
|
|
3417
|
+
reason = "signal_sigterm";
|
|
3418
|
+
} else if (signal === "SIGKILL") {
|
|
3419
|
+
terminationReason = "force killed by SIGKILL signal (unresponsive process)";
|
|
3420
|
+
reason = "signal_sigkill";
|
|
3421
|
+
} else {
|
|
3422
|
+
terminationReason = `killed by signal ${signal}`;
|
|
3423
|
+
reason = `signal_${signal.toLowerCase()}`;
|
|
3424
|
+
}
|
|
3425
|
+
} else {
|
|
3426
|
+
terminationReason = "crashed or terminated abnormally (no exit code)";
|
|
3427
|
+
reason = "abnormal_termination";
|
|
3428
|
+
}
|
|
3429
|
+
} else {
|
|
3430
|
+
const decodedSignal = exitCodeToSignal(result.exitCode, result.signal ?? void 0);
|
|
3431
|
+
if (decodedSignal) {
|
|
3432
|
+
if (decodedSignal === "SIGTERM") {
|
|
3433
|
+
terminationReason = `killed by SIGTERM signal (exit code ${result.exitCode})`;
|
|
3434
|
+
reason = "signal_sigterm";
|
|
3435
|
+
} else if (decodedSignal === "SIGKILL") {
|
|
3436
|
+
terminationReason = `force killed by SIGKILL signal (exit code ${result.exitCode})`;
|
|
3437
|
+
reason = "signal_sigkill";
|
|
3438
|
+
} else {
|
|
3439
|
+
terminationReason = `killed by signal ${decodedSignal} (exit code ${result.exitCode})`;
|
|
3440
|
+
reason = `signal_${decodedSignal.toLowerCase()}`;
|
|
3441
|
+
}
|
|
3442
|
+
} else {
|
|
3443
|
+
terminationReason = `exited with code ${result.exitCode}`;
|
|
3444
|
+
reason = "non_zero_exit";
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
const fullStdout = resolveResultStdout(result) || (useStreamingOutput || useFileBackedStdout ? capturedStdout.toString() || bufferedStdout : "") || "";
|
|
3448
|
+
const fullStderr = result.stderr || bufferedStderr || "";
|
|
3449
|
+
if (reason === "non_zero_exit") {
|
|
3450
|
+
const denial = sandboxPathDenialError(metadata, sandboxed, {
|
|
3451
|
+
exitCode: result.exitCode,
|
|
3452
|
+
stdout: fullStdout,
|
|
3453
|
+
stderr: fullStderr,
|
|
3454
|
+
stdoutBytes: spoolMeasuredBytes
|
|
3455
|
+
});
|
|
3456
|
+
if (denial) throw denial;
|
|
3457
|
+
}
|
|
3458
|
+
throw new ProviderExecutionError(
|
|
3459
|
+
`${metadata.name} CLI ${terminationReason}: ${sanitizedSelectedLine}`,
|
|
3460
|
+
{
|
|
3461
|
+
retryable: false,
|
|
3462
|
+
exitCode: result.exitCode ?? void 0,
|
|
3463
|
+
reason,
|
|
3464
|
+
signal: result.signal ?? void 0,
|
|
3465
|
+
...providerExecutionTransportStreamOptions(fullStdout, fullStderr, {
|
|
3466
|
+
streamBytes: { stdout: spoolMeasuredBytes }
|
|
3467
|
+
})
|
|
3468
|
+
}
|
|
3469
|
+
);
|
|
3470
|
+
}
|
|
3471
|
+
return {
|
|
3472
|
+
stdout: useFileBackedStdout ? resolveResultStdout(result) || "" : useStreamingOutput ? capturedStdout.toString() : result.stdout || "",
|
|
3473
|
+
stderr: useStreamingOutput ? capturedStderr.toString() : result.stderr || "",
|
|
3474
|
+
exitCode: result.exitCode,
|
|
3475
|
+
// Authoritative size from the spool inode. `stdout` above is
|
|
3476
|
+
// display-bounded, so a caller reporting a stream size must use this.
|
|
3477
|
+
// Reported only when bytes were actually measured: a zero-byte spool
|
|
3478
|
+
// alongside non-empty execa output means the redirect did not apply, and
|
|
3479
|
+
// zero would then be a claim about a stream this never saw.
|
|
3480
|
+
...spoolMeasuredBytes !== void 0 ? { stdoutBytes: spoolMeasuredBytes } : {},
|
|
3481
|
+
...stdoutSpoolDiskExhausted ? { stdoutSpoolDiskExhausted: true } : {}
|
|
3482
|
+
};
|
|
3483
|
+
} catch (error) {
|
|
3484
|
+
flushPipeDecoders();
|
|
3485
|
+
let failurePathSpoolBytes;
|
|
3486
|
+
if (stdoutSpoolTailer) {
|
|
3487
|
+
try {
|
|
3488
|
+
failurePathSpoolBytes = await stdoutSpoolTailer.finish();
|
|
3489
|
+
} catch {
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
if (streamCallbackError && error === streamCallbackError) {
|
|
3493
|
+
throw error;
|
|
3494
|
+
}
|
|
3495
|
+
if (completionSignalEarlyExit && terminationState.getPrimaryRecord() === null && resultIsOurCancellation(error)) {
|
|
3496
|
+
return {
|
|
3497
|
+
stdout: capturedStdout.toString() || stdoutChunks.join(""),
|
|
3498
|
+
stderr: capturedStderr.toString() || stderrChunks.join(""),
|
|
3499
|
+
exitCode: 0
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
throw classifyAndWrapExecaError({
|
|
3503
|
+
error,
|
|
3504
|
+
metadata,
|
|
3505
|
+
terminationState,
|
|
3506
|
+
startTime,
|
|
3507
|
+
timeout,
|
|
3508
|
+
maxBufferBytes,
|
|
3509
|
+
command,
|
|
3510
|
+
args,
|
|
3511
|
+
workingDir,
|
|
3512
|
+
// Under a file-backed stdout the execa error carries no stdout at all —
|
|
3513
|
+
// the child wrote to the spool, not to a pipe execa was reading. Without
|
|
3514
|
+
// these the timeout and abort classifications would lose every byte of
|
|
3515
|
+
// partial output, which is precisely the evidence a hung provider run is
|
|
3516
|
+
// diagnosed from.
|
|
3517
|
+
capturedStdout: capturedStdout.toString() || void 0,
|
|
3518
|
+
capturedStderr: capturedStderr.toString() || void 0,
|
|
3519
|
+
...failurePathSpoolBytes !== void 0 && failurePathSpoolBytes > 0 ? { streamBytes: { stdout: failurePathSpoolBytes } } : {}
|
|
3520
|
+
});
|
|
3521
|
+
} finally {
|
|
3522
|
+
if (completionSignalGraceTimer) {
|
|
3523
|
+
clearTimeout(completionSignalGraceTimer);
|
|
3524
|
+
}
|
|
3525
|
+
if (stdoutSpool) {
|
|
3526
|
+
await stdoutSpool.dispose().catch(() => {
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
await drainProviderTerminationGrace(terminationController);
|
|
3530
|
+
cleanupProviderExecutionResources({
|
|
3531
|
+
watchdogInterval,
|
|
3532
|
+
idleMonitorInterval,
|
|
3533
|
+
terminationController,
|
|
3534
|
+
abortSignal,
|
|
3535
|
+
abortListener,
|
|
3536
|
+
subprocess,
|
|
3537
|
+
stdoutDataListener,
|
|
3538
|
+
stderrListener,
|
|
3539
|
+
plmProcessId,
|
|
3540
|
+
limiterId,
|
|
3541
|
+
trackedPid,
|
|
3542
|
+
workingDir,
|
|
3543
|
+
providerName: metadata.name
|
|
3544
|
+
});
|
|
3545
|
+
if (processGroupOwnership.detach && await ensureDetachedProviderProcessGroupGone(providerProcessGroupPid)) {
|
|
3546
|
+
emitProviderProcessGroupClearedMarker(providerProcessGroupPid);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
export {
|
|
3552
|
+
emitBoundaryTriggered,
|
|
3553
|
+
setProcessLifecycleManager,
|
|
3554
|
+
getProcessLifecycleManager,
|
|
3555
|
+
resolveFileBackedStdoutOverride,
|
|
3556
|
+
isNonEmptyRawChunk,
|
|
3557
|
+
rawChunkToBuffer,
|
|
3558
|
+
composeRawChunkObservers,
|
|
3559
|
+
createPrivateSpool,
|
|
3560
|
+
BUILD_PROCESS_GROUP_OWNER_ENV,
|
|
3561
|
+
BUILD_PROVIDER_PROCESS_GROUP_MARKER_PREFIX,
|
|
3562
|
+
BUILD_PROVIDER_PROCESS_GROUP_CLEARED_MARKER_PREFIX,
|
|
3563
|
+
isProviderProcessGroupMarkerLine,
|
|
3564
|
+
resolveProviderProcessGroupOwnership,
|
|
3565
|
+
acquireProcessSlot,
|
|
3566
|
+
hasAcceptedPolicyBoundaryProof,
|
|
3567
|
+
emitHostClassBypassNotice,
|
|
3568
|
+
assertProviderBypassHasBoundaryProof,
|
|
3569
|
+
sandboxPathDenialError,
|
|
3570
|
+
MemoryGuardian,
|
|
3571
|
+
killAllFallbackTrackedSubprocesses,
|
|
3572
|
+
TMUX_COMPLETION_SIGNAL_GRACE_MS,
|
|
3573
|
+
createLineDelimitedTmuxCompletionSignal,
|
|
3574
|
+
withVariantInvocation,
|
|
3575
|
+
activeVariantServiceTier,
|
|
3576
|
+
prepareVariantCommand,
|
|
3577
|
+
recordVariantTmuxLaunch,
|
|
3578
|
+
computePromptFingerprint,
|
|
3579
|
+
DEFAULT_TIMEOUT,
|
|
3580
|
+
executeProviderCommand,
|
|
3581
|
+
executeProviderCommandAsync
|
|
3582
|
+
};
|
|
3583
|
+
//# sourceMappingURL=chunk-633XZ7UF.js.map
|