reeval 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -64
- package/THIRD_PARTY_NOTICES.md +288 -112
- package/assets/package.json +1 -1
- package/assets/pnpm-lock.yaml +30 -30
- package/dist/adapter-Bwy3QxAK.js +279 -0
- package/dist/adapter-Bwy3QxAK.js.map +1 -0
- package/dist/{agent-6FefIhX7.js → agent-D483ru7D.js} +6 -6
- package/dist/{agent-6FefIhX7.js.map → agent-D483ru7D.js.map} +1 -1
- package/dist/{agent-WSmTaesf.js → agent-DpE80WPz.js} +46 -15
- package/dist/agent-DpE80WPz.js.map +1 -0
- package/dist/{analysis-DlGBDjTj.js → analysis-Bxub9LMT.js} +4 -4
- package/dist/{analysis-DlGBDjTj.js.map → analysis-Bxub9LMT.js.map} +1 -1
- package/dist/assemble-D5KGrwnJ.js +1 -0
- package/dist/{assemble-BCZzTyR7.js → assemble-D9GWr-HS.js} +69 -41
- package/dist/assemble-D9GWr-HS.js.map +1 -0
- package/dist/{client-Bbo9SPlK.js → assert-EUIoRdIt.js} +10 -113
- package/dist/assert-EUIoRdIt.js.map +1 -0
- package/dist/bridge/index.mjs +260 -60
- package/dist/{capture-Bp74Z6Fr.js → capture-BQlGHiMC.js} +146 -32
- package/dist/capture-BQlGHiMC.js.map +1 -0
- package/dist/capture-CYGLpWxR.js +1 -0
- package/dist/{cli-CVrMh_qz.js → cli-ClpPT8kn.js} +229 -537
- package/dist/cli-ClpPT8kn.js.map +1 -0
- package/dist/{cli-spec-CKEYa8V2.js → cli-spec-qmEnQ25_.js} +50 -22
- package/dist/cli-spec-qmEnQ25_.js.map +1 -0
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/client-fcwgwOAD.js +106 -0
- package/dist/client-fcwgwOAD.js.map +1 -0
- package/dist/{command-DUT5l13I.js → command-Bj39y-Wm.js} +329 -87
- package/dist/command-Bj39y-Wm.js.map +1 -0
- package/dist/{command-BFP0hKEb.js → command-BlM0y8jN.js} +14 -14
- package/dist/{command-BFP0hKEb.js.map → command-BlM0y8jN.js.map} +1 -1
- package/dist/command-C-cVjlny.js +1 -0
- package/dist/{command-CzIFp1JQ.js → command-DbAe1Rzb.js} +19 -19
- package/dist/command-DbAe1Rzb.js.map +1 -0
- package/dist/{command-C5xFPKHM.js → command-TgbzPizb.js} +378 -74
- package/dist/command-TgbzPizb.js.map +1 -0
- package/dist/commands-CthqwkAn.js +1 -0
- package/dist/{commands-CLmPBrTS.js → commands-YUwlyTbr.js} +14 -11
- package/dist/commands-YUwlyTbr.js.map +1 -0
- package/dist/config-DrVSEVN0.js +774 -0
- package/dist/config-DrVSEVN0.js.map +1 -0
- package/dist/{context-DbSMEVKs.js → context-C1mxXh80.js} +11 -3
- package/dist/context-C1mxXh80.js.map +1 -0
- package/dist/contrasts-5zsj32gk.js +101 -0
- package/dist/contrasts-5zsj32gk.js.map +1 -0
- package/dist/db-CKg5NtiW.js +2 -0
- package/dist/{db-CfQFwJtq.js → db-DfhipHEF.js} +467 -38
- package/dist/db-DfhipHEF.js.map +1 -0
- package/dist/debug-D7bEv_UV.js +1 -0
- package/dist/{dist-CTzThfu_.js → dist-CuY4ehEk.js} +95 -15
- package/dist/dist-CuY4ehEk.js.map +1 -0
- package/dist/{docker-CRQGQbIt.js → docker-D1HZDS6q.js} +22 -21
- package/dist/docker-D1HZDS6q.js.map +1 -0
- package/dist/docker-DNmMPOBb.js +1 -0
- package/dist/{docker-DV2GNgle.js → docker-DOvjji9I.js} +3 -3
- package/dist/docker-DOvjji9I.js.map +1 -0
- package/dist/{doctor-DlKycx5H.js → doctor-D2MbNO8L.js} +262 -84
- package/dist/doctor-D2MbNO8L.js.map +1 -0
- package/dist/{egress-BhzNh7Yi.js → egress-2-qzPvGi.js} +146 -15
- package/dist/egress-2-qzPvGi.js.map +1 -0
- package/dist/environment-Cca6a4wB.js +1132 -0
- package/dist/environment-Cca6a4wB.js.map +1 -0
- package/dist/{errors-yDAEwmZo.js → errors-B6BSMs03.js} +2 -2
- package/dist/errors-B6BSMs03.js.map +1 -0
- package/dist/{errors-C9w_ud7g.js → errors-r025B0Zb.js} +4 -4
- package/dist/errors-r025B0Zb.js.map +1 -0
- package/dist/evaluate-DKIl54Hb.js +2348 -0
- package/dist/evaluate-DKIl54Hb.js.map +1 -0
- package/dist/evaluate-DMyaGQEj.d.ts +92 -0
- package/dist/files-BAt0i00X.js +1 -0
- package/dist/{files-DJUFg-4e.js → files-BXHZFWmx.js} +5 -5
- package/dist/{files-DJUFg-4e.js.map → files-BXHZFWmx.js.map} +1 -1
- package/dist/files-Bk1mC3AN.js +43 -0
- package/dist/files-Bk1mC3AN.js.map +1 -0
- package/dist/{files-CSzVADHn.js → files-BpUYBwGu.js} +11 -10
- package/dist/{files-CSzVADHn.js.map → files-BpUYBwGu.js.map} +1 -1
- package/dist/files-Bw8lWEjg.js +1 -0
- package/dist/files-D6J0iSdx.js +1 -0
- package/dist/{git-DfG-YJ1u.js → git-Crl3b87o.js} +2 -2
- package/dist/{git-DfG-YJ1u.js.map → git-Crl3b87o.js.map} +1 -1
- package/dist/harness-CEuntrla.js +323 -0
- package/dist/harness-CEuntrla.js.map +1 -0
- package/dist/harness.d.ts +16 -30
- package/dist/harness.js +1 -2
- package/dist/{header-CALhmafp.js → header-DHQv_ZrF.js} +2 -2
- package/dist/{header-CALhmafp.js.map → header-DHQv_ZrF.js.map} +1 -1
- package/dist/{images-BMgdpMAX.js → images-Bi3aGxb9.js} +114 -45
- package/dist/images-Bi3aGxb9.js.map +1 -0
- package/dist/index.d.ts +9 -37
- package/dist/index.js +6 -2138
- package/dist/{init-BIdLF0F9.js → init-Cl3f3NBT.js} +26 -8
- package/dist/init-Cl3f3NBT.js.map +1 -0
- package/dist/init-fhOXED8F.js +1 -0
- package/dist/{invocations-CLaL8AuW.js → invocations-BYDPe3fV.js} +4 -4
- package/dist/{invocations-CLaL8AuW.js.map → invocations-BYDPe3fV.js.map} +1 -1
- package/dist/{judge-CeYPN8P_.js → judge-DW_GDOmB.js} +11 -19
- package/dist/{judge-CeYPN8P_.js.map → judge-DW_GDOmB.js.map} +1 -1
- package/dist/judge.d.ts +2 -2
- package/dist/judge.js +1 -1
- package/dist/{live-S0qODb6o.js → live-jlfszAbb.js} +9 -20
- package/dist/live-jlfszAbb.js.map +1 -0
- package/dist/matchers-Ddmc2z7H.js +118 -0
- package/dist/matchers-Ddmc2z7H.js.map +1 -0
- package/dist/model-CXhrziBe.js +1 -0
- package/dist/{model-B1BZoZ9b.js → model-D0IcOQao.js} +22 -20
- package/dist/model-D0IcOQao.js.map +1 -0
- package/dist/{model-DEQo3FkZ.js → model-DaVCY3rb.js} +2 -2
- package/dist/{model-DEQo3FkZ.js.map → model-DaVCY3rb.js.map} +1 -1
- package/dist/{model-BOnlAPzs.js → model-DaX4bNLZ.js} +123 -22
- package/dist/model-DaX4bNLZ.js.map +1 -0
- package/dist/model-_bxNrk2h.js +1 -0
- package/dist/{node-Co7EXge0.js → node-B3ydHo6t.js} +10 -47
- package/dist/node-B3ydHo6t.js.map +1 -0
- package/dist/object-CLf2-tNq.js +9 -0
- package/dist/object-CLf2-tNq.js.map +1 -0
- package/dist/{opencode-providers-ByD85Q0l.js → opencode-providers-J9colP0-.js} +96 -21
- package/dist/opencode-providers-J9colP0-.js.map +1 -0
- package/dist/options-C0d0AzeR.js +1118 -0
- package/dist/options-C0d0AzeR.js.map +1 -0
- package/dist/{pairs-DDsjgm5F.js → pairs-CnInRu-G.js} +76 -2
- package/dist/{pairs-DDsjgm5F.js.map → pairs-CnInRu-G.js.map} +1 -1
- package/dist/{patch-bDV8YFW0.js → patch-B1bnfhN-.js} +6 -6
- package/dist/{patch-bDV8YFW0.js.map → patch-B1bnfhN-.js.map} +1 -1
- package/dist/{plan-DUydFc_1.js → plan-DgQxp9yy.js} +34 -13
- package/dist/plan-DgQxp9yy.js.map +1 -0
- package/dist/{preflight-BkoSxf4C.js → preflight-BRzD30L5.js} +8 -7
- package/dist/preflight-BRzD30L5.js.map +1 -0
- package/dist/preflight-DlQvbySb.js +1 -0
- package/dist/{prepare-CUAWHebF.js → prepare-B_y-AFMv.js} +38 -30
- package/dist/prepare-B_y-AFMv.js.map +1 -0
- package/dist/prepare-DCKTLHN3.js +2 -0
- package/dist/{provenance-D7HK3vzt.js → provenance-DQ0AXn6c.js} +5 -5
- package/dist/{provenance-D7HK3vzt.js.map → provenance-DQ0AXn6c.js.map} +1 -1
- package/dist/provider-DehR4CMN.js +1 -0
- package/dist/providers-B9yTdIqF.js +1 -0
- package/dist/providers-DoTq1jHW.js +165 -0
- package/dist/providers-DoTq1jHW.js.map +1 -0
- package/dist/query-BO-ux7RB.js +139 -0
- package/dist/query-BO-ux7RB.js.map +1 -0
- package/dist/query-CRQfsw0e.js +1 -0
- package/dist/questions-C2dym2ce.js +524 -0
- package/dist/questions-C2dym2ce.js.map +1 -0
- package/dist/{record-BRP9vZrj.js → record-BKmMvZ6O.js} +9 -1
- package/dist/{record-BRP9vZrj.js.map → record-BKmMvZ6O.js.map} +1 -1
- package/dist/remote-CPPW86Q9.js +378 -0
- package/dist/remote-CPPW86Q9.js.map +1 -0
- package/dist/{results-Di7m8dQ3.js → results-BE2f-1XS.js} +6 -5
- package/dist/results-BE2f-1XS.js.map +1 -0
- package/dist/{resume-SUz1GskV.js → resume-Ca1c8vix.js} +10 -10
- package/dist/{resume-SUz1GskV.js.map → resume-Ca1c8vix.js.map} +1 -1
- package/dist/{root-CvdRlRZX.js → root-DICDHCtp.js} +4 -4
- package/dist/{root-CvdRlRZX.js.map → root-DICDHCtp.js.map} +1 -1
- package/dist/{runtime-DdsfnRz_.js → runtime-PX_oXiCS.js} +23 -71
- package/dist/runtime-PX_oXiCS.js.map +1 -0
- package/dist/sandbox-DLrsvu7U.js +70 -0
- package/dist/sandbox-DLrsvu7U.js.map +1 -0
- package/dist/sandbox.d.ts +19 -6
- package/dist/sandbox.js +1 -2
- package/dist/server-Dp8eOeK5.js +1 -0
- package/dist/{server-BNnXtmS8.js → server-TUY5xA_A.js} +11 -11
- package/dist/server-TUY5xA_A.js.map +1 -0
- package/dist/shared-CA61BuPJ.js +188 -0
- package/dist/shared-CA61BuPJ.js.map +1 -0
- package/dist/{source-Dof0NQBm.js → source-DJGS0a0u.js} +2 -2
- package/dist/{source-Dof0NQBm.js.map → source-DJGS0a0u.js.map} +1 -1
- package/dist/source-DdbLGR1M.js +1 -0
- package/dist/src-BxfCKA3_.js +2 -0
- package/dist/{src-C1E9HDWO.js → src-C7_1SqDP.js} +3 -3
- package/dist/{src-C1E9HDWO.js.map → src-C7_1SqDP.js.map} +1 -1
- package/dist/{src-DDNXsD9H.js → src-CF17Ug08.js} +5 -4
- package/dist/src-CF17Ug08.js.map +1 -0
- package/dist/{src-C_4Umgh2.js → src-CFp86GtV.js} +52 -51
- package/dist/src-CFp86GtV.js.map +1 -0
- package/dist/src-CLoOMVWI.js +3 -0
- package/dist/src-CcrQbpG8.js +2 -0
- package/dist/src-DCFtJRp0.js +2 -0
- package/dist/src-DIL0jST0.js +368 -0
- package/dist/src-DIL0jST0.js.map +1 -0
- package/dist/src-DSYYPEOj.js +2 -0
- package/dist/src-DYO3DdnQ.js +1 -0
- package/dist/src-DvCg0NBs.js +324 -0
- package/dist/src-DvCg0NBs.js.map +1 -0
- package/dist/{src-COgE7991.js → src-DvK0EIZx.js} +5 -5
- package/dist/{src-COgE7991.js.map → src-DvK0EIZx.js.map} +1 -1
- package/dist/{src-DVy58-zZ.js → src-FGXhkEkH.js} +5 -5
- package/dist/{src-DVy58-zZ.js.map → src-FGXhkEkH.js.map} +1 -1
- package/dist/src-WD-zgQYZ.js +2 -0
- package/dist/src-_oI9AePb.js +2 -0
- package/dist/{src-8rRXy4u9.js → src-ftu5wPSt.js} +1652 -182
- package/dist/src-ftu5wPSt.js.map +1 -0
- package/dist/src-qsKCqv7a.js +2 -0
- package/dist/{src-D95O-Smv.js → src-u8_ixn_F.js} +15 -11
- package/dist/src-u8_ixn_F.js.map +1 -0
- package/dist/state-CeR6oz0k.js +66 -0
- package/dist/state-CeR6oz0k.js.map +1 -0
- package/dist/{state-CiXyLqg0.js → state-DfpHox6b.js} +36 -24
- package/dist/state-DfpHox6b.js.map +1 -0
- package/dist/state-DpAxYyED.js +1 -0
- package/dist/studio-app/assets/index-CBkhNAv8.js +58 -0
- package/dist/studio-app/assets/index-CwQN3f_h.css +2 -0
- package/dist/studio-app/index.html +2 -2
- package/dist/suite-Cw_7G2RM.js +429 -0
- package/dist/suite-Cw_7G2RM.js.map +1 -0
- package/dist/suite-DamAEnNF.js +1 -0
- package/dist/summary-BUKfU-9l.js +611 -0
- package/dist/summary-BUKfU-9l.js.map +1 -0
- package/dist/{summary-2OAWF2N7.js → summary-Baa4rTHS.js} +7 -7
- package/dist/{summary-2OAWF2N7.js.map → summary-Baa4rTHS.js.map} +1 -1
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +3 -2
- package/dist/{tool-target-BU1JnSE9.js → tool-target-D2mcYlSQ.js} +14 -7
- package/dist/tool-target-D2mcYlSQ.js.map +1 -0
- package/dist/{tools-B10Uj7OI.js → tools-DLHgetpx.js} +6 -6
- package/dist/tools-DLHgetpx.js.map +1 -0
- package/dist/transcript-BNNANU8A.js +64 -0
- package/dist/transcript-BNNANU8A.js.map +1 -0
- package/dist/{types-D8RLfkFP.d.ts → types-BYdlwMwo.d.ts} +398 -46
- package/dist/{usage-DDF_6tkV.js → usage-CleNKkly.js} +4 -2
- package/dist/{usage-DDF_6tkV.js.map → usage-CleNKkly.js.map} +1 -1
- package/dist/view/index.d.ts +290 -8
- package/dist/view/index.js +7 -7
- package/dist/{views-Dqa5ojfZ.js → views-B8w2J-gD.js} +19 -24
- package/dist/views-B8w2J-gD.js.map +1 -0
- package/dist/views-DblnI3kA.js +2 -0
- package/dist/{worker-entry-BAikHl9F.js → worker-entry-DNnveLqx.js} +16 -229
- package/dist/worker-entry-DNnveLqx.js.map +1 -0
- package/dist/{workspace-C-TrNIUc.js → workspace-C4RA3srV.js} +7 -7
- package/dist/{workspace-C-TrNIUc.js.map → workspace-C4RA3srV.js.map} +1 -1
- package/dist/workspace-C_X0tdv6.js +1 -0
- package/package.json +12 -12
- package/dist/agent-WSmTaesf.js.map +0 -1
- package/dist/assemble-BCZzTyR7.js.map +0 -1
- package/dist/assemble-C5qq5yT2.js +0 -2
- package/dist/capture-Bp74Z6Fr.js.map +0 -1
- package/dist/capture-DS-cPyuE.js +0 -2
- package/dist/cli-CVrMh_qz.js.map +0 -1
- package/dist/cli-spec-CKEYa8V2.js.map +0 -1
- package/dist/client-Bbo9SPlK.js.map +0 -1
- package/dist/command-BrmXUC-W.js +0 -2
- package/dist/command-C5xFPKHM.js.map +0 -1
- package/dist/command-CzIFp1JQ.js.map +0 -1
- package/dist/command-DUT5l13I.js.map +0 -1
- package/dist/commands-CLmPBrTS.js.map +0 -1
- package/dist/commands-fI3G2Wnh.js +0 -2
- package/dist/config-CGvBeewH.js +0 -457
- package/dist/config-CGvBeewH.js.map +0 -1
- package/dist/context-DbSMEVKs.js.map +0 -1
- package/dist/db-CSSFtGcN.js +0 -3
- package/dist/db-CfQFwJtq.js.map +0 -1
- package/dist/debug-SEmW4w8f.js +0 -2
- package/dist/dist-CTzThfu_.js.map +0 -1
- package/dist/docker-CRQGQbIt.js.map +0 -1
- package/dist/docker-DV2GNgle.js.map +0 -1
- package/dist/docker-Df_YOxxB.js +0 -2
- package/dist/doctor-DlKycx5H.js.map +0 -1
- package/dist/duration-JsJrIWXq.js +0 -26
- package/dist/duration-JsJrIWXq.js.map +0 -1
- package/dist/egress-BhzNh7Yi.js.map +0 -1
- package/dist/env-BQqj93gu.js +0 -21
- package/dist/env-BQqj93gu.js.map +0 -1
- package/dist/environment-1jE-HhO-.js +0 -657
- package/dist/environment-1jE-HhO-.js.map +0 -1
- package/dist/errors-C9w_ud7g.js.map +0 -1
- package/dist/errors-yDAEwmZo.js.map +0 -1
- package/dist/fake-instructions-CUI33I_F.js +0 -32
- package/dist/fake-instructions-CUI33I_F.js.map +0 -1
- package/dist/family-D2b7WD10.js +0 -67
- package/dist/family-D2b7WD10.js.map +0 -1
- package/dist/files-AiEKuEZe.js +0 -2
- package/dist/files-BM78_nnF.js +0 -2
- package/dist/files-BMCvfB4X.js +0 -2
- package/dist/harness-CeCp2GSW.js +0 -171
- package/dist/harness-CeCp2GSW.js.map +0 -1
- package/dist/images-BMgdpMAX.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init-B6vegaP_.js +0 -2
- package/dist/init-BIdLF0F9.js.map +0 -1
- package/dist/jiti-BcJHCtrb.js +0 -46
- package/dist/jiti-BcJHCtrb.js.map +0 -1
- package/dist/live-S0qODb6o.js.map +0 -1
- package/dist/model-B1BZoZ9b.js.map +0 -1
- package/dist/model-BOnlAPzs.js.map +0 -1
- package/dist/model-DWeg5J7z.js +0 -2
- package/dist/model-FXko2kl7.js +0 -2
- package/dist/node-Co7EXge0.js.map +0 -1
- package/dist/opencode-providers-ByD85Q0l.js.map +0 -1
- package/dist/options-CKqyPGxX.js +0 -739
- package/dist/options-CKqyPGxX.js.map +0 -1
- package/dist/plan-DUydFc_1.js.map +0 -1
- package/dist/preflight-BkoSxf4C.js.map +0 -1
- package/dist/preflight-D5LW7Oqi.js +0 -2
- package/dist/prepare-CUAWHebF.js.map +0 -1
- package/dist/prepare-oji2oO8W.js +0 -3
- package/dist/process-CSVO_jsc.js +0 -119
- package/dist/process-CSVO_jsc.js.map +0 -1
- package/dist/provider-BDbNOQhQ.js +0 -2
- package/dist/providers-B1DzMvmH.js +0 -107
- package/dist/providers-B1DzMvmH.js.map +0 -1
- package/dist/providers-CpEfNU1F.js +0 -2
- package/dist/query-BjBMLa-C.js +0 -91
- package/dist/query-BjBMLa-C.js.map +0 -1
- package/dist/query-oPO_XrTy.js +0 -2
- package/dist/questions-B8by4Bbt.js +0 -234
- package/dist/questions-B8by4Bbt.js.map +0 -1
- package/dist/results-Di7m8dQ3.js.map +0 -1
- package/dist/runtime-DdsfnRz_.js.map +0 -1
- package/dist/sandbox-D5v-of6d.js +0 -139
- package/dist/sandbox-D5v-of6d.js.map +0 -1
- package/dist/server-BNnXtmS8.js.map +0 -1
- package/dist/server-BNwoKzlm.js +0 -2
- package/dist/source-Bk_0ZaEY.js +0 -2
- package/dist/src-8rRXy4u9.js.map +0 -1
- package/dist/src-BGWuWp6I.js +0 -3
- package/dist/src-BRt0O02i.js +0 -3
- package/dist/src-Bx2x1ekD.js +0 -3
- package/dist/src-C_4Umgh2.js.map +0 -1
- package/dist/src-D95O-Smv.js.map +0 -1
- package/dist/src-DDNXsD9H.js.map +0 -1
- package/dist/src-DLa-ESW0.js +0 -4
- package/dist/src-cN0aB-WR.js +0 -3
- package/dist/src-cdIVjC5b.js +0 -2
- package/dist/src-sIsDHWYq.js +0 -3
- package/dist/state-Bo5zg4da.js +0 -2
- package/dist/state-CiXyLqg0.js.map +0 -1
- package/dist/studio-app/assets/index-B9MCTfuu.css +0 -2
- package/dist/studio-app/assets/index-CROH2NB3.js +0 -58
- package/dist/summary-BTksrIxn.js +0 -254
- package/dist/summary-BTksrIxn.js.map +0 -1
- package/dist/tool-target-BU1JnSE9.js.map +0 -1
- package/dist/tools-B10Uj7OI.js.map +0 -1
- package/dist/views-Cs2Oqayd.js +0 -3
- package/dist/views-Dqa5ojfZ.js.map +0 -1
- package/dist/worker-entry-BAikHl9F.js.map +0 -1
- package/dist/workspace-Do_SiWBs.js +0 -2
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { u as ownFileURL } from "./root-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { basename, dirname, join, posix, relative } from "node:path";
|
|
1
|
+
import { u as ownFileURL } from "./root-DICDHCtp.js";
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, readlinkSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { basename, dirname, join, posix, relative, win32 } from "node:path";
|
|
5
5
|
import net, { BlockList, isIP } from "node:net";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
6
|
import { createHash, generateKeyPairSync, randomBytes, randomInt, sign } from "node:crypto";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
8
9
|
import { createHarnessSandboxTemplate } from "@ai-sdk/harness/agent";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
10
10
|
import { isUtf8 } from "node:buffer";
|
|
11
11
|
import { Duplex, Readable, Transform } from "node:stream";
|
|
12
12
|
import http from "node:http";
|
|
@@ -14,6 +14,1073 @@ import http2 from "node:http2";
|
|
|
14
14
|
import https from "node:https";
|
|
15
15
|
import tls from "node:tls";
|
|
16
16
|
import { lookup } from "node:dns/promises";
|
|
17
|
+
//#region ../sandbox-docker/src/seccomp-profile.ts
|
|
18
|
+
const DOCKER_SECCOMP_PROFILE = {
|
|
19
|
+
defaultAction: "SCMP_ACT_ERRNO",
|
|
20
|
+
defaultErrnoRet: 1,
|
|
21
|
+
archMap: [
|
|
22
|
+
{
|
|
23
|
+
architecture: "SCMP_ARCH_X86_64",
|
|
24
|
+
subArchitectures: ["SCMP_ARCH_X86", "SCMP_ARCH_X32"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
architecture: "SCMP_ARCH_AARCH64",
|
|
28
|
+
subArchitectures: ["SCMP_ARCH_ARM"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
architecture: "SCMP_ARCH_MIPS64",
|
|
32
|
+
subArchitectures: ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64N32"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
architecture: "SCMP_ARCH_MIPS64N32",
|
|
36
|
+
subArchitectures: ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64"]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
architecture: "SCMP_ARCH_MIPSEL64",
|
|
40
|
+
subArchitectures: ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64N32"]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
architecture: "SCMP_ARCH_MIPSEL64N32",
|
|
44
|
+
subArchitectures: ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
architecture: "SCMP_ARCH_S390X",
|
|
48
|
+
subArchitectures: ["SCMP_ARCH_S390"]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
architecture: "SCMP_ARCH_RISCV64",
|
|
52
|
+
subArchitectures: null
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
architecture: "SCMP_ARCH_LOONGARCH64",
|
|
56
|
+
subArchitectures: null
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
syscalls: [
|
|
60
|
+
{
|
|
61
|
+
names: [
|
|
62
|
+
"accept",
|
|
63
|
+
"accept4",
|
|
64
|
+
"access",
|
|
65
|
+
"adjtimex",
|
|
66
|
+
"alarm",
|
|
67
|
+
"bind",
|
|
68
|
+
"brk",
|
|
69
|
+
"cachestat",
|
|
70
|
+
"capget",
|
|
71
|
+
"capset",
|
|
72
|
+
"chdir",
|
|
73
|
+
"chmod",
|
|
74
|
+
"chown",
|
|
75
|
+
"chown32",
|
|
76
|
+
"clock_adjtime",
|
|
77
|
+
"clock_adjtime64",
|
|
78
|
+
"clock_getres",
|
|
79
|
+
"clock_getres_time64",
|
|
80
|
+
"clock_gettime",
|
|
81
|
+
"clock_gettime64",
|
|
82
|
+
"clock_nanosleep",
|
|
83
|
+
"clock_nanosleep_time64",
|
|
84
|
+
"close",
|
|
85
|
+
"close_range",
|
|
86
|
+
"connect",
|
|
87
|
+
"copy_file_range",
|
|
88
|
+
"creat",
|
|
89
|
+
"dup",
|
|
90
|
+
"dup2",
|
|
91
|
+
"dup3",
|
|
92
|
+
"epoll_create",
|
|
93
|
+
"epoll_create1",
|
|
94
|
+
"epoll_ctl",
|
|
95
|
+
"epoll_ctl_old",
|
|
96
|
+
"epoll_pwait",
|
|
97
|
+
"epoll_pwait2",
|
|
98
|
+
"epoll_wait",
|
|
99
|
+
"epoll_wait_old",
|
|
100
|
+
"eventfd",
|
|
101
|
+
"eventfd2",
|
|
102
|
+
"execve",
|
|
103
|
+
"execveat",
|
|
104
|
+
"exit",
|
|
105
|
+
"exit_group",
|
|
106
|
+
"faccessat",
|
|
107
|
+
"faccessat2",
|
|
108
|
+
"fadvise64",
|
|
109
|
+
"fadvise64_64",
|
|
110
|
+
"fallocate",
|
|
111
|
+
"fanotify_mark",
|
|
112
|
+
"fchdir",
|
|
113
|
+
"fchmod",
|
|
114
|
+
"fchmodat",
|
|
115
|
+
"fchmodat2",
|
|
116
|
+
"fchown",
|
|
117
|
+
"fchown32",
|
|
118
|
+
"fchownat",
|
|
119
|
+
"fcntl",
|
|
120
|
+
"fcntl64",
|
|
121
|
+
"fdatasync",
|
|
122
|
+
"fgetxattr",
|
|
123
|
+
"flistxattr",
|
|
124
|
+
"flock",
|
|
125
|
+
"fork",
|
|
126
|
+
"fremovexattr",
|
|
127
|
+
"fsetxattr",
|
|
128
|
+
"fstat",
|
|
129
|
+
"fstat64",
|
|
130
|
+
"fstatat64",
|
|
131
|
+
"fstatfs",
|
|
132
|
+
"fstatfs64",
|
|
133
|
+
"fsync",
|
|
134
|
+
"ftruncate",
|
|
135
|
+
"ftruncate64",
|
|
136
|
+
"futex",
|
|
137
|
+
"futex_requeue",
|
|
138
|
+
"futex_time64",
|
|
139
|
+
"futex_wait",
|
|
140
|
+
"futex_waitv",
|
|
141
|
+
"futex_wake",
|
|
142
|
+
"futimesat",
|
|
143
|
+
"getcpu",
|
|
144
|
+
"getcwd",
|
|
145
|
+
"getdents",
|
|
146
|
+
"getdents64",
|
|
147
|
+
"getegid",
|
|
148
|
+
"getegid32",
|
|
149
|
+
"geteuid",
|
|
150
|
+
"geteuid32",
|
|
151
|
+
"getgid",
|
|
152
|
+
"getgid32",
|
|
153
|
+
"getgroups",
|
|
154
|
+
"getgroups32",
|
|
155
|
+
"getitimer",
|
|
156
|
+
"getpeername",
|
|
157
|
+
"getpgid",
|
|
158
|
+
"getpgrp",
|
|
159
|
+
"getpid",
|
|
160
|
+
"getppid",
|
|
161
|
+
"getpriority",
|
|
162
|
+
"getrandom",
|
|
163
|
+
"getresgid",
|
|
164
|
+
"getresgid32",
|
|
165
|
+
"getresuid",
|
|
166
|
+
"getresuid32",
|
|
167
|
+
"getrlimit",
|
|
168
|
+
"get_robust_list",
|
|
169
|
+
"getrusage",
|
|
170
|
+
"getsid",
|
|
171
|
+
"getsockname",
|
|
172
|
+
"getsockopt",
|
|
173
|
+
"get_thread_area",
|
|
174
|
+
"gettid",
|
|
175
|
+
"gettimeofday",
|
|
176
|
+
"getuid",
|
|
177
|
+
"getuid32",
|
|
178
|
+
"getxattr",
|
|
179
|
+
"getxattrat",
|
|
180
|
+
"inotify_add_watch",
|
|
181
|
+
"inotify_init",
|
|
182
|
+
"inotify_init1",
|
|
183
|
+
"inotify_rm_watch",
|
|
184
|
+
"io_cancel",
|
|
185
|
+
"ioctl",
|
|
186
|
+
"io_destroy",
|
|
187
|
+
"io_getevents",
|
|
188
|
+
"io_pgetevents",
|
|
189
|
+
"io_pgetevents_time64",
|
|
190
|
+
"ioprio_get",
|
|
191
|
+
"ioprio_set",
|
|
192
|
+
"io_setup",
|
|
193
|
+
"io_submit",
|
|
194
|
+
"ipc",
|
|
195
|
+
"kill",
|
|
196
|
+
"landlock_add_rule",
|
|
197
|
+
"landlock_create_ruleset",
|
|
198
|
+
"landlock_restrict_self",
|
|
199
|
+
"lchown",
|
|
200
|
+
"lchown32",
|
|
201
|
+
"lgetxattr",
|
|
202
|
+
"link",
|
|
203
|
+
"linkat",
|
|
204
|
+
"listen",
|
|
205
|
+
"listmount",
|
|
206
|
+
"listxattr",
|
|
207
|
+
"listxattrat",
|
|
208
|
+
"llistxattr",
|
|
209
|
+
"_llseek",
|
|
210
|
+
"lremovexattr",
|
|
211
|
+
"lseek",
|
|
212
|
+
"lsetxattr",
|
|
213
|
+
"lstat",
|
|
214
|
+
"lstat64",
|
|
215
|
+
"madvise",
|
|
216
|
+
"map_shadow_stack",
|
|
217
|
+
"membarrier",
|
|
218
|
+
"memfd_create",
|
|
219
|
+
"memfd_secret",
|
|
220
|
+
"mincore",
|
|
221
|
+
"mkdir",
|
|
222
|
+
"mkdirat",
|
|
223
|
+
"mknod",
|
|
224
|
+
"mknodat",
|
|
225
|
+
"mlock",
|
|
226
|
+
"mlock2",
|
|
227
|
+
"mlockall",
|
|
228
|
+
"mmap",
|
|
229
|
+
"mmap2",
|
|
230
|
+
"mprotect",
|
|
231
|
+
"mq_getsetattr",
|
|
232
|
+
"mq_notify",
|
|
233
|
+
"mq_open",
|
|
234
|
+
"mq_timedreceive",
|
|
235
|
+
"mq_timedreceive_time64",
|
|
236
|
+
"mq_timedsend",
|
|
237
|
+
"mq_timedsend_time64",
|
|
238
|
+
"mq_unlink",
|
|
239
|
+
"mremap",
|
|
240
|
+
"mseal",
|
|
241
|
+
"msgctl",
|
|
242
|
+
"msgget",
|
|
243
|
+
"msgrcv",
|
|
244
|
+
"msgsnd",
|
|
245
|
+
"msync",
|
|
246
|
+
"munlock",
|
|
247
|
+
"munlockall",
|
|
248
|
+
"munmap",
|
|
249
|
+
"name_to_handle_at",
|
|
250
|
+
"nanosleep",
|
|
251
|
+
"newfstatat",
|
|
252
|
+
"_newselect",
|
|
253
|
+
"open",
|
|
254
|
+
"openat",
|
|
255
|
+
"openat2",
|
|
256
|
+
"pause",
|
|
257
|
+
"pidfd_open",
|
|
258
|
+
"pidfd_send_signal",
|
|
259
|
+
"pipe",
|
|
260
|
+
"pipe2",
|
|
261
|
+
"pkey_alloc",
|
|
262
|
+
"pkey_free",
|
|
263
|
+
"pkey_mprotect",
|
|
264
|
+
"poll",
|
|
265
|
+
"ppoll",
|
|
266
|
+
"ppoll_time64",
|
|
267
|
+
"prctl",
|
|
268
|
+
"pread64",
|
|
269
|
+
"preadv",
|
|
270
|
+
"preadv2",
|
|
271
|
+
"prlimit64",
|
|
272
|
+
"process_mrelease",
|
|
273
|
+
"pselect6",
|
|
274
|
+
"pselect6_time64",
|
|
275
|
+
"pwrite64",
|
|
276
|
+
"pwritev",
|
|
277
|
+
"pwritev2",
|
|
278
|
+
"read",
|
|
279
|
+
"readahead",
|
|
280
|
+
"readlink",
|
|
281
|
+
"readlinkat",
|
|
282
|
+
"readv",
|
|
283
|
+
"recv",
|
|
284
|
+
"recvfrom",
|
|
285
|
+
"recvmmsg",
|
|
286
|
+
"recvmmsg_time64",
|
|
287
|
+
"recvmsg",
|
|
288
|
+
"remap_file_pages",
|
|
289
|
+
"removexattr",
|
|
290
|
+
"removexattrat",
|
|
291
|
+
"rename",
|
|
292
|
+
"renameat",
|
|
293
|
+
"renameat2",
|
|
294
|
+
"restart_syscall",
|
|
295
|
+
"riscv_hwprobe",
|
|
296
|
+
"rmdir",
|
|
297
|
+
"rseq",
|
|
298
|
+
"rt_sigaction",
|
|
299
|
+
"rt_sigpending",
|
|
300
|
+
"rt_sigprocmask",
|
|
301
|
+
"rt_sigqueueinfo",
|
|
302
|
+
"rt_sigreturn",
|
|
303
|
+
"rt_sigsuspend",
|
|
304
|
+
"rt_sigtimedwait",
|
|
305
|
+
"rt_sigtimedwait_time64",
|
|
306
|
+
"rt_tgsigqueueinfo",
|
|
307
|
+
"sched_getaffinity",
|
|
308
|
+
"sched_getattr",
|
|
309
|
+
"sched_getparam",
|
|
310
|
+
"sched_get_priority_max",
|
|
311
|
+
"sched_get_priority_min",
|
|
312
|
+
"sched_getscheduler",
|
|
313
|
+
"sched_rr_get_interval",
|
|
314
|
+
"sched_rr_get_interval_time64",
|
|
315
|
+
"sched_setaffinity",
|
|
316
|
+
"sched_setattr",
|
|
317
|
+
"sched_setparam",
|
|
318
|
+
"sched_setscheduler",
|
|
319
|
+
"sched_yield",
|
|
320
|
+
"seccomp",
|
|
321
|
+
"select",
|
|
322
|
+
"semctl",
|
|
323
|
+
"semget",
|
|
324
|
+
"semop",
|
|
325
|
+
"semtimedop",
|
|
326
|
+
"semtimedop_time64",
|
|
327
|
+
"send",
|
|
328
|
+
"sendfile",
|
|
329
|
+
"sendfile64",
|
|
330
|
+
"sendmmsg",
|
|
331
|
+
"sendmsg",
|
|
332
|
+
"sendto",
|
|
333
|
+
"setfsgid",
|
|
334
|
+
"setfsgid32",
|
|
335
|
+
"setfsuid",
|
|
336
|
+
"setfsuid32",
|
|
337
|
+
"setgid",
|
|
338
|
+
"setgid32",
|
|
339
|
+
"setgroups",
|
|
340
|
+
"setgroups32",
|
|
341
|
+
"setitimer",
|
|
342
|
+
"setpgid",
|
|
343
|
+
"setpriority",
|
|
344
|
+
"setregid",
|
|
345
|
+
"setregid32",
|
|
346
|
+
"setresgid",
|
|
347
|
+
"setresgid32",
|
|
348
|
+
"setresuid",
|
|
349
|
+
"setresuid32",
|
|
350
|
+
"setreuid",
|
|
351
|
+
"setreuid32",
|
|
352
|
+
"setrlimit",
|
|
353
|
+
"set_robust_list",
|
|
354
|
+
"setsid",
|
|
355
|
+
"setsockopt",
|
|
356
|
+
"set_thread_area",
|
|
357
|
+
"set_tid_address",
|
|
358
|
+
"setuid",
|
|
359
|
+
"setuid32",
|
|
360
|
+
"setxattr",
|
|
361
|
+
"setxattrat",
|
|
362
|
+
"shmat",
|
|
363
|
+
"shmctl",
|
|
364
|
+
"shmdt",
|
|
365
|
+
"shmget",
|
|
366
|
+
"shutdown",
|
|
367
|
+
"sigaltstack",
|
|
368
|
+
"signalfd",
|
|
369
|
+
"signalfd4",
|
|
370
|
+
"sigprocmask",
|
|
371
|
+
"sigreturn",
|
|
372
|
+
"socketcall",
|
|
373
|
+
"socketpair",
|
|
374
|
+
"splice",
|
|
375
|
+
"stat",
|
|
376
|
+
"stat64",
|
|
377
|
+
"statfs",
|
|
378
|
+
"statfs64",
|
|
379
|
+
"statmount",
|
|
380
|
+
"statx",
|
|
381
|
+
"symlink",
|
|
382
|
+
"symlinkat",
|
|
383
|
+
"sync",
|
|
384
|
+
"sync_file_range",
|
|
385
|
+
"syncfs",
|
|
386
|
+
"sysinfo",
|
|
387
|
+
"tee",
|
|
388
|
+
"tgkill",
|
|
389
|
+
"time",
|
|
390
|
+
"timer_create",
|
|
391
|
+
"timer_delete",
|
|
392
|
+
"timer_getoverrun",
|
|
393
|
+
"timer_gettime",
|
|
394
|
+
"timer_gettime64",
|
|
395
|
+
"timer_settime",
|
|
396
|
+
"timer_settime64",
|
|
397
|
+
"timerfd_create",
|
|
398
|
+
"timerfd_gettime",
|
|
399
|
+
"timerfd_gettime64",
|
|
400
|
+
"timerfd_settime",
|
|
401
|
+
"timerfd_settime64",
|
|
402
|
+
"times",
|
|
403
|
+
"tkill",
|
|
404
|
+
"truncate",
|
|
405
|
+
"truncate64",
|
|
406
|
+
"ugetrlimit",
|
|
407
|
+
"umask",
|
|
408
|
+
"uname",
|
|
409
|
+
"unlink",
|
|
410
|
+
"unlinkat",
|
|
411
|
+
"uretprobe",
|
|
412
|
+
"utime",
|
|
413
|
+
"utimensat",
|
|
414
|
+
"utimensat_time64",
|
|
415
|
+
"utimes",
|
|
416
|
+
"vfork",
|
|
417
|
+
"vmsplice",
|
|
418
|
+
"wait4",
|
|
419
|
+
"waitid",
|
|
420
|
+
"waitpid",
|
|
421
|
+
"write",
|
|
422
|
+
"writev"
|
|
423
|
+
],
|
|
424
|
+
action: "SCMP_ACT_ALLOW"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
names: [
|
|
428
|
+
"process_vm_readv",
|
|
429
|
+
"process_vm_writev",
|
|
430
|
+
"ptrace"
|
|
431
|
+
],
|
|
432
|
+
action: "SCMP_ACT_ALLOW",
|
|
433
|
+
includes: { minKernel: "4.8" }
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
names: ["socket"],
|
|
437
|
+
action: "SCMP_ACT_ALLOW",
|
|
438
|
+
args: [{
|
|
439
|
+
index: 0,
|
|
440
|
+
value: 3,
|
|
441
|
+
op: "SCMP_CMP_LT"
|
|
442
|
+
}]
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
names: ["socket"],
|
|
446
|
+
action: "SCMP_ACT_ALLOW",
|
|
447
|
+
args: [{
|
|
448
|
+
index: 0,
|
|
449
|
+
value: 7,
|
|
450
|
+
op: "SCMP_CMP_EQ"
|
|
451
|
+
}]
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
names: ["socket"],
|
|
455
|
+
action: "SCMP_ACT_ALLOW",
|
|
456
|
+
args: [{
|
|
457
|
+
index: 0,
|
|
458
|
+
value: 8,
|
|
459
|
+
op: "SCMP_CMP_EQ"
|
|
460
|
+
}]
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
names: ["socket"],
|
|
464
|
+
action: "SCMP_ACT_ALLOW",
|
|
465
|
+
args: [{
|
|
466
|
+
index: 0,
|
|
467
|
+
value: 9,
|
|
468
|
+
op: "SCMP_CMP_EQ"
|
|
469
|
+
}]
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
names: ["socket"],
|
|
473
|
+
action: "SCMP_ACT_ALLOW",
|
|
474
|
+
args: [{
|
|
475
|
+
index: 0,
|
|
476
|
+
value: 10,
|
|
477
|
+
op: "SCMP_CMP_EQ"
|
|
478
|
+
}]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
names: ["socket"],
|
|
482
|
+
action: "SCMP_ACT_ALLOW",
|
|
483
|
+
args: [{
|
|
484
|
+
index: 0,
|
|
485
|
+
value: 13,
|
|
486
|
+
op: "SCMP_CMP_EQ"
|
|
487
|
+
}]
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
names: ["socket"],
|
|
491
|
+
action: "SCMP_ACT_ALLOW",
|
|
492
|
+
args: [{
|
|
493
|
+
index: 0,
|
|
494
|
+
value: 14,
|
|
495
|
+
op: "SCMP_CMP_EQ"
|
|
496
|
+
}]
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
names: ["socket"],
|
|
500
|
+
action: "SCMP_ACT_ALLOW",
|
|
501
|
+
args: [{
|
|
502
|
+
index: 0,
|
|
503
|
+
value: 15,
|
|
504
|
+
op: "SCMP_CMP_EQ"
|
|
505
|
+
}]
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
names: ["socket"],
|
|
509
|
+
action: "SCMP_ACT_ALLOW",
|
|
510
|
+
args: [{
|
|
511
|
+
index: 0,
|
|
512
|
+
value: 16,
|
|
513
|
+
op: "SCMP_CMP_EQ"
|
|
514
|
+
}]
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
names: ["socket"],
|
|
518
|
+
action: "SCMP_ACT_ALLOW",
|
|
519
|
+
args: [{
|
|
520
|
+
index: 0,
|
|
521
|
+
value: 17,
|
|
522
|
+
op: "SCMP_CMP_EQ"
|
|
523
|
+
}]
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
names: ["socket"],
|
|
527
|
+
action: "SCMP_ACT_ALLOW",
|
|
528
|
+
args: [{
|
|
529
|
+
index: 0,
|
|
530
|
+
value: 18,
|
|
531
|
+
op: "SCMP_CMP_EQ"
|
|
532
|
+
}]
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
names: ["socket"],
|
|
536
|
+
action: "SCMP_ACT_ALLOW",
|
|
537
|
+
args: [{
|
|
538
|
+
index: 0,
|
|
539
|
+
value: 21,
|
|
540
|
+
op: "SCMP_CMP_EQ"
|
|
541
|
+
}]
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
names: ["socket"],
|
|
545
|
+
action: "SCMP_ACT_ALLOW",
|
|
546
|
+
args: [{
|
|
547
|
+
index: 0,
|
|
548
|
+
value: 22,
|
|
549
|
+
op: "SCMP_CMP_EQ"
|
|
550
|
+
}]
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
names: ["socket"],
|
|
554
|
+
action: "SCMP_ACT_ALLOW",
|
|
555
|
+
args: [{
|
|
556
|
+
index: 0,
|
|
557
|
+
value: 24,
|
|
558
|
+
op: "SCMP_CMP_EQ"
|
|
559
|
+
}]
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
names: ["socket"],
|
|
563
|
+
action: "SCMP_ACT_ALLOW",
|
|
564
|
+
args: [{
|
|
565
|
+
index: 0,
|
|
566
|
+
value: 26,
|
|
567
|
+
op: "SCMP_CMP_EQ"
|
|
568
|
+
}]
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
names: ["socket"],
|
|
572
|
+
action: "SCMP_ACT_ALLOW",
|
|
573
|
+
args: [{
|
|
574
|
+
index: 0,
|
|
575
|
+
value: 27,
|
|
576
|
+
op: "SCMP_CMP_EQ"
|
|
577
|
+
}]
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
names: ["socket"],
|
|
581
|
+
action: "SCMP_ACT_ALLOW",
|
|
582
|
+
args: [{
|
|
583
|
+
index: 0,
|
|
584
|
+
value: 28,
|
|
585
|
+
op: "SCMP_CMP_EQ"
|
|
586
|
+
}]
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
names: ["socket"],
|
|
590
|
+
action: "SCMP_ACT_ALLOW",
|
|
591
|
+
args: [{
|
|
592
|
+
index: 0,
|
|
593
|
+
value: 29,
|
|
594
|
+
op: "SCMP_CMP_EQ"
|
|
595
|
+
}]
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
names: ["socket"],
|
|
599
|
+
action: "SCMP_ACT_ALLOW",
|
|
600
|
+
args: [{
|
|
601
|
+
index: 0,
|
|
602
|
+
value: 30,
|
|
603
|
+
op: "SCMP_CMP_EQ"
|
|
604
|
+
}]
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
names: ["socket"],
|
|
608
|
+
action: "SCMP_ACT_ALLOW",
|
|
609
|
+
args: [{
|
|
610
|
+
index: 0,
|
|
611
|
+
value: 31,
|
|
612
|
+
op: "SCMP_CMP_EQ"
|
|
613
|
+
}]
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
names: ["socket"],
|
|
617
|
+
action: "SCMP_ACT_ALLOW",
|
|
618
|
+
args: [{
|
|
619
|
+
index: 0,
|
|
620
|
+
value: 32,
|
|
621
|
+
op: "SCMP_CMP_EQ"
|
|
622
|
+
}]
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
names: ["socket"],
|
|
626
|
+
action: "SCMP_ACT_ALLOW",
|
|
627
|
+
args: [{
|
|
628
|
+
index: 0,
|
|
629
|
+
value: 33,
|
|
630
|
+
op: "SCMP_CMP_EQ"
|
|
631
|
+
}]
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
names: ["socket"],
|
|
635
|
+
action: "SCMP_ACT_ALLOW",
|
|
636
|
+
args: [{
|
|
637
|
+
index: 0,
|
|
638
|
+
value: 35,
|
|
639
|
+
op: "SCMP_CMP_EQ"
|
|
640
|
+
}]
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
names: ["socket"],
|
|
644
|
+
action: "SCMP_ACT_ALLOW",
|
|
645
|
+
args: [{
|
|
646
|
+
index: 0,
|
|
647
|
+
value: 36,
|
|
648
|
+
op: "SCMP_CMP_EQ"
|
|
649
|
+
}]
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
names: ["socket"],
|
|
653
|
+
action: "SCMP_ACT_ALLOW",
|
|
654
|
+
args: [{
|
|
655
|
+
index: 0,
|
|
656
|
+
value: 39,
|
|
657
|
+
op: "SCMP_CMP_EQ"
|
|
658
|
+
}]
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
names: ["socket"],
|
|
662
|
+
action: "SCMP_ACT_ALLOW",
|
|
663
|
+
args: [{
|
|
664
|
+
index: 0,
|
|
665
|
+
value: 41,
|
|
666
|
+
op: "SCMP_CMP_EQ"
|
|
667
|
+
}]
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
names: ["socket"],
|
|
671
|
+
action: "SCMP_ACT_ALLOW",
|
|
672
|
+
args: [{
|
|
673
|
+
index: 0,
|
|
674
|
+
value: 42,
|
|
675
|
+
op: "SCMP_CMP_EQ"
|
|
676
|
+
}]
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
names: ["socket"],
|
|
680
|
+
action: "SCMP_ACT_ALLOW",
|
|
681
|
+
args: [{
|
|
682
|
+
index: 0,
|
|
683
|
+
value: 43,
|
|
684
|
+
op: "SCMP_CMP_EQ"
|
|
685
|
+
}]
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
names: ["socket"],
|
|
689
|
+
action: "SCMP_ACT_ALLOW",
|
|
690
|
+
args: [{
|
|
691
|
+
index: 0,
|
|
692
|
+
value: 44,
|
|
693
|
+
op: "SCMP_CMP_EQ"
|
|
694
|
+
}]
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
names: ["socket"],
|
|
698
|
+
action: "SCMP_ACT_ALLOW",
|
|
699
|
+
args: [{
|
|
700
|
+
index: 0,
|
|
701
|
+
value: 45,
|
|
702
|
+
op: "SCMP_CMP_EQ"
|
|
703
|
+
}]
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
names: ["personality"],
|
|
707
|
+
action: "SCMP_ACT_ALLOW",
|
|
708
|
+
args: [{
|
|
709
|
+
index: 0,
|
|
710
|
+
value: 0,
|
|
711
|
+
op: "SCMP_CMP_EQ"
|
|
712
|
+
}]
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
names: ["personality"],
|
|
716
|
+
action: "SCMP_ACT_ALLOW",
|
|
717
|
+
args: [{
|
|
718
|
+
index: 0,
|
|
719
|
+
value: 8,
|
|
720
|
+
op: "SCMP_CMP_EQ"
|
|
721
|
+
}]
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
names: ["personality"],
|
|
725
|
+
action: "SCMP_ACT_ALLOW",
|
|
726
|
+
args: [{
|
|
727
|
+
index: 0,
|
|
728
|
+
value: 131072,
|
|
729
|
+
op: "SCMP_CMP_EQ"
|
|
730
|
+
}]
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
names: ["personality"],
|
|
734
|
+
action: "SCMP_ACT_ALLOW",
|
|
735
|
+
args: [{
|
|
736
|
+
index: 0,
|
|
737
|
+
value: 131080,
|
|
738
|
+
op: "SCMP_CMP_EQ"
|
|
739
|
+
}]
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
names: ["personality"],
|
|
743
|
+
action: "SCMP_ACT_ALLOW",
|
|
744
|
+
args: [{
|
|
745
|
+
index: 0,
|
|
746
|
+
value: 4294967295,
|
|
747
|
+
op: "SCMP_CMP_EQ"
|
|
748
|
+
}]
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
names: ["sync_file_range2", "swapcontext"],
|
|
752
|
+
action: "SCMP_ACT_ALLOW",
|
|
753
|
+
includes: { arches: ["ppc64le"] }
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
names: [
|
|
757
|
+
"arm_fadvise64_64",
|
|
758
|
+
"arm_sync_file_range",
|
|
759
|
+
"sync_file_range2",
|
|
760
|
+
"breakpoint",
|
|
761
|
+
"cacheflush",
|
|
762
|
+
"set_tls"
|
|
763
|
+
],
|
|
764
|
+
action: "SCMP_ACT_ALLOW",
|
|
765
|
+
includes: { arches: ["arm", "arm64"] }
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
names: ["arch_prctl"],
|
|
769
|
+
action: "SCMP_ACT_ALLOW",
|
|
770
|
+
includes: { arches: ["amd64", "x32"] }
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
names: ["modify_ldt"],
|
|
774
|
+
action: "SCMP_ACT_ALLOW",
|
|
775
|
+
includes: { arches: [
|
|
776
|
+
"amd64",
|
|
777
|
+
"x32",
|
|
778
|
+
"x86"
|
|
779
|
+
] }
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
names: [
|
|
783
|
+
"s390_pci_mmio_read",
|
|
784
|
+
"s390_pci_mmio_write",
|
|
785
|
+
"s390_runtime_instr"
|
|
786
|
+
],
|
|
787
|
+
action: "SCMP_ACT_ALLOW",
|
|
788
|
+
includes: { arches: ["s390", "s390x"] }
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
names: ["riscv_flush_icache"],
|
|
792
|
+
action: "SCMP_ACT_ALLOW",
|
|
793
|
+
includes: { arches: ["riscv64"] }
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
names: ["open_by_handle_at"],
|
|
797
|
+
action: "SCMP_ACT_ALLOW",
|
|
798
|
+
includes: { caps: ["CAP_DAC_READ_SEARCH"] }
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
names: [
|
|
802
|
+
"bpf",
|
|
803
|
+
"clone",
|
|
804
|
+
"clone3",
|
|
805
|
+
"fanotify_init",
|
|
806
|
+
"fsconfig",
|
|
807
|
+
"fsmount",
|
|
808
|
+
"fsopen",
|
|
809
|
+
"fspick",
|
|
810
|
+
"lookup_dcookie",
|
|
811
|
+
"lsm_get_self_attr",
|
|
812
|
+
"lsm_list_modules",
|
|
813
|
+
"lsm_set_self_attr",
|
|
814
|
+
"mount",
|
|
815
|
+
"mount_setattr",
|
|
816
|
+
"move_mount",
|
|
817
|
+
"open_tree",
|
|
818
|
+
"perf_event_open",
|
|
819
|
+
"quotactl",
|
|
820
|
+
"quotactl_fd",
|
|
821
|
+
"setdomainname",
|
|
822
|
+
"sethostname",
|
|
823
|
+
"setns",
|
|
824
|
+
"syslog",
|
|
825
|
+
"umount",
|
|
826
|
+
"umount2",
|
|
827
|
+
"unshare"
|
|
828
|
+
],
|
|
829
|
+
action: "SCMP_ACT_ALLOW",
|
|
830
|
+
includes: { caps: ["CAP_SYS_ADMIN"] }
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
names: ["clone"],
|
|
834
|
+
action: "SCMP_ACT_ALLOW",
|
|
835
|
+
args: [{
|
|
836
|
+
index: 0,
|
|
837
|
+
value: 2114060288,
|
|
838
|
+
op: "SCMP_CMP_MASKED_EQ"
|
|
839
|
+
}],
|
|
840
|
+
excludes: {
|
|
841
|
+
caps: ["CAP_SYS_ADMIN"],
|
|
842
|
+
arches: ["s390", "s390x"]
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
names: ["clone"],
|
|
847
|
+
action: "SCMP_ACT_ALLOW",
|
|
848
|
+
args: [{
|
|
849
|
+
index: 1,
|
|
850
|
+
value: 2114060288,
|
|
851
|
+
op: "SCMP_CMP_MASKED_EQ"
|
|
852
|
+
}],
|
|
853
|
+
comment: "s390 parameter ordering for clone is different",
|
|
854
|
+
includes: { arches: ["s390", "s390x"] },
|
|
855
|
+
excludes: { caps: ["CAP_SYS_ADMIN"] }
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
names: ["clone3"],
|
|
859
|
+
action: "SCMP_ACT_ERRNO",
|
|
860
|
+
errnoRet: 38,
|
|
861
|
+
excludes: { caps: ["CAP_SYS_ADMIN"] }
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
names: ["reboot"],
|
|
865
|
+
action: "SCMP_ACT_ALLOW",
|
|
866
|
+
includes: { caps: ["CAP_SYS_BOOT"] }
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
names: ["chroot"],
|
|
870
|
+
action: "SCMP_ACT_ALLOW",
|
|
871
|
+
includes: { caps: ["CAP_SYS_CHROOT"] }
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
names: [
|
|
875
|
+
"delete_module",
|
|
876
|
+
"init_module",
|
|
877
|
+
"finit_module"
|
|
878
|
+
],
|
|
879
|
+
action: "SCMP_ACT_ALLOW",
|
|
880
|
+
includes: { caps: ["CAP_SYS_MODULE"] }
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
names: ["acct"],
|
|
884
|
+
action: "SCMP_ACT_ALLOW",
|
|
885
|
+
includes: { caps: ["CAP_SYS_PACCT"] }
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
names: [
|
|
889
|
+
"kcmp",
|
|
890
|
+
"pidfd_getfd",
|
|
891
|
+
"process_madvise",
|
|
892
|
+
"process_vm_readv",
|
|
893
|
+
"process_vm_writev",
|
|
894
|
+
"ptrace"
|
|
895
|
+
],
|
|
896
|
+
action: "SCMP_ACT_ALLOW",
|
|
897
|
+
includes: { caps: ["CAP_SYS_PTRACE"] }
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
names: ["iopl", "ioperm"],
|
|
901
|
+
action: "SCMP_ACT_ALLOW",
|
|
902
|
+
includes: { caps: ["CAP_SYS_RAWIO"] }
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
names: [
|
|
906
|
+
"settimeofday",
|
|
907
|
+
"stime",
|
|
908
|
+
"clock_settime",
|
|
909
|
+
"clock_settime64"
|
|
910
|
+
],
|
|
911
|
+
action: "SCMP_ACT_ALLOW",
|
|
912
|
+
includes: { caps: ["CAP_SYS_TIME"] }
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
names: ["vhangup"],
|
|
916
|
+
action: "SCMP_ACT_ALLOW",
|
|
917
|
+
includes: { caps: ["CAP_SYS_TTY_CONFIG"] }
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
names: [
|
|
921
|
+
"get_mempolicy",
|
|
922
|
+
"mbind",
|
|
923
|
+
"set_mempolicy",
|
|
924
|
+
"set_mempolicy_home_node"
|
|
925
|
+
],
|
|
926
|
+
action: "SCMP_ACT_ALLOW",
|
|
927
|
+
includes: { caps: ["CAP_SYS_NICE"] }
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
names: ["syslog"],
|
|
931
|
+
action: "SCMP_ACT_ALLOW",
|
|
932
|
+
includes: { caps: ["CAP_SYSLOG"] }
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
names: ["bpf"],
|
|
936
|
+
action: "SCMP_ACT_ALLOW",
|
|
937
|
+
includes: { caps: ["CAP_BPF"] }
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
names: ["perf_event_open"],
|
|
941
|
+
action: "SCMP_ACT_ALLOW",
|
|
942
|
+
includes: { caps: ["CAP_PERFMON"] }
|
|
943
|
+
}
|
|
944
|
+
]
|
|
945
|
+
};
|
|
946
|
+
//#endregion
|
|
947
|
+
//#region ../sandbox-docker/src/engine.ts
|
|
948
|
+
let seccompFile;
|
|
949
|
+
/**
|
|
950
|
+
* Docker's default seccomp profile as a file Podman can read, written once per content under the
|
|
951
|
+
* temp directory (a compiled reeval has no real file of its own to point at).
|
|
952
|
+
*/
|
|
953
|
+
function dockerSeccompFile() {
|
|
954
|
+
if (seccompFile && existsSync(seccompFile)) return seccompFile;
|
|
955
|
+
const json = JSON.stringify(DOCKER_SECCOMP_PROFILE);
|
|
956
|
+
const hash = createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
957
|
+
const path = join(tmpdir(), `reeval-seccomp-${hash}.json`);
|
|
958
|
+
if (!existsSync(path)) {
|
|
959
|
+
const partial = `${path}.${process.pid}.tmp`;
|
|
960
|
+
writeFileSync(partial, json);
|
|
961
|
+
renameSync(partial, path);
|
|
962
|
+
}
|
|
963
|
+
seccompFile = path;
|
|
964
|
+
return path;
|
|
965
|
+
}
|
|
966
|
+
const ENGINES = {
|
|
967
|
+
docker: {
|
|
968
|
+
bin: "docker",
|
|
969
|
+
name: "Docker",
|
|
970
|
+
fn: "docker",
|
|
971
|
+
stateFormat: "{{.ServerVersion}} {{.OSType}}",
|
|
972
|
+
capacityFormat: "{{.NCPU}} {{.MemTotal}}",
|
|
973
|
+
imageId: ".Id",
|
|
974
|
+
rmForce: ["rm", "-f"],
|
|
975
|
+
runArgs: () => [],
|
|
976
|
+
createsWorkdir: true
|
|
977
|
+
},
|
|
978
|
+
podman: {
|
|
979
|
+
bin: "podman",
|
|
980
|
+
name: "Podman",
|
|
981
|
+
fn: "podman",
|
|
982
|
+
stateFormat: "{{.Version.Version}} {{.Host.OS}}",
|
|
983
|
+
capacityFormat: "{{.Host.CPUs}} {{.Host.MemTotal}}",
|
|
984
|
+
imageId: ".ID",
|
|
985
|
+
rmForce: [
|
|
986
|
+
"rm",
|
|
987
|
+
"-f",
|
|
988
|
+
"--time",
|
|
989
|
+
"0"
|
|
990
|
+
],
|
|
991
|
+
runArgs: () => [
|
|
992
|
+
"--network",
|
|
993
|
+
"bridge",
|
|
994
|
+
"--security-opt",
|
|
995
|
+
`seccomp=${dockerSeccompFile()}`
|
|
996
|
+
],
|
|
997
|
+
createsWorkdir: false
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
const engineSpec = (engine = "docker") => ENGINES[engine];
|
|
1001
|
+
/**
|
|
1002
|
+
* The `CONTAINER_HOST` that reaches a `podman machine` straight through its API socket (macOS) or
|
|
1003
|
+
* named pipe (Windows), from `podman machine inspect`'s JSON.
|
|
1004
|
+
*/
|
|
1005
|
+
function machineHost(inspect, platform) {
|
|
1006
|
+
const machine = Array.isArray(inspect) ? inspect[0] : void 0;
|
|
1007
|
+
const info = machine?.ConnectionInfo;
|
|
1008
|
+
const name = machine?.Name ?? "podman-machine-default";
|
|
1009
|
+
if (platform === "darwin" && info?.PodmanSocket?.Path) {
|
|
1010
|
+
const path = info.PodmanSocket.Path;
|
|
1011
|
+
return {
|
|
1012
|
+
host: `unix://${path}`,
|
|
1013
|
+
path,
|
|
1014
|
+
name
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
if (platform === "win32" && info?.PodmanPipe?.Path) {
|
|
1018
|
+
const path = info.PodmanPipe.Path;
|
|
1019
|
+
return {
|
|
1020
|
+
host: `npipe://${path.replaceAll("\\", "/")}`,
|
|
1021
|
+
path,
|
|
1022
|
+
name
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* The environment every podman command gets. On macOS and Windows the client talks to the
|
|
1029
|
+
* machine's VM over SSH, a connection per command, and sshd's `MaxStartups` drops some of a burst
|
|
1030
|
+
* (exit 125, `ssh: handshake failed`), so commands go through the machine's API socket instead.
|
|
1031
|
+
* Not when the user picked a host (`CONTAINER_HOST`, `CONTAINER_CONNECTION`, or a default
|
|
1032
|
+
* connection that isn't the machine's), or the socket isn't there.
|
|
1033
|
+
*/
|
|
1034
|
+
function podmanHostEnv(inputs) {
|
|
1035
|
+
const { platform, env } = inputs;
|
|
1036
|
+
if (platform !== "darwin" && platform !== "win32") return {};
|
|
1037
|
+
if (env.CONTAINER_HOST || env.CONTAINER_CONNECTION) return {};
|
|
1038
|
+
const machine = machineHost(inputs.inspect(), platform);
|
|
1039
|
+
if (!machine || !inputs.exists(machine.path)) return {};
|
|
1040
|
+
const connections = inputs.connections();
|
|
1041
|
+
const chosen = Array.isArray(connections) ? connections.find((c) => c.Default)?.Name : void 0;
|
|
1042
|
+
if (chosen && chosen !== machine.name && chosen !== `${machine.name}-root`) return {};
|
|
1043
|
+
return { CONTAINER_HOST: machine.host };
|
|
1044
|
+
}
|
|
1045
|
+
const readJson = (bin, args) => {
|
|
1046
|
+
try {
|
|
1047
|
+
const out = execFileSync(bin, args, {
|
|
1048
|
+
encoding: "utf8",
|
|
1049
|
+
stdio: [
|
|
1050
|
+
"ignore",
|
|
1051
|
+
"pipe",
|
|
1052
|
+
"ignore"
|
|
1053
|
+
],
|
|
1054
|
+
timeout: 1e4,
|
|
1055
|
+
windowsHide: true
|
|
1056
|
+
});
|
|
1057
|
+
return JSON.parse(out);
|
|
1058
|
+
} catch {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
let podmanEnv;
|
|
1063
|
+
/** What every command on `engine` adds to its environment; read once per process. */
|
|
1064
|
+
function engineEnv(engine = "docker") {
|
|
1065
|
+
if (engine !== "podman") return {};
|
|
1066
|
+
podmanEnv ??= podmanHostEnv({
|
|
1067
|
+
platform: process.platform,
|
|
1068
|
+
env: process.env,
|
|
1069
|
+
inspect: () => readJson("podman", ["machine", "inspect"]),
|
|
1070
|
+
connections: () => readJson("podman", [
|
|
1071
|
+
"system",
|
|
1072
|
+
"connection",
|
|
1073
|
+
"list",
|
|
1074
|
+
"--format",
|
|
1075
|
+
"json"
|
|
1076
|
+
]),
|
|
1077
|
+
exists: existsSync
|
|
1078
|
+
});
|
|
1079
|
+
return podmanEnv;
|
|
1080
|
+
}
|
|
1081
|
+
/** Docker's ids are `sha256:…`; Podman's are the bare hex. */
|
|
1082
|
+
const normalizeImageId = (id) => id.includes(":") ? id : `sha256:${id}`;
|
|
1083
|
+
//#endregion
|
|
17
1084
|
//#region ../sandbox-docker/src/build-failure.ts
|
|
18
1085
|
var ImageBuildError = class extends Error {
|
|
19
1086
|
name = "ImageBuildError";
|
|
@@ -27,6 +1094,7 @@ const stripAnsi = (text) => text.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
|
27
1094
|
/** The failing step and its output from `--progress plain` build output. */
|
|
28
1095
|
function parseBuildOutput(text) {
|
|
29
1096
|
const lines = stripAnsi(text).split(/\r?\n/);
|
|
1097
|
+
if (!lines.some((l) => /^(?:#\d+ )?ERROR: /.test(l)) && lines.some((l) => l.startsWith("Error: "))) return parseBuildahOutput(lines);
|
|
30
1098
|
let step = null;
|
|
31
1099
|
let error = null;
|
|
32
1100
|
for (const line of lines) {
|
|
@@ -47,14 +1115,35 @@ function parseBuildOutput(text) {
|
|
|
47
1115
|
exitCode: code ? Number(code) : null
|
|
48
1116
|
};
|
|
49
1117
|
}
|
|
1118
|
+
/**
|
|
1119
|
+
* buildah's output (`podman build`): `STEP n/m: …` headers, each step's output as is, then
|
|
1120
|
+
* `Error: building at STEP "…": …`. Its own warnings are `time="…" level=…` lines.
|
|
1121
|
+
*/
|
|
1122
|
+
function parseBuildahOutput(lines) {
|
|
1123
|
+
let error = null;
|
|
1124
|
+
let output = [];
|
|
1125
|
+
for (const line of lines) if (/^STEP \d+\/\d+: /.test(line)) output = [];
|
|
1126
|
+
else if (line.startsWith("Error: ")) error = line.slice(7);
|
|
1127
|
+
else if (!error && line.trim() && !/^time="[^"]*" level=|^--> /.test(line)) output.push(line);
|
|
1128
|
+
error = (error ?? "").replace(/^building at STEP "(?:[^"\\]|\\.)*": /, "").replace(/ &imagebuilder\.Step\{.*$/, "").trim() || null;
|
|
1129
|
+
const code = /exit status (\d+)$/.exec(error ?? "")?.[1];
|
|
1130
|
+
return {
|
|
1131
|
+
error,
|
|
1132
|
+
output: output.slice(-8),
|
|
1133
|
+
exitCode: code ? Number(code) : null
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
50
1136
|
/** ENOSPC or a SIGKILL (137, usually the out-of-memory killer), from anything the build printed. */
|
|
51
1137
|
function failureCause(text, exitCode) {
|
|
52
1138
|
if (/no space left on device|ENOSPC/i.test(text)) return "enospc";
|
|
53
|
-
if (exitCode === 137 || /exit(?:ed| code
|
|
1139
|
+
if (exitCode === 137 || /exit(?:ed| code:?| status) 137\b|signal: killed|OOMKilled/i.test(text)) return "oom";
|
|
54
1140
|
return null;
|
|
55
1141
|
}
|
|
56
|
-
const causeLine = (cause
|
|
57
|
-
const
|
|
1142
|
+
const causeLine = (cause, engine) => {
|
|
1143
|
+
const { name, bin } = engineSpec(engine);
|
|
1144
|
+
return cause === "enospc" ? `out of disk space (ENOSPC): free space for ${name} (${bin} system df, reeval clean --images) and rerun` : `killed (exit 137): usually out of memory; give ${name} more memory or lower the concurrency`;
|
|
1145
|
+
};
|
|
1146
|
+
const DETERMINISTIC = /did not complete successfully|while running runtime: exit status|did not resolve to an alias|requested access to the resource is denied|dockerfile parse error|unknown instruction|should not be blank|pull access denied|repository does not exist|manifest unknown|not found: manifest|: not found\b|unauthorized|denied:|reeval needs a Debian or Ubuntu based image/i;
|
|
58
1147
|
const TRANSIENT = /EAI_AGAIN|ECONNRESET|ETIMEDOUT|ECONNREFUSED|i\/o timeout|TLS handshake timeout|no such host|temporary failure in name resolution|connection reset by peer|dial tcp|net\/http: request canceled|unexpected EOF|\b(?:500|502|503|504) |429 Too Many Requests|toomanyrequests|Service Unavailable|Bad Gateway|Gateway Timeout|ENOTFOUND/i;
|
|
59
1148
|
/** A registry or network error: a pull, a metadata lookup, or a download in the harness bootstrap. */
|
|
60
1149
|
const isTransient = (text) => !DETERMINISTIC.test(text) && TRANSIENT.test(text);
|
|
@@ -77,7 +1166,7 @@ const RETRY_DELAYS = [2e3, 8e3];
|
|
|
77
1166
|
*/
|
|
78
1167
|
function failureMessage(parts) {
|
|
79
1168
|
const lines = [parts.headline];
|
|
80
|
-
if (parts.cause) lines.push(causeLine(parts.cause));
|
|
1169
|
+
if (parts.cause) lines.push(causeLine(parts.cause, parts.engine));
|
|
81
1170
|
if (parts.output.length) lines.push(...parts.output.map((l) => ` | ${l}`));
|
|
82
1171
|
if (parts.log) lines.push(`(full log: ${parts.log})`);
|
|
83
1172
|
return lines.join("\n");
|
|
@@ -87,6 +1176,119 @@ function outputTail(text, max = 8) {
|
|
|
87
1176
|
return stripAnsi(text).split(/\r?\n/).map((l) => l.trimEnd()).filter((l) => l.trim()).slice(-max);
|
|
88
1177
|
}
|
|
89
1178
|
//#endregion
|
|
1179
|
+
//#region ../sandbox-docker/src/process.ts
|
|
1180
|
+
const CMD_META = /([()\][%!^"`<>&|;, *?])/g;
|
|
1181
|
+
const cmdEscape = (arg, twice) => {
|
|
1182
|
+
const quoted = `"${arg.replace(/(\\*)"/g, "$1$1\\\"").replace(/(\\*)$/, "$1$1")}"`.replace(CMD_META, "^$1");
|
|
1183
|
+
return twice ? quoted.replace(CMD_META, "^$1") : quoted;
|
|
1184
|
+
};
|
|
1185
|
+
/**
|
|
1186
|
+
* How to spawn `cmd` on Windows, where npm, npx and pnpm are `.cmd` scripts that only cmd.exe
|
|
1187
|
+
* runs: the program PATH and PATHEXT find, and for a script, cmd.exe with every argument escaped.
|
|
1188
|
+
*/
|
|
1189
|
+
function windowsSpawn(cmd, args, env = process.env, exists = existsSync) {
|
|
1190
|
+
const exts = (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
1191
|
+
const file = (/[\\/]/.test(cmd) ? [""] : (env.PATH ?? env.Path ?? "").split(";").filter(Boolean)).flatMap((dir) => {
|
|
1192
|
+
const base = dir ? win32.join(dir, cmd) : cmd;
|
|
1193
|
+
return [...win32.extname(cmd) ? [base] : [], ...exts.map((ext) => base + ext.toLowerCase())];
|
|
1194
|
+
}).find(exists) ?? cmd;
|
|
1195
|
+
if (!/\.(cmd|bat)$/i.test(file)) return {
|
|
1196
|
+
file,
|
|
1197
|
+
args,
|
|
1198
|
+
verbatim: false
|
|
1199
|
+
};
|
|
1200
|
+
const twice = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i.test(file);
|
|
1201
|
+
const line = [win32.normalize(file).replace(CMD_META, "^$1"), ...args.map((a) => cmdEscape(a, twice))].join(" ");
|
|
1202
|
+
return {
|
|
1203
|
+
file: env.ComSpec ?? "cmd.exe",
|
|
1204
|
+
args: [
|
|
1205
|
+
"/d",
|
|
1206
|
+
"/s",
|
|
1207
|
+
"/c",
|
|
1208
|
+
`"${line}"`
|
|
1209
|
+
],
|
|
1210
|
+
verbatim: true
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Kills a process and everything it started: its process group on POSIX (a detached child leads
|
|
1215
|
+
* one), its process tree with `taskkill` on Windows, which has neither groups nor signals.
|
|
1216
|
+
*/
|
|
1217
|
+
function killTree(pid) {
|
|
1218
|
+
if (process.platform === "win32") {
|
|
1219
|
+
spawn("taskkill", [
|
|
1220
|
+
"/pid",
|
|
1221
|
+
String(pid),
|
|
1222
|
+
"/t",
|
|
1223
|
+
"/f"
|
|
1224
|
+
], {
|
|
1225
|
+
stdio: "ignore",
|
|
1226
|
+
windowsHide: true
|
|
1227
|
+
}).on("error", () => {});
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
try {
|
|
1231
|
+
process.kill(-pid, "SIGKILL");
|
|
1232
|
+
} catch {
|
|
1233
|
+
try {
|
|
1234
|
+
process.kill(pid, "SIGKILL");
|
|
1235
|
+
} catch {}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
/** `s` as one single-quoted POSIX shell word. */
|
|
1239
|
+
const shellQuote = (s) => `'${s.replaceAll("'", `'\\''`)}'`;
|
|
1240
|
+
function run(cmd, args, options = {}) {
|
|
1241
|
+
const { signal } = options;
|
|
1242
|
+
return new Promise((resolve, reject) => {
|
|
1243
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
1244
|
+
const detached = options.detached ?? true;
|
|
1245
|
+
const target = process.platform === "win32" ? windowsSpawn(cmd, args, options.env ? {
|
|
1246
|
+
...process.env,
|
|
1247
|
+
...options.env
|
|
1248
|
+
} : process.env) : {
|
|
1249
|
+
file: cmd,
|
|
1250
|
+
args,
|
|
1251
|
+
verbatim: false
|
|
1252
|
+
};
|
|
1253
|
+
const child = spawn(target.file, target.args, {
|
|
1254
|
+
cwd: options.cwd,
|
|
1255
|
+
stdio: "pipe",
|
|
1256
|
+
detached: detached && !target.verbatim,
|
|
1257
|
+
windowsHide: true,
|
|
1258
|
+
windowsVerbatimArguments: target.verbatim,
|
|
1259
|
+
env: options.env ? {
|
|
1260
|
+
...process.env,
|
|
1261
|
+
...options.env
|
|
1262
|
+
} : process.env
|
|
1263
|
+
});
|
|
1264
|
+
const out = [];
|
|
1265
|
+
let stderr = "";
|
|
1266
|
+
child.stdout.on("data", (d) => out.push(d));
|
|
1267
|
+
child.stderr.on("data", (d) => {
|
|
1268
|
+
stderr += d;
|
|
1269
|
+
});
|
|
1270
|
+
const onAbort = () => {
|
|
1271
|
+
if ((detached || process.platform === "win32") && child.pid) killTree(child.pid);
|
|
1272
|
+
else child.kill("SIGKILL");
|
|
1273
|
+
};
|
|
1274
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1275
|
+
child.on("error", reject);
|
|
1276
|
+
child.on("close", (code) => {
|
|
1277
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1278
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
1279
|
+
const stdout = Buffer.concat(out);
|
|
1280
|
+
resolve({
|
|
1281
|
+
exitCode: code ?? 1,
|
|
1282
|
+
stdout: options.binary ? stdout : stdout.toString(),
|
|
1283
|
+
stderr
|
|
1284
|
+
});
|
|
1285
|
+
});
|
|
1286
|
+
if (child.pid) options.onSpawn?.(child.pid);
|
|
1287
|
+
child.stdin.on("error", () => {});
|
|
1288
|
+
child.stdin.end(options.input ?? "");
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
//#endregion
|
|
90
1292
|
//#region ../sandbox-docker/src/trace.ts
|
|
91
1293
|
/** Most of each stream a trace keeps; the rest is counted. */
|
|
92
1294
|
const MAX_STREAM = 4096;
|
|
@@ -104,9 +1306,10 @@ function streamPreview(data) {
|
|
|
104
1306
|
if (data.includes(0) || !isUtf8(data)) return `<${data.length} bytes>`;
|
|
105
1307
|
return clip(data.toString("utf8"), MAX_STREAM);
|
|
106
1308
|
}
|
|
107
|
-
function traceDocker(args, startedAt, result) {
|
|
1309
|
+
function traceDocker(args, startedAt, result, bin = "docker") {
|
|
108
1310
|
if (!listener) return;
|
|
109
1311
|
const base = {
|
|
1312
|
+
bin,
|
|
110
1313
|
args: args.map((a) => clip(a, MAX_ARG)),
|
|
111
1314
|
ms: Date.now() - startedAt
|
|
112
1315
|
};
|
|
@@ -133,107 +1336,199 @@ function traceDocker(args, startedAt, result) {
|
|
|
133
1336
|
}
|
|
134
1337
|
//#endregion
|
|
135
1338
|
//#region ../sandbox-docker/src/docker-cli.ts
|
|
136
|
-
/**
|
|
1339
|
+
/** How long to wait before each retry of a command that never reached Podman. */
|
|
1340
|
+
const CONNECTION_RETRY_MS = [
|
|
1341
|
+
100,
|
|
1342
|
+
250,
|
|
1343
|
+
500,
|
|
1344
|
+
1e3,
|
|
1345
|
+
2e3
|
|
1346
|
+
];
|
|
1347
|
+
/**
|
|
1348
|
+
* Whether a podman command failed before reaching the engine: the remote client's SSH handshake
|
|
1349
|
+
* or socket connection to `podman machine`, so running it again is safe.
|
|
1350
|
+
*/
|
|
1351
|
+
const isConnectionFailure = (r) => r.exitCode === 125 && /ssh: handshake failed|unable to connect to Podman socket/.test(r.stderr);
|
|
1352
|
+
const sleep = (ms, signal) => new Promise((resolve) => {
|
|
1353
|
+
const timer = setTimeout(done, ms);
|
|
1354
|
+
function done() {
|
|
1355
|
+
clearTimeout(timer);
|
|
1356
|
+
signal?.removeEventListener("abort", done);
|
|
1357
|
+
resolve();
|
|
1358
|
+
}
|
|
1359
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
1360
|
+
});
|
|
1361
|
+
/**
|
|
1362
|
+
* Runs `attempt` again, after each of `delays`, while its result is a connection failure (see
|
|
1363
|
+
* {@link isConnectionFailure}), and returns the last result.
|
|
1364
|
+
*/
|
|
1365
|
+
async function retryConnection(attempt, options = {}) {
|
|
1366
|
+
const { delays = CONNECTION_RETRY_MS, signal, wait = sleep } = options;
|
|
1367
|
+
let r = await attempt();
|
|
1368
|
+
for (const ms of delays) {
|
|
1369
|
+
if (!isConnectionFailure(r) || signal?.aborted) break;
|
|
1370
|
+
await wait(ms, signal);
|
|
1371
|
+
r = await attempt();
|
|
1372
|
+
}
|
|
1373
|
+
return r;
|
|
1374
|
+
}
|
|
1375
|
+
/** Runs the docker (or podman) CLI. `env` values are passed via the CLI's environment, never argv. */
|
|
137
1376
|
async function docker(args, options = {}) {
|
|
138
|
-
const
|
|
1377
|
+
const { engine, ...rest } = options;
|
|
1378
|
+
const { bin } = engineSpec(engine);
|
|
139
1379
|
const stdin = options.input?.length ?? 0;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
1380
|
+
const env = {
|
|
1381
|
+
...engineEnv(engine),
|
|
1382
|
+
...rest.env
|
|
1383
|
+
};
|
|
1384
|
+
const once = async () => {
|
|
1385
|
+
const startedAt = Date.now();
|
|
1386
|
+
try {
|
|
1387
|
+
const r = await run(bin, args, {
|
|
1388
|
+
...rest,
|
|
1389
|
+
env,
|
|
1390
|
+
binary: true
|
|
1391
|
+
});
|
|
1392
|
+
traceDocker(args, startedAt, {
|
|
1393
|
+
...r,
|
|
1394
|
+
stdin
|
|
1395
|
+
}, bin);
|
|
1396
|
+
return r;
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
traceDocker(args, startedAt, { error }, bin);
|
|
1399
|
+
throw error;
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
return engine === "podman" ? retryConnection(once, { signal: options.signal }) : once();
|
|
154
1403
|
}
|
|
155
1404
|
async function dockerOk(args, opts) {
|
|
156
1405
|
const r = await docker(args, opts);
|
|
157
|
-
if (r.exitCode !== 0) throw new Error(
|
|
1406
|
+
if (r.exitCode !== 0) throw new Error(`${engineSpec(opts?.engine).bin} ${args[0]} failed: ${r.stderr.trim()}`);
|
|
158
1407
|
return r.stdout.toString("utf8").trim();
|
|
159
1408
|
}
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
1409
|
+
/** Whether `rm -f` left the container gone: it removed it, or it was already gone. */
|
|
1410
|
+
const removalDone = (r) => r.exitCode === 0 || /no such container/i.test(r.stderr);
|
|
1411
|
+
/** How long to wait before each retry of a removal that failed. */
|
|
1412
|
+
const REMOVAL_RETRY_MS = [
|
|
1413
|
+
250,
|
|
1414
|
+
500,
|
|
1415
|
+
1e3,
|
|
1416
|
+
2e3,
|
|
1417
|
+
4e3
|
|
1418
|
+
];
|
|
1419
|
+
/**
|
|
1420
|
+
* Removes a container in its own process group, so removal survives this process being killed,
|
|
1421
|
+
* and tries again while it fails. Resolves either way.
|
|
1422
|
+
*/
|
|
1423
|
+
async function removeContainerDetached(id, engine, options = {}) {
|
|
1424
|
+
const { bin, rmForce } = engineSpec(engine);
|
|
1425
|
+
const { delays = REMOVAL_RETRY_MS, wait = sleep } = options;
|
|
1426
|
+
const args = [...rmForce, id];
|
|
1427
|
+
const env = {
|
|
1428
|
+
...process.env,
|
|
1429
|
+
...engineEnv(engine)
|
|
1430
|
+
};
|
|
1431
|
+
const once = () => new Promise((resolve) => {
|
|
163
1432
|
const keepAlive = setInterval(() => {}, 1e3);
|
|
164
1433
|
const startedAt = Date.now();
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"-f",
|
|
168
|
-
id
|
|
169
|
-
];
|
|
1434
|
+
let stderr = "";
|
|
1435
|
+
let settled = false;
|
|
170
1436
|
const done = (exitCode) => {
|
|
1437
|
+
if (settled) return;
|
|
1438
|
+
settled = true;
|
|
171
1439
|
clearInterval(keepAlive);
|
|
172
|
-
traceDocker(args, startedAt, {
|
|
173
|
-
|
|
1440
|
+
traceDocker(args, startedAt, {
|
|
1441
|
+
exitCode,
|
|
1442
|
+
stderr
|
|
1443
|
+
}, bin);
|
|
1444
|
+
resolve({
|
|
1445
|
+
exitCode,
|
|
1446
|
+
stderr
|
|
1447
|
+
});
|
|
174
1448
|
};
|
|
175
|
-
const child = spawn(
|
|
1449
|
+
const child = spawn(bin, args, {
|
|
176
1450
|
detached: true,
|
|
177
|
-
stdio:
|
|
178
|
-
|
|
1451
|
+
stdio: [
|
|
1452
|
+
"ignore",
|
|
1453
|
+
"ignore",
|
|
1454
|
+
"pipe"
|
|
1455
|
+
],
|
|
1456
|
+
windowsHide: true,
|
|
1457
|
+
env
|
|
1458
|
+
});
|
|
1459
|
+
child.stderr.on("data", (d) => {
|
|
1460
|
+
stderr += d;
|
|
1461
|
+
});
|
|
1462
|
+
child.on("error", (error) => {
|
|
1463
|
+
stderr += String(error);
|
|
1464
|
+
done(null);
|
|
179
1465
|
});
|
|
180
|
-
child.on("error", () => done(null));
|
|
181
1466
|
child.on("close", done);
|
|
182
1467
|
});
|
|
1468
|
+
let r = await once();
|
|
1469
|
+
for (const ms of delays) {
|
|
1470
|
+
if (removalDone(r) || r.exitCode === null) return;
|
|
1471
|
+
await wait(ms);
|
|
1472
|
+
r = await once();
|
|
1473
|
+
}
|
|
183
1474
|
}
|
|
184
1475
|
/**
|
|
185
1476
|
* Removes every container with `label` (`key=value`) for the next few seconds, in its own process
|
|
186
1477
|
* group so it outlives this process. For a `docker run` killed mid-way: the daemon may create the
|
|
187
1478
|
* container after the CLI dies, and its id never came back.
|
|
188
1479
|
*/
|
|
189
|
-
function sweepLabelDetached(label, seconds = 15) {
|
|
1480
|
+
function sweepLabelDetached(label, seconds = 15, engine) {
|
|
1481
|
+
const { bin, rmForce } = engineSpec(engine);
|
|
190
1482
|
const child = spawn(process.execPath, [
|
|
191
1483
|
"-e",
|
|
192
1484
|
`const { execFileSync } = require('node:child_process')
|
|
193
|
-
const [filter, seconds] = process.argv.slice(1)
|
|
1485
|
+
const [filter, seconds, bin, ...rm] = process.argv.slice(1)
|
|
194
1486
|
const o = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true }
|
|
195
1487
|
for (let i = 0; i < Number(seconds); i++) {
|
|
196
1488
|
try {
|
|
197
|
-
const ids = execFileSync(
|
|
198
|
-
if (ids.length) execFileSync(
|
|
1489
|
+
const ids = execFileSync(bin, ['ps', '-aq', '--filter', filter], o).split(/\\s+/).filter(Boolean)
|
|
1490
|
+
if (ids.length) execFileSync(bin, [...rm, ...ids], o)
|
|
199
1491
|
} catch {}
|
|
200
1492
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 1000)
|
|
201
1493
|
}`,
|
|
202
1494
|
`label=${label}`,
|
|
203
|
-
String(seconds)
|
|
1495
|
+
String(seconds),
|
|
1496
|
+
bin,
|
|
1497
|
+
...rmForce
|
|
204
1498
|
], {
|
|
205
1499
|
detached: true,
|
|
206
1500
|
stdio: "ignore",
|
|
207
1501
|
windowsHide: true,
|
|
208
1502
|
env: {
|
|
209
1503
|
...process.env,
|
|
1504
|
+
...engineEnv(engine),
|
|
210
1505
|
BUN_BE_BUN: "1"
|
|
211
1506
|
}
|
|
212
1507
|
});
|
|
213
1508
|
child.on("error", () => {});
|
|
214
1509
|
child.unref();
|
|
215
1510
|
}
|
|
216
|
-
async function dockerState() {
|
|
1511
|
+
async function dockerState(engine) {
|
|
217
1512
|
const r = await docker([
|
|
218
1513
|
"info",
|
|
219
1514
|
"--format",
|
|
220
|
-
|
|
221
|
-
]).catch(() => null);
|
|
1515
|
+
engineSpec(engine).stateFormat
|
|
1516
|
+
], { engine }).catch(() => null);
|
|
222
1517
|
const [version, os] = r?.exitCode === 0 ? r.stdout.toString("utf8").trim().split(/\s+/) : [];
|
|
223
1518
|
if (!version) return "down";
|
|
224
1519
|
return os === "windows" ? "windows-containers" : "running";
|
|
225
1520
|
}
|
|
226
|
-
/** Whether Docker is running Linux containers, as every sandbox needs. */
|
|
227
|
-
async function dockerAvailable() {
|
|
228
|
-
return await dockerState() === "running";
|
|
1521
|
+
/** Whether Docker (or Podman) is running Linux containers, as every sandbox needs. */
|
|
1522
|
+
async function dockerAvailable(engine) {
|
|
1523
|
+
return await dockerState(engine) === "running";
|
|
229
1524
|
}
|
|
230
|
-
/** CPUs and memory of the machine containers run on (the VM, under Docker Desktop). */
|
|
231
|
-
async function dockerCapacity() {
|
|
1525
|
+
/** CPUs and memory of the machine containers run on (the VM, under Docker Desktop or `podman machine`). */
|
|
1526
|
+
async function dockerCapacity(engine) {
|
|
232
1527
|
const r = await docker([
|
|
233
1528
|
"info",
|
|
234
1529
|
"--format",
|
|
235
|
-
|
|
236
|
-
]).catch(() => null);
|
|
1530
|
+
engineSpec(engine).capacityFormat
|
|
1531
|
+
], { engine }).catch(() => null);
|
|
237
1532
|
if (r?.exitCode !== 0) return null;
|
|
238
1533
|
const [cpus, memoryBytes] = r.stdout.toString("utf8").trim().split(/\s+/).map(Number);
|
|
239
1534
|
return cpus && memoryBytes ? {
|
|
@@ -247,23 +1542,51 @@ async function dockerCapacity() {
|
|
|
247
1542
|
* their bare local name (`reeval-sandbox@sha256:…`). A bare name is Docker Hub's `library/`
|
|
248
1543
|
* namespace, which only official images can push to, so only a digest whose name has a registry
|
|
249
1544
|
* host or a namespace counts. Official images such as `busybox` count as local; no image with
|
|
250
|
-
* the harness baked in is one.
|
|
1545
|
+
* the harness baked in is one. Podman names local images `localhost/…`, which no one pulls from.
|
|
1546
|
+
*/
|
|
1547
|
+
const isRegistryDigest = (digest) => {
|
|
1548
|
+
const name = digest.split("@")[0] ?? "";
|
|
1549
|
+
return name.includes("/") && !name.startsWith("localhost/");
|
|
1550
|
+
};
|
|
1551
|
+
/** `repository:tag` of every local image in `repository`; none when the CLI fails. */
|
|
1552
|
+
async function imageTags(repository, engine) {
|
|
1553
|
+
const r = await docker([
|
|
1554
|
+
"image",
|
|
1555
|
+
"ls",
|
|
1556
|
+
"--format",
|
|
1557
|
+
"{{.Repository}}:{{.Tag}}",
|
|
1558
|
+
repository
|
|
1559
|
+
], { engine }).catch(() => null);
|
|
1560
|
+
if (r?.exitCode !== 0) return [];
|
|
1561
|
+
return r.stdout.toString("utf8").split(/\r?\n/).filter(Boolean).map((t) => t.replace(/^localhost\//, ""));
|
|
1562
|
+
}
|
|
1563
|
+
/** `image inspect`'s answer when the engine has no such image (Docker's wording, then Podman's). */
|
|
1564
|
+
const isNoSuchImage = (stderr) => /no such image|image not known/i.test(stderr);
|
|
1565
|
+
/**
|
|
1566
|
+
* The image, or null when the engine says it has no such image. A CLI that can't start or a daemon
|
|
1567
|
+
* that doesn't answer throws instead, naming the error: that's not knowing, not a missing image.
|
|
251
1568
|
*/
|
|
252
|
-
|
|
253
|
-
|
|
1569
|
+
async function imageInfo(image, engine) {
|
|
1570
|
+
const { bin, imageId } = engineSpec(engine);
|
|
254
1571
|
const r = await docker([
|
|
255
1572
|
"image",
|
|
256
1573
|
"inspect",
|
|
257
1574
|
"--format",
|
|
258
|
-
|
|
1575
|
+
`{"config":{{json .Config}},"id":{{json ${imageId}}},"repoDigests":{{json .RepoDigests}}}`,
|
|
259
1576
|
image
|
|
260
|
-
]).catch(() =>
|
|
261
|
-
|
|
1577
|
+
], { engine }).catch((error) => {
|
|
1578
|
+
throw new Error(`${bin} image inspect ${image} couldn't run: ${error.message}`);
|
|
1579
|
+
});
|
|
1580
|
+
if (r.exitCode !== 0) {
|
|
1581
|
+
const stderr = r.stderr.trim();
|
|
1582
|
+
if (isNoSuchImage(stderr)) return null;
|
|
1583
|
+
throw new Error(`${bin} image inspect ${image} failed: ${stderr || `exit ${r.exitCode}`}`);
|
|
1584
|
+
}
|
|
262
1585
|
const info = JSON.parse(r.stdout.toString("utf8"));
|
|
263
1586
|
const repoDigests = (info.repoDigests ?? []).filter(isRegistryDigest);
|
|
264
1587
|
return {
|
|
265
1588
|
labels: info.config?.Labels ?? {},
|
|
266
|
-
id: info.id,
|
|
1589
|
+
id: normalizeImageId(info.id),
|
|
267
1590
|
repoDigests
|
|
268
1591
|
};
|
|
269
1592
|
}
|
|
@@ -278,32 +1601,40 @@ async function imageInfo(image) {
|
|
|
278
1601
|
const HARNESS_PACKAGES = {
|
|
279
1602
|
cursor: {
|
|
280
1603
|
name: "@reeval/harness-cursor-sdk",
|
|
281
|
-
version: "0.1.
|
|
1604
|
+
version: "0.1.5"
|
|
282
1605
|
},
|
|
283
1606
|
"claude-code": {
|
|
284
1607
|
name: "@ai-sdk/harness-claude-code",
|
|
285
|
-
version: "1.0.
|
|
286
|
-
patch: "
|
|
1608
|
+
version: "1.0.160",
|
|
1609
|
+
patch: "907f9184"
|
|
287
1610
|
},
|
|
288
1611
|
antigravity: {
|
|
289
1612
|
name: "@reeval/harness-antigravity",
|
|
290
|
-
version: "0.1.
|
|
1613
|
+
version: "0.1.8"
|
|
291
1614
|
},
|
|
292
1615
|
opencode: {
|
|
293
1616
|
name: "@reeval/harness-opencode",
|
|
294
|
-
version: "0.2.
|
|
1617
|
+
version: "0.2.8"
|
|
295
1618
|
},
|
|
296
1619
|
goose: {
|
|
297
1620
|
name: "@reeval/harness-goose",
|
|
298
|
-
version: "0.1.
|
|
1621
|
+
version: "0.1.3"
|
|
299
1622
|
},
|
|
300
1623
|
pi: {
|
|
301
1624
|
name: "@reeval/harness-pi",
|
|
302
|
-
version: "0.2.
|
|
1625
|
+
version: "0.2.7"
|
|
303
1626
|
},
|
|
304
1627
|
codex: {
|
|
305
1628
|
name: "@reeval/harness-codex",
|
|
306
|
-
version: "0.1.
|
|
1629
|
+
version: "0.1.7"
|
|
1630
|
+
},
|
|
1631
|
+
fx: {
|
|
1632
|
+
name: "@reeval/harness-fx",
|
|
1633
|
+
version: "0.1.0"
|
|
1634
|
+
},
|
|
1635
|
+
"grok-build": {
|
|
1636
|
+
name: "@reeval/harness-grok-build",
|
|
1637
|
+
version: "0.1.0"
|
|
307
1638
|
}
|
|
308
1639
|
};
|
|
309
1640
|
/** Every harness with an image, in {@link HARNESS_PACKAGES} order. */
|
|
@@ -668,7 +1999,9 @@ const HARNESS_HOSTS = {
|
|
|
668
1999
|
opencode: ["generativelanguage.googleapis.com", "mcp.exa.ai"],
|
|
669
2000
|
goose: ["generativelanguage.googleapis.com"],
|
|
670
2001
|
pi: ["generativelanguage.googleapis.com"],
|
|
671
|
-
codex: ["api.openai.com"]
|
|
2002
|
+
codex: ["api.openai.com"],
|
|
2003
|
+
fx: ["ai-gateway.vercel.sh"],
|
|
2004
|
+
"grok-build": ["api.x.ai"]
|
|
672
2005
|
};
|
|
673
2006
|
/** The package registries and GitHub, for a restricted policy that still installs. */
|
|
674
2007
|
const PACKAGE_HOSTS = Object.freeze([
|
|
@@ -1782,14 +3115,14 @@ const PROXY_PORT = 43128;
|
|
|
1782
3115
|
const CA_PATH = "/usr/local/share/ca-certificates/reeval-egress.crt";
|
|
1783
3116
|
/** Debian's system bundle, which `update-ca-certificates` rebuilds with the gateway's CA in it. */
|
|
1784
3117
|
const CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
|
|
1785
|
-
const BUNDLED = "
|
|
3118
|
+
const BUNDLED = "reeval:ca-bundle";
|
|
1786
3119
|
const READY_TIMEOUT_MS = 15e3;
|
|
1787
3120
|
/**
|
|
1788
3121
|
* The gateway's side of the container runs as root, whoever the sandbox runs as: the sandbox user
|
|
1789
3122
|
* can't signal, trace or replace the forwarder, or change the CA it installed.
|
|
1790
3123
|
*/
|
|
1791
3124
|
const AS_ROOT = ["--user", "0:0"];
|
|
1792
|
-
const installCa = (containerId, cert, signal) => docker([
|
|
3125
|
+
const installCa = (containerId, cert, signal, engine) => docker([
|
|
1793
3126
|
"exec",
|
|
1794
3127
|
"--interactive",
|
|
1795
3128
|
...AS_ROOT,
|
|
@@ -1799,15 +3132,16 @@ const installCa = (containerId, cert, signal) => docker([
|
|
|
1799
3132
|
`umask 022 && mkdir -p "$(dirname ${CA_PATH})" && cat > ${CA_PATH} && { command -v update-ca-certificates >/dev/null && update-ca-certificates >/dev/null 2>&1 && grep -q "$(sed -n 2p ${CA_PATH})" ${CA_BUNDLE} && echo ${BUNDLED}; true; }`
|
|
1800
3133
|
], {
|
|
1801
3134
|
input: cert,
|
|
1802
|
-
signal
|
|
3135
|
+
signal,
|
|
3136
|
+
engine
|
|
1803
3137
|
});
|
|
1804
3138
|
/**
|
|
1805
3139
|
* Starts the egress gateway for one container: a forwarder inside it, tunnelled over one
|
|
1806
3140
|
* `docker exec -i`, and the proxy in this process, which holds the real credentials.
|
|
1807
3141
|
*/
|
|
1808
|
-
async function startGateway(containerId, options, signal) {
|
|
3142
|
+
async function startGateway(containerId, options, signal, engine) {
|
|
1809
3143
|
const ca = createCertificateAuthority();
|
|
1810
|
-
const installed = await installCa(containerId, ca.cert, signal);
|
|
3144
|
+
const installed = await installCa(containerId, ca.cert, signal, engine);
|
|
1811
3145
|
if (installed.exitCode !== 0) throw new Error(`couldn't install the gateway CA: ${installed.stderr.trim()}`);
|
|
1812
3146
|
const bundled = installed.stdout.toString("utf8").includes(BUNDLED);
|
|
1813
3147
|
const proxy = createEgressProxy({
|
|
@@ -1824,14 +3158,19 @@ async function startGateway(containerId, options, signal) {
|
|
|
1824
3158
|
forwarderScript(PROXY_PORT)
|
|
1825
3159
|
];
|
|
1826
3160
|
const startedAt = Date.now();
|
|
1827
|
-
const
|
|
3161
|
+
const { bin } = engineSpec(engine);
|
|
3162
|
+
const child = spawn(bin, args, {
|
|
1828
3163
|
stdio: "pipe",
|
|
1829
|
-
detached: true
|
|
3164
|
+
detached: true,
|
|
3165
|
+
env: {
|
|
3166
|
+
...process.env,
|
|
3167
|
+
...engineEnv(engine)
|
|
3168
|
+
}
|
|
1830
3169
|
});
|
|
1831
3170
|
child.once("close", (code) => traceDocker(args, startedAt, {
|
|
1832
3171
|
exitCode: code,
|
|
1833
3172
|
stderr
|
|
1834
|
-
}));
|
|
3173
|
+
}, bin));
|
|
1835
3174
|
let stderr = "";
|
|
1836
3175
|
child.stderr.on("data", (d) => {
|
|
1837
3176
|
stderr += d;
|
|
@@ -1982,14 +3321,21 @@ const ROOT_CAPABILITIES = [
|
|
|
1982
3321
|
"SETUID",
|
|
1983
3322
|
"SETGID"
|
|
1984
3323
|
];
|
|
1985
|
-
|
|
3324
|
+
const hostCpus = /* @__PURE__ */ new Map();
|
|
1986
3325
|
/** Docker refuses `--cpus` above the host's count, so cap it there. */
|
|
1987
|
-
async function cpuLimit(cpus) {
|
|
1988
|
-
|
|
1989
|
-
|
|
3326
|
+
async function cpuLimit(cpus, engine = "docker") {
|
|
3327
|
+
let known = hostCpus.get(engine);
|
|
3328
|
+
if (!known) {
|
|
3329
|
+
known = dockerCapacity(engine).then((c) => c?.cpus, () => void 0);
|
|
3330
|
+
hostCpus.set(engine, known);
|
|
3331
|
+
}
|
|
3332
|
+
const available = await known;
|
|
1990
3333
|
return String(available ? Math.min(cpus, available) : cpus);
|
|
1991
3334
|
}
|
|
1992
|
-
/**
|
|
3335
|
+
/**
|
|
3336
|
+
* The `docker run` flags that confine the container: its user, capabilities and limits, and on
|
|
3337
|
+
* Podman the bridge network and Docker's seccomp profile.
|
|
3338
|
+
*/
|
|
1993
3339
|
async function confinementArgs(options) {
|
|
1994
3340
|
const limits = {
|
|
1995
3341
|
...DEFAULT_LIMITS,
|
|
@@ -2011,7 +3357,8 @@ async function confinementArgs(options) {
|
|
|
2011
3357
|
"--pids-limit",
|
|
2012
3358
|
String(limits.pids),
|
|
2013
3359
|
"--cpus",
|
|
2014
|
-
await cpuLimit(limits.cpus)
|
|
3360
|
+
await cpuLimit(limits.cpus, options.engine),
|
|
3361
|
+
...engineSpec(options.engine).runArgs()
|
|
2015
3362
|
];
|
|
2016
3363
|
}
|
|
2017
3364
|
/** Runtime `npm i -g` goes to the sandbox user's own prefix, whose `bin` the images put on PATH. */
|
|
@@ -2023,14 +3370,14 @@ const EXEC_MARKER = "REEVAL_EXEC_ID";
|
|
|
2023
3370
|
* `memory.events` and v1's `memory.oom_control` both have an `oom_kill` line. Without either,
|
|
2024
3371
|
* Docker's `OOMKilled` says only whether there was one.
|
|
2025
3372
|
*/
|
|
2026
|
-
async function oomKills(id) {
|
|
3373
|
+
async function oomKills(id, engine) {
|
|
2027
3374
|
const events = await docker([
|
|
2028
3375
|
"exec",
|
|
2029
3376
|
id,
|
|
2030
3377
|
"sh",
|
|
2031
3378
|
"-c",
|
|
2032
3379
|
"cat /sys/fs/cgroup/memory.events 2>/dev/null || cat /sys/fs/cgroup/memory/memory.oom_control"
|
|
2033
|
-
]).catch(() => null);
|
|
3380
|
+
], { engine }).catch(() => null);
|
|
2034
3381
|
const count = events?.stdout.toString("utf8").match(/^oom_kill (\d+)$/m)?.[1];
|
|
2035
3382
|
if (events?.exitCode === 0 && count !== void 0) return Number(count);
|
|
2036
3383
|
const inspected = await docker([
|
|
@@ -2038,21 +3385,31 @@ async function oomKills(id) {
|
|
|
2038
3385
|
"--format",
|
|
2039
3386
|
"{{.State.OOMKilled}}",
|
|
2040
3387
|
id
|
|
2041
|
-
]).catch(() => null);
|
|
3388
|
+
], { engine }).catch(() => null);
|
|
2042
3389
|
if (inspected?.exitCode !== 0) return null;
|
|
2043
3390
|
const killed = inspected.stdout.toString("utf8").trim();
|
|
2044
3391
|
return killed === "true" ? 1 : killed === "false" ? 0 : null;
|
|
2045
3392
|
}
|
|
2046
3393
|
/** The Docker networks a container is attached to. */
|
|
2047
|
-
async function networksOf(id) {
|
|
3394
|
+
async function networksOf(id, engine) {
|
|
2048
3395
|
const out = await dockerOk([
|
|
2049
3396
|
"inspect",
|
|
2050
3397
|
"--format",
|
|
2051
3398
|
"{{json .NetworkSettings.Networks}}",
|
|
2052
3399
|
id
|
|
2053
|
-
]);
|
|
3400
|
+
], { engine });
|
|
2054
3401
|
return Object.keys(JSON.parse(out) ?? {});
|
|
2055
3402
|
}
|
|
3403
|
+
/** How the container is networked when it's on no network: `host`, `slirp4netns`, `pasta`… */
|
|
3404
|
+
async function networkMode(id, engine) {
|
|
3405
|
+
const r = await docker([
|
|
3406
|
+
"inspect",
|
|
3407
|
+
"--format",
|
|
3408
|
+
"{{.HostConfig.NetworkMode}}",
|
|
3409
|
+
id
|
|
3410
|
+
], { engine }).catch(() => null);
|
|
3411
|
+
return r?.exitCode === 0 ? r.stdout.toString("utf8").trim() || "unknown" : "unknown";
|
|
3412
|
+
}
|
|
2056
3413
|
/**
|
|
2057
3414
|
* A harness sandbox session backed by one local Docker container. Commands and file I/O go
|
|
2058
3415
|
* through `docker exec`. With the egress gateway, ports are reached through its tunnel whatever
|
|
@@ -2067,6 +3424,8 @@ async function createDockerSession(options) {
|
|
|
2067
3424
|
};
|
|
2068
3425
|
const lifetime = Math.ceil((options.lifetimeMs ?? 864e5) / 1e3);
|
|
2069
3426
|
const signal = options.abortSignal;
|
|
3427
|
+
const { engine } = options;
|
|
3428
|
+
const spec = engineSpec(engine);
|
|
2070
3429
|
const env = {
|
|
2071
3430
|
...userEnv(options),
|
|
2072
3431
|
...options.env
|
|
@@ -2081,20 +3440,21 @@ async function createDockerSession(options) {
|
|
|
2081
3440
|
...Object.keys(env).flatMap((k) => ["--env", k]),
|
|
2082
3441
|
"--publish",
|
|
2083
3442
|
`127.0.0.1::${BRIDGE_PORT}`,
|
|
2084
|
-
"--workdir",
|
|
2085
|
-
SANDBOX_ROOT,
|
|
3443
|
+
...spec.createsWorkdir ? ["--workdir", SANDBOX_ROOT] : [],
|
|
2086
3444
|
"--entrypoint",
|
|
2087
3445
|
"sleep",
|
|
2088
3446
|
options.image,
|
|
2089
3447
|
String(lifetime)
|
|
2090
3448
|
], {
|
|
2091
3449
|
signal,
|
|
2092
|
-
env
|
|
3450
|
+
env,
|
|
3451
|
+
engine
|
|
2093
3452
|
}).catch((error) => {
|
|
2094
|
-
|
|
2095
|
-
if (
|
|
2096
|
-
|
|
2097
|
-
|
|
3453
|
+
const sweep = () => sweepLabelDetached(`${LABEL.session}=${nonce}`, void 0, engine);
|
|
3454
|
+
if (signal?.aborted) sweep();
|
|
3455
|
+
if (/unable to find user|no matching entries in passwd/i.test(error.message)) {
|
|
3456
|
+
sweep();
|
|
3457
|
+
throw new Error(`${options.image} has no ${SANDBOX_USER} user to run the sandbox as. Images reeval builds have one: rebuild it (reeval image build), or start from reeval's image or ${spec.fn}({ from }). An eval that needs root at run time can opt in with ${spec.fn}({ root: true }).`);
|
|
2098
3458
|
}
|
|
2099
3459
|
throw error;
|
|
2100
3460
|
});
|
|
@@ -2102,33 +3462,57 @@ async function createDockerSession(options) {
|
|
|
2102
3462
|
let removed;
|
|
2103
3463
|
const remove = () => {
|
|
2104
3464
|
gateway?.close();
|
|
2105
|
-
removed ??= removeContainerDetached(id);
|
|
3465
|
+
removed ??= removeContainerDetached(id, engine);
|
|
2106
3466
|
return removed;
|
|
2107
3467
|
};
|
|
2108
3468
|
if (signal?.aborted) {
|
|
2109
3469
|
await remove();
|
|
2110
3470
|
throw signal.reason;
|
|
2111
3471
|
}
|
|
3472
|
+
if (!spec.createsWorkdir) {
|
|
3473
|
+
const made = await docker([
|
|
3474
|
+
"exec",
|
|
3475
|
+
"--user",
|
|
3476
|
+
"0:0",
|
|
3477
|
+
id,
|
|
3478
|
+
"mkdir",
|
|
3479
|
+
"-p",
|
|
3480
|
+
SANDBOX_ROOT
|
|
3481
|
+
], {
|
|
3482
|
+
engine,
|
|
3483
|
+
signal
|
|
3484
|
+
}).catch((error) => ({
|
|
3485
|
+
exitCode: 1,
|
|
3486
|
+
stderr: error.message
|
|
3487
|
+
}));
|
|
3488
|
+
if (made.exitCode !== 0) {
|
|
3489
|
+
await remove();
|
|
3490
|
+
if (signal?.aborted) throw signal.reason;
|
|
3491
|
+
throw new Error(`Couldn't create ${SANDBOX_ROOT} in the container: ${made.stderr.trim()}`);
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
2112
3494
|
const egressOptions = options.egress === true ? {} : options.egress || void 0;
|
|
2113
3495
|
let egress;
|
|
2114
3496
|
let detached = [];
|
|
2115
3497
|
const attach = async (policy) => {
|
|
2116
3498
|
if (isRestricted(policy) && detached.length === 0) {
|
|
2117
|
-
detached = await networksOf(id);
|
|
3499
|
+
detached = await networksOf(id, engine);
|
|
3500
|
+
const mode = detached.length === 0 ? await networkMode(id, engine) : "none";
|
|
3501
|
+
if (detached.length === 0 && mode !== "none") throw new Error(`the container's network is ${mode}, which ${spec.name} can't detach it from, so a restricted policy would still have a route out`);
|
|
2118
3502
|
for (const n of detached) await dockerOk([
|
|
2119
3503
|
"network",
|
|
2120
3504
|
"disconnect",
|
|
2121
3505
|
"--force",
|
|
2122
3506
|
n,
|
|
2123
3507
|
id
|
|
2124
|
-
]);
|
|
3508
|
+
], { engine });
|
|
2125
3509
|
} else if (!isRestricted(policy) && detached.length > 0) {
|
|
2126
3510
|
for (const n of detached) await dockerOk([
|
|
2127
3511
|
"network",
|
|
2128
3512
|
"connect",
|
|
2129
3513
|
n,
|
|
2130
3514
|
id
|
|
2131
|
-
]);
|
|
3515
|
+
], { engine });
|
|
2132
3516
|
detached = [];
|
|
2133
3517
|
}
|
|
2134
3518
|
};
|
|
@@ -2139,7 +3523,7 @@ async function createDockerSession(options) {
|
|
|
2139
3523
|
gateway = await startGateway(id, {
|
|
2140
3524
|
...rest,
|
|
2141
3525
|
policy
|
|
2142
|
-
}, signal);
|
|
3526
|
+
}, signal, engine);
|
|
2143
3527
|
egress = { active: true };
|
|
2144
3528
|
} catch (error) {
|
|
2145
3529
|
if (signal?.aborted || isRestricted(policy)) {
|
|
@@ -2182,14 +3566,15 @@ async function createDockerSession(options) {
|
|
|
2182
3566
|
"sh",
|
|
2183
3567
|
"-c",
|
|
2184
3568
|
`for e in /proc/[0-9]*/environ; do grep -qsxzF ${shellQuote(`${EXEC_MARKER}=${marker}`)} "$e" && kill -9 "$(basename "$(dirname "$e")")" 2>/dev/null; done; true`
|
|
2185
|
-
]).then(() => {}, () => {});
|
|
3569
|
+
], { engine }).then(() => {}, () => {});
|
|
2186
3570
|
const run = async (o) => {
|
|
2187
3571
|
const env = envFor(o.env);
|
|
2188
3572
|
const marker = nextMarker();
|
|
2189
3573
|
try {
|
|
2190
3574
|
const r = await docker(execArgs(o.command, marker, o.workingDirectory, env), {
|
|
2191
3575
|
env,
|
|
2192
|
-
signal: o.abortSignal
|
|
3576
|
+
signal: o.abortSignal,
|
|
3577
|
+
engine
|
|
2193
3578
|
});
|
|
2194
3579
|
return {
|
|
2195
3580
|
exitCode: r.exitCode,
|
|
@@ -2208,7 +3593,10 @@ async function createDockerSession(options) {
|
|
|
2208
3593
|
"cat",
|
|
2209
3594
|
"--",
|
|
2210
3595
|
posix.resolve(SANDBOX_ROOT, o.path)
|
|
2211
|
-
], {
|
|
3596
|
+
], {
|
|
3597
|
+
signal: o.abortSignal,
|
|
3598
|
+
engine
|
|
3599
|
+
});
|
|
2212
3600
|
return r.exitCode === 0 ? new Uint8Array(r.stdout) : null;
|
|
2213
3601
|
};
|
|
2214
3602
|
const writeBinaryFile = async (o) => {
|
|
@@ -2222,7 +3610,8 @@ async function createDockerSession(options) {
|
|
|
2222
3610
|
`mkdir -p ${shellQuote(posix.dirname(path))} && cat > ${shellQuote(path)}`
|
|
2223
3611
|
], {
|
|
2224
3612
|
input: Buffer.from(o.content),
|
|
2225
|
-
signal: o.abortSignal
|
|
3613
|
+
signal: o.abortSignal,
|
|
3614
|
+
engine
|
|
2226
3615
|
});
|
|
2227
3616
|
};
|
|
2228
3617
|
const brokering = gateway ? {
|
|
@@ -2245,7 +3634,7 @@ async function createDockerSession(options) {
|
|
|
2245
3634
|
} : {};
|
|
2246
3635
|
const session = {
|
|
2247
3636
|
id,
|
|
2248
|
-
description:
|
|
3637
|
+
description: `${spec.name} container ${id.slice(0, 12)} (${options.image})`,
|
|
2249
3638
|
defaultWorkingDirectory: SANDBOX_ROOT,
|
|
2250
3639
|
ports: [BRIDGE_PORT],
|
|
2251
3640
|
get egress() {
|
|
@@ -2257,30 +3646,31 @@ async function createDockerSession(options) {
|
|
|
2257
3646
|
},
|
|
2258
3647
|
...brokering,
|
|
2259
3648
|
...substituting,
|
|
2260
|
-
oomKills: () => oomKills(id),
|
|
3649
|
+
oomKills: () => oomKills(id, engine),
|
|
2261
3650
|
run,
|
|
2262
3651
|
async spawn(o) {
|
|
2263
3652
|
const env = envFor(o.env);
|
|
2264
3653
|
const marker = nextMarker();
|
|
2265
3654
|
const args = execArgs(o.command, marker, o.workingDirectory, env);
|
|
2266
3655
|
const startedAt = Date.now();
|
|
2267
|
-
const child = spawn(
|
|
3656
|
+
const child = spawn(spec.bin, args, {
|
|
2268
3657
|
stdio: [
|
|
2269
3658
|
"ignore",
|
|
2270
3659
|
"pipe",
|
|
2271
3660
|
"pipe"
|
|
2272
3661
|
],
|
|
2273
3662
|
detached: true,
|
|
2274
|
-
env:
|
|
3663
|
+
env: {
|
|
2275
3664
|
...process.env,
|
|
3665
|
+
...engineEnv(engine),
|
|
2276
3666
|
...env
|
|
2277
|
-
}
|
|
3667
|
+
}
|
|
2278
3668
|
});
|
|
2279
3669
|
const exited = new Promise((resolve) => child.on("close", (code) => {
|
|
2280
3670
|
traceDocker(args, startedAt, {
|
|
2281
3671
|
exitCode: code,
|
|
2282
3672
|
stdout: "<streamed>"
|
|
2283
|
-
});
|
|
3673
|
+
}, spec.bin);
|
|
2284
3674
|
resolve({ exitCode: code ?? 1 });
|
|
2285
3675
|
}));
|
|
2286
3676
|
const kill = async () => {
|
|
@@ -2329,10 +3719,10 @@ async function createDockerSession(options) {
|
|
|
2329
3719
|
"port",
|
|
2330
3720
|
id,
|
|
2331
3721
|
`${port}/tcp`
|
|
2332
|
-
]);
|
|
3722
|
+
], { engine });
|
|
2333
3723
|
const hostPort = mapped.stdout.toString("utf8").split("\n")[0]?.split(":").at(-1);
|
|
2334
3724
|
if (mapped.exitCode !== 0 || !hostPort) {
|
|
2335
|
-
const reason = mapped.stderr.trim() ||
|
|
3725
|
+
const reason = mapped.stderr.trim() || `${spec.name} reports no host port for it`;
|
|
2336
3726
|
throw new Error(`Port ${port} of ${id.slice(0, 12)} isn't reachable: ${reason}`);
|
|
2337
3727
|
}
|
|
2338
3728
|
return { url: `${scheme}://127.0.0.1:${hostPort}` };
|
|
@@ -2363,7 +3753,7 @@ async function createDockerSession(options) {
|
|
|
2363
3753
|
/** The session as an AI SDK harness sandbox provider. */
|
|
2364
3754
|
const dockerSandboxProvider = (options) => ({
|
|
2365
3755
|
specificationVersion: "harness-sandbox-v1",
|
|
2366
|
-
providerId: "reeval-docker",
|
|
3756
|
+
providerId: options.engine === "podman" ? "reeval-podman" : "reeval-docker",
|
|
2367
3757
|
async createSession(o) {
|
|
2368
3758
|
const session = await createDockerSession({
|
|
2369
3759
|
...options,
|
|
@@ -2396,22 +3786,22 @@ const BOOTSTRAPS = {
|
|
|
2396
3786
|
name: "Cursor",
|
|
2397
3787
|
cli: "@cursor/sdk",
|
|
2398
3788
|
version: `node -p "'@cursor/sdk ' + require(process.env.HOME + '/.ai-sdk-harness/.harness-bootstrap/cursor-sdk/node_modules/@cursor/sdk/package.json').version"`,
|
|
2399
|
-
adapter: async () => (await import("./src-
|
|
3789
|
+
adapter: async () => (await import("./src-WD-zgQYZ.js")).createCursorSdk()
|
|
2400
3790
|
},
|
|
2401
3791
|
"claude-code": {
|
|
2402
3792
|
name: "Claude Code",
|
|
2403
3793
|
cli: "claude",
|
|
2404
|
-
adapter: async () => (await import("./dist-
|
|
3794
|
+
adapter: async () => (await import("./dist-CuY4ehEk.js")).createClaudeCode()
|
|
2405
3795
|
},
|
|
2406
3796
|
antigravity: {
|
|
2407
3797
|
name: "Antigravity",
|
|
2408
3798
|
cli: "agy",
|
|
2409
|
-
adapter: async () => (await import("./src-
|
|
3799
|
+
adapter: async () => (await import("./src-_oI9AePb.js")).createAntigravity()
|
|
2410
3800
|
},
|
|
2411
3801
|
opencode: {
|
|
2412
3802
|
name: "OpenCode",
|
|
2413
3803
|
cli: "opencode",
|
|
2414
|
-
adapter: async () => (await import("./src-
|
|
3804
|
+
adapter: async () => (await import("./src-CLoOMVWI.js")).createOpenCode({
|
|
2415
3805
|
auth: { GOOGLE_GENERATIVE_AI_API_KEY: "unused" },
|
|
2416
3806
|
model: "google/unused"
|
|
2417
3807
|
})
|
|
@@ -2419,7 +3809,7 @@ const BOOTSTRAPS = {
|
|
|
2419
3809
|
goose: {
|
|
2420
3810
|
name: "Goose",
|
|
2421
3811
|
cli: "goose",
|
|
2422
|
-
adapter: async () => (await import("./src-
|
|
3812
|
+
adapter: async () => (await import("./src-qsKCqv7a.js")).createGoose({
|
|
2423
3813
|
auth: { GOOGLE_API_KEY: "unused" },
|
|
2424
3814
|
model: "unused"
|
|
2425
3815
|
})
|
|
@@ -2427,12 +3817,28 @@ const BOOTSTRAPS = {
|
|
|
2427
3817
|
pi: {
|
|
2428
3818
|
name: "pi",
|
|
2429
3819
|
cli: "pi",
|
|
2430
|
-
adapter: async () => (await import("./src-
|
|
3820
|
+
adapter: async () => (await import("./src-DSYYPEOj.js")).createPi({ auth: { GEMINI_API_KEY: "unused" } })
|
|
2431
3821
|
},
|
|
2432
3822
|
codex: {
|
|
2433
3823
|
name: "Codex",
|
|
2434
3824
|
cli: "codex",
|
|
2435
|
-
adapter: async () => (await import("./src-
|
|
3825
|
+
adapter: async () => (await import("./src-DCFtJRp0.js")).createCodex({ auth: { OPENAI_API_KEY: "unused" } })
|
|
3826
|
+
},
|
|
3827
|
+
fx: {
|
|
3828
|
+
name: "fx",
|
|
3829
|
+
cli: "fx",
|
|
3830
|
+
adapter: async () => (await import("./src-BxfCKA3_.js")).createFx({
|
|
3831
|
+
auth: { AI_GATEWAY_API_KEY: "unused" },
|
|
3832
|
+
model: "unused/unused"
|
|
3833
|
+
})
|
|
3834
|
+
},
|
|
3835
|
+
"grok-build": {
|
|
3836
|
+
name: "Grok Build",
|
|
3837
|
+
cli: "grok",
|
|
3838
|
+
adapter: async () => (await import("./src-CcrQbpG8.js")).createGrokBuild({
|
|
3839
|
+
auth: { XAI_API_KEY: "unused" },
|
|
3840
|
+
model: "unused"
|
|
3841
|
+
})
|
|
2436
3842
|
}
|
|
2437
3843
|
};
|
|
2438
3844
|
/** Keeps the image's `PATH` in login shells, which Debian's `/etc/profile` resets. */
|
|
@@ -2464,10 +3870,10 @@ WORKDIR ${SANDBOX_ROOT}/${WORKSPACE_DIR}
|
|
|
2464
3870
|
`;
|
|
2465
3871
|
const tail = (text) => text.trim().split("\n").slice(-15).join("\n");
|
|
2466
3872
|
const clipLine = (text, max = 300) => text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
2467
|
-
/** Why a `docker build` failed, in a line: the failing step's exit, or BuildKit's own error. */
|
|
2468
|
-
function buildReason(error) {
|
|
2469
|
-
if (!error) return
|
|
2470
|
-
const exit = /^process ".*" did not complete successfully: exit code: (\d+)$/s.exec(error);
|
|
3873
|
+
/** Why a `docker build` failed, in a line: the failing step's exit, or BuildKit's (or buildah's) own error. */
|
|
3874
|
+
function buildReason(error, engine) {
|
|
3875
|
+
if (!error) return `${engineSpec(engine).bin} build failed`;
|
|
3876
|
+
const exit = /^process ".*" did not complete successfully: exit code: (\d+)$/s.exec(error) ?? /^while running runtime: exit status (\d+)$/.exec(error);
|
|
2471
3877
|
return exit ? `a RUN step exited ${exit[1]}` : clipLine(error.replace(/\s+/g, " "));
|
|
2472
3878
|
}
|
|
2473
3879
|
/**
|
|
@@ -2479,12 +3885,13 @@ function buildError(output, what, reason, keep = () => true) {
|
|
|
2479
3885
|
const text = [parsed.error ?? "", ...parsed.output].join("\n");
|
|
2480
3886
|
const cause = failureCause(text, parsed.exitCode);
|
|
2481
3887
|
parsed.output = parsed.output.filter(keep);
|
|
2482
|
-
const why = reason?.(parsed) ?? buildReason(parsed.error);
|
|
3888
|
+
const why = reason?.(parsed) ?? buildReason(parsed.error, what.engine);
|
|
2483
3889
|
return new ImageBuildError(failureMessage({
|
|
2484
3890
|
headline: `${what.headline} failed: ${why}`,
|
|
2485
3891
|
cause,
|
|
2486
3892
|
output: parsed.output,
|
|
2487
|
-
log: what.log ?? null
|
|
3893
|
+
log: what.log ?? null,
|
|
3894
|
+
...what.engine ? { engine: what.engine } : {}
|
|
2488
3895
|
}), {
|
|
2489
3896
|
stage: what.stage,
|
|
2490
3897
|
ref: what.ref,
|
|
@@ -2499,12 +3906,12 @@ const BUILT_BY = ["--label", `${LABEL.managed}=image`];
|
|
|
2499
3906
|
* Removes the images reeval built that nothing tags any more (a rebuild's old image) and the
|
|
2500
3907
|
* intermediate `-base` tags of `from` builds. Returns how many it removed.
|
|
2501
3908
|
*/
|
|
2502
|
-
async function pruneImages() {
|
|
3909
|
+
async function pruneImages(engine) {
|
|
2503
3910
|
const list = async (args) => (await docker([
|
|
2504
3911
|
"image",
|
|
2505
3912
|
"ls",
|
|
2506
3913
|
...args
|
|
2507
|
-
])).stdout.toString("utf8").split("\n").map((l) => l.trim()).filter(Boolean);
|
|
3914
|
+
], { engine })).stdout.toString("utf8").split("\n").map((l) => l.trim()).filter(Boolean);
|
|
2508
3915
|
const dangling = [...await list([
|
|
2509
3916
|
"-q",
|
|
2510
3917
|
"--filter",
|
|
@@ -2528,46 +3935,93 @@ async function pruneImages() {
|
|
|
2528
3935
|
"image",
|
|
2529
3936
|
"rm",
|
|
2530
3937
|
ref
|
|
2531
|
-
]).catch(() => null))?.exitCode === 0) removed++;
|
|
3938
|
+
], { engine }).catch(() => null))?.exitCode === 0) removed++;
|
|
2532
3939
|
return removed;
|
|
2533
3940
|
}
|
|
3941
|
+
/** Docker's names for Node's architectures, as image indexes list platforms. */
|
|
3942
|
+
const OCI_ARCH = {
|
|
3943
|
+
x64: "amd64",
|
|
3944
|
+
arm64: "arm64",
|
|
3945
|
+
arm: "arm",
|
|
3946
|
+
ppc64: "ppc64le"
|
|
3947
|
+
};
|
|
3948
|
+
/**
|
|
3949
|
+
* A registry digest for `from` without Docker's buildx: skopeo's, which is the same digest
|
|
3950
|
+
* buildx gives, or else this platform's manifest from `podman manifest inspect`.
|
|
3951
|
+
*/
|
|
3952
|
+
async function podmanRemoteDigest(from, signal) {
|
|
3953
|
+
const skopeo = await run("skopeo", [
|
|
3954
|
+
"inspect",
|
|
3955
|
+
"--format",
|
|
3956
|
+
"{{.Digest}}",
|
|
3957
|
+
`docker://${from}`
|
|
3958
|
+
], { ...signal ? { signal } : {} }).catch(() => null);
|
|
3959
|
+
const digest = skopeo?.exitCode === 0 ? skopeo.stdout.trim() : "";
|
|
3960
|
+
if (digest.startsWith("sha256:")) return { digest };
|
|
3961
|
+
const r = await docker([
|
|
3962
|
+
"manifest",
|
|
3963
|
+
"inspect",
|
|
3964
|
+
from
|
|
3965
|
+
], {
|
|
3966
|
+
signal,
|
|
3967
|
+
engine: "podman"
|
|
3968
|
+
}).catch(() => null);
|
|
3969
|
+
if (r?.exitCode === 0) {
|
|
3970
|
+
const manifest = JSON.parse(r.stdout.toString("utf8"));
|
|
3971
|
+
const arch = OCI_ARCH[process.arch] ?? process.arch;
|
|
3972
|
+
const own = manifest.manifests?.find((m) => m.platform?.os === "linux" && m.platform.architecture === arch)?.digest;
|
|
3973
|
+
if (own?.startsWith("sha256:")) return { digest: own };
|
|
3974
|
+
}
|
|
3975
|
+
return { error: (r?.stderr.trim() || skopeo?.stderr.trim() || "").replace(/^(?:Error|time="[^"]*" level=fatal msg=): ?/, "").replace(/\s+/g, " ") || `${from} doesn't resolve` };
|
|
3976
|
+
}
|
|
2534
3977
|
/**
|
|
2535
3978
|
* What a `from` names now, without pulling: its registry digest, or for an image that's only
|
|
2536
3979
|
* local, its id (`local`). `error` when neither resolves, e.g. a typo or no network.
|
|
2537
3980
|
*/
|
|
2538
|
-
async function resolveFrom(from, signal) {
|
|
3981
|
+
async function resolveFrom(from, signal, engine) {
|
|
2539
3982
|
const pinned = /@(sha256:[0-9a-f]{64})$/.exec(from)?.[1];
|
|
2540
3983
|
if (pinned) return {
|
|
2541
3984
|
digest: pinned,
|
|
2542
3985
|
local: false
|
|
2543
3986
|
};
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
"
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
3987
|
+
let error = null;
|
|
3988
|
+
if (engine === "podman") {
|
|
3989
|
+
const remote = await podmanRemoteDigest(from, signal);
|
|
3990
|
+
if ("digest" in remote) return {
|
|
3991
|
+
digest: remote.digest,
|
|
3992
|
+
local: false
|
|
3993
|
+
};
|
|
3994
|
+
error = remote.error;
|
|
3995
|
+
} else {
|
|
3996
|
+
const remote = await docker([
|
|
3997
|
+
"buildx",
|
|
3998
|
+
"imagetools",
|
|
3999
|
+
"inspect",
|
|
4000
|
+
"--format",
|
|
4001
|
+
"{{json .Manifest.Digest}}",
|
|
4002
|
+
from
|
|
4003
|
+
], { signal }).catch(() => null);
|
|
4004
|
+
const digest = remote?.exitCode === 0 ? JSON.parse(remote.stdout.toString("utf8").trim()) : null;
|
|
4005
|
+
if (typeof digest === "string" && digest.startsWith("sha256:")) return {
|
|
4006
|
+
digest,
|
|
4007
|
+
local: false
|
|
4008
|
+
};
|
|
4009
|
+
error = remote?.stderr.trim().replace(/^ERROR: /, "").replace(/\s+/g, " ") || null;
|
|
4010
|
+
}
|
|
4011
|
+
const local = await imageInfo(from, engine).catch(() => null);
|
|
2558
4012
|
if (local) return {
|
|
2559
4013
|
digest: local.id,
|
|
2560
4014
|
local: true
|
|
2561
4015
|
};
|
|
2562
|
-
return { error:
|
|
4016
|
+
return { error: error || `${from} doesn't resolve` };
|
|
2563
4017
|
}
|
|
2564
4018
|
/** Removes a tag or an image reeval replaced, unless something still uses it. */
|
|
2565
|
-
const removeQuietly = async (ref) => {
|
|
4019
|
+
const removeQuietly = async (ref, engine) => {
|
|
2566
4020
|
await docker([
|
|
2567
4021
|
"image",
|
|
2568
4022
|
"rm",
|
|
2569
4023
|
ref
|
|
2570
|
-
]).catch(() => {});
|
|
4024
|
+
], { engine }).catch(() => {});
|
|
2571
4025
|
};
|
|
2572
4026
|
/** The hash of reeval's base image build context, which its images carry as a label. */
|
|
2573
4027
|
function contextHash() {
|
|
@@ -2587,14 +4041,14 @@ function contextHash() {
|
|
|
2587
4041
|
* intermediate `-base` tag of a `from` build, are removed once the new one is committed.
|
|
2588
4042
|
*/
|
|
2589
4043
|
async function buildImage(log, options) {
|
|
2590
|
-
const { harness, from, signal } = options;
|
|
2591
|
-
const resolved = from ? await resolveFrom(from, signal) : null;
|
|
4044
|
+
const { harness, from, signal, engine } = options;
|
|
4045
|
+
const resolved = from ? await resolveFrom(from, signal, engine) : null;
|
|
2592
4046
|
const digest = resolved && "digest" in resolved ? resolved.digest : null;
|
|
2593
4047
|
const pinned = from && digest && resolved && "local" in resolved && !resolved.local && !from.includes("@") ? `${from}@${digest}` : from;
|
|
2594
4048
|
const bootstrap = BOOTSTRAPS[harness];
|
|
2595
4049
|
const base = from ? `${harnessImageFor(harness, from)}-base` : BASE_IMAGE;
|
|
2596
4050
|
const target = from ? harnessImageFor(harness, from) : imageFor(harness);
|
|
2597
|
-
const previous = [(await imageInfo(target))?.id, from ? null : (await imageInfo(base))?.id];
|
|
4051
|
+
const previous = [(await imageInfo(target, engine))?.id, from ? null : (await imageInfo(base, engine))?.id];
|
|
2598
4052
|
log(`Building ${base}${from ? ` from ${from}` : ""}`);
|
|
2599
4053
|
const build = from ? await docker([
|
|
2600
4054
|
"build",
|
|
@@ -2607,7 +4061,8 @@ async function buildImage(log, options) {
|
|
|
2607
4061
|
"-"
|
|
2608
4062
|
], {
|
|
2609
4063
|
input: toolchainDockerfile(pinned ?? from),
|
|
2610
|
-
signal
|
|
4064
|
+
signal,
|
|
4065
|
+
engine
|
|
2611
4066
|
}) : await docker([
|
|
2612
4067
|
"build",
|
|
2613
4068
|
"--progress",
|
|
@@ -2620,20 +4075,25 @@ async function buildImage(log, options) {
|
|
|
2620
4075
|
"--tag",
|
|
2621
4076
|
base,
|
|
2622
4077
|
DOCKER_CONTEXT
|
|
2623
|
-
], {
|
|
4078
|
+
], {
|
|
4079
|
+
signal,
|
|
4080
|
+
engine
|
|
4081
|
+
});
|
|
2624
4082
|
const output = `${build.stdout.toString("utf8")}${build.stderr}`;
|
|
2625
4083
|
options.output?.write(output);
|
|
2626
4084
|
if (build.exitCode !== 0) throw buildError(output, {
|
|
2627
4085
|
stage: from ? "from" : "harness",
|
|
2628
4086
|
ref: target,
|
|
2629
4087
|
headline: from ? `Building the toolchain layer FROM ${from}` : "Building reeval's base image",
|
|
2630
|
-
log: options.output?.path ?? null
|
|
4088
|
+
log: options.output?.path ?? null,
|
|
4089
|
+
engine
|
|
2631
4090
|
}, (parsed) => parsed.output.find((l) => l.startsWith("reeval needs a Debian")) ?? null);
|
|
2632
4091
|
log(`Installing the ${bootstrap.name} harness bootstrap`);
|
|
2633
4092
|
const session = await createDockerSession({
|
|
2634
4093
|
image: base,
|
|
2635
4094
|
lifetimeMs: 18e5,
|
|
2636
|
-
...signal ? { abortSignal: signal } : {}
|
|
4095
|
+
...signal ? { abortSignal: signal } : {},
|
|
4096
|
+
...engine ? { engine } : {}
|
|
2637
4097
|
});
|
|
2638
4098
|
let agentVersion;
|
|
2639
4099
|
try {
|
|
@@ -2653,7 +4113,7 @@ async function buildImage(log, options) {
|
|
|
2653
4113
|
"CMD [\"bash\"]",
|
|
2654
4114
|
session.id,
|
|
2655
4115
|
target
|
|
2656
|
-
]);
|
|
4116
|
+
], { engine });
|
|
2657
4117
|
} catch (error) {
|
|
2658
4118
|
if (signal?.aborted || error instanceof ImageBuildError) throw error;
|
|
2659
4119
|
const [first = "", ...rest] = error.message.split("\n");
|
|
@@ -2663,7 +4123,8 @@ async function buildImage(log, options) {
|
|
|
2663
4123
|
headline: `Baking the ${bootstrap.name} CLI onto ${from ?? "reeval-sandbox-base:latest"} failed: ${clipLine(first)}`,
|
|
2664
4124
|
cause,
|
|
2665
4125
|
output: outputTail(rest.join("\n")),
|
|
2666
|
-
log: options.output?.path ?? null
|
|
4126
|
+
log: options.output?.path ?? null,
|
|
4127
|
+
...engine ? { engine } : {}
|
|
2667
4128
|
}), {
|
|
2668
4129
|
stage: "harness",
|
|
2669
4130
|
ref: target,
|
|
@@ -2674,9 +4135,9 @@ async function buildImage(log, options) {
|
|
|
2674
4135
|
} finally {
|
|
2675
4136
|
await session.destroy();
|
|
2676
4137
|
}
|
|
2677
|
-
if (from) await removeQuietly(base);
|
|
2678
|
-
const now = (await imageInfo(target))?.id;
|
|
2679
|
-
for (const id of previous) if (id && id !== now) await removeQuietly(id);
|
|
4138
|
+
if (from) await removeQuietly(base, engine);
|
|
4139
|
+
const now = (await imageInfo(target, engine))?.id;
|
|
4140
|
+
for (const id of previous) if (id && id !== now) await removeQuietly(id, engine);
|
|
2680
4141
|
return {
|
|
2681
4142
|
image: target,
|
|
2682
4143
|
agentVersion
|
|
@@ -2719,15 +4180,18 @@ function installScript(steps) {
|
|
|
2719
4180
|
}
|
|
2720
4181
|
/** Builds the install layer if it's missing. `output` gets the build's output, for a log file. */
|
|
2721
4182
|
async function buildInstallLayer(parent, steps, options = {}) {
|
|
2722
|
-
const { signal } = options;
|
|
4183
|
+
const { signal, engine } = options;
|
|
2723
4184
|
const tag = installLayerTag(parent, steps);
|
|
2724
4185
|
if ((await docker([
|
|
2725
4186
|
"image",
|
|
2726
4187
|
"inspect",
|
|
2727
4188
|
"--format",
|
|
2728
|
-
"{{.
|
|
4189
|
+
"{{.Created}}",
|
|
2729
4190
|
tag
|
|
2730
|
-
], {
|
|
4191
|
+
], {
|
|
4192
|
+
signal,
|
|
4193
|
+
engine
|
|
4194
|
+
})).exitCode === 0) return tag;
|
|
2731
4195
|
const script = installScript(steps);
|
|
2732
4196
|
const encoded = Buffer.from(script).toString("base64");
|
|
2733
4197
|
const home = `$(getent passwd ${SANDBOX_USER} | cut -d: -f6)`;
|
|
@@ -2750,7 +4214,8 @@ async function buildInstallLayer(parent, steps, options = {}) {
|
|
|
2750
4214
|
"-"
|
|
2751
4215
|
], {
|
|
2752
4216
|
input: dockerfile,
|
|
2753
|
-
signal
|
|
4217
|
+
signal,
|
|
4218
|
+
engine
|
|
2754
4219
|
});
|
|
2755
4220
|
const output = `${build.stdout.toString("utf8")}${build.stderr}`;
|
|
2756
4221
|
options.output?.write(`${script}\n${dockerfile}\n\n${output}`);
|
|
@@ -2760,7 +4225,8 @@ async function buildInstallLayer(parent, steps, options = {}) {
|
|
|
2760
4225
|
stage: "install",
|
|
2761
4226
|
ref: tag,
|
|
2762
4227
|
headline: `Building the install layer (${`${steps.length} command${steps.length === 1 ? "" : "s"}`})`,
|
|
2763
|
-
log: options.output?.path ?? null
|
|
4228
|
+
log: options.output?.path ?? null,
|
|
4229
|
+
engine
|
|
2764
4230
|
}, () => failed ? `install ${failed[1]} exited ${failed[2]}: ${clipLine(failed[3] ?? "")}` : null, (line) => !line.startsWith("reeval: install "));
|
|
2765
4231
|
}
|
|
2766
4232
|
return tag;
|
|
@@ -2820,16 +4286,16 @@ function contextFiles(context, dockerfile) {
|
|
|
2820
4286
|
return files.sort();
|
|
2821
4287
|
}
|
|
2822
4288
|
/** Whether the Dockerfile starts from reeval's sandbox image, which then has to exist first. */
|
|
2823
|
-
const
|
|
4289
|
+
const usesReevalBase = (dockerfile) => new RegExp(`\\$\\{?${REEVAL_BASE_ARG}\\b`).test(readFileSync(dockerfile, "utf8"));
|
|
2824
4290
|
/**
|
|
2825
4291
|
* The tag of a `docker({ dockerfile })` build for a harness: a hash of the Dockerfile, every file
|
|
2826
4292
|
* of its build context (its directory, less `.dockerignore`) and, for a Dockerfile `FROM
|
|
2827
4293
|
* ${REEVAL_BASE}`, the id of the harness's image it names, so an edit to any of them, or a rebuilt reeval
|
|
2828
|
-
* image, builds anew. `
|
|
4294
|
+
* image, builds anew. `reevalBase` is that id (null when it isn't built).
|
|
2829
4295
|
*/
|
|
2830
|
-
function dockerfileTag(dockerfile, harness,
|
|
4296
|
+
function dockerfileTag(dockerfile, harness, reevalBase = null) {
|
|
2831
4297
|
const context = dirname(dockerfile);
|
|
2832
|
-
const base =
|
|
4298
|
+
const base = usesReevalBase(dockerfile) ? `${imageFor(harness)}@${reevalBase ?? "unbuilt"}` : "";
|
|
2833
4299
|
const hash = createHash("sha256").update(`${REEVAL_BASE_ARG}=${base}\0`);
|
|
2834
4300
|
for (const path of contextFiles(context, dockerfile)) {
|
|
2835
4301
|
const full = join(context, path);
|
|
@@ -2846,14 +4312,14 @@ function dockerfileTag(dockerfile, harness, esBase = null) {
|
|
|
2846
4312
|
}
|
|
2847
4313
|
/**
|
|
2848
4314
|
* Builds the user's Dockerfile with plain `docker build` (so Docker's layer cache applies), from
|
|
2849
|
-
* its directory, with `REEVAL_BASE` set to the harness's sandbox image (whose id is `
|
|
4315
|
+
* its directory, with `REEVAL_BASE` set to the harness's sandbox image (whose id is `reevalBase`).
|
|
2850
4316
|
* Rebuilds even when the tag exists with `force`, as `reeval image build` does, and then removes the
|
|
2851
4317
|
* image the tag pointed at before. `name` is the path failures name.
|
|
2852
4318
|
*/
|
|
2853
4319
|
async function buildDockerfile(dockerfile, options) {
|
|
2854
|
-
const { harness, signal } = options;
|
|
2855
|
-
const { tag } = dockerfileTag(dockerfile, harness, options.
|
|
2856
|
-
const previous = (await imageInfo(tag))?.id;
|
|
4320
|
+
const { harness, signal, engine } = options;
|
|
4321
|
+
const { tag } = dockerfileTag(dockerfile, harness, options.reevalBase ?? null);
|
|
4322
|
+
const previous = (await imageInfo(tag, engine))?.id;
|
|
2857
4323
|
if (previous && !options.force) return tag;
|
|
2858
4324
|
const build = await docker([
|
|
2859
4325
|
"build",
|
|
@@ -2868,20 +4334,24 @@ async function buildDockerfile(dockerfile, options) {
|
|
|
2868
4334
|
"--tag",
|
|
2869
4335
|
tag,
|
|
2870
4336
|
dirname(dockerfile)
|
|
2871
|
-
], {
|
|
4337
|
+
], {
|
|
4338
|
+
signal,
|
|
4339
|
+
engine
|
|
4340
|
+
});
|
|
2872
4341
|
const output = `${build.stdout.toString("utf8")}${build.stderr}`;
|
|
2873
4342
|
options.output?.write(output);
|
|
2874
4343
|
if (build.exitCode !== 0) throw buildError(output, {
|
|
2875
4344
|
stage: "dockerfile",
|
|
2876
4345
|
ref: tag,
|
|
2877
4346
|
headline: `Building ${options.name ?? dockerfile}`,
|
|
2878
|
-
log: options.output?.path ?? null
|
|
4347
|
+
log: options.output?.path ?? null,
|
|
4348
|
+
engine
|
|
2879
4349
|
});
|
|
2880
|
-
const now = (await imageInfo(tag))?.id;
|
|
2881
|
-
if (previous && previous !== now) await removeQuietly(previous);
|
|
4350
|
+
const now = (await imageInfo(tag, engine))?.id;
|
|
4351
|
+
if (previous && previous !== now) await removeQuietly(previous, engine);
|
|
2882
4352
|
return tag;
|
|
2883
4353
|
}
|
|
2884
4354
|
//#endregion
|
|
2885
|
-
export {
|
|
4355
|
+
export { removeContainerDetached as $, hostAllowed as A, WORKSPACE_DIR as B, applyTransformations as C, HARNESS_HOSTS as D, DOCKER_EGRESS as E, HARNESS_PACKAGES as F, docker as G, harnessLabel as H, LABEL as I, dockerOk as J, dockerAvailable as K, SANDBOX_HOME as L, BASE_IMAGE as M, BRIDGE_PORT as N, PACKAGE_HOSTS as O, DEFAULT_IMAGE as P, isRegistryDigest as Q, SANDBOX_ROOT as R, placeholder as S, onEndpoint as T, imageFor as U, adapterVersion as V, isBakedHarness as W, imageInfo as X, dockerState as Y, imageTags as Z, ROOT_CAPABILITIES as _, buildInstallLayer as a, causeLine as at, CA_PATH as b, dockerfileTag as c, parseBuildOutput as ct, installLayerTag as d, dockerSeccompFile as dt, onDockerCommand as et, installScript as f, engineSpec as ft, DEFAULT_LIMITS as g, usesReevalBase as h, buildImage as i, RETRY_DELAYS as it, BAKED_HARNESSES as j, defaultAllowedHosts as k, dockerignoreMatcher as l, retryTransient as lt, resolveFrom as m, REEVAL_BASE_ARG as n, shellQuote as nt, contextFiles as o, failureCause as ot, pruneImages as p, normalizeImageId as pt, dockerCapacity as q, buildDockerfile as r, ImageBuildError as rt, contextHash as s, isTransient as st, DOCKER_CONTEXT as t, run as tt, harnessImageFor as u, ENGINES as ut, createDockerSession as v, matches as w, PROXY_PORT as x, dockerSandboxProvider as y, SANDBOX_USER as z };
|
|
2886
4356
|
|
|
2887
|
-
//# sourceMappingURL=src-
|
|
4357
|
+
//# sourceMappingURL=src-ftu5wPSt.js.map
|