@runcofactor/cli 0.52.0 → 0.53.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 +2 -0
- package/dist/{account-3ZG24MOE.js → account-MRZ54P5P.js} +5 -5
- package/dist/{alert-5G7PTAX2.js → alert-XBHELHEI.js} +8 -8
- package/dist/{api-catalog-IBYAFKKQ.js → api-catalog-GWYRX53L.js} +5 -5
- package/dist/{api-key-UXC44BDR.js → api-key-FH6JN447.js} +5 -5
- package/dist/{attachment-T6IRFQW3.js → attachment-J7GRVT5F.js} +5 -5
- package/dist/{auth-SHOCJE6Z.js → auth-VZZQXRDR.js} +3 -3
- package/dist/{benchmark-NJ2U6VTC.js → benchmark-A3SNDARF.js} +5 -5
- package/dist/{blackcat-RQJVFGXQ.js → blackcat-UM6TBQBN.js} +3 -3
- package/dist/{chunk-DHAGRM2F.js → chunk-33U56YGD.js} +14 -5
- package/dist/{chunk-DHAGRM2F.js.map → chunk-33U56YGD.js.map} +1 -1
- package/dist/{chunk-M752EUK3.js → chunk-3JU4UYYN.js} +5 -5
- package/dist/{chunk-G6BCST3G.js → chunk-3ZOUNCA5.js} +7 -7
- package/dist/{chunk-76Z6E7XX.js → chunk-4UZN6TVT.js} +74 -131
- package/dist/chunk-4UZN6TVT.js.map +1 -0
- package/dist/{chunk-ABT3BLRC.js → chunk-5TKXHS3X.js} +3 -3
- package/dist/{chunk-QCYSONXI.js → chunk-CRYHWO4M.js} +2 -2
- package/dist/{chunk-QTYUM4ZB.js → chunk-EJ3ZPH5U.js} +23 -187
- package/dist/chunk-EJ3ZPH5U.js.map +1 -0
- package/dist/{chunk-I5RHTCPY.js → chunk-EL6KJ4PO.js} +4 -4
- package/dist/{chunk-ZL4O6C42.js → chunk-HXBK3AGD.js} +2 -2
- package/dist/{chunk-PRERH4DN.js → chunk-I2SOHQ2C.js} +3 -3
- package/dist/{chunk-CYSPCGO6.js → chunk-JEHHEMUL.js} +5 -5
- package/dist/{chunk-LIEADTEF.js → chunk-LJFVRJCA.js} +2 -2
- package/dist/{chunk-UEJ2HTKU.js → chunk-MJVUMVLO.js} +19 -1
- package/dist/chunk-MJVUMVLO.js.map +1 -0
- package/dist/chunk-N55RKCGR.js +24 -0
- package/dist/chunk-N55RKCGR.js.map +1 -0
- package/dist/{chunk-PHNH3CEU.js → chunk-O4Z44BR7.js} +5 -5
- package/dist/{chunk-QA37WVF6.js → chunk-OQFCZERZ.js} +6 -6
- package/dist/{chunk-EUVB6Y3C.js → chunk-P7L5KELH.js} +8 -8
- package/dist/chunk-P7L5KELH.js.map +1 -0
- package/dist/{chunk-Z3MDQSZG.js → chunk-PJFFGQSB.js} +2 -2
- package/dist/{chunk-IY3ZK46I.js → chunk-QOVJUSSL.js} +2 -2
- package/dist/{chunk-RPJMQBB5.js → chunk-QXU4G4YN.js} +3 -3
- package/dist/{chunk-SZSTJIYU.js → chunk-RRSUKARV.js} +4 -4
- package/dist/{chunk-775FI436.js → chunk-RVD6J6IZ.js} +2 -2
- package/dist/{chunk-5UUIBY6E.js → chunk-TAVBFLCR.js} +3 -3
- package/dist/{chunk-ROX7N3RK.js → chunk-V7HB5LAU.js} +10 -7
- package/dist/chunk-V7HB5LAU.js.map +1 -0
- package/dist/{chunk-LL6TSQWV.js → chunk-VKZOHJCE.js} +3 -3
- package/dist/chunk-VKZOHJCE.js.map +1 -0
- package/dist/{chunk-3HI6QC4V.js → chunk-WRBU6TX7.js} +2 -2
- package/dist/{proofs-TXJGAP5F.js → chunk-X7SUSIY3.js} +118 -20
- package/dist/chunk-X7SUSIY3.js.map +1 -0
- package/dist/{chunk-BF3IPGMP.js → chunk-XSSZMG65.js} +4 -4
- package/dist/{credential-TWSWV64F.js → credential-4RFRU6OH.js} +5 -5
- package/dist/{credential-template-XO5D5AI3.js → credential-template-36PFUTBN.js} +5 -5
- package/dist/{digest-SLEFOLPV.js → digest-THMKFMP7.js} +8 -8
- package/dist/{docs-IDC4V4WQ.js → docs-ZGM53L67.js} +5 -5
- package/dist/{doctor-SDAYEA6C.js → doctor-Z2XBGBM3.js} +3 -3
- package/dist/{environment-VBUIBQ6O.js → environment-5YVZXBN7.js} +7 -7
- package/dist/eval-SO5TPD37.js +1177 -0
- package/dist/eval-SO5TPD37.js.map +1 -0
- package/dist/{exploration-IREIR5PO.js → exploration-GZ4WUJJH.js} +5 -5
- package/dist/{exploratory-test-4Q7BMEFT.js → exploratory-test-Y7P4ZDLD.js} +5 -5
- package/dist/{external-run-S7LULKPT.js → external-run-IJGGZOGI.js} +7 -7
- package/dist/{feature-flag-IASHTUGU.js → feature-flag-UH6Z7774.js} +5 -5
- package/dist/{flow-IBEZFZTU.js → flow-MRWAQYZZ.js} +5 -5
- package/dist/{github-HAEZRULP.js → github-VDJU3PU3.js} +6 -6
- package/dist/index.js +101 -82
- package/dist/index.js.map +1 -1
- package/dist/{init-LT2TZMQY.js → init-CROSTPA6.js} +5 -5
- package/dist/{install-RTEYH6TK.js → install-ECR7OLAY.js} +5 -5
- package/dist/{issues-5GE277R2.js → issues-K5TL2S4Z.js} +7 -7
- package/dist/{knobs-AMIWGYUZ.js → knobs-OKQFBQLK.js} +5 -5
- package/dist/{landing-3VLVVQL5.js → landing-XANIBPVM.js} +7 -7
- package/dist/{list-GDJR6UY6.js → list-64GX47Q4.js} +5 -5
- package/dist/{local-4R6GVW7U.js → local-QMQY2ZUX.js} +7 -7
- package/dist/{login-IXVAWTLO.js → login-I7UQ3GTM.js} +5 -5
- package/dist/{mcp-BM2MJWX4.js → mcp-XZV63DX5.js} +11 -11
- package/dist/{mcp-BM2MJWX4.js.map → mcp-XZV63DX5.js.map} +1 -1
- package/dist/{metrics-SSIP5CEB.js → metrics-KBTQ3KGZ.js} +5 -5
- package/dist/{monitor-ESZYHXT2.js → monitor-UGWY43IU.js} +5 -5
- package/dist/perf-CCHWHCSR.js +961 -0
- package/dist/perf-CCHWHCSR.js.map +1 -0
- package/dist/{product-VCGUV5TH.js → product-KLG2ZTHC.js} +5 -5
- package/dist/{product-map-AXKZNQDM.js → product-map-AMEZ76EQ.js} +5 -5
- package/dist/{prompts-FVPSON2N.js → prompts-ZVQNGL7X.js} +5 -5
- package/dist/proofs-QCNSQAPX.js +20 -0
- package/dist/{property-XEPCC7KQ.js → property-PJDQFHAL.js} +6 -6
- package/dist/{psql-JKF3OYYJ.js → psql-AURAN27C.js} +5 -5
- package/dist/{record-REQ37S54.js → record-SYWBTUOB.js} +6 -6
- package/dist/{redis-WGHT2GJB.js → redis-2H752SKO.js} +5 -5
- package/dist/{release-SNDIVIA6.js → release-OSIYUXSC.js} +5 -5
- package/dist/{runbook-XFJG2LFC.js → runbook-7LVHF6KV.js} +5 -5
- package/dist/{sandbox-DYCKLCMY.js → sandbox-TJQPMRGS.js} +18 -30
- package/dist/sandbox-TJQPMRGS.js.map +1 -0
- package/dist/{scenarios-PVHCKP55.js → scenarios-P2ABU4HK.js} +5 -5
- package/dist/{schedule-DGC7CX23.js → schedule-XLS56NP5.js} +8 -8
- package/dist/{session-ERGUFDDI.js → session-ACSFC3UB.js} +8 -8
- package/dist/{skill-6QO2E3TB.js → skill-F7I5P6AI.js} +43 -28
- package/dist/{skill-6QO2E3TB.js.map → skill-F7I5P6AI.js.map} +1 -1
- package/dist/{slack-WE7THLLH.js → slack-ZEK4GRND.js} +6 -6
- package/dist/{spec-SEOY7N3L.js → spec-XPIRRSV6.js} +5 -5
- package/dist/{src-CCK3TKBX.js → src-N7ZCYCXU.js} +4 -4
- package/dist/{src-AEWV5HYH.js → src-XSBBMVGW.js} +3 -3
- package/dist/{start-EPGLHTND.js → start-DKWLOQI6.js} +5 -5
- package/dist/{status-DTE2GXM2.js → status-K7KH5DI3.js} +5 -5
- package/dist/{tag-GIFEWGBL.js → tag-2Y3OMQQC.js} +6 -6
- package/dist/{task-VLJCHSFB.js → task-4PIOC4QV.js} +6 -6
- package/dist/{teams-J2OOJBL4.js → teams-KUM56BE2.js} +83 -47
- package/dist/teams-KUM56BE2.js.map +1 -0
- package/dist/{test-suite-CTACGIAZ.js → test-suite-SW25YGJV.js} +138 -96
- package/dist/test-suite-SW25YGJV.js.map +1 -0
- package/dist/{trace-RWQDYL4K.js → trace-QPXHYBLA.js} +7 -7
- package/dist/{triage-6UAXLCGU.js → triage-EZABRZZM.js} +6 -6
- package/dist/trigger-7C7KX5KH.js +137 -0
- package/dist/trigger-7C7KX5KH.js.map +1 -0
- package/dist/{update-check-QRREAM3N.js → update-check-E3X7UIY6.js} +3 -3
- package/dist/{upgrade-5Y655TQ6.js → upgrade-EJGTMW44.js} +4 -4
- package/dist/{usage-KCXFY46V.js → usage-GZCQDULF.js} +7 -7
- package/dist/{utilities-SUON2KZK.js → utilities-RIITD53A.js} +6 -6
- package/dist/{visual-GJZVJC32.js → visual-FH67OKS2.js} +5 -5
- package/dist/{workflow-R5QAXWBX.js → workflow-34YJZ2QQ.js} +38 -86
- package/dist/workflow-34YJZ2QQ.js.map +1 -0
- package/dist/{workflow-cache-PFBTWLGM.js → workflow-cache-YQ3BGJIG.js} +17 -17
- package/dist/{workflow-cache-convergence-resolve-UZGRPC3B.js → workflow-cache-convergence-resolve-SPLBRSNK.js} +5 -5
- package/dist/{workflow-debug-RACNWWOL.js → workflow-debug-HZB7TJGK.js} +10 -10
- package/dist/workflow-debug-HZB7TJGK.js.map +1 -0
- package/dist/{workflow-feedback-RJIQ5R3R.js → workflow-feedback-6UOTIWIQ.js} +5 -5
- package/dist/{workflow-fork-LINDCYJ3.js → workflow-fork-IDYHAHCX.js} +5 -5
- package/dist/workflow-promotion-U6WYFY5U.js +62 -0
- package/dist/workflow-promotion-U6WYFY5U.js.map +1 -0
- package/dist/{workflow-proposals-E4EXKAGM.js → workflow-proposals-MMVNIQT3.js} +5 -5
- package/dist/{workflow-revisions-5M6APN6R.js → workflow-revisions-BPUOLCZB.js} +5 -5
- package/dist/{workflow-status-7KL73WS2.js → workflow-status-RA3IEMEW.js} +5 -5
- package/package.json +3 -2
- package/dist/chat-target-IZ7YLLU3.js +0 -502
- package/dist/chat-target-IZ7YLLU3.js.map +0 -1
- package/dist/chunk-76Z6E7XX.js.map +0 -1
- package/dist/chunk-EUVB6Y3C.js.map +0 -1
- package/dist/chunk-LL6TSQWV.js.map +0 -1
- package/dist/chunk-QTYUM4ZB.js.map +0 -1
- package/dist/chunk-ROX7N3RK.js.map +0 -1
- package/dist/chunk-UEJ2HTKU.js.map +0 -1
- package/dist/proofs-TXJGAP5F.js.map +0 -1
- package/dist/sandbox-DYCKLCMY.js.map +0 -1
- package/dist/teams-J2OOJBL4.js.map +0 -1
- package/dist/test-suite-CTACGIAZ.js.map +0 -1
- package/dist/workflow-R5QAXWBX.js.map +0 -1
- /package/dist/{account-3ZG24MOE.js.map → account-MRZ54P5P.js.map} +0 -0
- /package/dist/{alert-5G7PTAX2.js.map → alert-XBHELHEI.js.map} +0 -0
- /package/dist/{api-catalog-IBYAFKKQ.js.map → api-catalog-GWYRX53L.js.map} +0 -0
- /package/dist/{api-key-UXC44BDR.js.map → api-key-FH6JN447.js.map} +0 -0
- /package/dist/{attachment-T6IRFQW3.js.map → attachment-J7GRVT5F.js.map} +0 -0
- /package/dist/{auth-SHOCJE6Z.js.map → auth-VZZQXRDR.js.map} +0 -0
- /package/dist/{benchmark-NJ2U6VTC.js.map → benchmark-A3SNDARF.js.map} +0 -0
- /package/dist/{blackcat-RQJVFGXQ.js.map → blackcat-UM6TBQBN.js.map} +0 -0
- /package/dist/{chunk-M752EUK3.js.map → chunk-3JU4UYYN.js.map} +0 -0
- /package/dist/{chunk-G6BCST3G.js.map → chunk-3ZOUNCA5.js.map} +0 -0
- /package/dist/{chunk-ABT3BLRC.js.map → chunk-5TKXHS3X.js.map} +0 -0
- /package/dist/{chunk-QCYSONXI.js.map → chunk-CRYHWO4M.js.map} +0 -0
- /package/dist/{chunk-I5RHTCPY.js.map → chunk-EL6KJ4PO.js.map} +0 -0
- /package/dist/{chunk-ZL4O6C42.js.map → chunk-HXBK3AGD.js.map} +0 -0
- /package/dist/{chunk-PRERH4DN.js.map → chunk-I2SOHQ2C.js.map} +0 -0
- /package/dist/{chunk-CYSPCGO6.js.map → chunk-JEHHEMUL.js.map} +0 -0
- /package/dist/{chunk-LIEADTEF.js.map → chunk-LJFVRJCA.js.map} +0 -0
- /package/dist/{chunk-PHNH3CEU.js.map → chunk-O4Z44BR7.js.map} +0 -0
- /package/dist/{chunk-QA37WVF6.js.map → chunk-OQFCZERZ.js.map} +0 -0
- /package/dist/{chunk-Z3MDQSZG.js.map → chunk-PJFFGQSB.js.map} +0 -0
- /package/dist/{chunk-IY3ZK46I.js.map → chunk-QOVJUSSL.js.map} +0 -0
- /package/dist/{chunk-RPJMQBB5.js.map → chunk-QXU4G4YN.js.map} +0 -0
- /package/dist/{chunk-SZSTJIYU.js.map → chunk-RRSUKARV.js.map} +0 -0
- /package/dist/{chunk-775FI436.js.map → chunk-RVD6J6IZ.js.map} +0 -0
- /package/dist/{chunk-5UUIBY6E.js.map → chunk-TAVBFLCR.js.map} +0 -0
- /package/dist/{chunk-3HI6QC4V.js.map → chunk-WRBU6TX7.js.map} +0 -0
- /package/dist/{chunk-BF3IPGMP.js.map → chunk-XSSZMG65.js.map} +0 -0
- /package/dist/{credential-TWSWV64F.js.map → credential-4RFRU6OH.js.map} +0 -0
- /package/dist/{credential-template-XO5D5AI3.js.map → credential-template-36PFUTBN.js.map} +0 -0
- /package/dist/{digest-SLEFOLPV.js.map → digest-THMKFMP7.js.map} +0 -0
- /package/dist/{docs-IDC4V4WQ.js.map → docs-ZGM53L67.js.map} +0 -0
- /package/dist/{doctor-SDAYEA6C.js.map → doctor-Z2XBGBM3.js.map} +0 -0
- /package/dist/{environment-VBUIBQ6O.js.map → environment-5YVZXBN7.js.map} +0 -0
- /package/dist/{exploration-IREIR5PO.js.map → exploration-GZ4WUJJH.js.map} +0 -0
- /package/dist/{exploratory-test-4Q7BMEFT.js.map → exploratory-test-Y7P4ZDLD.js.map} +0 -0
- /package/dist/{external-run-S7LULKPT.js.map → external-run-IJGGZOGI.js.map} +0 -0
- /package/dist/{feature-flag-IASHTUGU.js.map → feature-flag-UH6Z7774.js.map} +0 -0
- /package/dist/{flow-IBEZFZTU.js.map → flow-MRWAQYZZ.js.map} +0 -0
- /package/dist/{github-HAEZRULP.js.map → github-VDJU3PU3.js.map} +0 -0
- /package/dist/{init-LT2TZMQY.js.map → init-CROSTPA6.js.map} +0 -0
- /package/dist/{install-RTEYH6TK.js.map → install-ECR7OLAY.js.map} +0 -0
- /package/dist/{issues-5GE277R2.js.map → issues-K5TL2S4Z.js.map} +0 -0
- /package/dist/{knobs-AMIWGYUZ.js.map → knobs-OKQFBQLK.js.map} +0 -0
- /package/dist/{landing-3VLVVQL5.js.map → landing-XANIBPVM.js.map} +0 -0
- /package/dist/{list-GDJR6UY6.js.map → list-64GX47Q4.js.map} +0 -0
- /package/dist/{local-4R6GVW7U.js.map → local-QMQY2ZUX.js.map} +0 -0
- /package/dist/{login-IXVAWTLO.js.map → login-I7UQ3GTM.js.map} +0 -0
- /package/dist/{metrics-SSIP5CEB.js.map → metrics-KBTQ3KGZ.js.map} +0 -0
- /package/dist/{monitor-ESZYHXT2.js.map → monitor-UGWY43IU.js.map} +0 -0
- /package/dist/{product-VCGUV5TH.js.map → product-KLG2ZTHC.js.map} +0 -0
- /package/dist/{product-map-AXKZNQDM.js.map → product-map-AMEZ76EQ.js.map} +0 -0
- /package/dist/{prompts-FVPSON2N.js.map → prompts-ZVQNGL7X.js.map} +0 -0
- /package/dist/{src-AEWV5HYH.js.map → proofs-QCNSQAPX.js.map} +0 -0
- /package/dist/{property-XEPCC7KQ.js.map → property-PJDQFHAL.js.map} +0 -0
- /package/dist/{psql-JKF3OYYJ.js.map → psql-AURAN27C.js.map} +0 -0
- /package/dist/{record-REQ37S54.js.map → record-SYWBTUOB.js.map} +0 -0
- /package/dist/{redis-WGHT2GJB.js.map → redis-2H752SKO.js.map} +0 -0
- /package/dist/{release-SNDIVIA6.js.map → release-OSIYUXSC.js.map} +0 -0
- /package/dist/{runbook-XFJG2LFC.js.map → runbook-7LVHF6KV.js.map} +0 -0
- /package/dist/{scenarios-PVHCKP55.js.map → scenarios-P2ABU4HK.js.map} +0 -0
- /package/dist/{schedule-DGC7CX23.js.map → schedule-XLS56NP5.js.map} +0 -0
- /package/dist/{session-ERGUFDDI.js.map → session-ACSFC3UB.js.map} +0 -0
- /package/dist/{slack-WE7THLLH.js.map → slack-ZEK4GRND.js.map} +0 -0
- /package/dist/{spec-SEOY7N3L.js.map → spec-XPIRRSV6.js.map} +0 -0
- /package/dist/{src-CCK3TKBX.js.map → src-N7ZCYCXU.js.map} +0 -0
- /package/dist/{tag-GIFEWGBL.js.map → src-XSBBMVGW.js.map} +0 -0
- /package/dist/{start-EPGLHTND.js.map → start-DKWLOQI6.js.map} +0 -0
- /package/dist/{status-DTE2GXM2.js.map → status-K7KH5DI3.js.map} +0 -0
- /package/dist/{trace-RWQDYL4K.js.map → tag-2Y3OMQQC.js.map} +0 -0
- /package/dist/{task-VLJCHSFB.js.map → task-4PIOC4QV.js.map} +0 -0
- /package/dist/{triage-6UAXLCGU.js.map → trace-QPXHYBLA.js.map} +0 -0
- /package/dist/{update-check-QRREAM3N.js.map → triage-EZABRZZM.js.map} +0 -0
- /package/dist/{upgrade-5Y655TQ6.js.map → update-check-E3X7UIY6.js.map} +0 -0
- /package/dist/{utilities-SUON2KZK.js.map → upgrade-EJGTMW44.js.map} +0 -0
- /package/dist/{usage-KCXFY46V.js.map → usage-GZCQDULF.js.map} +0 -0
- /package/dist/{workflow-debug-RACNWWOL.js.map → utilities-RIITD53A.js.map} +0 -0
- /package/dist/{visual-GJZVJC32.js.map → visual-FH67OKS2.js.map} +0 -0
- /package/dist/{workflow-cache-PFBTWLGM.js.map → workflow-cache-YQ3BGJIG.js.map} +0 -0
- /package/dist/{workflow-cache-convergence-resolve-UZGRPC3B.js.map → workflow-cache-convergence-resolve-SPLBRSNK.js.map} +0 -0
- /package/dist/{workflow-feedback-RJIQ5R3R.js.map → workflow-feedback-6UOTIWIQ.js.map} +0 -0
- /package/dist/{workflow-fork-LINDCYJ3.js.map → workflow-fork-IDYHAHCX.js.map} +0 -0
- /package/dist/{workflow-proposals-E4EXKAGM.js.map → workflow-proposals-MMVNIQT3.js.map} +0 -0
- /package/dist/{workflow-revisions-5M6APN6R.js.map → workflow-revisions-BPUOLCZB.js.map} +0 -0
- /package/dist/{workflow-status-7KL73WS2.js.map → workflow-status-RA3IEMEW.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sandbox.ts","../../content/src/sandbox-v2-guide/overview.txt","../../content/src/sandbox-v2-guide/definition.txt","../../content/src/sandbox-v2-guide/environments.txt","../../content/src/sandbox-v2-guide/seeds-and-actions.txt","../../content/src/sandbox-v2-guide/data-fast-forward.txt","../../content/src/sandbox-v2-guide/personas.txt","../../content/src/sandbox-v2-guide/authentication.txt","../../content/src/sandbox-v2-guide/env.txt","../../content/src/sandbox-v2-guide/secrets.txt","../../content/src/sandbox-v2-guide/emulation.txt","../../content/src/sandbox-v2-guide/object-storage.txt","../../content/src/sandbox-v2-guide/ports.txt","../../content/src/sandbox-v2-guide/verify.txt","../../content/src/sandbox-v2-guide/cli.txt","../../content/src/sandbox-v2-guide/upload.txt","../../content/src/sandbox-v2-guide/warm.txt","../../content/src/sandbox-v2-guide/local.txt","../../content/src/sandbox-v2-guide/index.ts","../src/sandbox-emulation.ts","../src/sandbox-logs.ts","../src/sandbox-preview.ts","../src/sandbox-v2.ts","../../sandbox-helper/src/manifest.ts","../../sandbox-helper/src/object-storage.ts","../../sandbox-helper/src/sync-protocol.ts","../../sandbox-helper/src/emulation-refs.ts","../src/sandbox-push.ts","../src/sandbox-push-syncable.ts","../../sandbox-helper/src/image-archive.ts","../src/sandbox-sync.ts","../src/sandbox-refresh.ts","../src/sandbox-warm.ts","../src/sandbox-secrets.ts"],"sourcesContent":["/**\n * CLI Sandbox Management\n *\n * Create, provision, and manage sandbox templates and instances.\n * Drive template authoring through artifact uploads, command execution,\n * and diagnostics — designed for Claude Code to iterate on sandbox setup.\n */\n\nimport process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues, getGuideSectionArg, selectProperty } from './cli-helpers.js';\nimport { splitAtSeparator } from './argv.js';\nimport { apiFetch, isTerminalFailure, exitOnFailure } from './api-fetch.js';\nimport { SANDBOX_GUIDE_SECTIONS, printGuide } from '@chatsdet/content';\nimport { SANDBOX_V2_GUIDE_SECTIONS } from '@chatsdet/content/sandbox-v2-guide';\nimport {\n INSTANCE_SIZES,\n INSTANCE_SIZE_SPEC,\n SANDBOX_EGRESS_MODES,\n parseInstanceSize,\n type InstanceSize,\n type SandboxEgressAllowlist,\n type SandboxEgressMode,\n} from '@chatsdet/types';\nimport { getPluginIds } from '@chatsdet/emulation/plugins';\nimport { handleEmulation } from './sandbox-emulation.js';\nimport { handleLogs } from './sandbox-logs.js';\nimport { handlePreview } from './sandbox-preview.js';\nimport {\n handleSandboxClaim,\n handleSandboxConnect,\n handleSandboxDestroy,\n handleSandboxEnvs,\n handleSandboxExec,\n handleSandboxRun,\n handleSandboxShow,\n handleSandboxValidate,\n handleSandboxVerifyEnv,\n rejectUnknownSandboxV2Args,\n v2Positionals,\n} from './sandbox-v2.js';\nimport { handleSandboxPush } from './sandbox-push.js';\nimport { handleSandboxSync } from './sandbox-sync.js';\nimport { handleSandboxRefresh } from './sandbox-refresh.js';\nimport { handleSandboxWarm, isSandboxV2WarmCommand } from './sandbox-warm.js';\nimport { handleSandboxSecret } from './sandbox-secrets.js';\nimport { writeRawOutput } from './brand.js';\nexport {\n withEmulationMaster,\n withPluginConfig,\n withPluginEnabled,\n withoutPlugin,\n} from './sandbox-emulation.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Template = {\n id: string;\n name: string;\n propertyId: string | null;\n repositoryFullName: string | null;\n branch: string;\n subdirectory: string | null;\n status: string;\n checklistProfile: string | null;\n configSchema: Array<{ key: string; description?: string; required: boolean; sensitive: boolean }> | null;\n configDefaults: Record<string, string> | null;\n configuredSensitiveKeys?: string[];\n configuredSensitiveAutoConfigKeys?: string[];\n createdAt: string;\n updatedAt: string;\n};\n\ntype GitHubRepo = {\n id: string;\n repoFullName: string;\n repoName: string;\n defaultBranch: string;\n rootDir: string | null;\n agentHint: string | null;\n};\n\ntype Instance = {\n id: string;\n templateId: string;\n name: string;\n status: string;\n hostUrl: string | null;\n isPrivate: boolean;\n expiresAt: string | null;\n createdAt: string;\n /**\n * What the instance is for. `flow_authoring` is the org-shared instance the\n * Flow Designer auto-spins and that sandbox workflow runs reuse — surfaced in\n * `sandbox list` so agents can find/warm it.\n */\n purpose?: string | null;\n};\n\nexport const parseEgressMode = (value: string | undefined): SandboxEgressMode => {\n if (!value) return 'off';\n if ((SANDBOX_EGRESS_MODES as readonly string[]).includes(value)) {\n return value as SandboxEgressMode;\n }\n console.error(\n `Error: invalid --egress-mode '${value}'. Must be one of: ${SANDBOX_EGRESS_MODES.join(', ')}`\n );\n process.exit(1);\n};\n\nexport const parseEgressAllowlist = (argv: string[]): SandboxEgressAllowlist => {\n const rawEntries = getArgValues(argv, '--egress-allow')\n .flatMap((value) => value.split(','))\n .map((value) => value.trim())\n .filter(Boolean);\n\n return rawEntries.map((entry) => {\n const separator = entry.indexOf(':');\n if (separator <= 0) {\n console.error(\n `Error: invalid --egress-allow '${entry}'. Use domain:example.com or cidr:203.0.113.0/24`\n );\n process.exit(1);\n }\n const kind = entry.slice(0, separator);\n const value = entry.slice(separator + 1);\n if (kind !== 'domain' && kind !== 'cidr') {\n console.error(`Error: invalid --egress-allow kind '${kind}'. Must be domain or cidr.`);\n process.exit(1);\n }\n return { kind, value };\n });\n};\n\ntype Artifact = {\n id: string;\n taskId: string;\n artifactType: string;\n filename: string;\n content?: string;\n createdAt: string;\n updatedAt: string;\n};\n\ntype ChecklistItem = {\n id: string;\n label: string;\n description: string;\n dependsOn: string[];\n producesArtifacts: string[];\n agentAutomatable: boolean;\n itemStatus: { status: string; startedAt?: string; completedAt?: string; summary?: string; error?: string };\n actionable: boolean;\n artifacts: Array<{ artifactType: string; filename: string }>;\n};\n\ntype CommandOrError =\n | { stdout: string; stderr: string; exitCode: number }\n | { error: string; code?: string };\n\ntype DiagnosticsResult = {\n instance: Record<string, unknown>;\n lifecycle?: {\n databaseStatus: string;\n poolStatus: string | null;\n ec2InstanceState: string | null;\n live: boolean;\n message: string | null;\n };\n docker: CommandOrError;\n git: CommandOrError;\n disk: CommandOrError;\n resources?: CommandOrError;\n agentHealth: CommandOrError;\n appHealth: CommandOrError;\n provision?: {\n phase: string;\n jobStatus: string;\n startedAt?: string;\n elapsedSeconds: number | null;\n lastError?: string;\n };\n};\n\ntype ServiceRoute = { service: string; route: string; port: string };\n\nasync function fetchInstanceRoutes(apiUrl: string, token: string, instanceId: string): Promise<ServiceRoute[]> {\n const res = await apiRequest<{ ok: boolean; error?: string; data: { routes: ServiceRoute[] } }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/routes`,\n undefined,\n { timeoutMs: 30_000 }\n );\n return res.ok ? res.data.routes : [];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst STATUS_ICONS: Record<string, string> = {\n completed: '[done]',\n running: '[....]',\n pending: '[ ]',\n failed: '[FAIL]',\n skipped: '[skip]',\n};\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nfunction pad(s: string, n: number): string {\n return s.padEnd(n);\n}\n\nconst INSTANCE_SIZE_HELP_LINES = INSTANCE_SIZES.map((size) => {\n const spec = INSTANCE_SIZE_SPEC[size];\n return ` ${pad(size, 8)} ${spec.ec2InstanceType} · ${spec.vcpu} vCPU · ${spec.memoryMiB / 1024} GiB`;\n});\n\n// ---------------------------------------------------------------------------\n// Help\n// ---------------------------------------------------------------------------\n\nfunction printHelp(): void {\n console.log(\n [\n 'cofactor sandbox: Live, seeded copies of an application (Sandboxes v2), plus the legacy v1 templates and instances',\n '',\n 'Sandboxes v2 (start here: an application\\'s cofactor.sandbox.yaml — see \"cofactor sandbox guide v2\"):',\n ' validate [file] [--upload] Offline schema + cross-check (default ./cofactor.sandbox.yaml; --upload: for pushed images)',\n ' connect --application <a> --repo <owner/name> [--dir <path>] Build the application\\'s sandboxes from a GitHub repo',\n ' push --application <a> <image> [options] Push a built image as the application\\'s next version (see \"Push options\" below)',\n ' (connect and push create the application when <a> is a new name; its sandbox is its default environment)',\n ' envs --application <a> The application\\'s delivery, version, environment heads, environments, actions, operations',\n ' refresh --application <a> [--environment <e>] [--wait] [--json] Move the environment\\'s head to the default branch\\'s head / push head now (see \"Refresh options\" below)',\n ' secret set <NAME> --application <a> [--environment <e>] Set a secret from stdin (see \"Secret options\" below)',\n ' secret list --application <a> List an application\\'s secrets (names and scope, never values)',\n ' secret rm <NAME> --application <a> [--environment <e>] Remove a secret',\n ' claim --application <a> [options] Claim a sandbox (see \"Claim options\" below)',\n ' show <id> Status, URLs, logins, seed outputs, actions and operations',\n ' run <id> <action|plugin.op> [options] Run a repo action or emulator operation (see \"Run options\" below)',\n ' exec <id> [--timeout S] [--json] -- <command...> Run one shell command in a claimed sandbox (see \"Exec options\" below)',\n ' sync <id> [--build] [--json] Make a sandbox you claimed match your working tree, uncommitted changes included',\n ' sync <id> <local-dir> --to <path> Mirror local build output into <path> in the sandbox (see \"Sync options\" below)',\n ' destroy <id> Tear a claimed sandbox down',\n ' warm list|set|show|clear [options] Keep an environment warm so claims start in about a second (staff only; see \"Warm options\" below)',\n ' verify-env --application <a> [options] Claim each environment of the current version, sign its personas in, destroy',\n ' preview --application <a> --pr <n> A pull request\\'s preview and its logins',\n ' guide v2 [topic] Show the Sandboxes v2 guide',\n ` topics: ${SANDBOX_V2_GUIDE_SECTIONS.map((section) => section.slug).join(', ')}`,\n ' (pushing a built image: start with `guide v2 upload`)',\n '',\n 'Refresh options (Sandboxes v2):',\n ' --environment <name> Environment to refresh (default: \"default\")',\n ' --wait Block until the new head\\'s snapshot is ready or failed',\n ' The head is what flow runs, the flow designer, scheduled runs and unpinned claims use: the ready snapshot of the',\n ' default branch\\'s head commit (GitHub) or the newest push tagged main or nightly (the newest push before either). It also refreshes every 6 hours; this does it now.',\n '',\n 'Push options (Sandboxes v2):',\n ' --definition <file> The cofactor.sandbox.yaml to push with the image (default: ./cofactor.sandbox.yaml)',\n ' --tag <tag> Name this push (repeatable); a tag always names the newest push carrying it',\n ' --wait Block until Cofactor has imported (booted) the image',\n '',\n 'Claim options (Sandboxes v2):',\n ' --environment <name> Environment to claim (default: \"default\")',\n ' --at <version> A commit SHA, or a push id or tag (default: the default branch head / push head)',\n ' --timeout <duration> The claim\\'s time limit: not ready by then, it fails (e.g. 45m, 3h; default 2h,',\n ' 1m to 24h). A failure in your own build or seeds fails it at once.',\n ' --wait Block until the sandbox is ready or failed, then print `show`',\n '',\n 'Secret options (Sandboxes v2):',\n ' --environment <name> Set or remove the secret for this environment only (default: all environments)',\n ' The value is read from stdin, never an argument: printf %s \"$KEY\" | cofactor sandbox secret set NAME --application <a>',\n ' Services read it as an environment variable on every claim; it is never shown again.',\n '',\n 'Run options (Sandboxes v2):',\n ' --input <key>=<value> Set an input (repeatable), coerced to the action/operation\\'s declared type',\n ' --json-input \\'{...}\\' Full input as JSON (overrides --input)',\n '',\n 'Warm options (Sandboxes v2, superadmin):',\n ' warm list Every environment kept warm on this stage',\n ' warm set --application <a> --environment <e> --slots <1-12> [--tag <t>] Turn on, or change slots or tag (--tag: pushed applications only, and required)',\n ' warm show --application <a> --environment <e> Filling / On / Switching / Needs attention, and the free warm slots now',\n ' warm clear --application <a> --environment <e> Turn off and release the capacity',\n ' --target-org <id> Act in another org (give the application by id); default: your session\\'s org',\n '',\n 'Exec options (Sandboxes v2):',\n ' --timeout <seconds> Kill the command after this long (default 300, max 1800); exit code 124',\n ' Everything after `--` is the command, joined with spaces (ssh semantics): quote pipes and',\n ' redirects yourself, e.g. -- \\'ls | wc -l\\'. Output is the last 4000 characters of each stream,',\n ' scrubbed of the sandbox\\'s secrets. Exits with the command\\'s exit code. Needs the `sandbox:exec` scope for API keys.',\n '',\n 'Sync options (Sandboxes v2; see \"cofactor sandbox guide v2 local\"):',\n ' Syncs into a sandbox you claimed with `claim`, straight to the sandbox. It lists what it writes (untracked files',\n ' included) and deletes, then applies. A file only the sandbox has is deleted only if it is not ignored here and',\n ' was once tracked at that path; everything else (node_modules/, build output) is kept. Each sync extends the',\n ' sandbox\\'s lease, up to 8 h after the claim. Needs the `sandbox:exec` scope for API keys.',\n ' Root: the repository (GitHub) or the directory holding cofactor.sandbox.yaml (pushed: its WORKDIR mirrors it).',\n ' --build GitHub only: rerun the build steps whose inputs changed and restart after_build services',\n ' --to <path> With <local-dir>: mirror it into <path> under the sync root, ignore rules aside',\n ' (e.g. `bun run build`, then `sync <id> frontend/dist --to static`)',\n ' Nothing restarts on its own: `cofactor sandbox exec <id> -- cofactor-sandbox restart <service>`.',\n '',\n 'Verify-env options (Sandboxes v2):',\n ' --environment <name> Verify only this environment (default: every environment in the definition)',\n '',\n 'Sandboxes v1 (legacy templates and instances):',\n ' guide [section] Show the v1 sandbox authoring guide (v2: `guide v2` above)',\n ' list List templates and instances',\n ' template [id] Show template details',\n ' repo list --template <id> List repos linked to a template',\n ' repo add --template <id> --property-github-repo-id <uuid> [--role <r>] [--spec <r=owner/name@branch:subdir>]',\n ' repo update --template <id> --link-id <uuid> [--role <r>] [--branch <b>] [--subdirectory <s>] [--service-name <n>]',\n ' repo remove --template <id> --link-id <uuid>',\n ' checklist <template-id> Show checklist state',\n ' checklist skip <template-id> <item-id> Skip a checklist item',\n ' checklist complete <template-id> <item-id> [options] Mark item complete from CLI',\n ' artifacts <template-id> List artifacts',\n ' artifact get <template-id> <type> Get artifact content',\n ' artifact create <template-id> <type> <file> Upload artifact (use --path for file type)',\n ' create [property] [options] Create template from property (triggers provisioning)',\n ' provision <template-id> [options] Create a dedicated instance from a verified template',\n ' warm <template-id> [--wait] [--timeout <sec>] Warm the org-shared flow-authoring instance',\n ' (the one the flow designer auto-spins; sandbox runs reuse it)',\n ' verify <template-id> Verify an authoring template (provision + probe + flip to verified)',\n ' set-size <template-id> <size> Set EC2 size for next provision (see \"Instance sizes\" below)',\n ' pool set-size <template-id> <count> Set the warm stopped-pool size for a template (0 = off)',\n ' build <instance-id> Build and start app services on sandbox',\n ' run-command <instance-id> <command...> Execute shell command on sandbox',\n ' --service <name> Run inside a Docker container instead of on the host VM',\n ' diagnostics <instance-id> Show sandbox diagnostics',\n ' logs <instance-id> [options] Show container logs (live if running, else the captured failure snapshot)',\n ' terminate <instance-id> Terminate an instance',\n ' config list <template-id> Show config schema and defaults',\n ' config set <template-id> <key> <value> Set a config default value',\n ' config unset <template-id> <key> Remove a config default value',\n ' emulation get <template-id> Show emulation config + available plugins',\n ' emulation enable/disable <template-id> [pluginId] Toggle master switch (or one plugin)',\n ' emulation set <template-id> <pluginId> [opts] Configure a plugin entry',\n ' emulation remove <template-id> <pluginId> Drop a plugin entry',\n ' action <list|get|create|delete|execute|executions> Manage template actions (see \"Action sub-commands\" below)',\n ' delete <template-id> Delete template and all instances',\n '',\n 'Create options:',\n ' --property <name|id> Property (prompted if omitted, auto-selected if only one)',\n ' --branch <branch> Branch (default: from linked repo)',\n ' --name <name> Template name (default: repo name)',\n ' --subdirectory <path> Subdirectory (default: from linked repo rootDir)',\n ' --size <size> EC2 size for first authoring sandbox (default: medium; see \"Instance sizes\" below)',\n '',\n 'Provision options:',\n ' --name <name> Instance name',\n ' --ttl <hours> Time to live in hours (default: 8)',\n ' --with-agent Start with sandbox agent',\n ' --private Skip public ALB routing',\n ' --egress-mode <mode> off|observe (observe logs unmatched egress only; default: off)',\n ' --egress-allow <kind:value> Repeat or comma-separate domain:/cidr: entries',\n '',\n 'Artifact get options:',\n ' --output <file> Write to file instead of stdout',\n '',\n 'Logs options:',\n ' --service <name> Service name (default: all services; live path only)',\n ' --tail <n> Number of lines (default: 200; live path only)',\n ' --failure Force the captured failure snapshot (full logs + deploy.log)',\n ' Works for failed/terminated instances after the host is gone.',\n ' Running instances auto-use live logs; failed ones auto-fall back',\n ' to the snapshot when the live tail can\\'t run.',\n '',\n 'Guide sections (v1):',\n ' goal, research, artifacts, actions, verify, checklist',\n '',\n 'Instance sizes:',\n ...INSTANCE_SIZE_HELP_LINES,\n '',\n 'Action sub-commands:',\n ' action list <template-id> List actions for a template',\n ' action get <template-id> <slug> Show action definition + script',\n ' action create <template-id> <slug> [opts] Create or update an action',\n ' action delete <template-id> <slug> Delete an action',\n ' action execute <instance-id> <slug> [opts] Execute action on a running instance',\n ' action executions <instance-id> [opts] List execution history',\n ' action execution get <inst-id> <exec-id> Show one execution (advances a background run)',\n ' action execution cancel <inst-id> <exec-id> Stop a background execution',\n '',\n 'Action execute options:',\n ' --param key=value Set an input param (repeatable)',\n ' --params-file <file> JSON file of input params (--param overrides)',\n ' --async Return once the script is running, without waiting.',\n ' Required for anything longer than the 300s',\n ' synchronous ceiling. Poll `action execution get`',\n ' to reach a terminal status.',\n ' --timeout-ms <ms> Remote ceiling for an --async run (default 1h, max 48h)',\n '',\n 'Action create options:',\n ' --label <label> Display name (default: slug → Title Case)',\n ' --description <text> Description',\n ' --icon <icon> Phosphor icon name',\n ' --script <file> Path to script file (bash)',\n ' --input-schema <file> Path to JSON Schema file for input params',\n ' --output-schema <file> Path to JSON Schema file for output',\n ' --credential-output-config <file> JSON file with ActionCredentialOutputConfig (auto-creates a Canary credential from the action result).',\n ' --output-config <file> JSON file with an array of output slots the action hands back (test-results / artifacts-archive).',\n ' Supports `variableMapping` (persona var name → $output.* token) to fill the persona\\'s declared variables ($var.USER.<NAME>)',\n ' --credential-template <id> Credential template this action provisions; login nodes using it auto-provision',\n ' --provisioning-mode <shared|unique> shared = provision once / reused (default); unique = one credential per run (script must accept a runId input param)',\n ' --run-on-provision Base seed: run automatically once per provisioned instance (not login-triggered). Its --output-schema keys are exposed to flows as $var.SEED.<key>',\n ' --provision-order <n> Order for multiple base seeds on a template (ascending; unset runs first)',\n ' --timeout <ms> Timeout in ms (default: 55000)',\n ' --encrypt-script Encrypt script content at rest',\n '',\n 'Action execute options:',\n ' --param <key>=<value> Set parameter (repeatable)',\n ' --params-file <file> JSON file with all params',\n '',\n 'Emulation sub-commands (third-party service emulation: Slack/Jira/Google-OAuth/email):',\n ' emulation get <template-id> Master switch, configured plugins, available catalog',\n ' emulation enable <template-id> [pluginId] Turn on emulation (or enable a single plugin)',\n ' emulation disable <template-id> [pluginId] Turn off emulation (or disable a single plugin)',\n ' emulation set <template-id> <pluginId> [opts] Create/update a plugin entry (does not flip master switch)',\n ' emulation remove <template-id> <pluginId> Remove a plugin entry',\n ` Available plugins: ${getPluginIds().join(', ')}`,\n '',\n 'Emulation set options:',\n ' --config <file> JSON file with plugin-specific config (e.g. { \"tenant\": \"acme\" })',\n ' --fixture <file> JSON file with seed/fixture data for the plugin',\n ' --env-mapping <file> JSON file mapping logical env-contract keys to the app env var(s)',\n '',\n 'Global options:',\n ' --json Output raw JSON',\n ' --env <env> Environment (prod, dev, local)',\n ' -h, --help Show help',\n ].join('\\n')\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-command handlers\n// ---------------------------------------------------------------------------\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n\n const [templatesRes, instancesRes] = await Promise.all([\n apiRequest<{ ok: boolean; error?: string; data: Template[] }>(\n apiUrl, token, 'GET', '/org/environments/templates'\n ),\n apiRequest<{ ok: boolean; error?: string; data: Instance[] }>(\n apiUrl, token, 'GET', '/org/environments/instances'\n ),\n ]);\n\n if (!templatesRes.ok) {\n console.error(`Error: ${templatesRes.error}`);\n process.exit(1);\n }\n if (!instancesRes.ok) {\n console.error(`Error loading instances: ${instancesRes.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify({ templates: templatesRes.data, instances: instancesRes.data }, null, 2));\n return;\n }\n\n const templates = templatesRes.data ?? [];\n const instances = instancesRes.data ?? [];\n\n console.log('Templates:');\n if (templates.length === 0) {\n console.log(' (none)');\n } else {\n console.log(` ${pad('ID', 38)} ${pad('Name', 24)} ${pad('Status', 14)} ${pad('Profile', 12)} Repository`);\n for (const t of templates) {\n console.log(` ${pad(t.id, 38)} ${pad(t.name, 24)} ${pad(t.status, 14)} ${pad(t.checklistProfile ?? '-', 12)} ${t.repositoryFullName ? `${t.repositoryFullName}:${t.branch}` : '(no repository)'}`);\n }\n }\n\n console.log('');\n console.log('Instances:');\n if (instances.length === 0) {\n console.log(' (none)');\n } else {\n console.log(` ${pad('ID', 38)} ${pad('Status', 14)} ${pad('Purpose', 16)} ${pad('Access', 10)} ${pad('Template', 38)} Expires`);\n for (const i of instances) {\n const access = i.isPrivate ? 'private' : 'public';\n // The org-shared flow-authoring instance is the one workflow runs reuse —\n // flag it so it's easy to spot (and warm via `sandbox warm <template>`).\n const purpose =\n i.purpose === 'flow_authoring' ? 'flow_authoring*' : (i.purpose ?? '-');\n console.log(` ${pad(i.id, 38)} ${pad(i.status, 14)} ${pad(purpose, 16)} ${pad(access, 10)} ${pad(i.templateId, 38)} ${i.expiresAt ? formatDate(i.expiresAt) : '-'}`);\n }\n console.log('');\n console.log(' * flow_authoring = org-shared instance the flow designer auto-spins and');\n console.log(' sandbox workflow runs reuse. Warm it with: canary sandbox warm <template-id>');\n }\n}\n\nasync function handleTemplate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n if (!id) {\n await handleList(argv, apiUrl, token);\n return;\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'GET', `/org/environments/templates/${id}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const t = res.data;\n console.log(`Template: ${t.name}`);\n console.log(` ID: ${t.id}`);\n console.log(` Repository: ${t.repositoryFullName ? `${t.repositoryFullName}:${t.branch}` : '(none)'}`);\n if (t.subdirectory) console.log(` Subdir: ${t.subdirectory}`);\n console.log(` Status: ${t.status}`);\n console.log(` Profile: ${t.checklistProfile ?? 'full-stack (default)'}`);\n console.log(` Created: ${formatDate(t.createdAt)}`);\n console.log(` Updated: ${formatDate(t.updatedAt)}`);\n\n if (t.configSchema && t.configSchema.length > 0) {\n console.log('');\n console.log(' Config Schema:');\n for (const c of t.configSchema) {\n const flags = [c.required ? 'required' : 'optional', c.sensitive ? 'sensitive' : ''].filter(Boolean).join(', ');\n writeRawOutput(` ${c.key} (${flags})${c.description ? ` — ${c.description}` : ''}`);\n }\n }\n}\n\nasync function handleArtifacts(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId] = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox artifacts <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Artifact[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/artifacts`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const artifacts = res.data ?? [];\n if (artifacts.length === 0) {\n console.log('No artifacts found.');\n return;\n }\n\n console.log(`${pad('Type', 22)} ${pad('Filename', 30)} ${pad('Task', 24)} Created`);\n for (const a of artifacts) {\n console.log(`${pad(a.artifactType, 22)} ${pad(a.filename, 30)} ${pad(a.taskId, 24)} ${formatDate(a.createdAt)}`);\n }\n}\n\nasync function handleChecklist(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n // Handle checklist sub-commands: skip, complete\n if (positional[0] === 'skip') {\n await handleChecklistSkip(argv.slice(1), apiUrl, token);\n return;\n }\n if (positional[0] === 'complete') {\n await handleChecklistComplete(argv.slice(1), apiUrl, token);\n return;\n }\n\n const templateId = positional[0];\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox checklist <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ChecklistItem[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/checklist`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const items = res.data ?? [];\n if (items.length === 0) {\n console.log('No checklist items.');\n return;\n }\n\n console.log('Checklist:');\n for (const item of items) {\n const icon = STATUS_ICONS[item.itemStatus.status] ?? '[????]';\n const actionable = item.actionable ? ' *' : '';\n console.log(` ${icon} ${item.label}${actionable}`);\n if (item.itemStatus.summary) {\n console.log(` ${item.itemStatus.summary}`);\n }\n if (item.itemStatus.error) {\n console.log(` ERROR: ${item.itemStatus.error}`);\n }\n if (item.artifacts.length > 0) {\n console.log(` Artifacts: ${item.artifacts.map((a) => a.artifactType).join(', ')}`);\n }\n }\n console.log('');\n console.log(' * = actionable (deps met, ready to run)');\n}\n\nasync function handleChecklistSkip(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const itemId = positional[1];\n\n if (!templateId || !itemId) {\n console.error('Error: both template ID and item ID required. Usage: canary sandbox checklist skip <template-id> <item-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/checklist/${itemId}/skip`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Skipped checklist item: ${itemId}`);\n}\n\nasync function handleChecklistComplete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const itemId = positional[1];\n\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !itemId) {\n console.error('Error: both template ID and item ID required.');\n console.error('Usage: canary sandbox checklist complete <template-id> <item-id> [--status success|failed] [--summary \"...\"]');\n process.exit(1);\n }\n\n const status = getArgValue(argv, '--status') ?? 'success';\n const summary = getArgValue(argv, '--summary');\n const configSchemaArg = getArgValue(argv, '--config-schema');\n const healthCheckPath = getArgValue(argv, '--health-check-path');\n\n let configSchema: unknown;\n if (configSchemaArg) {\n // Accept inline JSON or a file path\n try {\n configSchema = JSON.parse(configSchemaArg);\n } catch {\n try {\n configSchema = JSON.parse(readFileSync(configSchemaArg, 'utf-8'));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Error reading config schema from \"${configSchemaArg}\": ${msg}`);\n process.exit(1);\n }\n }\n }\n\n const body: Record<string, unknown> = {\n status: status === 'failed' ? 'failed' : 'completed',\n ...(summary ? { summary } : {}),\n ...(configSchema ? { configSchema } : {}),\n ...(healthCheckPath ? { healthCheckPath } : {}),\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: { itemId: string; status: string } }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/checklist/${itemId}/complete`, body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n console.log(`Completed checklist item: ${itemId} (${res.data?.status})`);\n}\n\nasync function handleConfigList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox config list <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n const t = res.data;\n const schema = t.configSchema ?? [];\n const defaults = t.configDefaults ?? {};\n const configuredSensitiveKeys = t.configuredSensitiveKeys ?? [];\n const configuredSensitiveKeySet = new Set(configuredSensitiveKeys);\n\n if (json) {\n writeRawOutput(JSON.stringify(\n { configSchema: schema, configDefaults: defaults, configuredSensitiveKeys },\n null,\n 2\n )\n );\n return;\n }\n\n if (schema.length === 0) {\n console.log('No config schema defined for this template.');\n return;\n }\n\n console.log('Config Schema:');\n for (const item of schema) {\n const value = defaults[item.key];\n const display = item.sensitive\n ? configuredSensitiveKeySet.has(item.key)\n ? '•••••••• (configured)'\n : '(not set)'\n : (value ?? '(not set)');\n const required = item.required ? ' (required)' : '';\n const sensitive = item.sensitive ? ' [sensitive]' : '';\n console.log(` ${item.key}${required}${sensitive}`);\n if (item.description) writeRawOutput(` ${item.description}`);\n console.log(` Value: ${display}`);\n }\n}\n\nexport const configSetPayload = (key: string, value: string) => ({\n configDefaults: { [key]: value },\n unsetKeys: [] as string[],\n});\n\nexport const configUnsetPayload = (key: string) => ({\n configDefaults: {} as Record<string, string>,\n unsetKeys: [key],\n});\n\nasync function handleConfigSet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const fromStdin = hasFlag(argv, '--value-stdin');\n const templateId = positional[0];\n const key = positional[1];\n let value = positional[2];\n\n if (!templateId || !key) {\n console.error('Error: template ID and key required.');\n console.error('Usage: canary sandbox config set <template-id> <key> <value>');\n console.error(' For sensitive values: echo \"sk-ant-...\" | canary sandbox config set <template-id> <key> --value-stdin');\n process.exit(1);\n }\n\n if (fromStdin) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n value = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (value === undefined) {\n console.error('Error: value required. Pass as argument or use --value-stdin');\n process.exit(1);\n }\n\n // The server may take longer than 30s to acknowledge config writes\n // (encrypted columns, audit log). Even when it does the write succeeds —\n // bump the CLI ceiling so the user sees the success rather than a\n // misleading timeout.\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'PATCH', `/org/environments/templates/${templateId}/config`, {\n ...configSetPayload(key, value),\n },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Config set: ${key}`);\n}\n\nasync function handleConfigUnset(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, key] = argv.filter((arg) => !arg.startsWith('-'));\n if (!templateId || !key) {\n console.error('Usage: canary sandbox config unset <template-id> <key>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/config`,\n configUnsetPayload(key),\n { timeoutMs: 90_000 }\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Config unset: ${key}`);\n}\n\nasync function handleSetSize(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, sizeArg] = argv.filter((a) => !a.startsWith('-'));\n\n if (!templateId || !sizeArg) {\n console.error('Error: template ID and size required.');\n console.error(`Usage: canary sandbox set-size <template-id> <${INSTANCE_SIZES.join('|')}>`);\n process.exit(1);\n }\n\n const size = parseInstanceSize(sizeArg);\n if (!size) {\n console.error(`Error: invalid size '${sizeArg}'. Must be one of: ${INSTANCE_SIZES.join(', ')}`);\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; instanceSize?: InstanceSize }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/instance-size`,\n { instanceSize: size }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Instance size set to ${size}`);\n}\n\nasync function handlePoolSetSize(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [templateId, countArg] = argv.filter((a) => !a.startsWith('-'));\n\n if (!templateId || countArg === undefined) {\n console.error('Error: template ID and count required.');\n console.error('Usage: canary sandbox pool set-size <template-id> <count>');\n process.exit(1);\n }\n\n // Strict decimal-integer parse — reject '1e2', '0x10', '3.5', etc. that\n // Number() would silently accept, so the count the operator typed is exactly\n // what the server receives.\n const poolMinReady = /^\\d+$/.test(countArg) ? Number(countArg) : Number.NaN;\n if (!Number.isInteger(poolMinReady)) {\n console.error(`Error: invalid count '${countArg}'. Must be a non-negative integer.`);\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; poolMinReady?: number }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/pool-min-ready`,\n { poolMinReady }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Warm stopped-pool size set to ${poolMinReady}`);\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n\n // Resolve property — positional arg or --property flag, prompted if omitted\n const propertyHint = positional[0] ?? getArgValue(argv, '--property');\n const property = await selectProperty(apiUrl, token, propertyHint ?? undefined);\n if (!property) process.exit(1);\n\n // Fetch linked repos for the property\n const reposRes = await apiRequest<{ ok: boolean; error?: string; items: GitHubRepo[] }>(\n apiUrl, token, 'GET', `/org/properties/${property.id}/github-repos`\n );\n if (!reposRes.ok || !reposRes.items?.length) {\n console.error(`No GitHub repos linked to property \"${property.name}\". Link one first in the web app.`);\n process.exit(1);\n }\n\n const linkedRepo = reposRes.items[0];\n const branch = getArgValue(argv, '--branch') ?? linkedRepo.defaultBranch ?? 'main';\n const name = getArgValue(argv, '--name') ?? linkedRepo.repoName ?? linkedRepo.repoFullName.split('/').pop() ?? property.name;\n const subdirectory = getArgValue(argv, '--subdirectory') ?? linkedRepo.rootDir ?? undefined;\n\n const sizeArg = getArgValue(argv, '--size');\n const instanceSize = sizeArg ? parseInstanceSize(sizeArg) : undefined;\n if (sizeArg && !instanceSize) {\n console.error(`Error: invalid --size '${sizeArg}'. Must be one of: ${INSTANCE_SIZES.join(', ')}`);\n process.exit(1);\n }\n\n const body: Record<string, unknown> = {\n name,\n propertyId: property.id,\n repositoryFullName: linkedRepo.repoFullName,\n branch,\n };\n if (subdirectory) body.subdirectory = subdirectory;\n if (linkedRepo.agentHint) body.hints = linkedRepo.agentHint;\n if (instanceSize) body.instanceSize = instanceSize;\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Template }>(\n apiUrl, token, 'POST', '/org/environments/templates', body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Template created: ${res.data.id}`);\n console.log(` Property: ${property.name}`);\n console.log(` Repository: ${res.data.repositoryFullName ? `${res.data.repositoryFullName}:${res.data.branch}` : '(none)'}`);\n console.log(` Status: ${res.data.status}`);\n console.log('');\n console.log('Authoring job enqueued. Poll status with:');\n console.log(` canary sandbox template ${res.data.id}`);\n}\n\nasync function handleProvision(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox provision <template-id> [--ttl 8]');\n process.exit(1);\n }\n\n const name = getArgValue(argv, '--name') ?? `instance-${Date.now()}`;\n const ttl = parseInt(getArgValue(argv, '--ttl') ?? '8', 10);\n const withAgent = hasFlag(argv, '--with-agent');\n const isPrivate = hasFlag(argv, '--private');\n const egressMode = parseEgressMode(getArgValue(argv, '--egress-mode'));\n const egressAllowlist = parseEgressAllowlist(argv);\n\n const body: Record<string, unknown> = {\n templateId,\n name,\n expiresInHours: ttl,\n userConfig: {},\n withAgent,\n isPrivate,\n egressMode,\n egressAllowlist,\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Instance }>(\n apiUrl, token, 'POST', '/org/environments/instances', body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Instance created: ${res.data.id}`);\n console.log(` Status: ${res.data.status}`);\n if (isPrivate) console.log(` Access: private (no public URL)`);\n if (egressMode !== 'off') {\n console.log(` Egress: ${egressMode} (${egressAllowlist.length} allowlist entries)`);\n }\n console.log(` Expires: ${res.data.expiresAt ? formatDate(res.data.expiresAt) : '-'}`);\n}\n\ntype AuthoringInstance = {\n instanceId: string;\n status: string;\n url: string | null;\n expiresAt: string | null;\n createdAt: string | null;\n created: boolean;\n};\n\n/**\n * Warm (get-or-create) the org-shared flow-authoring instance for a template —\n * the same instance the Flow Designer auto-spins and that a sandbox workflow run\n * reuses. Pre-warming avoids the first run blocking on a cold provision.\n *\n * `--wait` polls the idempotent ensure endpoint (safe: it reuses the live\n * instance and just slides the TTL) until the instance is running, fails, or\n * the timeout elapses.\n */\nasync function handleWarm(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n const wait = hasFlag(argv, '--wait');\n const timeoutSec = parseInt(getArgValue(argv, '--timeout') ?? '600', 10);\n\n if (!templateId) {\n console.error(\n 'Error: template ID required. Usage: canary sandbox warm <template-id> [--wait] [--timeout <sec>]'\n );\n console.error('Find the template id with: canary sandbox list');\n process.exit(1);\n }\n\n const ensure = (): Promise<{ ok: boolean; error?: string; data?: AuthoringInstance }> =>\n apiRequest<{ ok: boolean; error?: string; data?: AuthoringInstance }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/authoring-instance`\n );\n\n const first = await ensure();\n if (!first.ok || !first.data) {\n if (json) {\n writeRawOutput(JSON.stringify(first, null, 2));\n } else {\n console.error(`Error: ${first.error ?? 'Failed to warm sandbox'}`);\n }\n process.exit(1);\n }\n\n const terminalOk = new Set(['running']);\n const terminalFail = new Set(['failed', 'terminated', 'stopping']);\n\n let current = first.data;\n if (wait && !terminalOk.has(current.status) && !terminalFail.has(current.status)) {\n const deadline = Date.now() + timeoutSec * 1000;\n if (!json) console.log(`Warming flow-authoring sandbox (${current.status})...`);\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, 5000));\n const poll = await ensure();\n if (!poll.ok || !poll.data) {\n console.error(`Error polling sandbox: ${poll.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n current = poll.data;\n if (terminalOk.has(current.status) || terminalFail.has(current.status)) break;\n }\n }\n\n if (json) {\n writeRawOutput(JSON.stringify({ ok: true, data: current }, null, 2));\n } else {\n console.log(`Sandbox ${current.created ? 'created' : 'reused'}: ${current.instanceId}`);\n console.log(` Status: ${current.status}`);\n if (current.url) writeRawOutput(` URL: ${current.url}`);\n if (current.expiresAt) console.log(` Expires: ${formatDate(current.expiresAt)}`);\n if (current.status === 'running') {\n console.log('\\nReady. Run a workflow against it with:');\n console.log(' canary workflow run <workflowId> [--environment <name|id>] --stream');\n } else if (wait && terminalFail.has(current.status)) {\n console.log('\\nSandbox did not reach a ready state.');\n } else if (wait) {\n console.log('\\nTimed out waiting for the sandbox to become ready. Re-run to keep waiting.');\n } else {\n console.log('\\nWarming in the background. Poll with: canary sandbox warm ' + templateId + ' --wait');\n }\n }\n\n if (terminalFail.has(current.status)) process.exit(1);\n if (wait && current.status !== 'running') process.exit(1);\n}\n\nexport interface ContainerLine {\n name: string;\n status: string;\n state: 'up-healthy' | 'up' | 'up-unhealthy' | 'restarting' | 'exited' | 'other';\n}\n\n/**\n * Parse `docker ps --format '{{.Names}}\\t{{.Status}}\\t{{.Ports}}'` output\n * into a structured list. State classification is coarse — the verify loop\n * only needs to know \"is this container healthy / exited / restart-looping\".\n */\nexport function parseDockerPs(stdout: string): ContainerLine[] {\n const lines = stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n return lines.map((line) => {\n const [name = '', status = ''] = line.split('\\t');\n const lower = status.toLowerCase();\n let state: ContainerLine['state'] = 'other';\n if (lower.startsWith('restarting')) state = 'restarting';\n else if (lower.startsWith('exited')) state = 'exited';\n else if (lower.startsWith('up') && lower.includes('unhealthy')) state = 'up-unhealthy';\n else if (lower.startsWith('up') && lower.includes('healthy')) state = 'up-healthy';\n else if (lower.startsWith('up')) state = 'up';\n return { name, status, state };\n });\n}\n\n/** Containers that aren't cleanly up. Used to decide what to dump logs for. */\nexport function isBrokenContainer(c: ContainerLine): boolean {\n return c.state === 'restarting' || c.state === 'exited' || c.state === 'up-unhealthy';\n}\n\n/**\n * Map a container name back to its compose service.\n *\n * docker-compose names containers `<project>-<service>-<replica>`. The\n * sandbox platform sets the project to `workspace` via\n * `--project-directory /workspace` (see `SANDBOX_COMPOSE_BASE` in\n * apps/api/.../sandbox-cli/service.ts), so the common case is\n * `workspace-<svc>-<n>`. Fall back to stripping the trailing replica\n * index only, so attribution still works if the project name ever\n * changes or someone uses an explicit `container_name:` override.\n */\nexport function containerNameToService(containerName: string): string {\n const withProject = containerName.match(/^workspace-(.+)-\\d+$/);\n if (withProject) return withProject[1]!;\n const withoutProject = containerName.match(/^(.+)-\\d+$/);\n if (withoutProject) return withoutProject[1]!;\n return containerName;\n}\n\nasync function fetchServiceLogs(\n apiUrl: string,\n token: string,\n instanceId: string,\n service: string,\n tail: number\n): Promise<string> {\n const res = await apiRequest<{ ok: boolean; error?: string; data: { logs: string } }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/docker/logs`,\n { service, tail }\n );\n if (!res.ok) return `(log fetch failed: ${res.error ?? 'unknown'})`;\n return res.data?.logs ?? '';\n}\n\nasync function handleVerify(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n // --keep-instance is kept as a success-path alias (existing behavior).\n // On failure the probe is now preserved by default so the user can debug;\n // pass --auto-terminate-on-failure for CI where you want cleanup anyway.\n const keepInstance = hasFlag(argv, '--keep-instance');\n const autoTerminateOnFailure = hasFlag(argv, '--auto-terminate-on-failure');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox verify <template-id>');\n process.exit(1);\n }\n\n console.log(`Verifying template ${templateId}...`);\n\n // Kick off a verification instance — server bypasses the \"verified\" gate\n // via verificationMode but otherwise runs the full provisioning flow.\n const verifyRes = await apiRequest<{ ok: boolean; error?: string; data: { instanceId: string; expiresAt: string } }>(\n apiUrl, token, 'POST', `/org/environments/templates/${templateId}/verify`, {}\n );\n if (!verifyRes.ok) {\n console.error(`Error: ${verifyRes.error}`);\n process.exit(1);\n }\n const instanceId = verifyRes.data.instanceId;\n console.log(` Verification instance: ${instanceId}`);\n\n // Poll diagnostics until instance reaches 'running' or a terminal state.\n const POLL_INTERVAL_MS = 10_000;\n const TIMEOUT_MS = 600_000; // 10 minutes\n const DETAIL_DUMP_INTERVAL_MS = 30_000; // rate-limit noisy dumps\n const LOG_TAIL_LINES = 40;\n const startTime = Date.now();\n let finalStatus = '';\n let publicUrl: string | undefined;\n let lastDetailDumpAt = 0;\n let routes: ServiceRoute[] = [];\n let routesFetched = false;\n\n while (Date.now() - startTime < TIMEOUT_MS) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n\n const diag = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n if (!diag.ok) continue;\n\n const now = Date.now();\n const elapsed = Math.round((now - startTime) / 1000);\n const remaining = Math.max(0, Math.round((TIMEOUT_MS - (now - startTime)) / 1000));\n const status = String((diag.data.instance as { status?: string; publicUrl?: string }).status ?? '');\n publicUrl = (diag.data.instance as { publicUrl?: string }).publicUrl;\n const appOutput = 'stdout' in diag.data.appHealth ? diag.data.appHealth.stdout : '';\n const appUp = appOutput.length > 0 && !appOutput.includes('UNREACHABLE');\n const dockerOutput = 'stdout' in diag.data.docker ? diag.data.docker.stdout : '';\n const containers = parseDockerPs(dockerOutput);\n const broken = containers.filter(isBrokenContainer);\n\n // Compact progress line (overwriting, like before) — adds remaining time.\n const ttl = remaining > 60 ? `${elapsed}s elapsed` : `${remaining}s remaining`;\n process.stdout.write(\n `\\r [${ttl}] status: ${status}, app: ${appUp ? 'up' : 'waiting'}, containers: ${containers.length}, unhealthy: ${broken.length} `\n );\n\n if (status === 'running' && appUp) {\n process.stdout.write('\\n');\n finalStatus = 'running';\n break;\n }\n if (status === 'failed' || status === 'terminated') {\n process.stdout.write('\\n');\n finalStatus = status;\n break;\n }\n\n // When the app is still down and at least one container is broken,\n // dump details + log tails once every DETAIL_DUMP_INTERVAL_MS so the\n // user actually sees what's failing instead of staring at a spinner.\n const shouldDump =\n !appUp &&\n broken.length > 0 &&\n now - lastDetailDumpAt >= DETAIL_DUMP_INTERVAL_MS;\n if (shouldDump) {\n lastDetailDumpAt = now;\n process.stdout.write('\\n');\n console.log('');\n console.log(` App unreachable after ${elapsed}s. Container state:`);\n for (const c of containers) {\n console.log(` ${c.name.padEnd(40)} ${c.status}`);\n }\n console.log('');\n // Per-broken-container log tails. Cap to MAX_LOG_CONTAINERS to avoid\n // SSM call amplification on pathological cases (a template with 6\n // restart-looping services would otherwise issue 6 sequential SSM\n // calls every 30s). Fetch in parallel — these are independent.\n const MAX_LOG_CONTAINERS = 3;\n const toFetch = broken.slice(0, MAX_LOG_CONTAINERS);\n const overflow = broken.length - toFetch.length;\n const fetched = await Promise.all(\n toFetch.map((c) =>\n fetchServiceLogs(apiUrl, token, instanceId, containerNameToService(c.name), LOG_TAIL_LINES)\n .then((logs) => ({ c, logs }))\n )\n );\n for (const { c, logs } of fetched) {\n const tail = logs.trim().split('\\n').slice(-LOG_TAIL_LINES).join('\\n ');\n console.log(` --- ${c.name} (${c.state}) ---`);\n console.log(` ${tail || '(no logs)'}`);\n console.log('');\n }\n if (overflow > 0) {\n console.log(\n ` (+${overflow} more broken container${overflow === 1 ? '' : 's'} — re-run with: canary sandbox logs ${instanceId} --service <name>)`\n );\n console.log('');\n }\n // Attribution: if nginx-proxy is looping because of a labeled upstream\n // that's exited, call that out so the user doesn't chase nginx's\n // \"host not found in upstream\" as the root cause.\n if (!routesFetched) {\n routes = await fetchInstanceRoutes(apiUrl, token, instanceId);\n routesFetched = true;\n }\n const nginxProxy = containers.find((c) => containerNameToService(c.name) === 'nginx-proxy');\n if (nginxProxy?.state === 'restarting' && routes.length > 0) {\n const failedUpstreams = routes\n .map((r) => ({\n route: r.route,\n service: r.service,\n container: broken.find((c) => containerNameToService(c.name) === r.service),\n }))\n .filter((x) => x.container);\n if (failedUpstreams.length > 0) {\n for (const fu of failedUpstreams) {\n console.log(\n ` >> nginx-proxy is restart-looping because upstream '${fu.service}' (route '${fu.route}') is ${fu.container!.state}. Read that service's logs above, not nginx's.`\n );\n }\n console.log('');\n }\n }\n }\n }\n\n if (finalStatus !== 'running') {\n console.error('');\n console.error(`Verification failed — instance ended in status '${finalStatus || 'timeout'}'.`);\n if (publicUrl) console.error(` Instance URL: ${publicUrl}`);\n console.error(` Instance ID: ${instanceId}`);\n console.error(` Debug with: canary sandbox diagnostics ${instanceId}`);\n console.error(` canary sandbox logs ${instanceId} --service <name>`);\n console.error(` canary sandbox run-command ${instanceId} 'docker ps -a'`);\n if (autoTerminateOnFailure) {\n await apiRequest(apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`);\n console.error(` Terminated (--auto-terminate-on-failure).`);\n } else {\n // Default flip (was: auto-terminate). Call it out so CI users don't\n // silently accumulate orphan instances.\n console.error(` Instance preserved by default for debugging. Pass --auto-terminate-on-failure in CI.`);\n console.error(` Terminate manually with:`);\n console.error(` canary sandbox terminate ${instanceId}`);\n }\n process.exit(1);\n }\n\n // We just observed the instance reach status === 'running' && appUp.\n // That's the same observation that backs the server-running and\n // server-accessible checklist items, so flip them too — best-effort\n // (templates without those items in their checklist 404 silently).\n const evidenceSummary = `Verified via canary sandbox verify — fresh instance ${instanceId} came up healthy.`;\n for (const itemId of ['server-running', 'server-accessible']) {\n await apiRequest(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/checklist/${itemId}/complete`,\n { status: 'completed', summary: evidenceSummary }\n ).catch(() => {\n // Item not present on this checklist profile — fine.\n });\n }\n\n // Mark the template-verified checklist item complete — this is what flips\n // template.status to 'verified' (via ChecklistService.completeChecklistItem).\n const completeRes = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/checklist/template-verified/complete`,\n {\n status: 'completed',\n summary: evidenceSummary,\n }\n );\n if (!completeRes.ok) {\n console.error(`Failed to record verification: ${completeRes.error}`);\n console.error(`Instance ${instanceId} left running for inspection.`);\n process.exit(1);\n }\n\n if (!keepInstance) {\n await apiRequest(apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`);\n console.log(` Terminated verification instance.`);\n } else {\n console.log(` --keep-instance: instance ${instanceId} left running${publicUrl ? ` at ${publicUrl}` : ''}.`);\n }\n\n // Confirm the template actually flipped to verified. The status flip only\n // happens when every item in the checklist profile is terminal\n // (completed or skipped); if other items are still pending, the probe\n // succeeded but the template stays in its prior status.\n const templateAfter = await apiRequest<{ ok: boolean; data?: { status?: string } }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}`\n ).catch(() => ({ ok: false }) as { ok: boolean; data?: { status?: string } });\n const templateStatus = templateAfter.data?.status;\n const verified = templateStatus === 'verified';\n\n if (json) {\n writeRawOutput(JSON.stringify({ templateId, verified, status: templateStatus, instanceId, publicUrl }, null, 2));\n return;\n }\n\n if (verified) {\n console.log('Template verified.');\n return;\n }\n\n // Probe passed but status didn't flip — surface the pending items so the\n // user knows what to complete/skip before re-running verify.\n console.log(`Verification probe passed (instance ${instanceId} came up healthy),`);\n console.log(`but template status is still '${templateStatus ?? 'unknown'}'.`);\n type ChecklistItemSummary = { id: string; label: string; itemStatus?: { status?: string } };\n const checklistRes = await apiRequest<{ ok: boolean; data?: ChecklistItemSummary[] }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}/checklist`\n ).catch(() => ({ ok: false }) as { ok: boolean; data?: ChecklistItemSummary[] });\n const pending = (checklistRes.data ?? []).filter((it) => {\n const s = it.itemStatus?.status ?? 'pending';\n return s !== 'completed' && s !== 'skipped';\n });\n if (pending.length > 0) {\n console.log('Pending checklist items must be completed or skipped:');\n for (const item of pending) {\n console.log(` - ${item.label} (${item.id})`);\n }\n console.log('');\n console.log('Complete: canary sandbox checklist complete <template-id> <item-id> --status success --summary \"...\"');\n console.log('Skip: canary sandbox checklist skip <template-id> <item-id>');\n }\n process.exit(1);\n}\n\nasync function handleArtifactGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const type = positional[1];\n const outputFile = getArgValue(argv, '--output');\n\n if (!templateId || !type) {\n console.error('Error: template ID and artifact type required. Usage: canary sandbox artifact get <template-id> <type>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: Artifact }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/artifacts/${type}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (outputFile) {\n const { writeFileSync } = await import('node:fs');\n writeFileSync(outputFile, res.data.content ?? '', 'utf-8');\n console.log(`Written to ${outputFile}`);\n } else {\n process.stdout.write(res.data.content ?? '');\n }\n}\n\nasync function handleArtifactCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const type = positional[1];\n const filePath = positional[2];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !type || !filePath) {\n console.error('Error: all args required. Usage: canary sandbox artifact create <template-id> <type> <file>');\n process.exit(1);\n }\n\n // Keep in sync with WellKnownArtifactType in packages/types/src/sandbox-tasks.ts + 'file' (custom with --path)\n const WELL_KNOWN_TYPES = ['docker-compose', 'dockerfile', 'startup-script', 'setup-script', 'seed-script', 'environment-config', 'file'];\n if (!WELL_KNOWN_TYPES.includes(type)) {\n console.error(`Error: invalid artifact type \"${type}\". Valid types: ${WELL_KNOWN_TYPES.join(', ')}`);\n process.exit(1);\n }\n\n // File artifacts require --path <destination>\n const destinationPath = getArgValue(argv, '--path');\n if (type === 'file' && !destinationPath) {\n console.error('Error: file artifacts require --path <destination>. Example: --path /workspace/.canary/vite.sandbox.config.ts');\n process.exit(1);\n }\n if (destinationPath && !destinationPath.startsWith('/workspace/.canary/')) {\n const suggestedName = destinationPath.split('/').filter(Boolean).pop() ?? 'yourfile';\n console.error(\n 'Error: --path must be under /workspace/.canary/ (the repo tree is user-owned — sandbox files live in .canary/).\\n' +\n ` try: --path /workspace/.canary/${suggestedName}`\n );\n process.exit(1);\n }\n\n let content: string;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch (err) {\n console.error(`Error reading file: ${err}`);\n process.exit(1);\n }\n\n // Derive filename from path\n const filename = filePath.split('/').pop() ?? filePath;\n\n // Artifact upload writes through to the live authoring instance, which on\n // first push (or a large file) can outlast 30s easily. 90s lines up with\n // the slowest legitimate upload we've observed.\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n data?: { kind: 'created' | 'replaced'; previousPath?: string };\n }>(\n apiUrl, token, 'PUT', `/org/environments/templates/${templateId}/artifacts`, {\n artifactType: type,\n filename,\n content,\n ...(destinationPath ? { destinationPath } : {}),\n },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const kind = res.data?.kind;\n const marker = kind === 'replaced' ? ' (replaced)' : kind === 'created' ? ' (created)' : '';\n console.log(`Artifact uploaded: ${type} (${filename}, ${content.length} bytes)${marker}`);\n if (kind === 'replaced' && res.data?.previousPath) {\n console.log(` previous destination removed: ${res.data.previousPath}`);\n }\n}\n\nasync function handleBuild(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const json = hasFlag(argv, '--json');\n const noWait = hasFlag(argv, '--no-wait');\n const instanceId = positional[0];\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox build <instance-id>');\n process.exit(1);\n }\n\n console.log('Building sandbox (this takes a few minutes)...');\n\n // The build is async by design: a single HTTP connection cannot reliably\n // span a multi-minute build (ALB caps, our own client timeouts, flaky\n // networks). Kick the build off with a short timeout — the server has\n // already started the work by the time it would otherwise reply — then\n // move to polling diagnostics for the truth.\n const KICKOFF_TIMEOUT_MS = 8_000;\n\n let buildCompleted = false;\n let buildResult: { stdout: string; stderr: string; exitCode: number } | null = null;\n\n const result = await apiFetch(`${apiUrl}/org/environments/instances/${instanceId}/build`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n timeoutMs: KICKOFF_TIMEOUT_MS,\n });\n\n if (result.ok) {\n // Fast path: server responded within the kickoff window (cached/no-op build).\n const data = (await result.response.json().catch(() => null)) as\n | { ok: boolean; error?: string; data?: { stdout: string; stderr: string; exitCode: number } }\n | null;\n if (data?.ok && data.data) {\n buildCompleted = true;\n buildResult = data.data;\n } else if (data && !data.ok) {\n console.error(`Error: ${data.error ?? 'build failed'}`);\n process.exit(1);\n }\n } else {\n const { failure } = result;\n // network = our kickoff timeout firing; 502/504 = gateway giving up while\n // the build runs on. Both mean \"watch via polling, not an error\".\n const isExpectedAsync =\n failure.kind === 'network' ||\n (failure.kind === 'http-error' && (failure.status === 502 || failure.status === 504));\n if (!isExpectedAsync) {\n if (isTerminalFailure(failure)) exitOnFailure(failure);\n console.error(`Error: ${failure.message}`);\n process.exit(1);\n }\n }\n\n // If build completed synchronously, print results and exit\n if (buildCompleted && buildResult) {\n if (json) {\n writeRawOutput(JSON.stringify(buildResult, null, 2));\n return;\n }\n\n if (buildResult.stdout) {\n process.stdout.write(buildResult.stdout);\n if (!buildResult.stdout.endsWith('\\n')) process.stdout.write('\\n');\n }\n if (buildResult.stderr) {\n process.stderr.write(`STDERR: ${buildResult.stderr}`);\n if (!buildResult.stderr.endsWith('\\n')) process.stderr.write('\\n');\n }\n\n if (buildResult.exitCode === 0) {\n console.log('Build complete.');\n } else {\n console.error(`Build failed with exit code ${buildResult.exitCode}`);\n process.exit(1);\n }\n return;\n }\n\n // Build is async — poll diagnostics until the app is reachable or timeout\n if (noWait) {\n console.log('Build started. Use diagnostics to monitor progress:');\n console.log(` canary sandbox diagnostics ${instanceId}`);\n return;\n }\n\n console.log('Build started, watching for services...');\n\n const BUILD_POLL_TIMEOUT_MS = 300_000; // 5 minutes\n const POLL_INTERVAL_MS = 10_000;\n const startTime = Date.now();\n\n while (Date.now() - startTime < BUILD_POLL_TIMEOUT_MS) {\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n\n const diag = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n\n if (!diag.ok) continue;\n\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n const dockerOutput = 'stdout' in diag.data.docker ? diag.data.docker.stdout : '';\n const appOutput = 'stdout' in diag.data.appHealth ? diag.data.appHealth.stdout : '';\n const appUp = appOutput.length > 0 && !appOutput.includes('UNREACHABLE');\n\n // Show a compact progress line\n const containerCount = (dockerOutput.match(/\\n/g) || []).length + (dockerOutput ? 1 : 0);\n const healthyCount = (dockerOutput.match(/\\(healthy\\)/g) || []).length;\n process.stdout.write(`\\r [${elapsed}s] containers: ${containerCount}, healthy: ${healthyCount}, app: ${appUp ? 'up' : 'waiting'} `);\n\n // Trust the HTTP probe: if nginx is serving the app on :3000 or :8000, we're done.\n // Compose healthchecks are optional and most user services don't declare them.\n if (appUp) {\n process.stdout.write('\\n');\n console.log('Build complete.');\n return;\n }\n }\n\n process.stdout.write('\\n');\n console.error('Build timed out waiting for the app to respond. Check diagnostics and logs:');\n console.error(` canary sandbox diagnostics ${instanceId}`);\n console.error(` canary sandbox logs ${instanceId} --service <name>`);\n process.exit(1);\n}\n\nasync function handleRunCommand(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const service = getArgValue(argv, '--service');\n const timeoutFlag = getArgValue(argv, '--timeout');\n\n // Strip our flags (--json, --service <value>, --timeout <value>) so they\n // don't leak into the remote command.\n const filtered = argv.filter((a, i, arr) => {\n if (a === '--json') return false;\n if (a === '--service' || a === '--timeout') return false;\n if (i > 0 && (arr[i - 1] === '--service' || arr[i - 1] === '--timeout')) return false;\n return true;\n });\n\n // First non-flag arg is the instance ID; everything after it is the command\n // (including flags like -la that belong to the remote command, not to us)\n const firstNonFlag = filtered.findIndex((a) => !a.startsWith('-'));\n const instanceId = firstNonFlag >= 0 ? filtered[firstNonFlag] : undefined;\n const command = firstNonFlag >= 0 ? filtered.slice(firstNonFlag + 1).join(' ') : '';\n\n if (!instanceId || !command) {\n console.error('Error: instance ID and command required. Usage: canary sandbox run-command <instance-id> [--service <name>] [--timeout <ms>] <command...>');\n process.exit(1);\n }\n\n // Server-side run-command caps at 55s (SANDBOX_COMMAND_TIMEOUT). Default\n // the CLI just past that so we surface the server's typed timeout rather\n // than an opaque client-side cancel. For longer ops (test suites,\n // playwright), pass a higher value or use first-class actions.\n const timeoutMs = timeoutFlag ? Math.max(parseInt(timeoutFlag, 10), 1000) : 60_000;\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: { stdout: string; stderr: string; exitCode: number } }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/run-command`,\n { command, ...(service ? { service } : {}) },\n { timeoutMs }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n if (res.data.stdout) {\n process.stdout.write(res.data.stdout);\n if (!res.data.stdout.endsWith('\\n')) process.stdout.write('\\n');\n }\n if (res.data.stderr) {\n process.stderr.write(`STDERR: ${res.data.stderr}`);\n if (!res.data.stderr.endsWith('\\n')) process.stderr.write('\\n');\n }\n if (res.data.exitCode !== 0) {\n console.log(`Exit code: ${res.data.exitCode}`);\n }\n}\n\nasync function handleDiagnostics(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox diagnostics <instance-id>');\n process.exit(1);\n }\n\n // Server-side DIAGNOSTICS_TIMEOUT_MS is 30s; the client needs a bit more\n // headroom than that or we race the server and report a misleading client\n // timeout for a server-side timeout.\n const res = await apiRequest<{ ok: boolean; error?: string; data: DiagnosticsResult }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/diagnostics`,\n undefined,\n { timeoutMs: 60_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n const routes = await fetchInstanceRoutes(apiUrl, token, instanceId);\n const data = { ...res.data, routes };\n\n if (json) {\n writeRawOutput(JSON.stringify(data, null, 2));\n return;\n }\n\n const d = data;\n\n console.log('=== Instance ===');\n for (const [key, value] of Object.entries(d.instance)) {\n console.log(` ${key}: ${value}`);\n }\n\n if (d.lifecycle) {\n console.log('');\n console.log('=== Lifecycle ===');\n console.log(` databaseStatus: ${d.lifecycle.databaseStatus}`);\n console.log(` poolStatus: ${d.lifecycle.poolStatus ?? 'missing'}`);\n console.log(` ec2InstanceState: ${d.lifecycle.ec2InstanceState ?? 'unknown'}`);\n console.log(` live: ${d.lifecycle.live}`);\n if (d.lifecycle.message) writeRawOutput(` message: ${d.lifecycle.message}`);\n }\n\n if (d.provision) {\n console.log('');\n console.log('=== Provisioning ===');\n console.log(` phase: ${d.provision.phase}`);\n console.log(` jobStatus: ${d.provision.jobStatus}`);\n if (d.provision.startedAt) console.log(` startedAt: ${d.provision.startedAt}`);\n if (d.provision.elapsedSeconds !== null) console.log(` elapsed: ${d.provision.elapsedSeconds}s`);\n if (d.provision.lastError) console.log(` lastError: ${d.provision.lastError}`);\n }\n\n const sections: Array<[string, CommandOrError]> = [\n ['Docker Containers', d.docker],\n ['Git Status', d.git],\n ['Disk Usage', d.disk],\n ['Agent Health', d.agentHealth],\n ['App Health', d.appHealth],\n ];\n if (d.resources) sections.splice(3, 0, ['Resource Usage', d.resources]);\n\n for (const [title, result] of sections) {\n console.log('');\n console.log(`=== ${title} ===`);\n if ('error' in result) {\n writeRawOutput(` Error${result.code ? ` [${result.code}]` : ''}: ${result.error}`);\n } else {\n if (result.stdout) writeRawOutput(result.stdout);\n if (result.stderr) writeRawOutput(` STDERR: ${result.stderr}`);\n }\n }\n}\n\nasync function handleTerminate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox terminate <instance-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/terminate`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Instance ${instanceId} termination initiated.`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox delete <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'DELETE', `/org/environments/templates/${templateId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Template ${templateId} deleted.`);\n}\n\n// ---------------------------------------------------------------------------\n// Action handlers\n// ---------------------------------------------------------------------------\n\ntype ActionDef = {\n id: string;\n slug: string;\n label: string;\n description: string | null;\n icon: string | null;\n enabled: boolean;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown> | null;\n /** Declared handover slots — what this action hands back, and of what kind. */\n outputConfig?: Array<Record<string, unknown>> | null;\n scriptContent: string;\n timeoutMs: number;\n createdAt: string;\n updatedAt: string;\n};\n\ntype ActionExecution = {\n id: string;\n actionId: string;\n instanceId: string;\n source: string;\n status: string;\n result: Record<string, unknown> | null;\n stdout: string | null;\n stderr: string | null;\n exitCode: number | null;\n durationMs: number | null;\n error: string | null;\n createdAt: string;\n};\n\nasync function handleActionList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox action list <template-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionDef[] }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/actions`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const actions = res.data ?? [];\n if (actions.length === 0) {\n console.log('No actions defined for this template.');\n return;\n }\n\n console.log(`Actions (${actions.length}):`);\n for (const a of actions) {\n const status = a.enabled ? '' : ' [disabled]';\n console.log(` ${pad(a.slug, 30)} ${pad(a.label, 25)}${status}`);\n }\n}\n\nasync function handleActionGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action get <template-id> <slug>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionDef }>(\n apiUrl, token, 'GET', `/org/environments/templates/${templateId}/actions/${slug}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const a = res.data;\n console.log(`Action: ${a.slug}`);\n console.log(` Label: ${a.label}`);\n writeRawOutput(` Description: ${a.description ?? '(none)'}`);\n console.log(` Icon: ${a.icon ?? '(none)'}`);\n console.log(` Enabled: ${a.enabled}`);\n console.log(` Timeout: ${a.timeoutMs}ms`);\n console.log(` Input Schema:`);\n console.log(` ${JSON.stringify(a.inputSchema, null, 2).split('\\n').join('\\n ')}`);\n if (a.outputSchema) {\n console.log(` Output Schema:`);\n console.log(` ${JSON.stringify(a.outputSchema, null, 2).split('\\n').join('\\n ')}`);\n }\n // Printed unconditionally when present: \"did the handover slot actually save\"\n // is the first thing to check when a run reports no results.\n if (a.outputConfig && a.outputConfig.length > 0) {\n console.log(` Output Config (handover slots):`);\n console.log(` ${JSON.stringify(a.outputConfig, null, 2).split('\\n').join('\\n ')}`);\n }\n console.log(` Script:`);\n console.log(` ${a.scriptContent.split('\\n').join('\\n ')}`);\n}\n\nasync function handleActionCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action create <template-id> <slug> [options]');\n process.exit(1);\n }\n\n const scriptPath = getArgValue(argv, '--script');\n const inputSchemaPath = getArgValue(argv, '--input-schema');\n const outputSchemaPath = getArgValue(argv, '--output-schema');\n const credentialOutputConfigPath = getArgValue(argv, '--credential-output-config');\n const outputConfigPath = getArgValue(argv, '--output-config');\n const label = getArgValue(argv, '--label') ?? slug.replace(/-/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n const description = getArgValue(argv, '--description');\n const icon = getArgValue(argv, '--icon');\n const timeoutStr = getArgValue(argv, '--timeout');\n const encryptScript = hasFlag(argv, '--encrypt-script');\n const credentialTemplateId = getArgValue(argv, '--credential-template');\n const provisioningMode = getArgValue(argv, '--provisioning-mode');\n const runOnProvision = hasFlag(argv, '--run-on-provision');\n const provisionOrderStr = getArgValue(argv, '--provision-order');\n\n if (provisioningMode && provisioningMode !== 'shared' && provisioningMode !== 'unique') {\n console.error(\"Error: --provisioning-mode must be 'shared' or 'unique'\");\n process.exit(1);\n }\n\n if (provisionOrderStr && !/^-?\\d+$/.test(provisionOrderStr)) {\n console.error('Error: --provision-order must be an integer');\n process.exit(1);\n }\n\n if (!scriptPath) {\n console.error('Error: --script <file> is required');\n process.exit(1);\n }\n if (!inputSchemaPath) {\n console.error('Error: --input-schema <file> is required');\n process.exit(1);\n }\n\n let scriptContent: string;\n try {\n scriptContent = readFileSync(scriptPath, 'utf-8');\n } catch (err) {\n console.error(`Error reading script file: ${err}`);\n process.exit(1);\n }\n\n let inputSchema: Record<string, unknown>;\n try {\n inputSchema = JSON.parse(readFileSync(inputSchemaPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading input schema file: ${err}`);\n process.exit(1);\n }\n\n let outputSchema: Record<string, unknown> | undefined;\n if (outputSchemaPath) {\n try {\n outputSchema = JSON.parse(readFileSync(outputSchemaPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading output schema file: ${err}`);\n process.exit(1);\n }\n }\n\n let credentialOutputConfig: Record<string, unknown> | undefined;\n if (credentialOutputConfigPath) {\n try {\n credentialOutputConfig = JSON.parse(readFileSync(credentialOutputConfigPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading credential output config file: ${err}`);\n process.exit(1);\n }\n }\n\n // The declared handover: which files this action leaves behind and what kind\n // each one is. Without this flag the column is only reachable through a raw\n // API call, which would make a shipped feature unusable from the tools an\n // operator actually has. Validated server-side against the output-kind\n // registry, so a bad slot is rejected at save time rather than mid-run.\n let outputConfig: unknown;\n if (outputConfigPath) {\n try {\n outputConfig = JSON.parse(readFileSync(outputConfigPath, 'utf-8'));\n } catch (err) {\n console.error(`Error reading output config file: ${err}`);\n process.exit(1);\n }\n if (!Array.isArray(outputConfig)) {\n console.error('Error: --output-config must contain a JSON array of output slots');\n process.exit(1);\n }\n }\n\n const body: Record<string, unknown> = {\n label,\n scriptContent,\n inputSchema,\n ...(description ? { description } : {}),\n ...(icon ? { icon } : {}),\n ...(outputSchema ? { outputSchema } : {}),\n ...(credentialOutputConfig ? { credentialOutputConfig } : {}),\n ...(outputConfig ? { outputConfig } : {}),\n ...(credentialTemplateId ? { credentialTemplateId } : {}),\n ...(provisioningMode ? { provisioningMode } : {}),\n ...(runOnProvision ? { runOnProvision: true } : {}),\n ...(provisionOrderStr ? { provisionOrder: parseInt(provisionOrderStr, 10) } : {}),\n ...(timeoutStr ? { timeoutMs: parseInt(timeoutStr, 10) } : {}),\n ...(encryptScript ? { scriptEncrypted: true } : {}),\n };\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionDef }>(\n apiUrl, token, 'PUT', `/org/environments/templates/${templateId}/actions/${slug}`, body\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n console.log(`Action '${slug}' saved (${scriptContent.length} bytes script, label: \"${label}\")`);\n}\n\nasync function handleActionDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const templateId = positional[0];\n const slug = positional[1];\n\n if (!templateId || !slug) {\n console.error('Error: template ID and slug required. Usage: canary sandbox action delete <template-id> <slug>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl, token, 'DELETE', `/org/environments/templates/${templateId}/actions/${slug}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n console.log(`Action '${slug}' deleted.`);\n}\n\nasync function handleActionExecute(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const slug = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !slug) {\n console.error('Error: instance ID and slug required. Usage: canary sandbox action execute <instance-id> <slug> [options]');\n process.exit(1);\n }\n\n // Collect params from --param key=value flags\n let params: Record<string, unknown> = {};\n\n const paramsFile = getArgValue(argv, '--params-file');\n if (paramsFile) {\n try {\n params = JSON.parse(readFileSync(paramsFile, 'utf-8'));\n } catch (err) {\n console.error(`Error reading params file: ${err}`);\n process.exit(1);\n }\n }\n\n // --param flags override params-file\n for (let i = 0; i < argv.length; i++) {\n if (argv[i] === '--param' && argv[i + 1]) {\n const [key, ...valueParts] = argv[i + 1]!.split('=');\n if (key && valueParts.length > 0) {\n params[key] = valueParts.join('=');\n }\n i++; // skip value\n }\n }\n\n interface ActionExecuteResponse {\n executionId: string;\n status: string;\n result: unknown;\n stdout: string | null;\n stderr: string | null;\n exitCode: number | null;\n durationMs: number | null;\n error: string | null;\n credentialId?: string | null;\n }\n\n const runAsync = hasFlag(argv, '--async');\n const timeoutArg = getArgValue(argv, '--timeout-ms');\n let executionTimeoutMs: number | undefined;\n if (timeoutArg) {\n const parsed = Number(timeoutArg);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n console.error(`Error: --timeout-ms must be a positive number, got '${timeoutArg}'`);\n process.exit(1);\n }\n if (!runAsync) {\n // The server ignores it without --async, so accepting it quietly would\n // leave the caller believing a long ceiling was applied when it was not.\n console.error('Error: --timeout-ms only applies with --async');\n process.exit(1);\n }\n executionTimeoutMs = parsed;\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecuteResponse }>(\n apiUrl, token, 'POST', `/org/environments/instances/${instanceId}/actions/${slug}/execute`, {\n params,\n source: 'cli',\n ...(runAsync ? { async: true } : {}),\n ...(executionTimeoutMs !== undefined ? { executionTimeoutMs } : {}),\n }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const exec = res.data!;\n\n // A detached run has no outcome yet, so the completion summary below would\n // print a misleading \"exit ?\" line. Tell the caller how to follow it instead.\n if (runAsync) {\n console.log(`[started] Action '${slug}' running in the background.`);\n console.log(`Execution: ${exec.executionId}`);\n console.log(\n `Follow: cofactor sandbox action execution get ${instanceId} ${exec.executionId}`\n );\n console.log(\n `Cancel: cofactor sandbox action execution cancel ${instanceId} ${exec.executionId}`\n );\n return;\n }\n const statusIcon = exec.status === 'completed' ? '[done]' : exec.status === 'failed' ? '[FAIL]' : `[${exec.status}]`;\n console.log(`${statusIcon} Action '${slug}' ${exec.status} (${exec.durationMs ?? 0}ms, exit ${exec.exitCode ?? '?'})`);\n if (exec.result) {\n console.log(`Result: ${JSON.stringify(exec.result, null, 2)}`);\n }\n if (exec.credentialId) {\n console.log(`Credential: ${exec.credentialId} (linked in property vault)`);\n }\n if (exec.status === 'failed' && exec.stderr) {\n // Cap by bytes first so a single multi-MB line doesn't blow up the terminal,\n // then by line count for readability.\n const tail = exec.stderr.slice(-10_000).trim().split('\\n').slice(-20).join('\\n');\n console.log(`Stderr (last 20 lines):\\n${tail}`);\n }\n if (exec.error) {\n console.log(`Error: ${exec.error}`);\n }\n}\n\nasync function handleActionExecutions(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox action executions <instance-id>');\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data: ActionExecution[] }>(\n apiUrl, token, 'GET', `/org/environments/instances/${instanceId}/actions/executions`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n const execs = res.data ?? [];\n if (execs.length === 0) {\n console.log('No executions yet.');\n return;\n }\n\n console.log('Executions:');\n for (const e of execs) {\n const icon = STATUS_ICONS[e.status] ?? `[${e.status}]`;\n const dur = e.durationMs !== null && e.durationMs !== undefined ? `${e.durationMs}ms` : '?ms';\n console.log(` ${icon} ${pad(e.id.slice(0, 8), 10)} ${pad(e.source, 6)} ${pad(e.status, 12)} ${pad(dur, 8)} ${formatDate(e.createdAt)}`);\n }\n}\n\n/** Print one execution the same way `action execute` prints a finished run. */\nfunction printExecutionDetail(exec: ActionExecution): void {\n const icon = STATUS_ICONS[exec.status] ?? `[${exec.status}]`;\n const dur = exec.durationMs !== null && exec.durationMs !== undefined ? `${exec.durationMs}ms` : '?ms';\n console.log(`${icon} ${exec.status} (${dur}, exit ${exec.exitCode ?? '?'})`);\n console.log(`Execution: ${exec.id}`);\n if (exec.result) {\n console.log(`Result: ${JSON.stringify(exec.result, null, 2)}`);\n }\n if (exec.stderr) {\n const tail = exec.stderr.slice(-10_000).trim().split('\\n').slice(-20).join('\\n');\n if (tail) console.log(`Stderr (last 20 lines):\\n${tail}`);\n }\n if (exec.error) {\n console.log(`Error: ${exec.error}`);\n }\n}\n\n/**\n * Read one execution. For a detached run this is also what advances it: the\n * server reconciles a still-`running` row against SSM on read, so polling this\n * is how a background action reaches a terminal state.\n */\nasync function handleActionExecutionGet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const executionId = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !executionId) {\n console.error(\n 'Error: instance ID and execution ID required. Usage: cofactor sandbox action execution get <instance-id> <execution-id>'\n );\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecution }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/instances/${instanceId}/actions/executions/${executionId}`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n printExecutionDetail(res.data!);\n}\n\n/** Stop a detached execution. Idempotent; a finished run is reported as-is. */\nasync function handleActionExecutionCancel(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const executionId = positional[1];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId || !executionId) {\n console.error(\n 'Error: instance ID and execution ID required. Usage: cofactor sandbox action execution cancel <instance-id> <execution-id>'\n );\n process.exit(1);\n }\n\n const res = await apiRequest<{ ok: boolean; error?: string; data?: ActionExecution }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/actions/executions/${executionId}/cancel`\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n printExecutionDetail(res.data!);\n}\n\n// ---------------------------------------------------------------------------\n// Multi-repo: manage repos linked to a sandbox template\n// ---------------------------------------------------------------------------\n\ntype TemplateRepoLink = {\n id: string;\n templateId: string;\n propertyGithubRepoId: string;\n role: string | null;\n branch: string;\n subdirectory: string | null;\n serviceName: string;\n};\n\n/**\n * Parse `role=owner/name@branch:subdir` (branch and subdir optional) into the\n * pieces the API expects. Used by `canary sandbox repo add --spec ...`.\n */\nfunction parseRepoSpec(spec: string): {\n role?: string;\n repoFullName: string;\n branch?: string;\n subdirectory?: string;\n} {\n let rest = spec;\n let role: string | undefined;\n const eq = rest.indexOf('=');\n if (eq > -1 && !rest.slice(0, eq).includes('/')) {\n role = rest.slice(0, eq);\n rest = rest.slice(eq + 1);\n }\n let subdirectory: string | undefined;\n const colon = rest.indexOf(':');\n if (colon > -1) {\n subdirectory = rest.slice(colon + 1);\n rest = rest.slice(0, colon);\n }\n let branch: string | undefined;\n const at = rest.indexOf('@');\n if (at > -1) {\n branch = rest.slice(at + 1);\n rest = rest.slice(0, at);\n }\n return { role, repoFullName: rest, branch, subdirectory };\n}\n\nasync function handleRepoList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n if (!templateId) {\n console.error('Error: --template is required.');\n process.exit(1);\n }\n\n const result = await apiRequest<{ ok: boolean; items?: TemplateRepoLink[]; error?: string }>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/templates/${templateId}/repos`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(result.items ?? [], null, 2));\n return;\n }\n\n const items = result.items ?? [];\n if (items.length === 0) {\n console.log('No repos linked to this template.');\n return;\n }\n for (const r of items) {\n const role = r.role ? ` [${r.role}]` : '';\n const subdir = r.subdirectory ? `:${r.subdirectory}` : '';\n console.log(` ${r.serviceName} → ${r.propertyGithubRepoId}@${r.branch}${subdir}${role} (link=${r.id})`);\n }\n}\n\nasync function handleRepoAdd(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const propertyGithubRepoId = getArgValue(argv, '--property-github-repo-id');\n const spec = getArgValue(argv, '--spec');\n const role = getArgValue(argv, '--role');\n const branch = getArgValue(argv, '--branch');\n const subdirectory = getArgValue(argv, '--subdirectory');\n const serviceName = getArgValue(argv, '--service-name');\n\n if (!templateId || !propertyGithubRepoId) {\n console.error('Error: --template and --property-github-repo-id are required.');\n console.error('Tip: link the repo to the property first via `canary property repo link`, then pass its id here.');\n process.exit(1);\n }\n\n const parsed: ReturnType<typeof parseRepoSpec> | { role?: undefined; branch?: undefined; subdirectory?: undefined } = spec ? parseRepoSpec(spec) : {};\n\n const body: Record<string, unknown> = { propertyGithubRepoId };\n const finalRole = role ?? parsed.role;\n const finalBranch = branch ?? parsed.branch;\n const finalSubdir = subdirectory ?? parsed.subdirectory;\n if (finalRole !== undefined) body.role = finalRole;\n if (finalBranch !== undefined) body.branch = finalBranch;\n if (finalSubdir !== undefined) body.subdirectory = finalSubdir;\n if (serviceName !== undefined) body.serviceName = serviceName;\n\n const result = await apiRequest<{ ok: boolean; data?: TemplateRepoLink; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/templates/${templateId}/repos`,\n body\n );\n\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result.data, null, 2));\n else console.log(`Linked repo to template ${templateId} (link=${result.data?.id})`);\n}\n\nasync function handleRepoUpdate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const linkId = getArgValue(argv, '--link-id');\n const role = getArgValue(argv, '--role');\n const branch = getArgValue(argv, '--branch');\n const subdirectory = getArgValue(argv, '--subdirectory');\n const serviceName = getArgValue(argv, '--service-name');\n\n if (!templateId || !linkId) {\n console.error('Error: --template and --link-id are required.');\n process.exit(1);\n }\n\n const body: Record<string, unknown> = {};\n if (role !== undefined) body.role = role;\n if (branch !== undefined) body.branch = branch;\n if (subdirectory !== undefined) body.subdirectory = subdirectory;\n if (serviceName !== undefined) body.serviceName = serviceName;\n\n const result = await apiRequest<{ ok: boolean; data?: TemplateRepoLink; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/repos/${linkId}`,\n body\n );\n\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result.data, null, 2));\n else console.log(`Updated repo link ${linkId}`);\n}\n\nasync function handleRepoRemove(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const templateId = getArgValue(argv, '--template');\n const linkId = getArgValue(argv, '--link-id');\n if (!templateId || !linkId) {\n console.error('Error: --template and --link-id are required.');\n process.exit(1);\n }\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/environments/templates/${templateId}/repos/${linkId}`\n );\n if (!result.ok) {\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.error(`Error: ${result.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (json) writeRawOutput(JSON.stringify(result, null, 2));\n else console.log(`Removed repo link ${linkId} from template ${templateId}`);\n}\n\nasync function handleRepo(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [action, ...rest] = argv;\n switch (action) {\n case 'list':\n await handleRepoList(rest, apiUrl, token);\n break;\n case 'add':\n await handleRepoAdd(rest, apiUrl, token);\n break;\n case 'update':\n await handleRepoUpdate(rest, apiUrl, token);\n break;\n case 'remove':\n case 'rm':\n await handleRepoRemove(rest, apiUrl, token);\n break;\n default:\n console.error(\n `Unknown repo action: ${action ?? '(none)'}. Use list/add/update/remove.\\n` +\n 'Examples:\\n' +\n ' canary sandbox repo list --template <template-id>\\n' +\n ' canary sandbox repo add --template <template-id> --property-github-repo-id <uuid> --role frontend\\n' +\n ' canary sandbox repo add --template <template-id> --property-github-repo-id <uuid> --spec frontend=owner/web@main:/apps/web'\n );\n process.exit(1);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main dispatcher\n// ---------------------------------------------------------------------------\n\nexport async function runSandbox(argv: string[]): Promise<void> {\n // `exec` takes a command after `--`, which no flag scanning below may read.\n const { options, passthrough } =\n argv[0] === 'exec' ? splitAtSeparator(argv) : { options: argv, passthrough: [] };\n const [subCommand, ...rest] = options;\n\n if (!subCommand || subCommand === 'help' || hasFlag(options, '--help', '-h')) {\n printHelp();\n return;\n }\n\n // Guide is purely local — no auth needed. `guide v2 [topic]` serves the\n // Sandboxes v2 guide; plain `guide [section]` keeps serving the v1 guide.\n if (subCommand === 'guide') {\n if (rest[0] === 'v2') {\n const v2Rest = rest.slice(1);\n const section = getGuideSectionArg(v2Rest);\n const json = hasFlag(v2Rest, '--json');\n printGuide(SANDBOX_V2_GUIDE_SECTIONS, section, json);\n return;\n }\n const section = getGuideSectionArg(rest);\n const json = hasFlag(rest, '--json');\n printGuide(SANDBOX_GUIDE_SECTIONS, section, json);\n return;\n }\n\n // Sandboxes v2 `validate` is purely local (offline schema + cross-check) —\n // no auth needed, same as `guide`.\n if (subCommand === 'validate') {\n handleSandboxValidate(rest);\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(options);\n\n // Strip global flags consumed by resolveConfig so handlers don't see them as positional args.\n // `--app-url` travels with `--api-url` (a worktree or self-hosted target); the CI push\n // workflow passed it and every v2 command refused it (2026-10-04).\n const globalFlags = new Set(['--env', '--api-url', '--app-url', '--token', '--org']);\n const args = rest.filter((arg, i) => {\n if (globalFlags.has(arg)) return false;\n if (i > 0 && globalFlags.has(rest[i - 1])) return false;\n return true;\n });\n rejectUnknownSandboxV2Args(subCommand, args);\n\n switch (subCommand) {\n case 'list':\n await handleList(args, apiUrl, token);\n break;\n case 'template':\n await handleTemplate(args, apiUrl, token);\n break;\n case 'repo':\n await handleRepo(args, apiUrl, token);\n break;\n case 'artifacts':\n await handleArtifacts(args, apiUrl, token);\n break;\n case 'checklist':\n await handleChecklist(args, apiUrl, token);\n break;\n case 'create':\n await handleCreate(args, apiUrl, token);\n break;\n case 'provision':\n await handleProvision(args, apiUrl, token);\n break;\n case 'warm':\n // `warm list|set|show|clear` keeps a Sandboxes v2 environment warm; `warm <template-id>` is the v1 template warm-up.\n if (isSandboxV2WarmCommand(args[0])) await handleSandboxWarm(args, apiUrl, token);\n else await handleWarm(args, apiUrl, token);\n break;\n case 'verify':\n await handleVerify(args, apiUrl, token);\n break;\n case 'set-size':\n await handleSetSize(args, apiUrl, token);\n break;\n case 'pool': {\n const [poolAction, ...poolRest] = args;\n if (poolAction === 'set-size') {\n await handlePoolSetSize(poolRest, apiUrl, token);\n } else {\n console.error(`Unknown pool action: ${poolAction}. Use \"set-size\".`);\n process.exit(1);\n }\n break;\n }\n case 'artifact': {\n const [action, ...actionRest] = args;\n if (action === 'get') {\n await handleArtifactGet(actionRest, apiUrl, token);\n } else if (action === 'create') {\n await handleArtifactCreate(actionRest, apiUrl, token);\n } else {\n console.error(`Unknown artifact action: ${action}. Use \"get\" or \"create\".`);\n process.exit(1);\n }\n break;\n }\n case 'config': {\n const [configAction, ...configRest] = args;\n if (configAction === 'list') {\n await handleConfigList(configRest, apiUrl, token);\n } else if (configAction === 'set') {\n await handleConfigSet(configRest, apiUrl, token);\n } else if (configAction === 'unset') {\n await handleConfigUnset(configRest, apiUrl, token);\n } else {\n console.error(`Unknown config action: ${configAction}. Use \"list\", \"set\", or \"unset\".`);\n process.exit(1);\n }\n break;\n }\n case 'emulation':\n await handleEmulation(args, apiUrl, token);\n break;\n case 'action': {\n const [actionSub, ...actionRest] = args;\n switch (actionSub) {\n case 'list':\n await handleActionList(actionRest, apiUrl, token);\n break;\n case 'get':\n await handleActionGet(actionRest, apiUrl, token);\n break;\n case 'create':\n await handleActionCreate(actionRest, apiUrl, token);\n break;\n case 'delete':\n await handleActionDelete(actionRest, apiUrl, token);\n break;\n case 'execute':\n await handleActionExecute(actionRest, apiUrl, token);\n break;\n case 'executions':\n await handleActionExecutions(actionRest, apiUrl, token);\n break;\n case 'execution': {\n const [execSub, ...execRest] = actionRest;\n if (execSub === 'get') {\n await handleActionExecutionGet(execRest, apiUrl, token);\n } else if (execSub === 'cancel') {\n await handleActionExecutionCancel(execRest, apiUrl, token);\n } else {\n console.error(`Unknown execution sub-command: ${execSub}. Use \"get\" or \"cancel\".`);\n process.exit(1);\n }\n break;\n }\n default:\n console.error(`Unknown action sub-command: ${actionSub}. Use \"list\", \"get\", \"create\", \"delete\", \"execute\", \"executions\", or \"execution\".`);\n process.exit(1);\n }\n break;\n }\n case 'build':\n await handleBuild(args, apiUrl, token);\n break;\n case 'run-command':\n await handleRunCommand(args, apiUrl, token);\n break;\n case 'diagnostics':\n await handleDiagnostics(args, apiUrl, token);\n break;\n case 'logs':\n await handleLogs(args, apiUrl, token);\n break;\n case 'preview':\n await handlePreview(args, apiUrl, token);\n break;\n case 'envs':\n await handleSandboxEnvs(args, apiUrl, token);\n break;\n case 'refresh':\n await handleSandboxRefresh(args, apiUrl, token);\n break;\n case 'connect':\n await handleSandboxConnect(args, apiUrl, token);\n break;\n case 'push':\n await handleSandboxPush(args, apiUrl, token);\n break;\n case 'secret':\n await handleSandboxSecret(args, apiUrl, token);\n break;\n case 'claim':\n await handleSandboxClaim(args, apiUrl, token);\n break;\n case 'show':\n await handleSandboxShow(args, apiUrl, token);\n break;\n case 'run':\n await handleSandboxRun(args, apiUrl, token);\n break;\n case 'exec':\n await handleSandboxExec(args, passthrough, apiUrl, token);\n break;\n case 'sync':\n await handleSandboxSync(v2Positionals('sync', args), args, apiUrl, token);\n break;\n case 'verify-env':\n await handleSandboxVerifyEnv(args, apiUrl, token);\n break;\n case 'destroy':\n await handleSandboxDestroy(args, apiUrl, token);\n break;\n case 'terminate':\n await handleTerminate(args, apiUrl, token);\n break;\n case 'delete':\n await handleDelete(args, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printHelp();\n process.exit(1);\n }\n}\n","A Sandboxes v2 sandbox is a VM restored from a stack of snapshot layers,\nnot a container stood up from scratch each time. That's what makes claims\nfast: the work (installing the toolchain and dependencies, building the\napp, seeding its data) happens once per commit and environment and is\nbaked into a snapshot, and every claim restores its own copy.\n\nEvery example in this guide uses one invented app: a project-tracking\nSaaS with workspaces, their members (roles: owner, member, viewer),\nprojects and tasks, a REST API with cookie sessions, and a Postgres\ndatabase. Substitute your own app's objects, roles and endpoints.\n\nLAYERS\n\n platform Cofactor's sandbox helper, which runs your services,\n prepare commands and actions. Rebuilt only when the\n helper's contract with Cofactor changes.\n\n toolchain `setup` from cofactor.sandbox.yaml, plus `resources` and the\n declared `ports`. Rebuilt only when those change — this is\n the expensive layer (apt packages, language runtimes,\n database clusters).\n\n repo base The repository checked out and built at a commit:\n `lifecycle.build` steps, each rerun only when the git\n pathspecs in its `inputs` changed since the layer this one\n is stacked on. This is what makes \"the same commit, again\"\n nearly free.\n\n ready One commit and one environment: the repo base moved to the\n snapshot commit, prepared, and the environment's seeds run — then\n captured while its services are running. Built once, the\n first time anything asks for that commit and environment;\n every claim is a copy of it.\n\nA GitHub-delivered sandbox is identified by **repository + definition\npath**, not by repository alone, so a monorepo can hold several (see\nbelow). Pushed images have no repository or build layers: the image is\nthe base, and each push gets its own ready snapshots (see the `upload`\ntopic).\n\nTHE CLAIM LIFECYCLE\n\n 1. snapshot The claim needs the ready snapshot for its commit and\n environment. If there isn't one yet, it is built first\n (once, however many claims are waiting on it):\n - the nearest layer (toolchain or repo base) is\n restored;\n - the outstanding `lifecycle.build` steps bring it to\n the commit (only the steps whose `inputs` changed);\n - `prepare` runs (see step 3);\n - the environment's seed chain runs, in order (parent's\n seeds before its own — see the `environments` topic);\n each step is an action or an emulator operation,\n and its output can be referenced by later steps;\n - the VM is captured with its services running.\n 2. restore A copy of the ready snapshot is restored. Nothing is\n built or seeded: the copy starts exactly where the\n snapshot left off, data included.\n 3. identity A copy whose port params are all live, with stored snapshot\n prepare results, receives public port URLs through one\n copy-values exec. The sandbox materializes\n /var/lib/cofactor/live-env.json for the\n running app; lifecycle.prepare does NOT run. There is no\n service restart, emulator reconfiguration or bucket creation.\n Secrets, persona passwords, emulator token/config and seeded\n storage stay exactly as captured. A user claim receives its\n private sync key by a separate exec, never in the copy-values\n exec or its file. Non-user copies cannot be synced.\n Legacy prepare runs instead when any port param is non-live,\n an env key mixes live/non-live params, or an emulator config\n references a port param; it keeps per-copy prepare and restart.\n Pinned PR-preview captures without stored prepare results\n also keep the existing copy prepare path.\n 4. ready The sandbox reports its port URLs, persona logins, and the\n seed outputs (the same for every copy of the snapshot).\n From here it behaves like a real running instance of the\n app until it's destroyed or its lease expires. A copy is\n never reused.\n\n`cofactor sandbox show` breaks the claim's time down into these phases.\n\nA failure while building the snapshot (a build step, `prepare` or a seed)\nfails the claims waiting on it; the next claim of that commit and\nenvironment builds the snapshot again, so a failure that happens every\ntime needs a fix and a new commit or push. See the `verify` topic for what to do\nwhen that happens.\n\nA failure of the sandbox platform itself (a worker that is slow to restore,\na capture that is still running) is retried: the snapshot's build tries\nagain, resuming where it stopped, and so does claiming the copy when the\nfleet has no room. Every claim has a time limit — 2 hours unless\n`cofactor sandbox claim --timeout` sets another. A claim not ready by then\nfails with the last problem it met, and a sandbox is never handed over after\nit: a copy still being made at the limit is destroyed when it finishes. That\ncan be up to about 10 minutes of waiting for room plus the copy's prepare\nafter the limit, so the failure may arrive later than the limit itself.\n\nAPPLICATIONS, AND WHERE THEIR CODE COMES FROM\n\n A sandbox is always a sandbox of a Cofactor application. How the\n application's code reaches its sandboxes is its own setting:\n\n - GitHub (`cofactor sandbox connect --application A --repo owner/name\n [--dir D]`): the `cofactor.sandbox.yaml` under `D` at each commit\n installs (`setup`) and builds (`lifecycle.build`) a checkout. A\n monorepo can connect one application per definition directory.\n Commands run **from that definition's directory** — an action's\n `run:` path, build steps and services resolve relative to where the\n `cofactor.sandbox.yaml` lives, not the repo root.\n - Pushed images (`cofactor sandbox push`), for a team that gives\n Cofactor no repository access: see the `upload` topic.\n\n Either way, each environment in the definition is one sandbox\n environment of the application, and each persona one of its personas, so\n test runs on that environment sign in with the sandbox's own logins.\n There is no need to create the application first: `connect` or `push`\n with a new application name creates it, and its sandbox `default`\n environment is its default environment.\n\nHOW A TEST AGENT USES THIS\n\n 1. Browse what's available: `cofactor sandbox envs --application <name>`\n lists the definition's environments, actions, emulator operations,\n ports and personas.\n 2. Claim one: `cofactor sandbox claim --application <name>\n [--environment <name>] --wait`. Without `--environment` you get\n `default`.\n 3. Write the test against the claimed sandbox: drive the app through its\n port URLs, call actions to set up data the test needs\n (`cofactor sandbox run <id> <action>`), and call emulator operations\n to inspect or drive third parties (`cofactor sandbox run <id>\n <plugin.op>`).\n 4. Try uncommitted changes against it: `cofactor sandbox sync <id>`\n writes your working tree into the sandbox, then `cofactor workflow\n run <flow> --sandbox <id>` runs a flow on it (see the `local` topic).\n 5. Destroy it when done: `cofactor sandbox destroy <id>`.\n\n Staff can keep an environment warm so claims start in about a second\n instead of fetching its snapshot first: see the `warm` topic.\n","Everything about a sandbox lives in one file: `cofactor.sandbox.yaml`, at the\nrepository root or under a definition directory for a monorepo (for pushed\nimages, wherever you keep it — see the `upload` topic). The examples below\nare fragments of one file for the guide's example app: a project tracker\nwith a `web` front end, an `api` server and Postgres.\n`cofactor sandbox validate` checks every field below.\n\n Required: `version`, `resources`, `ports`, `params`, `env`, `services`,\n `lifecycle` and `environments`; write `{}` for one you don't use (e.g.\n `params: {}`, `lifecycle: { prepare: [] }`). `personas`, `actions`, `emulation`, `object_storage`\n and `databases` may be left out.\n Names of ports, services, personas, actions, environments and plugins\n are lowercase letters, digits and `-` (up to 32 characters, starting\n with a letter); a seed step's `id` is lowercase letters, digits and `_`\n (it is used in `${<id>.<output>}`, so no `-`).\n\nversion\n Always `1`. The schema version of the file itself.\n\nresources\n The machine every sandbox of this application gets.\n resources: { vcpus: 2, memory_mib: 4096 }\n\nsetup\n (GitHub-delivered only.) A root shell script that installs the\n toolchain: packages, language runtimes, database clusters. Runs once per\n toolchain layer (keyed by this script's text plus `resources` and\n `ports` — see the `overview` topic).\n setup: |\n apt-get update -qq\n apt-get install -y -qq postgresql-16 nodejs npm jq curl >/dev/null\n\nports\n Named ports the app listens on, each becoming its own public URL. Several\n are normal — bundle web and API together so one sandbox serves both UI\n flows and API sequences. `access: public` gets a routable URL; `private`\n stays internal. At most 8 total, including the implicit `sync` port every\n sandbox has (so at most 7 of your own), the `emulation` port added\n automatically when `emulation` is declared, and `object-storage` when\n `object_storage` is — see the `ports` topic.\n ports:\n web: { port: 3000, access: public }\n api: { port: 3001, access: public }\n\nparams\n Per-sandbox values, filled in by `prepare`, referenced from `env` as\n `${params.name}`. Three shapes:\n - a plain string: a fixed value, same in every sandbox.\n - `{port: <name>}`: the sandbox's own public URL for that port.\n `{port: emulation}` is the emulator's public URL, allowed when\n `emulation` is declared (it is the `oidc` plugin's issuer).\n - `{secret: true}`: a random value made when the version's ready\n snapshot is built, so every sandbox of that version and environment\n shares it (e.g. a session-signing key); not unique per claim.\n params:\n web_url: { port: web }\n api_url: { port: api }\n session_secret: { secret: true }\n A port param may be `live: true`: read its resolved env at request/use time\n from `/var/lib/cofactor/live-env.json`, a JSON object of env-name -> value.\n A fast copy receives its URLs through one copy-values exec, which\n keeps this file current using atomic replacement;\n re-read when the file's inode or modification time changes. No private\n Cofactor package is needed by your app.\n Eligible copies with stored snapshot prepare results skip prepare and keep\n services running: lifecycle.prepare does NOT run. They retain baked secrets\n until the snapshot is rebuilt. A copy is eligible for the fast path only\n when every port param is live.\n Any non-live port param keeps the per-copy prepare and service restart.\n An env key mixing a live port param with a non-live param also requires\n prepare, as does an emulation plugin config that references a port param.\n A real third-party secret (a Stripe or OpenAI key) is not a param: see\n the `secrets` topic.\n\nenv\n Environment variables the services, prepare commands and actions run\n with. `${params.name}` is substituted with that param's value. An\n environment can add its own on top (see the `env` topic). A secret of\n the same name (see the `secrets` topic) overrides the `env` entry.\n (`${env.NAME}` is a different, narrower reference: it appears only as\n the whole `url` of a `databases` entry — see the `databases` field.)\n env:\n DATABASE_URL: postgres://app@127.0.0.1:5432/app\n PUBLIC_URL: ${params.web_url}\n API_URL: ${params.api_url}\n SESSION_SECRET: ${params.session_secret}\n\nservices\n Long-running processes. `cofactor-sync` is reserved: every sandbox runs\n it (the sync server, see the `local` topic). Restart a service by hand\n with `cofactor sandbox exec <id> -- cofactor-sandbox restart <name>`.\n services:\n db:\n run: exec sandbox/start-postgres.sh\n health: { command: pg_isready -h 127.0.0.1 }\n api:\n run: cd api && exec npm start\n health: { http: 3001, path: /health }\n after_build: true # don't start until lifecycle.build has produced its output\n reload_on_prepare: true # restarted by prepare, so it sees per-sandbox env\n web:\n run: cd web && exec npm run serve\n health: { http: 3000, path: / }\n after_build: true\n reload_on_prepare: true # reads PUBLIC_URL, API_URL and FEATURE_FLAGS\n\n `health` is either `{ command: <shell> }` (checked by exit code) or\n `{ http: <port>, path: </...> }`. `after_build` is for GitHub-delivered\n definitions only.\n\npersonas\n Named login identities declared here; each one's password is generated\n when the snapshot is built and shared by its copies (see the `personas` topic). Only `email` is declared\n in the definition.\n personas:\n owner: { email: owner@example.test }\n member: { email: member@example.test }\n viewer: { email: viewer@example.test }\n\nlifecycle\n build (GitHub-delivered only.) Ordered steps that produce the\n repo-base layer. Each has `name`, `run` (a shell command) and\n `inputs` (git pathspecs, relative to the definition's\n directory) — a step reruns only when the blobs its `inputs`\n match changed, or an earlier step ran.\n lifecycle:\n build:\n - name: install\n run: npm ci\n inputs: [package-lock.json, \"**/package.json\"]\n\n prepare Shell scripts run every time a sandbox is prepared (a snapshot\n build, a legacy copy claim or an in-place move to a new commit), after services with\n `reload_on_prepare` have restarted with per-sandbox values. Each\n param is also available as `COFACTOR_PARAM_<NAME>`.\n lifecycle:\n prepare:\n - bash sandbox/personas.sh\n\nactions\n Typed scripts — the one way data gets into a sandbox (seeds, tests,\n `cofactor sandbox run`). See the `seeds-and-actions` topic for\n what makes a good one; the field shapes are:\n actions:\n create-workspace:\n description: A new workspace, created by the given persona, who becomes its owner.\n run: sandbox/actions/create-workspace.sh\n input:\n name: { type: string, required: true }\n as: { type: persona, required: true, description: Who creates it }\n output:\n workspace_id: { type: string }\n timeout: 60 # seconds, defaults to 60, max 1800\n add-member:\n description: Adds a persona to a workspace with a role.\n run: sandbox/actions/add-member.sh\n input:\n workspace_id: { type: string, required: true }\n persona: { type: persona, required: true, description: Who is added }\n role: { type: string, required: true, enum: [owner, member, viewer] }\n as: { type: persona, required: true, description: Who adds them }\n\n Input properties: `type` (string | number | integer | boolean |\n persona — a persona's name, checked against `personas`), `description`,\n `required`, `default`, `enum`, `sensitive` (masked wherever input is\n logged or returned).\n Output properties: `type`, `description`.\n\nemulation\n Plugin id → its config (often `{}`). Declaring any plugin here adds the\n implicit, public, port-80 `emulation` port automatically — you don't\n declare it yourself. See the `emulation` topic for what each plugin\n intercepts. Plugin ids: `aws`, `email`, `google-oauth`, `jira`, `slack`, `oidc`\n (a standard OIDC provider for the app to sign in against; see the\n `authentication` topic).\n emulation:\n email: {}\n\nobject_storage\n An emulated S3 or GCS server with these buckets in every sandbox, its\n standard client env given to everything that runs, and an implicit\n public `object-storage` port. See the `object-storage` topic.\n object_storage: { provider: s3, buckets: [attachments] }\n\ndatabases\n Postgres databases the built-in `database.fast-forward` operation can\n move forward, by name: `engine` (`postgres`), `url` and `as_of` (when\n the dump was captured, UTC with a `Z`). `url` is exactly one whole-string\n `${env.NAME}` reference — nothing before or after it — resolved inside\n the sandbox against the env actions get, including repository secrets.\n See the `data-fast-forward` topic.\n databases:\n app:\n engine: postgres\n url: ${env.DATABASE_URL}\n as_of: 2026-09-30T00:00:00Z\n\nenvironments\n Named flavors of the same sandbox — different starting data and config.\n Required, must include `default`. See the `environments` topic for the\n full model (`extends`, seed steps, `${id.key}` references, `env`).\n environments:\n default:\n description: |\n Workspace \"Acme\", owned by the owner persona. No projects.\n seeds:\n - { id: acme, action: create-workspace, input: { name: Acme, as: owner } }\n\nMULTI-LINE TEXT\n\n YAML `|` keeps line breaks (use it for multi-paragraph `description`s and\n the `setup` script). YAML `>-` folds lines into one string with no\n trailing newline (use it for a single long sentence, such as a long\n action `description`).\n","An environment (a \"flavor\") is the same sandbox — same code, same toolchain\n— started with different data and config. `default` is required; every\ndefinition must declare it. Previews use `default`, and so does `cofactor\nsandbox claim` when you don't pass `--environment`.\n\nWHEN TO ADD A FLAVOR VS. PASSING INPUTS IN THE TEST\n\n Rule of thumb: add a flavor when many tests need the same starting state.\n Call actions from inside the test when only that test needs the data.\n\n In the example app, `default` seeds one workspace with an owner, a\n member and a viewer, because nearly every test signs in to a workspace\n as one of those roles. A test about archiving a project, on the other\n hand, calls `create-project` itself (see the worked example in\n `seeds-and-actions`) rather than getting a new flavor. If you find\n yourself writing a flavor whose seeds only exist to serve one test,\n that data belongs in the test's own action calls instead.\n\n Add a second flavor only when real tests need a starting state `default`\n can't give them cheaply — typically a different plan or set of feature\n flags (see \"WHAT TO BUILD FIRST\" in `seeds-and-actions`). Don't add one\n speculatively.\n\nDESCRIPTIONS ARE FOR THE READER\n\n `description` is required and can be multi-line (YAML `|`). Write it for\n the agent choosing between environments — say what's already there and\n who can do what, not just a label:\n environments:\n default:\n description: |\n Workspace \"Acme\": owner is its owner, member a member,\n viewer a read-only viewer. No projects yet; create them\n with create-project.\n\n A vague description (\"the default setup\") gives an agent nothing to\n decide with. Say what exists, in terms of the ids and roles a test will\n actually use.\n\nEXTENDS\n\n `extends: <other-environment-name>` composes one environment on top of\n another. The child's snapshot runs the **parent's seeds first, then the\n child's own**, in that order. `env` merges the same way: parent's keys\n first, the child's last (so a child can override a parent's value).\n Chains can be more than two deep; a cycle is rejected by `validate`.\n\n Step ids must be **unique across the whole resolved chain** — a parent\n and its child can't reuse the same id, since the child's steps can\n reference the parent's outputs.\n\n An environment carries its own `description`, `extends`, `seeds` and\n `env`. It has no `params` or `personas` of its own: those belong to the\n definition and are the same in every environment.\n\nSEED STEPS AND `${id.key}`\n\n Each `seeds` step has an `id`, exactly one of `action` or `operation`,\n and an `input`:\n - `id`: unique within the resolved chain; later steps reference this\n step's output as `${id.key}`.\n - `action`: a declared action's slug, or `<plugin>.<op>` for an\n emulator operation (e.g. `email.sent-emails`).\n - `operation`: a platform built-in, today only `database.fast-forward`\n (see `data-fast-forward` and `seeds-and-actions`).\n - `input`: literal values, or `${id.key}` references to an earlier\n step's output.\n\n A reference that is the **entire** value keeps the output's real type:\n input: { workspace_id: \"${acme.workspace_id}\" } # exactly acme's workspace_id\n\n A reference **embedded** in a longer string becomes a string\n interpolation instead:\n input: { name: \"Board for ${acme.workspace_id}\" } # becomes a string\n\n `${id.key}` must point at a step that ran earlier in the chain and at one\n of that step's *declared* output keys — `cofactor sandbox validate`\n checks both and reports the path.\n\n The example app's environments, in full:\n environments:\n default:\n description: |\n Workspace \"Acme\": owner is its owner, member a member,\n viewer a read-only viewer. No projects yet.\n seeds:\n - id: acme\n action: create-workspace\n input: { name: Acme, as: owner }\n - id: add_member\n action: add-member\n input: { workspace_id: \"${acme.workspace_id}\", persona: member, role: member, as: owner }\n - id: add_viewer\n action: add-member\n input: { workspace_id: \"${acme.workspace_id}\", persona: viewer, role: viewer, as: owner }\n free-plan:\n extends: default\n description: Same as default, but Acme is moved to the Free plan (3-project limit).\n seeds:\n - id: downgrade\n action: set-plan\n input: { workspace_id: \"${acme.workspace_id}\", plan: free }\n beta-features:\n extends: default\n description: Same as default, with the timeline view turned on.\n env:\n FEATURE_FLAGS: timeline\n\n `add_member` couldn't run before `acme` — its `workspace_id` input\n depends on `acme`'s output. Seeds always run in the order they're\n written. `free-plan` sets its plan with a seed action because the app\n stores plans in its database; `beta-features` uses `env` because the\n app reads its flags from an environment variable (see the `env` topic\n for which processes see it).\n","An action is a script with a typed input and output schema. It is the one\nway data gets into a sandbox — seeds call it, and so do tests and\n`cofactor sandbox run`. Every environment's `seeds` list is\njust an ordered sequence of action (and emulator operation) calls. For\nthe script contract (`INPUT_<NAME>`, the result line), see the `upload`\ntopic's worked example; it is the same for GitHub-delivered apps.\n\nA SEED STEP THAT IS AN OPERATION\n\n A seed step runs exactly one of `action:` (a declared action, or an\n emulator operation named `<plugin>.<op>`) or `operation:` (a platform\n built-in). Today the only built-in is `database.fast-forward`, which\n moves a restored database's dates forward:\n - { id: fast_forward, operation: database.fast-forward, input: { database: app } }\n A step with both keys, or neither, fails `validate`. See the\n `data-fast-forward` topic.\n\nWHAT TO BUILD FIRST\n\n 1. One persona per role your tests need. The example app has three\n roles, so it declares `owner`, `member` and `viewer`. A persona in\n the definition is only a login (prepare makes it work — see\n `personas`); its role comes from a seed action such as `add-member`,\n or from prepare itself when the persona is an account your app\n already has with the right role. With one persona per role, tests can\n sign in as each and check what that role may and may not do.\n\n 2. One environment per plan or feature-flag combination your tests care\n about: `default`, `free-plan`, `beta-features`. Set each with a seed\n action that goes through your app's admin API or database (the\n example's `set-plan`), or with the environment's `env:` when your app\n reads the flag from an environment variable (the example's\n `FEATURE_FLAGS`; see the `env` topic for which processes see it).\n Params are per definition, not per environment, so they can't vary a\n flag between environments. See the `environments` topic.\n\n 3. Actions for the few core objects nearly every test needs, each taking\n `as: <persona>`: in the example, `create-workspace`, `add-member`,\n `create-project`, `create-task`. Keep the shared seeds thin; tests\n create the rest themselves with these actions.\n\n 4. If your app stores files, declare `object_storage` and seed the few\n files every test needs (a logo, an import template) with an action;\n see the `object-storage` topic.\n\nSEEDS VS. ACTIONS A TEST CALLS ITSELF\n\n Seeds are shared, thin, generic base data: the handful of things nearly\n every test needs before it can start — a workspace and its members, one\n per role. A shared seed stays thin and generic; it does not try to\n anticipate every test's needs.\n\n An action a test calls itself is data specific to that one test. If a\n test's job is \"archive a project,\" the project it archives should not\n live in a shared seed — a shared seed that every test's setup depends on\n becomes something every other test can break by changing it, and it\n bloats claim time for tests that never needed it. The test creates its\n own project by calling `create-project`, then operates on the id it got\n back.\n\n Rule of thumb: if removing this data would break tests that aren't about\n it, it's a seed. If only one test's own scenario needs it, it's an action\n call inside that test.\n\nWHAT MAKES A GOOD ACTION\n\n - Typed inputs and outputs. The input schema is what a test (or an\n agent) uses to call it correctly; the output schema is what the next\n step gets to chain from. Untyped free-form data defeats the ability to\n validate `${id.key}` references and CLI input ahead of time.\n - Fast. Seeds run once per version, while its snapshot is built; calls\n run inline in a test — slow seeds slow every new version's first\n claim, and slow calls slow every test.\n - Returns the ids a test needs next. `create-project` returns\n `project_id`, not just \"ok\" — that id is what the test's next action\n or assertion needs.\n - Safe to call more than once, or unique by its own input. Either the\n action is idempotent (`set-plan` re-run with the same plan changes\n nothing), or a distinct call naturally produces a distinct result\n (`create-project` with a new name makes a new project) rather than\n silently colliding with a previous call's data.\n - Explicit about who acts: the `as` persona pattern. Every action that\n performs a real business operation (creating a project, inviting a\n member) takes an explicit `as: <persona>` input naming which persona\n performs it, rather than assuming \"the admin\" or \"whoever is logged\n in.\" The script signs in as exactly that persona (its login is in\n `COFACTOR_PERSONAS`) and acts in the workspace it was given; the\n persona must already have a role there that allows the operation. So\n an action has **no hidden dependency** on some other seed having run\n first to establish who's \"in charge\" — the caller says so, in the\n input.\n - Goes through the app's own API when the data is encrypted or has side\n effects. `invite-user` calls the real invite endpoint so the\n invitation email is actually sent (and can be observed later with\n `email.sent-emails`); a raw insert would silently skip the send. The\n same goes for anything the app encrypts or hashes on write.\n - Goes through the database only for rows with no encryption and no side\n effects, and only when there is no API for it — e.g. `set-plan`\n updating a workspace's plan column, which in production only an\n internal billing process sets.\n\nANTI-PATTERNS\n\n - A giant do-everything seed. One seed step that creates a workspace,\n three members, two projects and their tasks in one script is harder to\n reuse, harder to debug when it fails partway, and forces every test\n that wants only part of it to take all of it. Break it into separate\n actions and chain them with `${id.key}`, as `default` does.\n - An action that silently acts as an admin. If an action doesn't take\n `as`, and instead assumes some fixed identity internally, a test\n writer has no way to know (or override) who actually performed the\n operation, and the action can't be reused for a test that needs a\n viewer to attempt (and be denied) the same thing.\n - Hardcoded ids. An action or seed that references a specific workspace\n or project id baked into the script defeats the whole point of\n chaining `${id.key}` — every id a later step needs should come from an\n earlier step's declared output.\n - An action that depends on seed ordering it doesn't declare. A script\n that assumes some other seed already ran (e.g. that a particular\n workspace exists) without that dependency showing up as an input is a\n hidden coupling — if the environment's seed order ever changes, the\n action breaks with no local sign of why. Take everything you need as\n an explicit input instead (as `create-project` takes `workspace_id`\n and `as`).\n - Writing output anywhere but the last `{`-starting stdout line. The\n action's result is exactly the *last* line of stdout that starts with\n `{`. Send every progress message, debugging `echo` and intermediate\n curl response to stderr — printing one to stdout by mistake silently\n corrupts the parsed output (or an earlier `{`-line gets picked up as a\n decoy if you print the wrong one last).\n\nWORKED EXAMPLE: CREATE THEN ARCHIVE A PROJECT\n\n Workspace \"Acme\" already exists (seeded by `default`'s `acme` step); the\n test supplies its own project rather than relying on a seeded one,\n because creating and archiving a project are the test's own subject.\n\n 1. Claim a sandbox on `default` and note `acme`'s output:\n cofactor sandbox claim --application \"Project Tracker\" --wait\n The claim's seed outputs include `acme.workspace_id`.\n\n 2. Create the project the test will operate on, acting as the member:\n cofactor sandbox run <id> create-project \\\n --input workspace_id=<acme.workspace_id> \\\n --input name=Temp \\\n --input as=member\n Returns `{\"project_id\": \"<id>\"}`.\n\n 3. Drive the app (via its `web`/`api` port URLs) to verify the project\n shows up wherever the product surfaces it, using the `project_id`\n from step 2 to find the right row.\n\n 4. Archive it the way the product's UI or API would — this is what the\n test is actually verifying, so it belongs in the test's own steps,\n not in a seed. Repeating it as `viewer` should be refused.\n\n 5. Destroy the sandbox:\n cofactor sandbox destroy <id>\n\n Nothing about the project this test created is shared with any other\n test, and nothing another seed does could break this test's setup.\n","A database dump keeps the dates of the day it was taken. Restore it into a\nsandbox months later and every trial, token and scheduled item in it has\nalready expired, so the app behaves like a dead system. The built-in\noperation `database.fast-forward` moves a restored Postgres database's\ndates forward to the moment the snapshot was built, so the data looks\nfreshly captured. It changes data only; the sandbox's clock is already\ncorrect (see \"WHAT IS NOT FAKED\" below).\n\nDECLARE THE DATABASE\n\n A top-level `databases` block names each database and the date its dump\n was captured. Like every top-level field, it works in GitHub-delivered\n and pushed definitions alike.\n databases:\n app:\n engine: postgres\n url: ${env.DATABASE_URL}\n as_of: 2026-09-30T00:00:00Z\n\n `engine` `postgres` is the only engine today.\n `url` Exactly one `${env.NAME}` reference and nothing else around\n it (no `postgres://${env.HOST}/db`). It is resolved inside the\n sandbox, against the env an action sees: the definition's\n `env` (with `${params.*}` filled in), the environment's `env`,\n the emulators' env and repository secrets. So a `DATABASE_URL` in `env`, or a\n secret of that name, works, and no credential is committed.\n `as_of` When the dump was captured, in UTC with a trailing `Z`. A date\n without a time, or a local offset, is rejected by `validate`.\n\nADD THE STEP AFTER THE RESTORE\n\n A seed step runs the operation with `operation:` instead of `action:`\n (see `seeds-and-actions`). It must come after the step that restores\n the dump:\n environments:\n default:\n description: A customer database restored from a 2026-09-30 dump,\n then moved forward to the snapshot's build time.\n seeds:\n - { id: restore, action: restore-dump }\n - { id: fast_forward, operation: database.fast-forward, input: { database: app } }\n\n `input.database` is a key of `databases`; `validate` reports an unknown\n one with its path. A restore that recreates the database also clears\n the record of earlier fast-forwards (below), which is right: the data is\n old again. A restore that only replaces tables must also run\n `drop schema if exists cofactor cascade`, or a later fast-forward\n starts from the old record and moves nothing (the worked example's\n dump does this).\n\nWHEN IT RUNS IS WHERE YOU PUT IT\n\n There is no mode flag. Placement is the \"when\":\n - As a seed step it runs once, while the version's snapshot is built,\n so every claim of that snapshot starts with shifted data.\n - On a live sandbox, run the same operation by hand:\n cofactor sandbox run <id> database.fast-forward --input database=app\n It shifts from the date the database recorded last time (below), so\n a sandbox kept for days can be brought current again.\n\nTHE SHIFT RULE\n\n - `as_of` is the anchor. The data moves by the whole days between its\n current date and now, rounded down, so it ends at most a day behind\n now and never in the future. Times of day are kept: a row at 14:30\n still reads 14:30.\n - The first run starts from `as_of`. When it finishes it records the\n data's new date in the database itself, in the table\n `cofactor.fast_forward` (its own schema, so your migration tool does\n not see it). Later runs start from that date instead of `as_of`, and\n the record travels with the data into every snapshot and copy.\n - If less than a day has passed the run changes no table (`offset_days:\n 0`). An `as_of` in the future fails.\n\nWHAT IS SHIFTED\n\n Every `timestamp`, `timestamptz` and `date` column of every base table\n in every schema except the system ones and `cofactor`. `infinity`\n values stay `infinity`.\n - A generated column is recomputed from its shifted inputs.\n - A partitioned table is updated through its parent, so rows move\n between partitions as their dates move.\n - User triggers are disabled while a table is shifted, so a trigger\n such as `updated_at = now()` leaves `updated_at` with its shifted\n value. Afterwards every user trigger on a touched table is enabled\n again, including one your dump had deliberately disabled.\n\nWHAT IS NOT SHIFTED\n\n Integer epoch columns, dates inside JSON, dates stored as text, columns\n of a domain over a date type, array columns, and materialized views.\n Refresh materialized views in a step after the fast-forward:\n - { id: refresh, action: refresh-views }\n\nREQUIREMENTS AND FAILURES\n\n - The role in `url` must own the tables (it is normally the role that\n restored the dump). A role that does not fails with Postgres's own\n \"must be owner of table\" error.\n - The whole shift is one transaction. Any error rolls everything back,\n so nothing is half-shifted, and the error names the table (and the\n constraint, when there is one):\n table public.sessions, constraint sessions_day_key: duplicate key ...\n - A unique constraint over a date column can collide while its rows are\n moving; that fails the run. A foreign key whose columns are shifted\n dates is checked as each table moves and can fail the run with\n Postgres's own error.\n - On a live sandbox the shift locks every table that has a date column\n until it commits. Run it when the app is idle.\n - After a large dump, follow the step with an action that runs\n `VACUUM (ANALYZE)`: the update leaves dead rows and stale statistics\n behind.\n\nWHAT IS NOT FAKED\n\n The sandbox's system clock is not changed, and does not need to be: it\n is correct after every claim. Only data moves. So state minted in memory\n while the snapshot was built (sessions, tokens an app created during a\n seed) is as old as the snapshot. A claim's data is as old as its\n snapshot plus under a day of rounding, and a snapshot is rebuilt on\n every push, so keep pushing (a nightly push is plenty). A preview moved\n in place to a new commit keeps its data and is not shifted again.\n\nOUTPUT\n\n The step (and a `sandbox run`) returns:\n database the key in `databases`\n offset_days whole days the data moved this run (0: nothing to do)\n tables, rows how many tables and rows were updated\n from, to the data's date before and after (ISO-8601, UTC)\n A later step can use them, e.g. `${fast_forward.offset_days}`.\n `cofactor sandbox show` lists each seed step with its time and prints\n these under the seed outputs.\n\nWORKED EXAMPLE\n\n `packages/demo-app-postgres/cofactor.sandbox.yaml` in TreebeardHQ/qa is\n a complete Postgres app: a dump from 2026-09-30, a restore action, the\n `databases` block and the two-step seed above, plus an `aged`\n environment that rewinds the data and fast-forwards it a second time.\n","A persona is a named login identity declared in `cofactor.sandbox.yaml`:\n personas:\n owner: { email: owner@example.test }\n member: { email: member@example.test }\n viewer: { email: viewer@example.test }\n\nDeclare one per role your tests need (see \"WHAT TO BUILD FIRST\" in\n`seeds-and-actions`). Only the email is fixed in the definition.\nEverything else about a persona is assigned per sandbox or per seed:\n\nPASSWORDS — SET WHEN THE SNAPSHOT IS BUILT\n\n Each ready snapshot gets a randomly generated password per persona,\n created by the snapshot's own `prepare` (see the `overview` topic); every\n copy claimed from that snapshot has the same passwords. Those passwords\n never appear in the definition — they're generated fresh for the snapshot\n and handed to\n `lifecycle.prepare` as `COFACTOR_PERSONAS`, a JSON map of persona name to\n `{email, password}`. Your prepare script reads it and makes each login\n work in your app: the example app's `sandbox/personas.sh` creates or\n updates one user per persona (email + password hash), with no workspace\n and no role — just the login itself. The `upload` topic covers the\n details (prepare runs while the app is up; logins must be read per\n sign-in, not once at startup).\n\n A copy's `lifecycle.prepare` still runs, with the snapshot's unchanged\n personas, so make the persona step cheap when nothing changed (skip\n hashing a password the user already has): the example app's personas.sh\n skips when a digest of COFACTOR_PERSONAS and of the users' stored hashes\n both match what its last run left, so a reset or replaced user still\n takes the full path.\n\n If your app gates sign-in behind a first-run step (accepting terms,\n initializing the instance, verifying the email address, finishing\n onboarding), clear it in prepare for each persona, the way you set the\n password: a seed or test signing in through the real login API should\n land signed in, not on the gate.\n\nROLES AND MEMBERSHIPS — SET BY SEEDS\n\n A persona existing (having a login) is separate from what it can do.\n Workspace membership and role are set by seed steps, not by the\n definition: `create-workspace` makes its `as` persona the owner of the\n new workspace, and `add-member` gives a persona a role in it. This split\n matters: the same persona can be \"just a login\" in one environment and\n a workspace owner in another, because that comes from which seeds\n actually ran. If a persona's email is an account your app already has\n with the right role, prepare setting its password is enough.\n\nHOW A SEED OR ACTION SIGNS IN AS A PERSONA\n\n Actions receive `COFACTOR_PERSONAS` too. An action that performs a real\n operation takes the acting persona as an input (`as`, of type\n `persona`, which arrives as `INPUT_AS` holding the persona's name),\n reads that persona's email and password from `COFACTOR_PERSONAS`, and\n signs in through the app's real login endpoint — the same way a user\n would — keeping the session cookie for its next calls:\n login=$(jq -c --arg p \"$INPUT_AS\" '.[$p] | {email, password}' <<<\"$COFACTOR_PERSONAS\")\n jar=$(mktemp)\n curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n -d \"$login\" \"$API_URL/api/auth/login\" >&2\n See the `seeds-and-actions` topic on why `as` is never assumed.\n\n An action runs inside the sandbox, so it can reach the app on\n localhost. If your app's session cookie is `Secure` (or `__Host-`\n prefixed) and your HTTP client won't send it back over plain http, call\n the app through its public port URL (here `API_URL`, from a\n `{port: api}` param) instead.\n\n Keep whatever the login returns that later calls need: the session\n cookie, a bearer token from the response body, or both (some apps bind\n a bearer token to an httpOnly cookie and refuse one without the other).\n\n Every action signs in from the same address, so a seed that runs several\n actions can trip the app's login rate limit (often around 10 attempts a\n minute). If your app has one, raise it for the sandbox through the\n definition's top-level `env:` (it reaches every service) rather than\n adding retries to your actions.\n\nWHERE LOGINS ARE SHOWN\n\n Because copies of a snapshot share persona passwords, anyone who has\n claimed one can sign in to another copy of the same snapshot whose URL\n they learn; keep sensitive data out of sandboxes.\n\n Persona logins (email + generated password) are surfaced by\n `cofactor sandbox show <id>` and by `cofactor sandbox preview` for PR\n previews. They are never printed in a PR comment — a PR comment only\n links to where they can be looked up, keeping generated passwords out of\n anything that ends up in GitHub's own history. Cofactor's test runs sign\n in with them automatically, as the application's persona of the same\n name.\n\n A person who claims a sandbox signs in the ordinary way: open its URL and\n use your app's own login with the email and password the claim prints.\n There is no CLI command that hands you a signed-in browser session;\n `authentication.run` (below) is only for Cofactor's automated test runs.\n\n\nGOOGLE ACCOUNTS\n\n When the definition declares the `google-oauth` emulator, a persona's\n email is also its Google identity: the Google account chooser lists each\n persona, and `authentication.emulated` signs a persona in through Google\n as that email. `google-oauth` needs at least one persona. See\n `cofactor sandbox guide v2 authentication`.\n\n\nNATIVE WORKFLOW LOGIN NODES\n\n Sandboxes v2 uses `authentication` (`run`, or `emulated` for Google\n sign-in) to mint a fresh browser session for the persona named by each\n native workflow login node. Read\n `cofactor sandbox guide v2 authentication` for the complete script contract,\n cookie and localStorage formats, and how to keep provider-login journeys\n clean and unauthenticated at the start.\n","This script is only for Cofactor's automated test runs: a person who claims a sandbox signs in through your app's own login with the email and password the claim prints, and only `cofactor sandbox verify-env` runs it outside a test (it signs every persona in on its claim to prove the script works). Native workflow login nodes use the persona declared in this sandbox definition. Declare `authentication` when your sandbox needs native persona login: exactly one of `run` (your script) or `emulated` (Cofactor signs the persona in through the Google or OIDC emulator; see GOOGLE SIGN-IN AS A PERSONA and STANDARD OIDC below). Existing definitions can use personas in prepare, seeds and actions without this script; a native login without it fails with `script_not_declared`. Each run gets its own copy, so Cofactor asks the copy's `authentication.run` script to create an application session for the requested persona. The script receives the prepared copy environment and `COFACTOR_AUTH_PERSONA`; it returns Playwright storage state as JSON on stdout. The cookies must be for the copy's public origin (sign in through the public URL, as below), not localhost. The example needs no language runtime: a POSIX shell, `curl`, `jq` and `awk`:\n\n```yaml\npersonas:\n admin: { email: admin@example.test }\n member: { email: member@example.test }\nauthentication:\n run: sandbox/auth.sh\n```\n\n```bash\n#!/bin/sh\nset -eu\n\npersona=${COFACTOR_AUTH_PERSONA:?missing persona}\n# This persona's email and generated password; fails if either is missing.\nlogin=$(printf '%s' \"$COFACTOR_PERSONAS\" | jq -ec --arg p \"$persona\" \\\n '.[$p] | {email, password} | select((.email | type) == \"string\" and (.password | type) == \"string\")')\nemail=$(printf '%s' \"$login\" | jq -r .email)\njar=$(mktemp)\ntrap 'rm -f \"$jar\"' EXIT\n\n# Sign in through the copy's public HTTPS API. This preserves Secure and\n# __Host- cookie behavior; localhost HTTP is not suitable for those cookies.\nprintf '%s' \"$login\" | curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n --data-binary @- \"$API_URL/api/auth/login\" >/dev/null\n\n# Check the resulting identity before giving the session to the browser.\ncurl -fsS -b \"$jar\" \"$API_URL/api/auth/me\" | jq -e --arg email \"$email\" '.user.email == $email' >/dev/null\n\n# Convert the cookie jar to Playwright storage-state JSON. Curl's Netscape\n# format does not carry SameSite, so adapt this field to your app's cookie.\nawk -F '\\t' '\n { httponly = \"false\" }\n /^#HttpOnly_/ { sub(/^#HttpOnly_/, \"\"); httponly = \"true\" }\n /^#/ || NF < 7 { next }\n { print $6 \"\\t\" $7 \"\\t\" $1 \"\\t\" $3 \"\\t\" $5 \"\\t\" httponly \"\\t\" $4 }\n' \"$jar\" | jq -Rn '{\n cookies: [inputs | split(\"\\t\") | {\n name: .[0], value: .[1], domain: .[2], path: .[3], expires: (.[4] | tonumber | if . == 0 then -1 else . end),\n httpOnly: (.[5] == \"true\"), secure: (.[6] == \"TRUE\"), sameSite: \"Lax\"\n }],\n origins: []\n}'\n```\n\nIf the app stores its access token in local storage, include\nan `origins` entry for the exact copy origin, with `{name, value}` pairs in\n`localStorage`. Do not print secrets to stderr, write state to a normal action,\nor use a fixed browser profile. Cofactor validates the returned state against\nthe current copy's public origins and keeps it private to that execution.\n\nWHEN THE SCRIPT FAILS\n\nWrite diagnostics to stderr: stdout is the state and is never shown. When the\nscript exits non-zero (or runs past its 60 s limit), the Login step fails with\nwhich persona, the exit code and the end of the script's stderr, for example:\n\n sandbox authentication failed: script_failed (persona \"admin\": sandbox/auth.sh exited 127). Its stderr ended:\n sandbox/auth.sh: line 4: curl: command not found\n\nEvery org member can read that error. Only the copy's known values (secrets,\nsecret params, persona passwords) are scrubbed from it, verbatim: a session\ncookie, token or login response the script prints is shown as is. So print\nwhat failed, never a response body, `curl -v` or `set -x` trace.\n\nExit 127 is a command the image does not have: install it in the image, or\nuse what it has. `cofactor sandbox verify-env` signs every persona in on a\nfresh claim and reports the same message per persona, so run it after every\npush that changes the image or the script, before a test depends on it.\n\nTHIRD-PARTY IDENTITY PROVIDERS\n\nFor ordinary application tests, create the app's session directly. Use the\nsandbox-only login API, a signed test token accepted only by the copy, an SDK,\nor the provider's emulator-backed token exchange. The auth action should\nfinish with the application's own session cookie or local-storage token. A\nflow that tests login itself must begin clean: retain its application login\nnode with `performLogin: false` to select the app and environment, without\nseeding or preloading an app session. Then navigate to the app and traverse\nits real sign-in UI against the declared provider emulator.\n\nGoogle OAuth requires interception on both sides. Cofactor intercepts the\nbrowser authorization request, including an app HTTP redirect to Google,\nand answers it from the emulator: its account chooser, or a code for the\ncopy's callback when the request's login_hint matches a persona. The\ndeclared sandbox emulator handles the app's token exchange and user-info\nrequests. Verify the complete callback and resulting app identity; an\nintercepted browser redirect alone does not prove the server handshake\nworks.\n\nGOOGLE SIGN-IN AS A PERSONA (authentication.emulated)\n\n When the app's only login is \"Sign in with Google\", there is no password\n endpoint for a script to call. Declare `emulated` instead of `run`, and\n Cofactor signs the persona in itself, inside the copy, with no script:\n authentication:\n emulated:\n provider: google-oauth\n sign_in: ${params.web_url}/sign-in\n\n `authentication` is exactly one of `run` or `emulated`; both, neither key\n inside it, or an unknown key fails validation. `provider` must be\n `google-oauth` or `oidc` (this section describes `google-oauth`; for `oidc`\n see STANDARD OIDC below) and must be declared under `emulation`. `sign_in` is the app URL that starts the Google\n sign-in: `${params.<name>}/path`, where the param is a `{port: ...}`\n param on a PUBLIC port, because the sign-in starts on the copy's public\n origin. Use the port that serves your sign-in route, for example\n `${params.api_url}/auth/google` when the API starts the flow and the\n web app only links to it, or `${params.web_url}/sign-in` for a single-port\n app. A bare path, an undeclared param or a private-port param fails\n validation.\n\n A complete definition that passes `cofactor sandbox validate`:\n\n version: 1\n resources: { vcpus: 2, memory_mib: 4096 }\n setup: |\n apt-get update -qq\n apt-get install -y -qq curl unzip >/dev/null\n curl -fsSL https://bun.sh/install | bash >/dev/null\n ports:\n web: { port: 8300, access: public }\n params:\n web_url: { port: web }\n env:\n PUBLIC_URL: ${params.web_url}\n GOOGLE_CLIENT_ID: sandbox\n GOOGLE_CLIENT_SECRET: sandbox\n GOOGLE_REDIRECT_URI: ${params.web_url}/auth/callback\n services:\n app:\n run: bun server.ts\n health: { http: 8300, path: /health }\n reload_on_prepare: true\n personas:\n alice: { email: alice@example.test }\n bob: { email: bob@example.test }\n authentication:\n emulated:\n provider: google-oauth\n sign_in: ${params.web_url}/sign-in\n emulation:\n google-oauth: {}\n lifecycle:\n build:\n - name: install\n run: bun install\n inputs: [bun.lock, package.json]\n prepare: []\n environments:\n default: { description: Stock data. }\n\n `setup` and `lifecycle.build` are for GitHub-delivered definitions, as in the\n `definition` topic; a pushed image omits both (it is already built).\n\n What happens for a Login step as persona `bob`:\n 1. The helper GETs `sign_in` on the copy's public URL and follows the\n redirects with a cookie jar (at most 10).\n 2. At the hop to accounts.google.com, which resolves to the emulator\n inside the VM, it adds `login_hint=bob@example.test`. The emulator\n completes the authorize request for that persona without showing\n the chooser.\n 3. The emulator redirects to the app's callback; the app exchanges the\n code and sets its session cookie.\n 4. The cookies for the copy's public origin become the Playwright\n storage state, validated like the output of `authentication.run`.\n\n Every redirect hop must stay on the origins of your public `{port}`\n params or on accounts.google.com; a hop anywhere else fails the\n sign-in. Success needs the provider hop AND a cookie set for the copy\n after it, so a pre-provider CSRF or state cookie is not mistaken for a\n session. The\n Login node, persona switching and landing check are the same as for\n `authentication.run`. A failed emulated sign-in shows the Login step\n error `sandbox authentication failed: script_failed (persona \"bob\": the\n sandbox helper reported <reason>)`, where the reason is one of\n `sign_in_unreachable`, `sign_in_rejected`, `sign_in_no_session`,\n `callback_origin` or `invalid_state`. Look for what a\n customer can fix: the app redirects to an origin the definition does not\n declare as a public `{port}` param; `sign_in` never reaches Google; the\n service that calls Google lacks `reload_on_prepare` or does not trust\n /etc/emulation/ca.crt. The Login step can also fail with\n `state_origin_mismatch` when the app sets a cookie for a parent Domain\n outside the copy's public origins. The specific failure category is\n visible to Cofactor support, and the emulator's own log is in the copy at\n /var/lib/cofactor/logs/emulator.log.\n\n Emulated sign-in covers sign-ins the server starts with HTTP redirects.\n It does not cover Google One Tap, FedCM or popup sign-ins done in\n browser JavaScript, and it does not cover a person using the copy in\n their own browser: accounts.google.com is hard-coded in apps and only\n Cofactor's browser (and the VM itself) reaches the emulator for it.\n\n The chooser. When Cofactor's browser agent reaches Google's authorize\n URL, the emulator serves a real account-chooser page listing every\n persona by name and email; clicking one completes the sign-in as that\n persona. A flow that tests the Google journey itself clicks \"Sign in with\n Google\", picks a persona on the chooser, and asserts the signed-in\n identity.\n\n `login_hint` preselects. An authorize request whose `login_hint` equals a\n persona's email (case-insensitive) skips the chooser. This holds even\n when the app also sends `prompt=select_account`, which real Google would\n honor by showing its chooser; that is a deliberate deviation so the\n helper and hint-sending apps sign in without a click. An absent or\n unmatched hint shows the chooser.\n\n Emulator limits. Authorization codes are single use, and are bound to the\n `redirect_uri` of the authorize request, and to its `client_id` when both\n the authorize request and the token request body carry client_id: a\n replayed code, a code the emulator did not issue, or a mismatch is\n `invalid_grant`. Send the same `redirect_uri` on authorize and on the token exchange, as real Google\n requires. The emulator does NOT check that `redirect_uri` is registered\n anywhere; any http(s) URL is accepted, so a wrong callback is caught only\n by your app.\n\nGOOGLE CALLBACK CONFIGURATION\n\n Supply the current copy's callback using the app's existing configuration:\n params:\n api_url: { port: api }\n env:\n GOOGLE_CLIENT_ID: sandbox\n GOOGLE_CLIENT_SECRET: sandbox\n GOOGLE_REDIRECT_URI: ${params.api_url}/auth/callback\n emulation:\n google-oauth: {}\n personas:\n alice: { email: alice@example.test }\n\n This is a fragment: declare the public api port, use your app's actual\n variable names and callback path, and set reload_on_prepare: true on the\n backend calling Google. An app whose callback comes from configuration\n can use this without auth code changes. A hardcoded production callback\n must become configurable. No per-copy Google Console registration is\n needed for the emulated path; real Google login keeps Google's rules.\n\n Do not add live: true merely to avoid service restarts. That optimization\n requires the app to re-read the live value per request; a normal {port}\n param with reload_on_prepare works for apps that read env at startup.\n\n Google needs both browser authorization interception and sandbox-side\n token/user-info emulation. Verify the app callback and a protected request,\n not just that the browser received an emulated code.\n\n Each persona is a Google account: its email is the Google email, and its\n Google `sub` is derived from the persona name, so it is the same in every\n copy. The identity claims are `sub`, `email`, `email_verified`, `name` and\n `given_name` (both the persona name), plus the id_token's standard\n iss/aud/azp/nonce and time claims. No `family_name`, `picture` or `hd`.\n There is no default Google user; a\n definition that declares `google-oauth` with no personas fails validation.\n\n If Google is reached through hosted Auth0, Clerk or another intermediary,\n this Google emulator does not emulate that intermediary. If the app lets\n you configure its own OIDC provider (an issuer or authority setting), use\n the `oidc` emulator instead: see STANDARD OIDC below. Otherwise inspect where\n token exchange occurs. Prove the customer's actual library and trust setup;\n do not disable token verification or alter production auth to pass a test.\n Non-Node clients must trust /etc/emulation/ca.crt through their runtime's\n supported mechanism. This native Login-node contract does not inject\n authentication into an external Playwright suite.\n\nSTANDARD OIDC (Auth0, Okta, Cognito, Keycloak, Clerk, ...)\n\n For an app that signs in through any standard OpenID Connect provider,\n declare the `oidc` emulator and point the app's issuer at it. The emulator\n is a generic OIDC provider, not a copy of one vendor: use it with the\n vendor's own SDK or any OIDC library, as long as the app's issuer (or\n authority, domain or base URL) setting is configurable.\n\n This is a fragment: declare the public web port, use your app's actual\n variable names and callback path, and set reload_on_prepare: true on the\n service that reads the issuer.\n params:\n web_url: { port: web }\n issuer_url: { port: emulation }\n env:\n OIDC_ISSUER: ${params.issuer_url}\n OIDC_CLIENT_ID: sandbox\n OIDC_CLIENT_SECRET: sandbox\n OIDC_REDIRECT_URI: ${params.web_url}/auth/callback\n services:\n app:\n run: bun server.ts\n health: { http: 8300, path: /health }\n reload_on_prepare: true\n personas:\n alice: { email: alice@example.test }\n emulation:\n oidc: {}\n\n `issuer_url: { port: emulation }` is the copy's public emulator address and\n is the issuer, exactly: an https origin with no trailing slash, with a real\n TLS certificate. The app needs no CA and no /etc/hosts entry, and a\n library that refuses an http issuer is satisfied. Point the vendor SDK's\n issuer, authority or domain setting at it (`OIDC_ISSUER` above); the SDK\n discovers everything else from\n `<issuer>/.well-known/openid-configuration`. The service that reads the\n issuer needs `reload_on_prepare: true`, because the address is per copy.\n\n Endpoints: /.well-known/openid-configuration, /authorize, /token,\n /userinfo (GET and POST), /jwks.\n\n Client id and secret are accepted as-is: use any values, such as\n `sandbox`. The token endpoint takes client credentials in the body or by\n HTTP Basic and never checks the secret. PKCE is advertised (S256 and\n plain) so libraries that send it proceed, but the challenge is not\n verified.\n\n Redirect rule. The `redirect_uri` on /authorize must be an http(s) URL on\n one of the copy's public origins (any public `{port}` param of the\n definition); anything else is a 400 and nothing is redirected. Use the\n port that serves the callback, as in GOOGLE CALLBACK CONFIGURATION.\n\n Each persona is an account: its email is the account email. Claims, in the\n id_token and from /userinfo, are `sub` (stable per persona, the same in every\n copy), `email`, `email_verified` (true) and `name`, plus the id_token's\n standard iss/aud/azp/nonce and time claims. Nothing else: no groups, roles,\n `picture` or vendor-specific claims, so an app that depends on those needs\n its own handling in the sandbox. Authorization codes are single use and are\n issued only by /authorize; a replayed or never-issued code is `invalid_grant`,\n as is a different `redirect_uri`. The id_token is signed RS256 with a key\n published at /jwks.\n\n The sign-in link. /authorize is locked: signing in needs a browser that has\n opened the copy's sign-in link. Without it, /authorize shows a page saying to\n open the sandbox's sign-in link from Cofactor (HTTP 403) and the app is not\n signed in. The link is `<emulation origin>/oidc/enter?key=...`. Opening it\n sets a browser-session cookie and shows \"Sign-in is unlocked\"; after that the\n app's Sign in button reaches the account chooser, which lists each persona by\n name and email, and picking one signs in as that persona. The unlock lasts\n until that browser session ends; the link itself works for the copy's life,\n so reopen it if you land on the locked page again. `cofactor sandbox claim` and `cofactor sandbox show <id>`\n print the link as `Open sign-in: ...` next to the persona logins, and the\n sandbox API returns it as `signInUrl`. Only organization members who can read\n sandboxes see it. A preview has no sign-in link. Treat the link like the\n persona passwords: it is a credential for that copy. `--json` output includes\n it, so keep that output out of CI logs.\n\n Cofactor's own browser agent and native Login steps pass the gate\n automatically, so a flow that tests the sign-in journey needs no link step.\n\n Login steps. `authentication.emulated` with `provider: oidc` signs a persona\n in with no script:\n authentication:\n emulated:\n provider: oidc\n sign_in: ${params.web_url}/login\n\n `oidc` must be declared under `emulation`, and `sign_in` follows the same\n rules as for Google (`${params.<name>}/path` on a public `{port}` param).\n Inside the copy the helper GETs `sign_in`, follows the redirects with a cookie\n jar, and at the hop to the issuer's /authorize adds the persona's\n `login_hint` and the gate; the emulator redirects to the app's callback, the\n app sets its session, and the cookies become the storage state. Every\n redirect hop must stay on a public `{port}` origin of the definition or the\n issuer, and a session cookie must be set after the issuer hop. Failures are\n reported as for Google, and the emulator's own log is in the copy at\n /var/lib/cofactor/logs/emulator.log. The helper requires the issuer to be\n https.\n\n A complete definition that passes `cofactor sandbox validate`:\n\n version: 1\n resources: { vcpus: 2, memory_mib: 4096 }\n setup: |\n apt-get update -qq\n apt-get install -y -qq curl unzip >/dev/null\n curl -fsSL https://bun.sh/install | bash >/dev/null\n ports:\n web: { port: 8400, access: public }\n params:\n web_url: { port: web }\n issuer_url: { port: emulation }\n session_secret: { secret: true }\n env:\n OIDC_ISSUER: ${params.issuer_url}\n OIDC_CLIENT_ID: sandbox\n OIDC_CLIENT_SECRET: sandbox\n OIDC_REDIRECT_URI: ${params.web_url}/auth/callback\n SESSION_SECRET: ${params.session_secret}\n services:\n app:\n run: bun server.ts\n health: { http: 8400, path: /health }\n reload_on_prepare: true\n personas:\n alice: { email: alice@example.test }\n bob: { email: bob@example.test }\n emulation:\n oidc: {}\n authentication:\n emulated:\n provider: oidc\n sign_in: ${params.web_url}/login\n lifecycle:\n build:\n - name: install\n run: bun install\n inputs: [bun.lock, package.json]\n prepare: []\n environments:\n default: { description: Stock data. }\n\n The sample app in the Cofactor repository, packages/demo-app-oidc, is a\n working example using the `openid-client` library.\n\nREFERENCES\n\n https://docs.runcofactor.com/reference/sandbox-definition#authentication\n https://docs.runcofactor.com/reference/sandbox-emulation#google-oauth-and-third-party-login\n cofactor sandbox guide v2 personas\n cofactor sandbox guide v2 emulation\n cofactor sandbox guide v2 ports\n","An environment's `env` block (under `environments.<name>.env`) overrides\nenvironment variables for exactly three things:\n\n - running services that declare `reload_on_prepare: true` — they are\n restarted by `prepare` (and by an in-place move to a new commit), so\n they always see the current chain's merged env;\n - `lifecycle.prepare` commands, run fresh on every prepare;\n - actions, which run with the prepare env plus that call's\n `INPUT_<NAME>` values.\n\n environments:\n beta-features:\n extends: default\n description: Same as default, with the timeline view turned on.\n env:\n FEATURE_FLAGS: timeline\n\nIt does NOT reach:\n\n - `lifecycle.build` steps. Build steps run once into the repo-base layer,\n before any environment is chosen, so an environment's `env` does not\n exist at build time. A value your build bakes into its output (e.g. a\n front-end bundle's API URL or feature flags) can't vary by\n environment: read it at serve time instead, or set it with a seed\n action.\n - services that don't declare `reload_on_prepare`. A service without it\n only ever sees the definition's top-level `env` and layer-time param\n values (from when its layer was built) — it is not restarted when a\n sandbox is prepared, so a per-environment override would never take\n effect for it.\n\nAn environment's `env` values are used as written: `${params.name}` is\nsubstituted only in the definition's top-level `env`. To vary a value per\nenvironment that must not live in the repository (an API key), use a\nsecret scoped to that environment: see the `secrets` topic.\n\n`env` MERGE ORDER\n\n Composition follows the same parent-then-child rule as seeds (see\n `environments`): the resolved chain's env is the parent's `env` merged\n first, then the child's own, so a child environment can override a\n specific key its parent sets without having to repeat the rest. Secrets\n (see the `secrets` topic) merge last, above every `env`.\n\nRELATION TO THE DEFINITION'S TOP-LEVEL `env`\n\n The definition's top-level `env` (outside any environment) is the base:\n fixed values and `${params.name}` substitutions that apply regardless of\n which environment is claimed — `DATABASE_URL`, `PUBLIC_URL` and so on in\n the example app. An environment's `env` is layered on top of that base\n for the reachable surfaces above, not a replacement for it.\n","A secret is a real third-party credential your app needs at run time: a\nStripe or OpenAI key, an email provider's API key, an outside service's\ntoken. Cofactor stores it encrypted per application and hands it to the\nsandbox's processes as an environment variable. It is never written into\nthe definition or your image, and no command prints it back.\n\nWHICH KIND OF VALUE IS IT?\n\n A shared constant (a database URL, a feature name)\n -> `env:` in the definition. See the `env` topic.\n A URL that differs per sandbox (the app's own public origin)\n -> a `{port: <name>}` param. See the `definition` topic.\n A value generated for you (a session-signing key)\n -> a `{secret: true}` param. One random value is made when the ready\n snapshot is built and shared by every copy of that snapshot.\n A real third-party secret\n -> the secret store, described below.\n\n Short non-secret values belong in `env:`: a value under 8 bytes is not a\n credential, and the store refuses it.\n\nUSE THE PROVIDER'S TEST KEYS\n\n Sandbox egress is open, except for the domains the definition's\n `emulation` intercepts (see the `emulation` topic): a call your app makes\n in a sandbox goes out to the real provider. Set the provider's test or\n sandbox key (Stripe's `sk_test_...`, a sandbox API key), never a live\n one. Never put a key in the image, a `.env` file or\n `cofactor.sandbox.yaml`: those end up in layers, snapshots and the\n repository.\n\nSETTING ONE\n\n The value comes from stdin only, so it never lands in shell history or\n `ps`. The CLI refuses to read it from a terminal.\n\n printf %s \"$STRIPE_TEST_KEY\" | cofactor sandbox secret set STRIPE_API_KEY --application shop\n\n That is one secret for all of the application's environments. To give\n one environment a different value, name it:\n\n printf %s \"$STRIPE_STAGING_KEY\" | cofactor sandbox secret set STRIPE_API_KEY --application shop --environment staging\n\n See what is set (names and scopes, never values):\n\n cofactor sandbox secret list --application shop\n\n NAME SCOPE UPDATED\n STRIPE_API_KEY environment staging 2026-10-08T09:13:41.000Z\n STRIPE_API_KEY all environments 2026-10-08T09:12:00.000Z\n\n Names are capital letters, digits and `_`, not starting with a digit,\n and must not be PATH, HOME, USER, SHELL, NODE_OPTIONS,\n NODE_EXTRA_CA_CERTS, `LD_*` or `COFACTOR_*`. A value is 8 to 8192\n bytes, with no NUL. The environment, when given, must be one the\n application's synced definition has.\n\nOVERRIDE AND INHERITANCE\n\n A secret with no `--environment` applies to every environment. One with\n `--environment` is an override that wins for that environment. Resolution\n follows the same chain as `env:` and seeds (see `environments`): the\n application-wide value, then each ancestor's override, then the claimed\n environment's own, last one wins. An override on a parent applies to\n every environment that extends it; a child's own override beats it.\n\n A secret also overrides an `env:` entry of the same name.\n\nREACHING THE APP\n\n Secrets are injected into:\n - services that declare `reload_on_prepare: true`;\n - `lifecycle.prepare` commands;\n - seeds and actions;\n - `authentication.run`.\n They are NOT injected into `lifecycle.build` steps or `setup`:\n build-time secrets are unsupported (a build bakes its output into a\n layer that is shared). And a service WITHOUT `reload_on_prepare: true`\n never sees a secret, not even once: set it on every service that reads\n one.\n\n # cofactor.sandbox.yaml: the key itself is not here, only the service\n # that reads it.\n services:\n api:\n run: node server.js\n health: { http: 3001, path: /healthz }\n reload_on_prepare: true # reads STRIPE_API_KEY; without this it never sees it\n\n environments:\n staging:\n extends: default\n description: Same as default, with the staging Stripe account.\n\n The app reads `process.env.STRIPE_API_KEY`; nothing in the definition\n names the secret.\n\n A secret can also be served through the `aws` emulator, for an app that\n fetches its real third-party key from AWS Secrets Manager at run time\n instead of reading an env var: `app/prod/ai-key: \"${secrets.AI_API_KEY}\"`\n under `emulation.aws.secrets_manager.secrets`. Sandbox-internal values (a\n local database password) are `{secret: true}` params, not store secrets.\n See the `emulation` topic.\n\nROTATION\n\n Set the new value with `secret set`, or remove it with `secret rm`.\n Neither operation rebuilds an existing ready snapshot.\n - Fast copies keep the snapshot's baked secret values, including a\n rotated or removed value, until that snapshot is rebuilt. A new\n commit or image push builds a snapshot with the current values.\n - Claims on the legacy prepare path pick up changes immediately:\n prepare reads the current store, omits removed secrets, and restarts\n `reload_on_prepare` services when values change. Prepare commands,\n actions and `authentication.run` then see those current values.\n - A sandbox that is already claimed keeps the values it was prepared\n with until it is destroyed. A pull request preview keeps the values\n of its first prepare (later pushes move it in place without\n preparing again), and is scrubbed of the application's current\n values; a value rotated away since that first prepare is not\n scrubbed.\n - Seeds ran once, when the ready snapshot was built, with the value\n that existed then. Anything a seed created in a third-party system\n with a since-rotated key stays as created. A new commit or push\n builds a fresh snapshot with the current values.\n\n So set secrets BEFORE the first claim: the first snapshot's seeds run\n with whatever is set at that moment.\n\n The ready snapshot is private to your organization, but the processes\n running when it was captured hold the values set when the snapshot was\n built, so the snapshot contains them: the snapshot's `prepare.json`\n holds the values. Layers built before prepare (platform, toolchain,\n repo base) never do.\n\nWHAT IS SCRUBBED\n\n Every injected value is replaced with `***` in action output and tails,\n seed outputs and failed-build messages. Object keys and numeric values\n equal to a secret are scrubbed too. The match is exact only:\n - a value your app prints transformed (base64, URL-encoded,\n JSON-escaped, split across lines) is not scrubbed;\n - a multi-line value (a private key) is scrubbed only when printed\n whole;\n - an action's `output` keeps the action's own `sensitive` inputs: only\n injected secrets are scrubbed from it;\n - values your app renders into a page are not scrubbed from\n screenshots, traces or page text.\n Don't print a secret, and don't render one into a page.\n","Emulation makes third-party services deterministic and inspectable: instead\nof a sandbox's outbound calls to an email provider, Google, Jira or Slack\nleaving the VM, they're intercepted by an in-sandbox emulator that behaves\nlike the real thing well enough for tests, and lets a test look at what\nwas sent.\n\nPLUGINS\n\n Declaring a plugin id under `emulation:` turns on its interception and\n adds it to what `envs` reports.\n emulation:\n email: {}\n\n Available plugin ids:\n aws Serves AWS service APIs to the app's own, unmodified AWS\n SDK: no endpoint override, no code change. Only the\n services you give a section are intercepted, in each of\n `regions` (default [us-east-1]). Today: Secrets Manager,\n KMS and SSM Parameter Store (see AWS below). SES stays\n with `email`.\n email Intercepts the HTTP send APIs of several providers\n (SES, SendGrid, Resend, Postmark, Mailgun) so outbound\n mail is captured instead of actually sent. SES is\n intercepted in us-east-1 unless you list others:\n `email: { sesRegions: [eu-west-1] }`; add other hosts\n with `extraDomains`. Mail through a host that isn't\n intercepted leaves the sandbox for real.\n `email: { mailosaur: {} }` also serves the Mailosaur\n API from what was captured (see MAILOSAUR below).\n SMTP (nodemailer, Django's SMTP backend, Action\n Mailer…) is not intercepted. Leave SMTP unconfigured\n in the sandbox so the app sends nothing, or ship an\n SMTP catcher such as Mailpit in the image, run it as\n one of your `services`, and point the app's SMTP host\n at 127.0.0.1.\n google-oauth Intercepts Google's OAuth endpoints, for testing login\n flows without a real Google account. Every declared\n persona is a Google account (its email is the Google\n email), so the definition must declare at least one\n persona.\n oidc A standard OpenID Connect provider for apps that sign in\n through Auth0, Okta, Cognito, Keycloak, Clerk or any\n other OIDC provider whose issuer the app lets you\n configure. It takes no options: `oidc: {}`. Its\n address is the copy's public `emulation` URL, which\n is the issuer; point the app's OIDC issuer setting at\n `${params.issuer_url}` with `issuer_url: { port:\n emulation }`. Every declared persona is an account,\n so the definition must declare at least one persona.\n See `cofactor sandbox guide v2 authentication`.\n jira Intercepts the Jira Cloud REST API of your site:\n `jira: { tenant: acme }` serves acme.atlassian.net, plus\n api.atlassian.com (the OAuth gateway) and\n auth.atlassian.com. `tenant` is required (validate\n doesn't check it). The app registers its own webhook\n through the emulated API; operations `jira.create-issue`,\n `jira.add-label` (delivers the tag webhook to what the app\n registered), `jira.get-issue` and `jira.get-comments`\n drive and read it. Details: docs/api/services/\n sandbox-emulation.md.\n slack Intercepts Slack's API for posted-message flows.\n\n `cofactor sandbox envs` lists each declared plugin's operations.\n\nAWS\n\n Seed the secrets an app reads from Secrets Manager at startup. A string is\n the SecretString as is; an object is served as its JSON. Values that are\n sandbox-internal (the in-sandbox database's password, a connection\n string) are plain values or `{secret: true}` params, referenced as\n `${params.NAME}` (only `aws` resolves references; one in another plugin's\n config fails validate):\n params:\n db_password: { secret: true }\n env:\n DB_PASSWORD: ${params.db_password} # what the local database is set up with\n AWS_ACCESS_KEY_ID: emulated # any credentials work; the SDK\n AWS_SECRET_ACCESS_KEY: emulated # needs some to sign a request\n emulation:\n aws:\n regions: [us-east-1] # optional; the default\n secrets_manager:\n secrets:\n app/prod/db: { host: 127.0.0.1, username: app, password: \"${params.db_password}\", dbname: app }\n app/prod/feature-flags: \"on\"\n\n If the app fetches a real third-party key (an AI provider's or payment\n provider's test key) from Secrets Manager at run time, keep that key in the\n secret store (see the `secrets` topic) and reference it as\n `${secrets.NAME}`; the definition only names it:\n app/prod/ai-key: \"${secrets.AI_API_KEY}\"\n Local values do not belong in the secret store: it is for real third-party\n credentials only.\n\n References are resolved inside the sandbox when prepare runs, with the\n environment's values (a secret's environment override wins, as for env).\n Fast copies keep the snapshot's config and secret values until it is rebuilt\n by a new commit or image push; legacy prepare claims see changes on the next\n claim. A `${secrets.NAME}` that is not set for the environment fails prepare,\n naming the secret, so the snapshot build or legacy prepare claim fails.\n A `${params.NAME}` must name a declared param (validate checks it).\n Resolved values reach only the emulator: they are\n not written into the definition, not printed, not recorded with the\n emulator's requests, and not returned by `aws.secrets`.\n\n Served: GetSecretValue (by name, full ARN or ARN without its suffix;\n VersionStage AWSCURRENT), BatchGetSecretValue, DescribeSecret and\n ListSecrets (Filters on `name` and `all`, MaxResults/NextToken). An\n unknown name is AWS's ResourceNotFoundException. ARNs look like\n arn:aws:secretsmanager:us-east-1:000000000000:secret:app/prod/db-1a2b3c\n and, like version ids, are the same in every copy. Secrets are read-only:\n CreateSecret, PutSecretValue and other writes answer 501.\n\n KMS. Ciphertext made by real KMS (say, a data key hardcoded in the app)\n cannot be decrypted in a sandbox, so seed what it decrypts to, both base64:\n kms:\n decrypt:\n - ciphertext: AQICAHh...base64...\n plaintext: \"${params.aes_key}\" # or a base64 literal\n key_id: alias/app # optional: the KeyId Decrypt answers with\n `kms: {}` alone serves Encrypt, Decrypt and GenerateDataKey under one\n emulated key (any KeyId is accepted), so an app that encrypts at run time\n round-trips, including what the snapshot's seeds encrypted: the key is\n generated inside the sandbox when the snapshot is built and is never shown.\n Unknown ciphertext is AWS's InvalidCiphertextException; a different\n EncryptionContext fails the same way (seeded ciphertext ignores the\n context). A referenced plaintext must resolve to base64, or the claim fails\n naming the field. Other KMS calls answer 501.\n\n SSM PARAMETER STORE. Seed parameters by name; `type` is String (the\n default), StringList or SecureString, and values take references too:\n ssm:\n parameters:\n /app/prod/feature-x: { value: \"on\" }\n /app/prod/db-password: { value: \"${params.db_password}\", type: SecureString }\n Served: GetParameter (by name or ARN), GetParameters (unknown names come\n back in InvalidParameters), GetParametersByPath (one level, or every depth\n with Recursive; MaxResults/NextToken) and DescribeParameters\n (ParameterFilters on Name, Type and Path). A SecureString's value is\n returned only WithDecryption; without it the value is an opaque stand-in.\n Every parameter is version 1; version and label selectors (`name:1`) are\n not supported. An unknown name is AWS's ParameterNotFound. Writes\n (PutParameter, ...) and other filters answer 501. Operation\n `aws.parameters` lists names, types, versions, ARNs and read counts,\n never a value.\n\n The SDK still needs credentials (any value) and a region: set them in\n `env:` unless the app or a declared `object_storage: { provider: s3 }`\n already does. Operation `aws.secrets` lists each secret's name, ARNs,\n version id and how often the app read it, never a value.\n\nMAILOSAUR\n\n A test suite that reads the app's mail through Mailosaur (its API or an\n official SDK) runs unchanged in the sandbox. Declare the inbox on the\n email plugin:\n emulation:\n email:\n mailosaur: {}\n `mailosaur.com` is then answered inside the sandbox from the mail the app\n sent through any intercepted provider (SES, SendGrid, Resend, Postmark,\n Mailgun). A message addressed to `<anything>@<server id>.mailosaur.net`\n is in that server's inbox; mail to other addresses is in none. Any server\n id works, there is nothing to create. The suite needs no base URL change\n and no code change: keep the SDK's default host, the real server id and\n the real addresses. A Playwright suite Cofactor runs on the sandbox gets\n NODE_EXTRA_CA_CERTS, as actions do, so its Node trusts the emulator (unless\n the suite sets its own NODE_EXTRA_CA_CERTS, which then wins and must\n include /etc/emulation/ca.crt). The\n SDK still needs a key; any value works, so give the suite one in its\n `cofactor.tests.yaml`:\n env:\n MAILOSAUR_API_KEY: emulated\n\n Served (the read side, as the SDKs call it): list messages, search them\n (sentFrom and sentTo match a whole address, subject and body match any\n part, ignoring case; match ALL or ANY; receivedAfter, page, itemsPerPage),\n get one message, delete one, delete all of a server's, list servers and\n get one. The SDKs' wait-for (`messages.get`, search with a timeout) works\n as against Mailosaur, polling until the mail arrives. A message carries\n from, to, cc, bcc, subject, received, the html and text bodies with their\n links (html `<a>` links, text URLs), html images, verification codes\n (each standalone run of 4 to 8 digits, or 4 to 8 upper-case letters and\n digits with both, in order of appearance, never inside a URL; a year in\n a footer counts too), attachment names and types, and From/To/Subject/\n Date headers. Deleting hides a message from the Mailosaur inbox only:\n `email.sent-emails` still returns it, and `email-reset` clears both.\n The emulator keeps the last 500 emails the app sent, to any address, so\n in a long run the oldest leave the inbox.\n\n Not emulated (501, recorded): creating servers or messages, forward,\n reply, attachment and raw-message downloads, spam and deliverability\n analysis, previews, devices (TOTP) and usage; SMS; mail the app sends\n over SMTP (see email above).\n\nHOW INTERCEPTION WORKS\n\n Most plugins own real domains. Inside the sandbox, those domains (e.g.\n `email.us-east-1.amazonaws.com` for the email plugin's SES routes, or\n `accounts.google.com` for `google-oauth`) are resolved via `/etc/hosts` to\n the emulator instead of the real internet. The emulator serves them over\n real TLS, using a CA generated when the version's ready snapshot is built\n (every copy shares it). It survives an emulator restart, so services that\n read `NODE_EXTRA_CA_CERTS` once at startup don't break when the emulator\n restarts. Services with `reload_on_prepare: true`, prepare commands and\n actions get `NODE_EXTRA_CA_CERTS=/etc/emulation/ca.crt` so they trust\n that CA. The CA is also added to the system trust store when the image\n has `update-ca-certificates` (Debian, Ubuntu, Alpine with the\n ca-certificates package), which .NET, Go, curl and OpenSSL-based clients\n read; on an image without it, a non-Node runtime must trust\n /etc/emulation/ca.crt itself. Some clients carry their own bundle and\n ignore the system store; point them at the system bundle in `env:`:\n boto3/botocore with `AWS_CA_BUNDLE: /etc/ssl/certs/ca-certificates.crt`,\n Python `requests` with `REQUESTS_CA_BUNDLE` (same path). Java reads its\n own keystore, updated by the same command only when the image has\n ca-certificates-java. A service WITHOUT `reload_on_prepare` never gets it (nor any\n emulation env): its calls to an emulated domain fail TLS. Any service that\n talks to an emulated provider must be `reload_on_prepare`.\n\n `oidc` owns no domain and needs neither `/etc/hosts` nor the CA. It is\n reached at its public URL (the copy's `emulation` port), which has a\n publicly trusted certificate, so any client reaches it the way it reaches a\n real provider. The other plugins keep using `/etc/hosts` and the CA.\n\n NO PASSTHROUGH. An unmatched path on an emulated domain doesn't\n fall through to the real provider — it returns 501 and the attempt is\n recorded, so a route the app calls that no plugin has implemented yet\n fails loudly and visibly instead of silently reaching a real third\n party.\n\n The emulator depends on `openssl` for certificate generation. For a\n GitHub-delivered app, the platform installs it before your `setup`\n runs, so a definition doesn't need to install it itself; a pushed image\n that declares `emulation` must include it (`apt-get install -y\n openssl`), or the emulator never turns healthy and prepare fails.\n\nOPERATIONS\n\n Each plugin declares typed operations — the same shape as an action\n (id, description, typed input, typed output) — that appear right alongside\n your definition's own actions, named `<plugin>.<op>`. They run exactly\n like an action: `cofactor sandbox run <id> <plugin.op>`, or as a seed\n step (`action: email.sent-emails`). Cofactor calls the emulator's control\n plane directly rather than shelling into the sandbox for these.\n\n Today: `aws.secrets` and `aws.parameters` — no input, return the seeded\n secrets' / parameters' names, ARNs, versions and read counts (never\n values). `email.sent-emails` — no input, returns every email captured since\n boot or the last reset. In the example app, it verifies that\n `invite-user` actually sent the invitation:\n cofactor sandbox run <id> invite-user \\\n --input workspace_id=<acme.workspace_id> \\\n --input email=new@example.test --input role=member --input as=owner\n cofactor sandbox run <id> email.sent-emails\n\n\nAUTHENTICATION AND PROVIDER LOGIN\n\n For normal persona-based flows, have `authentication.run` create the\n application's session; see `cofactor sandbox guide v2 authentication`.\n For a flow testing Google login, start without that pre-authenticated state\n and use the `google-oauth` emulator through the app's actual login button.\n This avoids treating a successful pre-authenticated landing as proof of the\n provider redirect and callback.\n\n For Google callback configuration, app-code compatibility, the account\n chooser and `authentication.emulated` (Cofactor signs a persona in through\n Google with no script), read `cofactor sandbox guide v2 authentication`.\n Each persona is a Google account; a definition declaring `google-oauth`\n with no personas fails validation: \"each persona is a Google account;\n declare at least one\". Google browser authorization and the backend\n token/user-info handshake both need to work; verify the full signed-out\n journey. For apps that use a standard OIDC provider (Auth0, Okta, Cognito,\n Keycloak, Clerk...), declare `oidc` instead; see the STANDARD OIDC\n section of `cofactor sandbox guide v2 authentication`. Hosted identity intermediaries are not covered by declaring\n google-oauth alone, and neither are One Tap, FedCM or popup sign-ins or\n a person using the copy in their own browser.\n\n Reference:\n https://docs.runcofactor.com/reference/sandbox-emulation#google-oauth-and-third-party-login\n","An app that stores files (uploads, attachments, exports) can have an\nemulated S3 or Google Cloud Storage server in every sandbox, instead of\nreaching a real bucket. You declare the provider and the buckets; the\nsandbox runs the server, creates the buckets, and gives your app the\nstandard client settings for that provider.\n\nDECLARING IT\n\n object_storage:\n provider: s3 # or gcs\n buckets: [attachments, exports]\n\n - One provider per app. Bucket names are 3–63 lowercase letters,\n digits, `-` and `.`, starting and ending with a letter or digit, with\n no `..` and not shaped like an IP address. A GCS bucket can't be\n named `storage`, `upload`, `download` or `batch`.\n - The buckets exist before your first prepare command runs.\n - Declaring it adds an implicit public port named `object-storage` (you\n don't declare it yourself, and you can't name a port of your own\n `object-storage`). It counts toward the 8-port limit.\n - The server listens on port 9000 (S3), or 4443 and 4444 (GCS);\n a port of your own can't use those numbers, and a service of your own\n can't be named `object-storage` or `object-storage-front`.\n\n For a pushed image, `cofactor sandbox push` adds the declared provider's\n server to the image; you don't install anything.\n\nYOUR DATA IN EACH SANDBOX\n\n Each sandbox gets its own copy of the buckets. Files your seeds put there\n (through an action, below) are captured in the ready snapshot with the\n rest of the sandbox, so every claim starts with them. A test that\n uploads a file changes only its own sandbox, and the data is gone when\n the sandbox is destroyed.\n\nTHE ENV YOUR APP GETS\n\n Every service, prepare command and action runs with the provider's\n standard client variables:\n\n s3 AWS_ENDPOINT_URL_S3 http://127.0.0.1:9000\n AWS_ACCESS_KEY_ID cofactor-sandbox\n AWS_SECRET_ACCESS_KEY cofactor-sandbox-secret\n AWS_REGION us-east-1\n\n gcs STORAGE_EMULATOR_HOST http://127.0.0.1:4443\n GOOGLE_CLOUD_PROJECT sandbox\n\n The credentials are fixed and the same in every sandbox. If your app\n reads different names (say `ATTACHMENTS_ENDPOINT`), set them in your\n definition's `env:`, which also overrides any of the above (an\n environment's `env:` too; your image's own ENV does not):\n env:\n ATTACHMENTS_ENDPOINT: http://127.0.0.1:9000\n ATTACHMENTS_BUCKET: attachments\n\nS3: CLIENT SETTINGS\n\n AWS SDK v3 (JavaScript) and boto3 1.28 or later read\n `AWS_ENDPOINT_URL_S3` themselves. Your app must use PATH-STYLE\n addressing (`http://host/bucket/key`, not `http://bucket.host/key`):\n\n // AWS SDK v3\n new S3Client({ forcePathStyle: true })\n\n # boto3\n boto3.client(\"s3\", config=Config(s3={\"addressing_style\": \"path\"}))\n\n Older SDKs that don't read `AWS_ENDPOINT_URL_S3`: pass it as the\n client's endpoint yourself.\n\nGCS: CLIENT SETTINGS\n\n Point your client at `STORAGE_EMULATOR_HOST`. Google's Node\n (`@google-cloud/storage`) and Python (`google-cloud-storage`) clients\n read it themselves and skip authentication when it is set, so `new\n Storage()` and `storage.Client()` work with no credentials. On an older\n Python client that doesn't, pass both yourself:\n storage.Client(\n credentials=AnonymousCredentials(),\n client_options={\"api_endpoint\": os.environ[\"STORAGE_EMULATOR_HOST\"]},\n )\n For another language, set your client's endpoint to\n `STORAGE_EMULATOR_HOST` and give it no (anonymous) credentials.\n\nLINKS A BROWSER OPENS: THE PUBLIC URL\n\n `STORAGE_EMULATOR_HOST` and `AWS_ENDPOINT_URL_S3` are only reachable\n inside the sandbox. A presigned (S3) or signed (GCS) URL your app hands\n to a browser must use the `object-storage` port's public URL instead.\n Its origin is a `{port}` param, which you pass to your app:\n params:\n storage_url: { port: object-storage }\n env:\n STORAGE_PUBLIC_URL: ${params.storage_url}\n\n The service that reads it needs `reload_on_prepare: true`, like any\n service that reads a param (see the `upload` topic).\n\n Anyone with that URL reaches the storage: S3 with the fixed credentials\n above, GCS with no authentication at all. It holds the sandbox's test\n data, like every other port of the sandbox, so never put real data in\n it.\n\n S3: sign the URL against the public origin with a second client used\n only for presigning (signing makes no network call), and keep every\n real read and write on the default one:\n const presigner = new S3Client({\n endpoint: process.env.STORAGE_PUBLIC_URL,\n forcePathStyle: true,\n });\n const url = await getSignedUrl(presigner,\n new GetObjectCommand({ Bucket: 'attachments', Key: key }));\n The name is yours: an app that already has such a setting (e.g.\n `S3_PRESIGN_PUBLIC_ENDPOINT: ${params.storage_url}`) just sets it. The\n public URL preserves the request's Host, so the signature holds, and\n browser uploads through a presigned PUT are allowed from any origin.\n\n GCS: generate signed URLs with the public origin as their host (Node:\n the `host` option of `file.getSignedUrl`; Python: the\n `api_access_endpoint` argument of `blob.generate_signed_url`). Signing\n still needs a credential that can sign, such as a service account key\n file; the emulator doesn't check signatures, so give the sandbox a\n throwaway key, never a real one. Signed downloads and signed uploads\n work. A browser must upload in ONE signed PUT: a resumable upload\n started from a browser is answered with an address inside the sandbox\n (127.0.0.1) that the browser can't reach, so resumable uploads work\n only from inside the sandbox (your app's server, an action).\n\nSEEDING FILES WITH AN ACTION\n\n An action can put files in a bucket, so a seed can start every claim\n with them. If your app records each file in its database (an attachment\n row pointing at the object), go through the app's own upload API\n instead, so the row exists too — the same rule as any other seed (see\n `seeds-and-actions`). For a file the app only reads (a logo, an import\n template), write it straight to the bucket. The examples need only\n curl (7.75 or later, for `--aws-sigv4`):\n\n #!/usr/bin/env bash\n # sandbox/actions/add-template.sh (S3)\n set -euo pipefail\n curl -fsS --aws-sigv4 \"aws:amz:$AWS_REGION:s3\" \\\n --user \"$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY\" \\\n -H 'content-type: text/csv' -T sandbox/files/import-template.csv \\\n \"$AWS_ENDPOINT_URL_S3/exports/templates/import-template.csv\" >&2\n echo '{\"key\": \"templates/import-template.csv\"}'\n\n #!/usr/bin/env bash\n # sandbox/actions/add-template.sh (GCS)\n set -euo pipefail\n query='uploadType=media&name=templates/import-template.csv'\n curl -fsS -X POST -H 'content-type: text/csv' \\\n --data-binary @sandbox/files/import-template.csv \\\n \"$STORAGE_EMULATOR_HOST/upload/storage/v1/b/exports/o?$query\" >&2\n echo '{\"key\": \"templates/import-template.csv\"}'\n\nLIMITS\n\n - One provider per app.\n - GCS: one upload request carries at most 128 MB, a signed PUT from a\n browser included.\n - GCS: a browser uploads only through a single signed PUT (above).\n - The data lives only in the sandbox: nothing is copied from or to a\n real bucket, and it goes when the sandbox is destroyed. It is\n reachable on the public `object-storage` URL: test data only.\n","A sandbox can declare several ports, and each one gets its own public URL:\n ports:\n web: { port: 3000, access: public }\n api: { port: 3001, access: public }\n\nWHY SEVERAL PORTS\n\n Bundling web and API into one sandbox means a single claimed environment\n serves both UI flows (driving the browser against `web`'s URL) and API\n sequences (calling `api`'s URL directly) against the *same* underlying\n data — the workspace, projects and tasks a UI test just created through\n the browser are the same rows an API test can immediately query.\n There's no separate \"API sandbox\" to keep in sync with the \"UI sandbox.\"\n\n`access`\n\n `public` gets a real, routable URL a test or a human can hit from\n outside the sandbox. `private` stays internal — reachable by other\n services in the sandbox, not from outside it.\n\n`{port}` PARAMS\n\n A `params` entry of the form `{port: <name>}` resolves to that port's own\n public URL, so the app (and its build/prepare steps) can be handed its\n own public origin rather than hardcoding it:\n params:\n web_url: { port: web }\n api_url: { port: api }\n env:\n PUBLIC_URL: ${params.web_url}\n API_URL: ${params.api_url}\n\n Each `{port}` param gives the app its own public URL for exactly the port\n it names — `web_url` always resolves to `web`'s URL, `api_url` to `api`'s,\n and so on for however many ports a definition declares.\n\n `{ port: emulation }` is also allowed when the definition declares an\n emulator under `emulation:`. It resolves to the emulator's public origin\n (the implicit `emulation` port), which is the issuer for the `oidc`\n plugin:\n params:\n issuer_url: { port: emulation }\n Without a declared emulator, `emulation` is an undeclared port and\n validation fails.\n\nLIMITS\n\n At most 8 ports total per sandbox — a platform limit. That count\n includes the implicit `emulation` port that gets added automatically the\n moment any plugin is declared under `emulation:` (see the `emulation`\n topic) — it isn't optional and isn't something you declare yourself, but\n it does count against the 8. `emulation` is also a reserved port name: you\n can't declare your own port called `emulation`. The same goes for the\n implicit `object-storage` port that `object_storage` adds (see the\n `object-storage` topic), and the implicit public `sync` port every\n sandbox has, which `cofactor sandbox sync` writes your working tree\n through (see the `local` topic): a definition can declare at most 7 ports\n of its own (fewer with `emulation` or `object_storage`), and `sync` and\n port 7099 are reserved. `cofactor sandbox\n validate` checks the reservations and the 8-port cap. The `sync` port is\n never shown as one of the sandbox's URLs.\n\n Ports 80 and 443 are reserved for the emulator whenever emulation is on —\n a definition can't put its own `web` or `api` port there in that case.\n\nOAUTH CALLBACKS\n\n Map the callback through the application's existing configuration, e.g.\n GOOGLE_REDIRECT_URI: ${params.api_url}/auth/callback. Use the port that\n serves the actual callback, not necessarily the web port. Services that\n read this environment variable at startup need reload_on_prepare: true.\n Read `cofactor sandbox guide v2 authentication` before using live: true or\n configuring Google emulation.\n\n https://docs.runcofactor.com/reference/sandbox-emulation#callback-configuration-and-app-changes\n","Two different checks, at two different costs.\n\n`cofactor sandbox validate [file]`\n\n Offline — no sandbox, no claim. Parses the YAML, runs the full\n schema, and cross-checks everything static analysis can catch: unknown\n actions or operations referenced by a seed, `${id.key}` references to a\n later step or an undeclared output key, an `extends` cycle, a duplicate\n step id across a chain, an unknown emulation plugin, port 80/443 declared\n while emulation is on, and more. Every issue is reported with a dotted\n path to exactly where it is, so a broken definition doesn't need a claim\n to find the problem:\n environments.default.seeds[0].input.workspace_id: references step \"acme\", which has not run yet (or does not exist)\n environments.free-plan.extends: unknown environment \"defualt\"\n ports.emulation: \"emulation\" is a reserved port name\n\n Run this before pushing any change to `cofactor.sandbox.yaml` — it's\n instant and catches most authoring mistakes before they cost a real\n claim.\n\n`cofactor sandbox verify-env --application A [--environment E]`\n\n Actually claims each requested environment of the application's current\n version (or every environment in the definition, if none is named) in turn:\n claim, wait for ready, report every seed step's output and timing, sign\n every persona in as a test's Login step would (when the definition\n declares `authentication`), then destroy. This is the only way to prove an\n environment's seed chain and persona logins actually run end to end —\n `validate` can tell you a reference is well-formed, but only a real claim\n proves the referenced action produces the output at runtime, and that\n `authentication.run` works in the image (a command the image lacks, a\n wrong URL). Each persona prints `signed in`, or FAILED with the script's\n exit code and the end of its stderr; any failure fails the environment.\n\nWHEN A SEED FAILS\n\n Seeds run once per version (commit or push) and environment, while its\n ready snapshot is built. Any step's failure fails that snapshot — the\n builder is destroyed, so there's no partially-seeded sandbox left around\n to poke at — and the claims waiting on it fail with that error. The next\n claim builds the snapshot again, so a seed that fails every time keeps\n failing until a fix is committed or pushed. An infrastructure failure,\n by contrast, is retried by the build, within the claim's time limit\n (`cofactor sandbox claim --timeout`, 2 hours by default).\n\n To debug a failing seed:\n 1. Read the error — the claim (or `verify-env`) reports which step\n failed, its action, and the script's stderr (everything a script\n prints besides its final `{...}` result line goes to stderr, so\n this is where the real error message is).\n 2. Claim a sandbox **without** the failing environment (or on an\n environment that stops short of the failing step) so you have a\n live sandbox to work on.\n 3. Run the failing action by hand against that live sandbox with\n `cofactor sandbox run <id> <action> --input k=v ...`, using the\n same inputs the seed step would have resolved (substitute in the\n actual values for any `${id.key}` references by hand). This lets\n you iterate on the action's script without repeatedly re-claiming.\n 4. Once the action succeeds standalone, fix the seed definition or the\n action script and rerun `verify-env` to confirm the full chain\n works.\n","All commands are under `cofactor sandbox`, and every sandbox belongs to an\napplication (`--application <name|id>`). `--env` is the CLI's *global*\nflag for which Cofactor stage to talk to (`dev` / `prod`) — it has nothing\nto do with a sandbox's environment, which is always `--environment`.\n\ncofactor sandbox validate [file] [--upload]\n Offline schema + cross-check of a `cofactor.sandbox.yaml`. Defaults to\n the definition in the current directory if `file` is omitted. `--upload`\n checks a definition for pushed images (no `setup`, no `lifecycle.build`);\n without it, one built from a GitHub checkout. See the `verify` topic for\n the issue format.\n\ncofactor sandbox connect --application A --repo owner/name [--dir D]\n Builds the application's sandboxes from a GitHub repository: the\n `cofactor.sandbox.yaml` under `--dir` (default: the repo root) at each\n commit. Its environments become the application's sandbox environments\n and its personas the application's personas. A new application name\n creates the application, with its sandbox `default` environment as its\n default environment; there is no base URL to give. It is created only\n after GitHub is connected (`cofactor github connect`), the repository is\n reachable and its definition is valid, so a refusal creates nothing.\n\ncofactor sandbox push --application A <image> [--definition F] [--tag T]... [--wait]\n For an application that gives Cofactor no repository access: pushes a\n built image (with its `cofactor.sandbox.yaml`) as the application's next\n version. A new application name creates the application, as `connect`\n does. See the `upload` topic.\n\ncofactor sandbox envs --application A [--json]\n The application's delivery (GitHub or pushed images), its current\n version, each environment's head (see `refresh`), recent pushes, sandbox environments, and what its definition\n offers: every environment (descriptions, seeds), ports, personas,\n actions and emulator operations (with their schemas). This is what an\n agent reads to decide which environment to claim and how to call each\n action once it has a sandbox.\n\ncofactor sandbox refresh --application A [--environment E] [--wait]\n Moves an environment's head to now. The head is what flow runs, the flow\n designer, scheduled runs and unpinned claims use: for GitHub, the ready\n snapshot of the default branch; for pushes, the newest push. It is never\n built per commit and never follows the default branch on its own, so it\n is normally behind it. It moves in two ways only: the background refresh\n (every 6 hours, for environments in use) and this command, which builds\n the default branch's tip as of this moment and advances the head when its\n snapshot is ready. A pushed application's head is already its newest push\n (\"already at head <push id>\"). Prints the old head, the new head and its\n build status; `--wait` blocks until the snapshot is ready or failed. A\n refresh while that snapshot is already building joins the build. Whoever\n can claim can refresh. An environment kept warm switches to the new head\n once its warm-up finishes (staff-managed, see `warm`). `envs` shows each head and whether the default\n branch has moved past it, for information only.\n\ncofactor sandbox claim --application A [--environment E] [--at V] [--wait]\n Claims a sandbox: a fresh copy of the ready snapshot for the version and\n environment (built first — build, `prepare`, seeds — if it doesn't exist\n yet), given its own URLs and logins by `prepare`. A version is a commit\n SHA for a GitHub-delivered application, or a push id or tag for a pushed\n one. Without `--at`: the environment's head (see `refresh`); for GitHub,\n the default branch snapshot, else the default branch's tip if the head is\n stale; for pushes, the newest push. `--environment` defaults to `default`. `--wait`\n blocks until the sandbox is ready (or failed) instead of returning\n immediately with a claim id you'd otherwise have to poll with `show`.\n\ncofactor sandbox show <id>\n Current status, per-port URLs, persona logins (decrypted — this is where\n they're meant to be read from, never a PR comment), the snapshot's seed\n steps with the time each took, seed outputs, and the available actions\n and operations.\n\ncofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input '{...}']\n Runs a definition action, an emulator operation (named `<plugin>.<op>`,\n e.g. `email.sent-emails`) or, when the definition declares `databases`,\n the built-in `database.fast-forward` (`--input database=<name>`; see the\n `data-fast-forward` topic) against a claimed sandbox, waits for it, and\n prints its output. `--input` is repeatable and coerced to the action/\n operation's declared type (string/number/integer/boolean/persona);\n `--json-input` passes the whole input as JSON, overriding `--input`.\n Invalid input prints the action or operation's input schema instead of\n running it, so you can see what's expected.\n\ncofactor sandbox exec <id> [--timeout S] [--json] -- <command...>\n Runs one shell command in a claimed sandbox, waits for it, and prints its\n output. One-shot: there is no interactive shell. It runs as the sandbox\n user in the definition's directory, with the environment actions get\n (the application's secrets included). Everything after `--` is the\n command, its words joined with single spaces (ssh semantics), so quote\n pipes and redirects yourself: `-- 'ls | wc -l'`.\n - Output is the last 4000 characters of stdout and of stderr, scrubbed of\n the sandbox's secrets and persona logins (best effort: an encoded value\n isn't caught). The CLI exits with the command's exit code (124 on a\n timeout). A failure that isn't the command's (the helper, or a sandbox\n that can't exec) prints `Error: ...` and exits 1. `--json` prints the\n whole run instead.\n - `--timeout S` is 1 to 1800 seconds (default 300). On a timeout the\n command's whole process group is killed. Background processes (`cmd &`)\n keep running after a normal exit; redirect their output\n (`cmd > log 2>&1 &`), because the exec's pipes close when it returns.\n If the CLI stops waiting (its deadline is the timeout plus 60 s), the\n command may still be running in the sandbox.\n - Permissions: a signed-in CLI session (`cofactor login`) isn't\n scope-checked, so every org member can exec, just as they can run\n actions. An API key needs the `sandbox:exec` scope explicitly:\n `sandbox:*` does not include it, full access (`*`) does. Create a key\n with `sandbox:exec` selected under Sandboxes in Settings -> API keys (or\n `POST /settings/api-keys` with `\"scopes\": [..., \"sandbox:exec\"]`).\n Existing `sandbox:*` keys don't gain it, and a key's scopes can't be\n edited, so mint a new key.\n - Commands and their scrubbed output are visible in the sandbox's run\n history to anyone with `sandbox:read`. Don't type credentials into a\n command; set them as secrets (see the `secrets` topic).\n - Every exec is audited in the organization's events: who ran it (and\n with which API key), the sandbox, the scrubbed command, and how it\n ended (exit code, or a failure). Never its output. An exec that cannot\n be audited does not run.\n - Platform: works on sandboxes built on a platform that has exec. An older\n sandbox answers \"This sandbox was built on an older platform; exec works\n once it is rebuilt on a newer one.\" Exec ships with the next sandbox\n platform update (it needs no bump of its own).\n\ncofactor sandbox sync <id> [--build] [--json]\ncofactor sandbox sync <id> <local-dir> --to <path> [--json]\n Makes a sandbox you claimed match your working tree, uncommitted and\n untracked changes included, straight over the sandbox's own sync port.\n It lists every file it writes (untracked ones marked) and deletes, then\n applies, records the synced tree id (`show` prints it) and extends the\n sandbox's lease (up to 8 h after the claim). Nothing restarts on its own:\n `exec <id> -- cofactor-sandbox restart <service>`. `--build` (GitHub\n applications) reruns the changed build steps and restarts `after_build`\n services. `--to` mirrors local build output into <path>. Needs the same\n `sandbox:exec` scope as exec. See the `local` topic.\n\ncofactor workflow run <flow> --sandbox <id>\n Runs a flow on a sandbox you claimed instead of a fresh copy, and never\n destroys it. The flow's environment must be the sandbox's application and\n environment (`--environment`). The run records the version and synced\n tree (`cofactor workflow debug overview <run>`). An API key also needs\n `sandbox:read`.\n\ncofactor sandbox destroy <id>\n Tears the sandbox down. Always do this when a claimed sandbox is no\n longer needed — it isn't reclaimed automatically mid-session.\n\ncofactor sandbox secret set <NAME> --application A [--environment E] [--json]\n Stores a third-party secret (an API key) for the application, for every\n environment, or for just `--environment E` (an override). The value is\n read from stdin only and the CLI refuses a terminal:\n `printf %s \"$KEY\" | cofactor sandbox secret set NAME --application A`.\n One trailing newline is stripped. A name is an environment variable name\n (capital letters, digits and `_`, not starting with a digit; not PATH,\n HOME, USER, SHELL, NODE_OPTIONS, NODE_EXTRA_CA_CERTS, LD_* or COFACTOR_*);\n a value is 8 to 8192 bytes, with no NUL. Fast copies of an existing\n ready snapshot keep its old value until a new commit or image push\n rebuilds the snapshot. Legacy-prepare claims see the change on the next\n claim. Prints `Secret NAME set for all environments of A.` (or\n `... for environment \"E\" of A.`). See the `secrets` topic.\n\ncofactor sandbox secret list --application A [--json]\n Names and scopes (all environments, or one) with the time last set.\n Never values. With none set it prints `No secrets set for A.`\n\ncofactor sandbox secret rm <NAME> --application A [--environment E] [--json]\n Removes the secret at that scope: without `--environment`, the\n application-wide one; with it, that environment's override. Prints\n `Secret NAME removed for all environments of A.` (or\n `... for environment \"E\" of A.`). A secret that is not set fails with\n \"No secret with that name and scope.\". Fast copies of an existing ready\n snapshot keep the removed value until a new commit or image push rebuilds\n the snapshot. Legacy-prepare claims see the removal on the next claim.\n See the `secrets` topic.\n\ncofactor sandbox verify-env --application A [--environment E]\n Claim → wait → report seed outputs and timings → sign every persona in\n (when the definition declares `authentication`) → destroy, for each\n environment of the current version (or the one named). The end-to-end\n proof that an environment's seed chain and persona logins actually work,\n not just that they're well-formed. See the `verify` topic.\n\ncofactor sandbox preview --application A --pr N\n A pull request's preview of a GitHub-delivered application: status, URLs\n and the logins its PR comment deliberately leaves out.\n To run a test on it (a private copy at its applied head, never the shared preview):\n cofactor workflow run <workflowId> --preview N --application A\n and to list tests in a pull request as proof: `cofactor workflow guide prove-changes`.\n\ncofactor sandbox warm list | set | show | clear\n Staff only: keep an environment warm so claims start in about a second.\n `warm set --application A --environment E --slots N [--tag T]`, then\n `warm show` (Filling / On / Switching / Needs attention) with the same\n arguments, `warm list` for the whole stage, `warm clear` to turn it off.\n See the `warm` topic.\n\ncofactor sandbox guide v2 [topic]\n Prints this guide. Omit `topic` to print every section; name one (e.g.\n `upload`) to print just that topic.\n\nTHE CORE LOOP\n\n validate → (connect | push) → envs → claim --wait → run (actions/\n operations, as many as the test needs) → destroy. Always validate a\n changed definition before pushing it — see the `verify` topic for why.\n","PUSHED IMAGES: SANDBOXES WITHOUT REPOSITORY ACCESS\n\nAn application whose team gives Cofactor no access to its repository\ndelivers its sandboxes as built images: build your app's image as usual,\nthen push it with its `cofactor.sandbox.yaml` whenever you have a new\nbuild (nightly, per release, per merge — your cadence, your CI or your\nlaptop). Each push is a version of the application. A tag names the newest\nready push that carries it: pushing `--tag nightly` again moves the tag to\nthe new push, and older pushes stay claimable by their id.\n\nTag your main builds `main` (or `nightly`). These two tags are reserved: once\nthe application has a push carrying either, its head (what the flow designer,\nscheduled runs and claims without `--at` use) is the newest push tagged `main`\nor `nightly`, and only those runs move the visual-review baseline. Pull\nrequest and branch pushes are compared against them and change nothing. Until\nthen the newest push is the head. Claim any other push with\n`--at <push id or tag>`.\n\nUse tags that move (`nightly`, `release`, a branch name), not one per push\nsuch as a date. Cofactor keeps the layers of each application's newest push,\nits head, and its 10 newest tagged pushes. Older pushes stay claimable, but when the\nsandbox platform is updated only kept pushes move to it on their own; an\nolder one says to push again.\n\n docker buildx build --platform linux/amd64 -t tracker:sandbox --load .\n cofactor sandbox validate --upload cofactor.sandbox.yaml\n cofactor sandbox push --application \"Project Tracker\" tracker:sandbox \\\n --definition cofactor.sandbox.yaml --tag nightly --wait\n cofactor sandbox envs --application \"Project Tracker\"\n cofactor sandbox claim --application \"Project Tracker\" --environment default --wait\n\n`push` adds Cofactor's layers to your image (its sandbox helper as the\nentrypoint, plus the object storage server when the definition declares\n`object_storage`) and a small layer with your definition, reads the image\nout of your local Docker (`docker save`), and uploads only the layers\nCofactor doesn't already have for your organization (see FAST RE-PUSHES).\nCofactor boots it once until every service is healthy (`--wait` waits for\nthat). Then each environment in the definition\nbecomes a sandbox environment of the application, and each persona one of\nits personas. The first claim of an environment builds its ready snapshot\n(prepare + seeds, about a minute); later claims copy it in seconds.\n\nWHAT THE IMAGE MUST BE\n\n - linux/amd64. On an Apple Silicon Mac, build with\n `--platform linux/amd64`; `push` refuses any other platform.\n - glibc-based with bash (Debian, Ubuntu, `python:*-slim`, `node:*`\n non-alpine…). Alpine/musl and distroless images do not work: the\n helper is a glibc binary and every command runs through `bash -lc`.\n If only your final stage is Alpine, keep the build stages and change\n that one `FROM` to its Debian equivalent (`node:24-alpine` →\n `node:24-bookworm-slim`, `python:3.12-alpine` → `python:3.12-slim`),\n with `apt-get install` in place of `apk add`. Native dependencies\n compiled on Alpine don't run on glibc: install them in a glibc stage\n rather than copying `node_modules` or wheels out of an Alpine one.\n - A WORKDIR other than `/`. Services, prepare commands and actions run\n from it, and the helper gives it to the sandbox user (below).\n - Built already. There is no build step in a pushed sandbox: everything\n the app needs (dependencies, compiled assets, a database server if it\n uses one) is in the image.\n\nFAST RE-PUSHES\n\n A push uploads only the layers that changed since an earlier push (in\n the last 30 days); `push` says how much it sent and how much Cofactor\n already had. These keep a re-push small:\n\n - Order the Dockerfile so what changes most comes last: system packages,\n then the dependency manifest and lockfile and the install step, then\n your source. A code change then rebuilds, and uploads, only the last\n layers:\n FROM python:3.12-slim\n RUN apt-get update && apt-get install -y --no-install-recommends curl jq\n WORKDIR /app\n COPY requirements.txt .\n RUN pip install -r requirements.txt\n COPY . .\n - In CI, keep the build cache between runs. A runner that starts with an\n empty cache rebuilds every step, and every rebuilt layer is new to\n Cofactor even when nothing in it changed. With GitHub Actions:\n docker buildx build --platform linux/amd64 --load -t app:sandbox \\\n --cache-from type=gha --cache-to type=gha,mode=max .\n (or `type=registry` with a cache image of your own). In a plain\n `run:` step buildx can't see the Actions cache token and skips\n `type=gha` without a warning: add\n `uses: crazy-max/ghaction-github-runtime@v3` before that step, or\n build with `docker/build-push-action` (same cache-from and cache-to).\n - For compiled stacks (.NET, Java, Rust), build outside Docker and keep\n the Dockerfile thin. `RUN --mount=type=cache` (NuGet, Maven, cargo,\n incremental `obj/` or `target/` directories) is not exported by\n `--cache-to type=gha`, so inside a Dockerfile those caches are empty\n on every CI run and the whole compile reruns. Compile on the runner\n with `actions/cache` for the compiler's incremental state, and have\n the Dockerfile only `COPY` the published output onto a base image\n holding what rarely changes (database server, runtimes, seed data;\n rebuild it only when its inputs change). Three details decide whether\n the re-push is actually small:\n - Incremental compilers (MSBuild, make, cargo) compare file mtimes.\n A fresh `git clone` stamps every file \"now\" and invalidates the\n cached state. Before compiling, set tracked files to an old date\n (`git ls-files -z | xargs -0 touch -h -d '2000-01-01'`), then\n touch the files changed since the commit you last built. Keep its\n SHA in the cache entry, check out with `fetch-depth: 0` so it is\n present, and skip this on a cache miss (that build is full anyway):\n `git diff -z --name-only --diff-filter=d <last-sha> HEAD | xargs -0 -r touch -h`.\n - Layers are digested by content including mtimes, and the parent\n directories a COPY creates can get the build time, so identical\n files can yield a new layer digest on every build. Stage the\n output under its final path (`staged/opt/app/...`), pin every\n mtime in it including the directories\n (`find staged -exec touch -h -d '2000-01-01' {} +`), and use\n `COPY --link staged/ /` (BuildKit).\n - Split third-party files from your own into separate COPYs so a\n code change rewrites only the small one.\n\n CI authenticates with an API key (`COFACTOR_API_TOKEN`). Give it only\n what the job does: `sandbox:read`, `sandbox:publish` and `property:read`\n to push (plus `property:create` if CI's push may be the first, which\n creates the application); add `sandbox:create` and `sandbox:delete` to claim and destroy,\n `sandbox:run` to run actions, and `sandbox:exec` to run shell commands\n (not included in `sandbox:*`). Create it from the CLI (the secret prints\n once): `cofactor api-key create --name \"CI image push\" --scope sandbox:read\n --scope sandbox:publish --scope property:read`.\n\n Push main builds with `--tag main` (or `nightly`). For pull requests, push\n each with its own tag and claim it by that tag:\n cofactor sandbox push --application \"Project Tracker\" app:sandbox --tag pr-123 --wait\n cofactor sandbox claim --application \"Project Tracker\" --environment default --at pr-123 --wait\n A pull request's push shares every layer below the source with main's.\n\nNIGHTLY PUSH FROM GITHUB ACTIONS\n\n A complete `.github/workflows/cofactor-nightly.yml` for the example\n application \"Acme Tracker\": it builds the image every night (and on\n demand), pushes it tagged `nightly`, then runs your suite on that push.\n Replace the application and suite names with yours.\n\n Before the first run, create the CI key (see above) and store it as the\n repository secret COFACTOR_API_TOKEN. For the last step add `--scope\n test-run:run --scope test-run:read`; without that step the push scopes\n are enough.\n\n name: cofactor-nightly\n on:\n schedule:\n - cron: \"0 3 * * *\"\n workflow_dispatch:\n concurrency:\n group: cofactor-nightly\n cancel-in-progress: false\n jobs:\n nightly:\n runs-on: ubuntu-latest\n timeout-minutes: 180\n env:\n COFACTOR_API_TOKEN: ${{ secrets.COFACTOR_API_TOKEN }}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: 22\n - run: npm install -g @runcofactor/cli\n - uses: docker/setup-buildx-action@v3\n - uses: crazy-max/ghaction-github-runtime@v3\n - name: Build the image\n run: |\n docker buildx build --platform linux/amd64 --load \\\n -t acme-tracker:sandbox \\\n --cache-from type=gha --cache-to type=gha,mode=max .\n - name: Check the definition\n run: cofactor sandbox validate --upload cofactor.sandbox.yaml\n - name: Push the image\n run: |\n cofactor sandbox push --application \"Acme Tracker\" acme-tracker:sandbox \\\n --definition cofactor.sandbox.yaml --tag nightly --wait\n - name: Run the suite on the new push\n run: cofactor test-suite run --suite \"Acme Tracker nightly\"\n\n The CLI reads `COFACTOR_API_TOKEN` from the environment (no login step).\n The cache flags and the ghaction-github-runtime step are the ones under FAST\n RE-PUSHES. `--wait` follows the import for up to 50 minutes and fails the\n job if Cofactor cannot boot the image. A push still importing after 50\n minutes is not a failure: the job moves on and the suite runs on the\n previous head. The suite runs on the application's head: the newest push\n tagged `main` or `nightly`. Start the workflow by hand with \"Run\n workflow\" (the `workflow_dispatch` trigger) to test a change to this\n file. `timeout-minutes` covers a build, the longest import wait and the\n suite; raise it if your suite runs longer.\n\n The last step is optional: a suite can also run on a Cofactor schedule.\n\nMAKING YOUR IMAGE SYNCABLE\n\n `cofactor sandbox sync` writes your working tree into a claimed sandbox\n (see `guide v2 local`). For a pushed image it syncs the directory holding\n cofactor.sandbox.yaml into the image's WORKDIR, so the image must keep\n that directory's files at WORKDIR in the same layout. The rule: a\n syncable image has your definition file at its WORKDIR, as `COPY . .`\n gives you.\n\n WORKDIR /app\n COPY . .\n RUN npm ci && npm run build\n\n A multi-stage build that copies only some directories works too, as\n long as each lands at the same relative path under WORKDIR and the\n definition file is copied in:\n WORKDIR /app\n COPY --from=build /src/api /app/api\n COPY cofactor.sandbox.yaml /app/cofactor.sandbox.yaml\n\n Files the image doesn't ship (sources it compiles away, docs) are fine:\n `sync` writes them as new files and nothing runs them.\n\n Compiled output isn't rebuilt in the sandbox (there is no build step):\n build it locally, then sync the result to where the image serves it:\n npm run build\n cofactor sandbox sync <id> dist --to public\n\n `push` checks the rule on the local image and says one of:\n Sync: syncable.\n Sync: syncable (the image is older than your working tree, which is fine).\n Sync: not syncable: cofactor.sandbox.yaml isn't at /app/cofactor.sandbox.yaml (...)\n Sync: not checked (...) (Docker, or git, couldn't answer)\n It never stops a push. It reads the definition at your WORKDIR, not the\n copy `push` adds for Cofactor itself.\n\nHOW YOUR IMAGE RUNS IN A SANDBOX\n\n - Its ENTRYPOINT and CMD are NOT used. Start each process yourself with a\n `services.<name>.run` command (often your entrypoint script).\n - Its ENV and WORKDIR ARE applied, as `docker run` would, its PATH\n included: commands run through a `bash -lc` login shell, and the image's\n own PATH is put back in front of whatever the login profile sets.\n - Leave the image's USER as root (or unset). The sandbox starts as root\n and drops to uid 1000 itself; an image that ended on another `USER`\n has failed to boot with a permission error on `/etc/cofactor`.\n - Everything — services, prepare commands, actions — runs as an\n unprivileged user with uid 1000 (the sandbox adds that account if your\n image has none). The WORKDIR tree is made theirs; anything the app\n writes must be under the WORKDIR or `/tmp`, not elsewhere in a root-\n owned path.\n - One machine, no Docker inside: a database is a process you run as a\n service (Postgres installed in the image, or SQLite), not a container.\n For S3 or GCS, declare `object_storage` and the sandbox runs the\n server for you (see the `object-storage` topic).\n - Each public port gets its own https URL, unique per sandbox. Its\n origin is a param — `params: { web_url: { port: web } }` — that you\n pass to the app through `env` (e.g. `PUBLIC_URL: ${params.web_url}`).\n\nTHE DEFINITION FOR PUSHED IMAGES\n\n The same `cofactor.sandbox.yaml` as for a repository (see the\n `definition` topic), minus everything about building: no `setup`, no\n `lifecycle.build`, no `after_build`. A minimal one for the example app,\n built as one image that serves its UI and API on port 8080:\n\n version: 1\n resources: { vcpus: 2, memory_mib: 4096 }\n ports:\n web: { port: 8080, access: public }\n params:\n web_url: { port: web }\n env:\n PUBLIC_URL: ${params.web_url}\n services:\n app:\n run: exec ./start.sh\n health: { http: 8080, path: /health }\n reload_on_prepare: true\n personas:\n owner: { email: owner@example.test }\n lifecycle:\n prepare:\n - bash sandbox/personas.sh\n actions:\n create-workspace:\n description: A new workspace, created by the given persona, who becomes its owner.\n run: sandbox/actions/create-workspace.sh\n input:\n name: { type: string, required: true }\n as: { type: persona, required: true }\n output:\n workspace_id: { type: string }\n environments:\n default:\n description: Workspace \"Acme\", owned by the owner persona.\n seeds:\n - id: acme\n action: create-workspace\n input: { name: Acme, as: owner }\n\n From here, add what your tests need, in the order \"WHAT TO BUILD FIRST\"\n in the `seeds-and-actions` topic gives: a persona per role, an\n environment per plan or flag combination, and actions for the core\n objects.\n\n Keep the file anywhere in your repo: `validate --upload <file>` and\n `push --definition <file>` read it from where you point them, and the\n push puts it in the image. Paths inside it (`run:` of actions, prepare\n commands) are relative to the WORKDIR, so ship your sandbox scripts in\n the image (e.g. `COPY sandbox/ sandbox/`).\n\n Services start when the image is first booted, BEFORE any sandbox exists,\n with params at placeholder values. A service that needs the sandbox's own\n values (its URL, a `{secret: true}` param) or reads a secret sets\n `reload_on_prepare: true`: it is started (and restarted) by each\n sandbox's prepare instead. In doubt, set it on your app's service.\n\n `health` passes when the http path answers below 400 (or the command\n exits 0) within 5 minutes of the service starting.\n\nPERSONAS: MAKING THE LOGINS WORK\n\n Cofactor generates a fresh password for every persona when a snapshot is\n built; every sandbox copied from it has the same ones. Your\n `lifecycle.prepare` commands (which also run on each copy) receive them as `COFACTOR_PERSONAS`, a\n JSON map `{ \"<persona>\": { \"email\": \"...\", \"password\": \"...\" } }`, and\n must make each login work in your app: create or update that user with\n that password (through your app's API, its CLI, or its database). A\n persona whose login doesn't work breaks every test that signs in as it.\n A persona's `email` is yours to choose: pick an account your app already\n has (its seed data) and set its password, or have prepare create it.\n Cofactor's test runs then sign in with these logins automatically, as\n the application's persona of the same name.\n\n Prepare runs while your app is up: the `reload_on_prepare` services are\n running and healthy (restarted first when their environment changed),\n the others have run since the image was booted. So write the logins\n somewhere the running app reads on every sign-in (its database, or its\n own API). If your app loads its accounts once at startup (a file or\n config read into memory), change it to read them per request, or the new\n passwords won't take. For an app whose logins are fixtures, a common fix\n is a small JSON file the prepare script writes from `COFACTOR_PERSONAS`\n and the app checks on each sign-in before its fixtures.\n\nACTIONS AND SEEDS\n\n An action is a script in the image with typed input and output (the\n `seeds-and-actions` topic covers designing them). Inputs arrive as\n `INPUT_<NAME>` env vars (a `persona` input carries the persona's name,\n e.g. `owner`), alongside the definition's `env:`, every param as\n `COFACTOR_PARAM_<NAME>`, and `COFACTOR_PERSONAS`. The script prints its\n result as a JSON object on the LAST stdout line that starts with `{`\n (send everything else to stderr). It runs inside the sandbox, so it\n reaches your app on localhost. An environment's `seeds` call actions in\n order, once per version, before the snapshot every claim copies;\n `${<step id>.<output>}` passes one step's output to a later step. Run\n one against a live sandbox with `cofactor sandbox run <id> <action>\n --input name=... --input as=owner`.\n\n An action that signs in as a persona and creates data through the app's\n own API (here a cookie-session JSON API on port 8080; the port, paths and\n JSON fields are the example app's, use your app's). It needs jq and\n curl, which slim base images lack: add them to your Dockerfile\n (`apt-get install -y --no-install-recommends jq curl`).\n\n #!/usr/bin/env bash\n # sandbox/actions/create-workspace.sh\n set -euo pipefail\n login=$(jq -c --arg p \"$INPUT_AS\" '.[$p] | {email, password}' <<<\"$COFACTOR_PERSONAS\")\n jar=$(mktemp)\n curl -fsS -c \"$jar\" -H 'content-type: application/json' \\\n -d \"$login\" http://127.0.0.1:8080/api/auth/login >&2\n id=$(curl -fsS -b \"$jar\" -H 'content-type: application/json' \\\n -d \"$(jq -nc --arg n \"$INPUT_NAME\" '{name: $n}')\" \\\n http://127.0.0.1:8080/api/workspaces | jq -er '.id')\n jq -nc --arg id \"$id\" '{workspace_id: $id}'\n\n\nTROUBLESHOOTING\n\n - `push` says the image is not linux/amd64, or its WORKDIR can't be\n used: rebuild as it says.\n - The push fails with \"Cofactor could not start this image\": a service\n exited or never turned healthy. The message ends with that service's\n log. To reproduce, open a shell in the image —\n docker run --rm -it --entrypoint bash <image>\n — and run that service's `run` command from the WORKDIR. In the\n sandbox it runs as uid 1000 with the WORKDIR made theirs; plain\n `docker run --user 1000` leaves the WORKDIR root-owned, so a write\n failure there is not the sandbox's.\n - A claim fails in prepare or a seed: the error carries the command's\n output. Fix the script, rebuild the image and push again.\n - To try prepare and actions before pushing, run the app as usual and\n call your scripts in it with the variables they read (a sandbox also\n sets the definition's `env:` and runs them as uid 1000):\n docker run -d --name try -p 8080:8080 <image>\n docker exec -e COFACTOR_PERSONAS='{\"owner\":{\"email\":\"owner@example.test\",\"password\":\"pw-1\"}}' try bash sandbox/personas.sh\n docker exec -e COFACTOR_PERSONAS='…' -e INPUT_NAME=Acme -e INPUT_AS=owner try bash sandbox/actions/create-workspace.sh\n (`docker exec` starts in the image's WORKDIR.)\n - `cofactor sandbox logs` and `diagnostics` are for the older sandboxes;\n for these, `cofactor sandbox show <id>` has the status and any error.\n - \"push the image again\": Cofactor's sandbox helper changed since that\n push; a fresh `cofactor sandbox push` picks up the current one.\n - A layer larger than 5 GB is refused; split or slim it.\n - Every push uploads the whole image: the build cache is missing (see\n FAST RE-PUSHES), or the Dockerfile copies the source before\n installing dependencies.\n","KEEPING AN ENVIRONMENT WARM (STAFF ONLY)\n\nA claim normally copies the environment's ready snapshot onto a worker that\nmay not hold it yet, and fetching it first takes a while. Keep warm makes\nsure some workers already hold the snapshot and have room to spare, so a\nclaim starts in about a second. Nothing is pre-booted: no sandbox runs until\nsomeone claims one. What is kept ready is the snapshot on the workers and\nfree slots to start copies in.\n\nOnly a superadmin can turn it on, because it holds capacity on the sandbox\nfleet. Everyone else just sees faster claims.\n\n cofactor sandbox warm set --application A --environment E --slots N [--tag T]\n cofactor sandbox warm show --application A --environment E\n cofactor sandbox warm list\n cofactor sandbox warm clear --application A --environment E\n\nThese act in the org your session is signed in to (`--org` picks which of\nyour sessions, as for every command). To act in another org, pass\n`--target-org <id>` and give the application by id. `--json` prints the\nraw data; `--env` stays the stage (dev / prod).\n\nSLOTS\n\n`--slots N` (1 to 12) is how many free slots to keep ready for this\nenvironment, one slot being what a claim of this environment takes (its\ndefinition's vCPUs and memory). When claims use some, Cofactor brings up or\nreuses another worker and puts the snapshot on it before the free slots run\nout. A rule of thumb for an environment of 2 vCPUs and 4 GiB: a worker has\nabout 3 slots with SMT off and about 6 with SMT on. Read the real figure for\nthe fleet you are on from the worker (`blackcat fleet nodes`), not from this\npage, and keep `N` at or below what one worker holds so a single extra\nworker is enough.\n\nCOST\n\nCost follows total headroom slots, and a warm worker is shared by every\nwarm environment: two environments at 2 slots each on the same worker cost\nabout what one does. Workers holding warm slots are not retired by idle\nscale-in, so at least one worker stays up while any environment is kept warm.\nClear what nobody needs.\n\nWarm needs the Blackcat release that supports it on the placer, the gateway\nand every worker, and every worker must report a cache budget before warm is\nenabled.\n\nTHE TAG\n\nA repository application has no tag: the snapshot kept warm is the default\nbranch's. A pushed application has no default branch, so `--tag` is required\nand names the push to keep warm (for example `nightly`). Giving a tag to a\nrepository application, or leaving it off a pushed one, is refused.\n\nWHAT CLAIMS SEE\n\nWhile an environment is kept warm, claims follow the warm snapshot, not the\nnewest one. When main (or the tag) moves to a new snapshot, Cofactor builds\nit, warms it on workers beside the old one, and only then moves claims over.\nSo a claim, a workflow run and `envs` can lag main by the 2 to 3 minutes it\ntakes to warm the new snapshot. If you change the tag, claims naming the new\ntag keep getting the push warmed for the old tag until the switch finishes.\n\nREADING `warm show`\n\n Keep warm: On (2 slots)\n Serving claims: 3f9a1c (2 warm slots free now)\n\nState is one of:\n\n Filling the first snapshot is being warmed; claims are not warm yet\n On claims start from a worker that holds the snapshot\n Switching a newer snapshot is being warmed; claims still use the\n old one, which stays warm until the new one is ready\n Needs attention warming failed and nothing is serving; `Error` says why\n\n\"Serving claims\" is the snapshot claims use, and the free warm slots are read\nlive from the fleet: On with fewer than N free means the fleet is topping up.\n\"Warming next\" appears while Switching. While Switching, `Last error` means\nthe new snapshot could not be warmed (for example its seed failed): the old\none keeps serving and the switch is retried. The CLI shows these labels; the\nJSON carries `warming`, `warm`, `switching` and `failed`.\n\nTURNING IT OFF, AND ROLLING BACK THE FLEET\n\n`warm clear` releases the environment's capacity and removes the setting;\nthe extra worker retires when the fleet's idle scale-in allows. `warm clear`\nreleases the reservations itself, and if a release fails it fails and keeps\nthe row, so an empty `warm list` means every warm reservation was released.\n\nDo not roll the sandbox fleet's placer back while any environment is kept\nwarm: an older placer would hold the slots instead of keeping them soft.\nFirst:\n\n 1. `cofactor sandbox warm list --env <stage>` lists every environment kept\n warm on the stage.\n 2. `warm clear` each one.\n 3. Confirm every reservation id the list showed now reports `released`\n (`blackcat reservation get <id>`), or that `warm list` is empty.\n 4. Only then roll the placer back.\n","YOUR UNCOMMITTED CHANGES IN A SANDBOX (SYNC)\n\nWrite a test next to your code and run it against your real app, before\nyou commit: claim a sandbox, sync your working tree into it, rebuild what\nthe change needs, and open it or run a flow on it. No commit, no image\nbuild, no pull request.\n\n cofactor sandbox claim --application \"Project Tracker\" --wait\n # edit files\n cofactor sandbox sync <id>\n cofactor sandbox exec <id> -- cofactor-sandbox restart app\n cofactor workflow run <flow> --sandbox <id>\n # edit, and sync again\n\nYou decide what to rebuild and restart: a sync only writes and deletes\nfiles. Each sync extends the sandbox's lease by up to 2 hours, up to 8 hours\nafter the claim. Syncing needs the `sandbox:exec` scope for API keys.\n\nWHAT A SYNC DOES\n\n `sync` makes the sandbox's files match your working tree, the way git\n sees it: tracked files with your uncommitted edits, plus untracked files\n that aren't ignored. Ignored files (node_modules/, .env if you ignore it,\n build output) are not synced. Your git index is never touched.\n\n - GitHub application: your repository's top level syncs into the\n sandbox's checkout. Run `sync` anywhere inside the repository.\n - Pushed image: run `sync` from the directory holding\n cofactor.sandbox.yaml; it syncs into the image's WORKDIR (see\n \"Making your image syncable\" in `guide v2 upload`).\n\n Before it writes anything, `sync` prints the files it will write (each\n untracked one marked), the files it will delete, and what it keeps. Check\n for a stray untracked file you didn't mean to send, such as an unignored\n .env.\n\n It ends with the synced tree id, which `cofactor sandbox show` and every\n flow run on the sandbox record. A later commit of exactly these files has\n the same tree (`git rev-parse <commit>^{tree}`, or `<commit>:<dir>` for a\n pushed image's directory).\n\nDELETIONS\n\n A file only the sandbox has is deleted only when all three hold:\n - it isn't in your working tree,\n - your ignore rules don't match it, and\n - some commit in your local history once had a file at that path.\n So a file you deleted, or a directory you removed, is deleted in the\n sandbox, while everything your image or build put there (node_modules/,\n .venv/, compiled assets, a script your Dockerfile copied elsewhere) is\n kept. `sync` lists both.\n\n Syncing again is always safe: it compares against the sandbox's real\n files, so a sync that stopped part-way converges on the next one.\n\nREBUILD AND RESTART\n\n Services keep running the old code until you restart them:\n cofactor sandbox exec <id> -- cofactor-sandbox restart app\n `restart` names one or more services, waits until they're healthy, and\n prints each one's status. An unknown name lists the sandbox's services.\n\n Install dependencies, run migrations or a build with `exec` first: it runs\n in the same directory and environment your services use (the definition's\n directory in the checkout, or a pushed image's WORKDIR):\n cofactor sandbox exec <id> -- npm ci\n cofactor sandbox exec <id> -- npx prisma migrate deploy\n\n Logs: each service writes /var/lib/cofactor/logs/<service>.log.\n cofactor sandbox exec <id> -- tail -n 100 /var/lib/cofactor/logs/app.log\n\n GitHub applications can let the definition do it:\n cofactor sandbox sync <id> --build\n reruns each `lifecycle.build` step whose `inputs` changed (and every step\n after it), then restarts the `after_build` services. A pushed image has no\n build steps: use `exec`.\n\nCOMPILED OUTPUT\n\n When the sandbox runs compiled output (a bundled frontend, compiled\n TypeScript), build it locally and sync the result to where the sandbox\n serves it:\n bun run build\n cofactor sandbox sync <id> frontend/dist --to static\n `--to` is relative to the sync root (the checkout, or the image's\n WORKDIR). This mirrors the local directory as is: ignore rules don't\n apply, so a git-ignored dist/ is sent; every file in it is written, and\n every other file under --to is deleted.\n `show` and runs then record it as build output.\n\nDATA IS FORWARD-ONLY\n\n A sync changes files, never data. A migration you ran stays applied when\n you sync older files back, and seeded rows stay. To start over, destroy\n the sandbox and claim a new one.\n\nRUN A FLOW ON IT\n\n cofactor workflow run <flow> --sandbox <id>\n\n The run uses this sandbox instead of claiming a fresh copy, and never\n destroys it. The flow's environment must be the sandbox's application\n and environment (otherwise the run is refused: pass --environment), the\n sandbox must be ready, and it must be one you claimed, not a test run's. The run records the version and the synced tree it ran\n against, as of when it started; `cofactor workflow debug overview <run>`\n shows them. Syncing during a run changes the files under it, so sync\n between runs.\n\nLIMITS\n\n - Sync only sandboxes claimed with `cofactor sandbox claim`; a test run's\n own copy can't be synced.\n - Symlinks and submodules are not synced (`sync` lists them).\n - A single file larger than 63 MiB is refused; a sync of many files is\n sent in several requests.\n - The `sync` port counts toward the 8-port limit (see `guide v2 ports`).\n","import type { GuideSection } from '../types.js';\n\nimport overview from './overview.txt';\nimport definition from './definition.txt';\nimport environments from './environments.txt';\nimport seedsAndActions from './seeds-and-actions.txt';\nimport dataFastForward from './data-fast-forward.txt';\nimport personas from './personas.txt';\nimport authentication from './authentication.txt';\nimport env from './env.txt';\nimport secrets from './secrets.txt';\nimport emulation from './emulation.txt';\nimport objectStorage from './object-storage.txt';\nimport ports from './ports.txt';\nimport verify from './verify.txt';\nimport cli from './cli.txt';\nimport upload from './upload.txt';\nimport warm from './warm.txt';\nimport local from './local.txt';\n\nexport const SANDBOX_V2_GUIDE_SECTIONS: GuideSection[] = [\n { title: 'Sandboxes v2 Overview', slug: 'overview', content: overview },\n { title: 'Definition (cofactor.sandbox.yaml)', slug: 'definition', content: definition },\n { title: 'Environments (Flavors)', slug: 'environments', content: environments },\n { title: 'Seeds vs. Actions', slug: 'seeds-and-actions', content: seedsAndActions },\n { title: 'Dumps and Stale Dates (database.fast-forward)', slug: 'data-fast-forward', content: dataFastForward },\n { title: 'Personas', slug: 'personas', content: personas },\n { title: 'Authentication', slug: 'authentication', content: authentication },\n { title: 'Environment Variables (env)', slug: 'env', content: env },\n { title: 'Secrets (Third-Party Keys)', slug: 'secrets', content: secrets },\n { title: 'Emulation', slug: 'emulation', content: emulation },\n { title: 'Object Storage (S3 or GCS)', slug: 'object-storage', content: objectStorage },\n { title: 'Ports', slug: 'ports', content: ports },\n { title: 'Pushed Images (No Repository Access)', slug: 'upload', content: upload },\n { title: 'Your Uncommitted Changes in a Sandbox (sync)', slug: 'local', content: local },\n { title: 'Keeping an Environment Warm', slug: 'warm', content: warm },\n { title: 'Verification', slug: 'verify', content: verify },\n { title: 'CLI Reference', slug: 'cli', content: cli },\n];\n","import process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport type { EmulationPluginEntry, EmulationTemplateConfig } from '@chatsdet/emulation';\nimport { getAllPlugins, getPluginIds } from '@chatsdet/emulation/plugins';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\n\n/** One configured plugin as enriched by GET .../emulation (descriptions only, never secret values). */\ntype EmulationCatalogEntry = {\n pluginId: string;\n name: string;\n version: string;\n enabled: boolean;\n envContract: Array<{\n logicalKey: string;\n strategy: string;\n sensitive: boolean;\n description?: string;\n }>;\n inboundEvents: Array<{ id: string; description?: string }>;\n actionInterfaces: Array<{ slug: string; implemented: boolean }>;\n};\n\nconst pad = (value: string, width: number): string => value.padEnd(width);\n\n/**\n * Read the template's stored emulation config + plugin catalog. The GET route\n * returns `{ ok, config, catalog }`, not a `data` wrapper. Falls back to the\n * empty default config when the template has none.\n */\nasync function fetchEmulation(\n apiUrl: string,\n token: string,\n templateId: string\n): Promise<{ config: EmulationTemplateConfig; catalog: EmulationCatalogEntry[] }> {\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n config?: EmulationTemplateConfig;\n catalog?: EmulationCatalogEntry[];\n }>(apiUrl, token, 'GET', `/org/environments/templates/${templateId}/emulation`);\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n // Fresh literal (not a shared module singleton) so callers/transforms can\n // never accidentally mutate a process-wide default.\n return { config: res.config ?? { enabled: false, plugins: [] }, catalog: res.catalog ?? [] };\n}\n\n/**\n * Write the full emulation config back. The PATCH route is a full replace of\n * `{ enabled, plugins[] }`, so every mutating command must read-modify-write\n * the whole object.\n */\nasync function patchEmulation(\n apiUrl: string,\n token: string,\n templateId: string,\n config: EmulationTemplateConfig\n): Promise<void> {\n const res = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/environments/templates/${templateId}/emulation`,\n { enabled: config.enabled, plugins: config.plugins },\n { timeoutMs: 90_000 }\n );\n\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n}\n\n/** Reject a pluginId that is not a known emulation plugin, with the valid list. */\nfunction assertKnownPlugin(pluginId: string): void {\n const ids = getPluginIds();\n if (!ids.includes(pluginId)) {\n console.error(`Error: unknown plugin '${pluginId}'. Available: ${ids.join(', ')}`);\n process.exit(1);\n }\n}\n\nfunction readJsonFileOrExit(path: string, flag: string): unknown {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (err) {\n console.error(\n `Error reading ${flag} file '${path}': ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n try {\n return JSON.parse(raw);\n } catch (err) {\n console.error(\n `Error parsing ${flag} file '${path}' as JSON: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n process.exit(1);\n }\n}\n\n/**\n * Like `readJsonFileOrExit` but rejects anything that is not a plain JSON object\n * (arrays, strings, numbers, null all parse fine but would fail the server's\n * `z.record(...)` validation with a cryptic error). Used for `--config` and\n * `--env-mapping`; `--fixture` stays unrestricted since fixtures are arbitrary.\n */\nfunction readJsonObjectOrExit(path: string, flag: string): Record<string, unknown> {\n const parsed = readJsonFileOrExit(path, flag);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n console.error(\n `Error: ${flag} file '${path}' must contain a JSON object, not ${\n Array.isArray(parsed) ? 'an array' : typeof parsed\n }.`\n );\n process.exit(1);\n }\n return parsed as Record<string, unknown>;\n}\n\n// The PATCH route is a full replace, so each mutating command produces a fresh\n// EmulationTemplateConfig from the fetched one. These are pure so the\n// read-modify-write semantics can be tested without network/IO.\n\n/** Flip the master switch, leaving plugin entries untouched. */\nexport function withEmulationMaster(\n config: EmulationTemplateConfig,\n enabled: boolean\n): EmulationTemplateConfig {\n return { enabled, plugins: [...(config.plugins ?? [])] };\n}\n\n/**\n * Toggle one plugin entry's `enabled`. Enabling a not-yet-configured plugin\n * adds a bare entry; enabling any plugin also turns the master switch on.\n * Disabling assumes the entry exists (callers validate first for a friendly error).\n */\nexport function withPluginEnabled(\n config: EmulationTemplateConfig,\n pluginId: string,\n enabled: boolean\n): EmulationTemplateConfig {\n const plugins = [...(config.plugins ?? [])];\n const idx = plugins.findIndex((plugin) => plugin.pluginId === pluginId);\n if (idx === -1) {\n if (enabled) plugins.push({ pluginId, enabled: true, config: {} });\n } else {\n plugins[idx] = { ...plugins[idx], enabled };\n }\n return { enabled: enabled ? true : config.enabled, plugins };\n}\n\n/**\n * Upsert a plugin entry's config/fixture/envMapping, preserving any field not\n * provided in `patch` and the existing `enabled`. New entries default to\n * `enabled: false`: `set` only configures; activation is always explicit.\n */\nexport function withPluginConfig(\n config: EmulationTemplateConfig,\n pluginId: string,\n patch: {\n config?: Record<string, unknown>;\n fixture?: unknown;\n envMapping?: Record<string, string | string[]>;\n }\n): EmulationTemplateConfig {\n const plugins = [...(config.plugins ?? [])];\n const idx = plugins.findIndex((plugin) => plugin.pluginId === pluginId);\n const existing = idx === -1 ? undefined : plugins[idx];\n\n const merged: EmulationPluginEntry = {\n pluginId,\n enabled: existing?.enabled ?? false,\n config: patch.config !== undefined ? patch.config : (existing?.config ?? {}),\n };\n const fixture = patch.fixture !== undefined ? patch.fixture : existing?.fixture;\n if (fixture !== undefined) merged.fixture = fixture;\n const envMapping = patch.envMapping !== undefined ? patch.envMapping : existing?.envMapping;\n if (envMapping !== undefined) merged.envMapping = envMapping;\n\n if (idx === -1) plugins.push(merged);\n else plugins[idx] = merged;\n return { enabled: config.enabled, plugins };\n}\n\n/** Drop a plugin entry (callers validate existence first). */\nexport function withoutPlugin(\n config: EmulationTemplateConfig,\n pluginId: string\n): EmulationTemplateConfig {\n return {\n enabled: config.enabled,\n plugins: (config.plugins ?? []).filter((plugin) => plugin.pluginId !== pluginId),\n };\n}\n\nasync function handleEmulationGet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!templateId) {\n console.error('Error: template ID required. Usage: canary sandbox emulation get <template-id>');\n process.exit(1);\n }\n\n const { config, catalog } = await fetchEmulation(apiUrl, token, templateId);\n const available = getAllPlugins().map((plugin) => ({ id: plugin.id, name: plugin.name }));\n\n if (json) {\n writeRawOutput(JSON.stringify({ config, catalog, availablePlugins: available }, null, 2));\n return;\n }\n\n console.log(`Emulation: ${config.enabled ? 'ENABLED' : 'disabled'} (master switch)`);\n const configured = config.plugins ?? [];\n if (configured.length === 0) {\n console.log(' No plugins configured.');\n } else {\n console.log(` Configured plugins (${configured.length}):`);\n for (const plugin of configured) {\n const status = plugin.enabled ? '' : ' [disabled]';\n console.log(` ${pad(plugin.pluginId, 16)}${status}`);\n const catalogEntry = catalog.find((entry) => entry.pluginId === plugin.pluginId);\n if (catalogEntry && catalogEntry.envContract.length > 0) {\n console.log(\n ` env contract: ${catalogEntry.envContract.map((entry) => entry.logicalKey).join(', ')}`\n );\n }\n if (catalogEntry && catalogEntry.actionInterfaces.length > 0) {\n const implemented = catalogEntry.actionInterfaces.filter((action) => action.implemented).length;\n console.log(\n ` action interfaces: ${implemented}/${catalogEntry.actionInterfaces.length} implemented`\n );\n }\n const keys = Object.keys(plugin.config ?? {});\n if (keys.length > 0) console.log(` config keys: ${keys.join(', ')}`);\n const extras = [\n plugin.fixture !== undefined ? 'fixture' : null,\n plugin.envMapping ? 'envMapping' : null,\n ].filter(Boolean);\n if (extras.length > 0) console.log(` set: ${extras.join(', ')}`);\n }\n }\n console.log(` Available plugins: ${available.map((plugin) => plugin.id).join(', ')}`);\n}\n\n/** Shared enable/disable for the master switch or a single plugin entry. */\nasync function setEmulationEnabled(\n argv: string[],\n apiUrl: string,\n token: string,\n enable: boolean\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n const verb = enable ? 'enable' : 'disable';\n\n if (!templateId) {\n console.error(\n `Error: template ID required. Usage: canary sandbox emulation ${verb} <template-id> [pluginId]`\n );\n process.exit(1);\n }\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n let next: EmulationTemplateConfig;\n\n if (!pluginId) {\n next = withEmulationMaster(config, enable);\n } else {\n assertKnownPlugin(pluginId);\n const exists = (config.plugins ?? []).some((plugin) => plugin.pluginId === pluginId);\n if (!enable && !exists) {\n console.error(`Error: plugin '${pluginId}' is not configured on this template.`);\n process.exit(1);\n }\n next = withPluginEnabled(config, pluginId, enable);\n }\n\n await patchEmulation(apiUrl, token, templateId, next);\n console.log(pluginId ? `Plugin ${pluginId} ${verb}d` : `Emulation ${verb}d`);\n}\n\nasync function handleEmulationSet(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n\n if (!templateId || !pluginId) {\n console.error('Error: template ID and pluginId required.');\n console.error(\n 'Usage: canary sandbox emulation set <template-id> <pluginId> [--config <file>] [--fixture <file>] [--env-mapping <file>]'\n );\n process.exit(1);\n }\n assertKnownPlugin(pluginId);\n\n const configPath = getArgValue(argv, '--config');\n const fixturePath = getArgValue(argv, '--fixture');\n const envMappingPath = getArgValue(argv, '--env-mapping');\n\n if (!configPath && !fixturePath && !envMappingPath) {\n console.error('Error: provide at least one of --config, --fixture, --env-mapping');\n process.exit(1);\n }\n\n const pluginConfig = configPath ? readJsonObjectOrExit(configPath, '--config') : undefined;\n const fixture = fixturePath ? readJsonFileOrExit(fixturePath, '--fixture') : undefined;\n const envMapping = envMappingPath\n ? (readJsonObjectOrExit(envMappingPath, '--env-mapping') as Record<\n string,\n string | string[]\n >)\n : undefined;\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n const next = withPluginConfig(config, pluginId, {\n ...(pluginConfig !== undefined ? { config: pluginConfig } : {}),\n ...(fixture !== undefined ? { fixture } : {}),\n ...(envMapping !== undefined ? { envMapping } : {}),\n });\n\n await patchEmulation(apiUrl, token, templateId, next);\n // A plugin only runs when BOTH the master switch and its own entry are on.\n // `set` never flips either, so point the user at `enable` unless it is already active.\n const entry = next.plugins.find((plugin) => plugin.pluginId === pluginId);\n const active = next.enabled && entry?.enabled;\n const hint = active ? '' : ` (inactive — run \"emulation enable ${templateId} ${pluginId}\" to activate)`;\n console.log(`Emulation plugin set: ${pluginId}${hint}`);\n}\n\nasync function handleEmulationRemove(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const positional = argv.filter((arg) => !arg.startsWith('-'));\n const templateId = positional[0];\n const pluginId = positional[1];\n\n if (!templateId || !pluginId) {\n console.error(\n 'Error: template ID and pluginId required. Usage: canary sandbox emulation remove <template-id> <pluginId>'\n );\n process.exit(1);\n }\n\n const { config } = await fetchEmulation(apiUrl, token, templateId);\n if (!(config.plugins ?? []).some((plugin) => plugin.pluginId === pluginId)) {\n console.error(`Error: plugin '${pluginId}' is not configured on this template.`);\n process.exit(1);\n }\n\n const next = withoutPlugin(config, pluginId);\n\n await patchEmulation(apiUrl, token, templateId, next);\n console.log(`Emulation plugin removed: ${pluginId}`);\n}\n\nexport async function handleEmulation(\n argv: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const [action, ...rest] = argv;\n switch (action) {\n case 'get':\n await handleEmulationGet(rest, apiUrl, token);\n break;\n case 'enable':\n await setEmulationEnabled(rest, apiUrl, token, true);\n break;\n case 'disable':\n await setEmulationEnabled(rest, apiUrl, token, false);\n break;\n case 'set':\n await handleEmulationSet(rest, apiUrl, token);\n break;\n case 'remove':\n case 'rm':\n await handleEmulationRemove(rest, apiUrl, token);\n break;\n default:\n console.error(\n `Unknown emulation action: ${action ?? '(none)'}. Use get/enable/disable/set/remove.`\n );\n process.exit(1);\n }\n}\n","import process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\n\ntype FailureLogsUrlResponse = {\n ok: boolean;\n error?: string;\n data?: { url: string; key: string };\n};\n\ntype LiveLogsResponse = {\n ok: boolean;\n error?: string;\n data: { logs: string };\n};\n\nexport type FailureLogsBundle = { text: string; key: string } | { error: string };\n\ntype FailureLogsDependencies = {\n requestFailureLogsUrl?: (\n apiUrl: string,\n token: string,\n instanceId: string\n ) => Promise<FailureLogsUrlResponse>;\n fetch?: (url: string) => Promise<Response>;\n};\n\nconst requestFailureLogsUrl = (apiUrl: string, token: string, instanceId: string) =>\n apiRequest<FailureLogsUrlResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/environments/instances/${instanceId}/logs/failure`\n );\n\nexport async function decodeFailureLogsBundle(buf: Buffer): Promise<string> {\n const { gunzipSync } = await import('node:zlib');\n try {\n return gunzipSync(buf).toString('utf8');\n } catch {\n return buf.toString('utf8');\n }\n}\n\n/**\n * Fetch a failed/terminated instance's captured full-log bundle: presign a GET\n * off the API, download the gzip from S3, and return the decompressed text.\n * Returns `{ error }` (never throws) so callers can fall back cleanly.\n */\nexport async function fetchFailureLogsBundle(\n apiUrl: string,\n token: string,\n instanceId: string,\n deps: FailureLogsDependencies = {}\n): Promise<FailureLogsBundle> {\n const res = await (deps.requestFailureLogsUrl ?? requestFailureLogsUrl)(apiUrl, token, instanceId);\n if (!res.ok || !res.data?.url) {\n return { error: res.error ?? 'No captured diagnostic logs' };\n }\n try {\n const resp = await (deps.fetch ?? fetch)(res.data.url);\n if (!resp.ok) return { error: `Failed to download bundle (HTTP ${resp.status})` };\n const buf = Buffer.from(await resp.arrayBuffer());\n return { text: await decodeFailureLogsBundle(buf), key: res.data.key };\n } catch (err) {\n return { error: err instanceof Error ? err.message : String(err) };\n }\n}\n\nexport async function handleLogs(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const instanceId = positional[0];\n const json = hasFlag(argv, '--json');\n\n if (!instanceId) {\n console.error('Error: instance ID required. Usage: canary sandbox logs <instance-id>');\n process.exit(1);\n }\n\n const service = getArgValue(argv, '--service');\n const tail = getArgValue(argv, '--tail') ?? '200';\n const source = getArgValue(argv, '--source');\n const forceFailure = hasFlag(argv, '--failure') || source === 'failure';\n\n // Failure snapshot: the durable full-log bundle captured to S3 when the\n // instance failed to provision. Works for `failed`/`terminated` instances\n // (live docker/logs needs a running host). Forced with --failure/--source\n // failure, and used as an automatic fallback when the live tail can't run.\n if (forceFailure) {\n const bundle = await fetchFailureLogsBundle(apiUrl, token, instanceId);\n if ('error' in bundle) {\n console.error(`Error: ${bundle.error}`);\n process.exit(1);\n }\n if (json) {\n writeRawOutput(JSON.stringify(bundle, null, 2));\n return;\n }\n process.stdout.write(bundle.text || '(empty bundle)');\n return;\n }\n\n // Live path: tail the running containers via SSM. Don't send `service: ''`\n // — the server's regex validator rejects empty strings even though the field\n // is optional, and the route then returns 500 instead of an all-services tail.\n const res = await apiRequest<LiveLogsResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/environments/instances/${instanceId}/docker/logs`,\n {\n ...(service ? { service } : {}),\n tail: parseInt(tail, 10),\n }\n );\n\n if (!res.ok) {\n // The instance likely isn't running (failed/terminated). Fall back to the\n // captured failure snapshot so a dead provision is still diagnosable.\n const bundle = await fetchFailureLogsBundle(apiUrl, token, instanceId);\n if (!('error' in bundle)) {\n if (json) {\n writeRawOutput(JSON.stringify(bundle, null, 2));\n return;\n }\n console.error(`(live logs unavailable: ${res.error}; showing captured failure snapshot)\\n`);\n process.stdout.write(bundle.text || '(empty bundle)');\n return;\n }\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n\n process.stdout.write(res.data.logs ?? '(no logs)');\n}\n","import process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { resolveApplicationId } from './environment.js';\n\ntype PreviewResponse = {\n ok: boolean;\n error?: string;\n data: {\n applicationId: string;\n prNumber: number;\n status: string;\n headSha: string;\n appliedSha: string | null;\n portUrls: Record<string, string>;\n error: string | null;\n readyMs: number | null;\n leaseExpiresAt: string | null;\n personas: { name: string; email: string; password: string }[];\n };\n};\n\n/**\n * `cofactor sandbox preview --application A --pr N`: a pull request's\n * Sandboxes v2 preview of an application, with the logins its comment\n * deliberately leaves out.\n */\nexport async function handlePreview(argv: string[], apiUrl: string, token: string): Promise<void> {\n const pr = getArgValue(argv, '--pr');\n if (!pr) {\n console.error('Usage: cofactor sandbox preview --application <name|id> --pr <number> [--json]');\n process.exit(1);\n }\n const application = await resolveApplicationId(argv, apiUrl, token);\n const query = new URLSearchParams({ application, pr });\n const res = await apiRequest<PreviewResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/previews?${query}`);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n const preview = res.data;\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(preview, null, 2));\n return;\n }\n const lines = [\n `#${preview.prNumber}: ${preview.status}`,\n ` commit ${(preview.appliedSha ?? preview.headSha).slice(0, 12)}${preview.appliedSha && preview.appliedSha !== preview.headSha ? ` (building ${preview.headSha.slice(0, 12)})` : ''}`,\n ...Object.entries(preview.portUrls).map(([name, url]) => ` ${name.padEnd(8)} ${url}`),\n ...preview.personas.map((p) => ` ${p.name.padEnd(8)} ${p.email} ${p.password}`),\n ];\n if (preview.leaseExpiresAt) lines.push(` expires ${preview.leaseExpiresAt}`);\n if (preview.error) lines.push('', preview.error);\n console.log(lines.join('\\n'));\n}\n","/**\n * Sandboxes v2 CLI, addressed by application: connect its repository (or\n * push images — `sandbox-push.ts`), browse what its `cofactor.sandbox.yaml`\n * offers, claim a sandbox, run its actions and emulator operations, and\n * prove an environment's seed chain end to end — all under `cofactor sandbox`.\n *\n * `--env` stays the CLI's global stage flag (dev/prod); a sandbox flavor is\n * always `--environment`. See `packages/content/src/sandbox-v2-guide/` for\n * the full guide (`cofactor sandbox guide v2`).\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { readFileSync } from 'node:fs';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues } from './cli-helpers.js';\nimport { resolveApplicationId, resolveOrCreateSandboxApplication } from './environment.js';\nimport { parseDurationSeconds } from './utilities.js';\nimport { writeRawOutput } from './brand.js';\nimport {\n EXEC_DEFAULT_TIMEOUT_SECONDS,\n EXEC_MAX_TIMEOUT_SECONDS,\n validateDefinition,\n type DefinitionIssue,\n} from '@chatsdet/sandbox-helper/manifest';\nimport { pluginOperations } from '@chatsdet/emulation';\n\n// ---------------------------------------------------------------------------\n// Shared types (mirroring apps/api/src/sandboxes-v2/routes.ts response shapes)\n// ---------------------------------------------------------------------------\n\ntype ActionInputProp = {\n type: 'string' | 'number' | 'integer' | 'boolean' | 'persona';\n description?: string;\n required?: boolean;\n default?: string | number | boolean;\n enum?: Array<string | number>;\n sensitive?: boolean;\n};\n\ntype ActionOutputProp = { type: 'string' | 'number' | 'integer' | 'boolean'; description?: string };\n\ntype ActionEntry = {\n slug: string;\n description: string;\n input: Record<string, ActionInputProp>;\n output: Record<string, ActionOutputProp>;\n timeout: number;\n};\n\ntype OperationEntry = {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema: Record<string, unknown>;\n};\n\ntype EnvironmentEntry = {\n name: string;\n description: string;\n extends: string | null;\n seeds: Array<{ id: string; action: string; input: Record<string, unknown> }>;\n envKeys: string[];\n};\n\ntype DefinitionEntry = {\n environments: EnvironmentEntry[];\n actions: ActionEntry[];\n operations: OperationEntry[];\n personas: string[];\n ports: Array<{ name: string; access: string }>;\n emulation: string[];\n};\n\nexport type PushEntry = {\n id: string;\n status: 'uploading' | 'importing' | 'ready' | 'failed';\n tags: string[];\n error: string | null;\n createdAt: string;\n};\n\n/** `GET /org/applications/:id/sandbox`: the application's sandbox setting and its definition at the default version. */\ntype ApplicationSandbox = {\n applicationId: string;\n delivery: 'github' | 'upload';\n repo: string | null;\n definitionDir: string | null;\n sandboxEnvironments: Array<{ id: string; name: string; environment: string }>;\n /** Each environment's head: what flow runs and unpinned claims use (a warm environment switches to it after its warm-up). Absent from an older API. */\n heads?: Array<{\n environment: string;\n status: 'ready' | 'warming' | 'stale' | 'none';\n version: string | null;\n builtAt: string | null;\n error: string | null;\n /** The default branch's head now (GitHub-delivered; null when GitHub could not be asked). */\n mainHead: string | null;\n behindMain: boolean | null;\n }>;\n /** An upload-delivered application's newest pushes; null for a GitHub-delivered one. */\n pushes: PushEntry[] | null;\n /** A commit SHA, or a push id; null before the first push is ready. */\n version: string | null;\n definition: DefinitionEntry | null;\n};\n\ntype Refusal = { ok: boolean; error?: string; code?: string; issues?: DefinitionIssue[] };\n\ntype ApplicationSandboxResponse = Refusal & { data: ApplicationSandbox };\n\ntype SandboxData = {\n id: string;\n applicationId: string;\n environment: string;\n /** A commit SHA, or a push id. */\n version: string;\n status: 'claiming' | 'ready' | 'failed' | 'destroyed';\n /** Claiming, and waiting on its push's upgrade to the current sandbox platform. */\n upgrading: boolean;\n /** Whether its definition declares `authentication` (native Login nodes). Absent from an older API. */\n nativeLogin?: boolean;\n error: string | null;\n portUrls: Record<string, string>;\n personas: Array<{ name: string; email: string; password: string }>;\n /** Opens the copy's sign-in chooser; only a copy whose definition declares `oidc` has one. */\n signInUrl: string | null;\n /** What `cofactor sandbox sync` last synced into it; absent from an older API. */\n synced?: { tree: string; buildOutput: boolean; at: string } | null;\n seedOutputs: Record<string, Record<string, unknown>>;\n leaseExpiresAt: string | null;\n expired: boolean;\n readyMs: number | null;\n /** Set when ready: waiting for the commit's ready snapshot, restoring the copy, and preparing it. */\n phases: { snapshotMs: number; restoreMs: number; prepareMs: number } | null;\n /** The ready snapshot this sandbox is a copy of: when it was built, and how long each part took. */\n snapshot: {\n builtAt: string;\n buildPhases: {\n buildMs: number;\n /** Absent on snapshots built before step timings were recorded. */\n build?: { steps: { name: string; ran: boolean; ms: number }[]; checkoutMs: number; restartMs: number };\n prepareMs: number;\n seeds: { id: string; action: string; ms: number }[];\n } | null;\n } | null;\n actions: ActionEntry[];\n operations: OperationEntry[];\n};\n\ntype SandboxResponse = { ok: boolean; error?: string; data: SandboxData };\n\ntype RunData = {\n id: string;\n sandboxId: string;\n action: string;\n seedStepId: string | null;\n input: Record<string, unknown>;\n status: 'running' | 'succeeded' | 'failed';\n output: Record<string, unknown> | null;\n exitCode: number | null;\n stdoutTail: string | null;\n stderrTail: string | null;\n error: string | null;\n durationMs: number | null;\n createdAt: string;\n completedAt: string | null;\n};\n\ntype RunResponse = { ok: boolean; error?: string; data: RunData };\n\ntype RunEnqueueResponse = {\n ok: boolean;\n error?: string;\n code?: string;\n issues?: Array<string | { path?: Array<string | number>; message?: string }>;\n data?: { id: string };\n};\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\nfunction pad(s: string, n: number): string {\n return s.padEnd(n);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction indentLines(text: string, indent: string): string {\n return text\n .split('\\n')\n .map((line) => `${indent}${line}`)\n .join('\\n');\n}\n\n/** `slug(input1, input2) → output1, output2`, shared by envs/show/run listings. */\nfunction formatActionSignature(a: { slug: string; input: Record<string, unknown>; output: Record<string, unknown> }): string {\n const inputs = Object.keys(a.input).join(', ');\n const outputs = Object.keys(a.output).join(', ');\n return `${a.slug}(${inputs})${outputs ? ` → ${outputs}` : ''}`;\n}\n\nfunction jsonSchemaPropTypes(schema: Record<string, unknown>): Record<string, string> {\n const properties = (schema as { properties?: Record<string, { type?: unknown }> }).properties ?? {};\n return Object.fromEntries(\n Object.entries(properties).map(([key, prop]) => [key, typeof prop.type === 'string' ? prop.type : 'string'])\n );\n}\n\nfunction formatOperationSignature(op: { name: string; inputSchema: Record<string, unknown>; outputSchema: Record<string, unknown> }): string {\n const inputs = Object.keys(jsonSchemaPropTypes(op.inputSchema)).join(', ');\n const outputs = Object.keys(jsonSchemaPropTypes(op.outputSchema)).join(', ');\n return `${op.name}(${inputs})${outputs ? ` → ${outputs}` : ''}`;\n}\n\n/** Exit with the API's reason, and a definition's issues, when it refused. */\nfunction exitOnRefusal(res: Refusal): void {\n if (res.ok) return;\n console.error(`Error: ${res.error}`);\n if (res.code === 'SANDBOX_V2_DEFINITION_INVALID') printIssues(res.issues, ' ');\n process.exit(1);\n}\n\nfunction printIssues(issues: DefinitionIssue[] | undefined, indent: string): void {\n for (const issue of issues ?? []) {\n console.log(`${indent}${issue.path || '(root)'}: ${issue.message}`);\n }\n}\n\n/** A version as people read it: a short commit, or a push id. */\nfunction shortVersion(version: string): string {\n return /^[0-9a-f]{40}$/.test(version) ? version.slice(0, 12) : version;\n}\n\nfunction printApplicationHuman(app: ApplicationSandbox): void {\n console.log(\n app.delivery === 'github'\n ? `Delivered from GitHub: ${app.repo}${app.definitionDir && app.definitionDir !== '.' ? `:${app.definitionDir}` : ''}`\n : 'Delivered as pushed images'\n );\n console.log(` version ${app.version ? shortVersion(app.version) : '(none yet: run `cofactor sandbox push`)'}`);\n if (app.pushes && app.pushes.length > 0) {\n console.log(' pushes:');\n for (const push of app.pushes) {\n const tags = push.tags.length > 0 ? ` [${push.tags.join(', ')}]` : '';\n console.log(` ${push.id} ${pad(push.status, 9)} ${push.createdAt}${tags}${push.error ? `\\n ${push.error}` : ''}`);\n }\n }\n if (app.sandboxEnvironments.length > 0) {\n console.log(' sandbox environments:');\n for (const env of app.sandboxEnvironments) console.log(` ${pad(env.environment, 16)} ${env.name}`);\n }\n if (app.heads && app.heads.length > 0) {\n console.log(' heads (what flow runs and unpinned claims use; a warm environment switches after its warm-up; update with `cofactor sandbox refresh`):');\n for (const head of app.heads) {\n const version = head.version ? shortVersion(head.version) : '(none)';\n const built = head.builtAt ? `, built ${head.builtAt}` : '';\n const behind = head.behindMain && head.mainHead ? ` main has moved: now ${shortVersion(head.mainHead)}` : '';\n console.log(` ${pad(head.environment, 16)} ${version} (${head.status}${built})${behind}`);\n if (head.error) console.log(` ${head.error}`);\n }\n }\n const def = app.definition;\n if (!def) return;\n\n console.log(' Environments:');\n for (const env of def.environments) {\n console.log(` ${env.name}${env.extends ? ` (extends ${env.extends})` : ''}`);\n console.log(indentLines(env.description, ' '));\n if (env.seeds.length > 0) {\n console.log(' seeds:');\n for (const step of env.seeds) console.log(` ${step.id}: ${step.action}`);\n }\n if (env.envKeys.length > 0) console.log(` env: ${env.envKeys.join(', ')}`);\n }\n\n if (def.actions.length > 0) {\n console.log(' Actions:');\n for (const a of def.actions) {\n console.log(` ${formatActionSignature(a)}`);\n console.log(` ${a.description}`);\n }\n }\n\n if (def.operations.length > 0) {\n console.log(' Operations:');\n for (const op of def.operations) {\n console.log(` ${formatOperationSignature(op)}`);\n console.log(` ${op.description}`);\n }\n }\n\n console.log(` Personas: ${def.personas.join(', ') || '(none)'}`);\n console.log(` Ports: ${def.ports.map((p) => `${p.name} (${p.access})`).join(', ') || '(none)'}`);\n}\n\nfunction printSandbox(data: SandboxData): void {\n console.log(`Sandbox ${data.id}: ${data.status}${data.expired ? ' (expired)' : ''}`);\n console.log(` environment ${data.environment}`);\n console.log(` version ${shortVersion(data.version)}`);\n if (data.error) console.log(` error ${data.error}`);\n if (data.readyMs !== null) {\n const seconds = (ms: number) => `${(ms / 1000).toFixed(1)}s`;\n const phases = data.phases\n ? ` (snapshot ${seconds(data.phases.snapshotMs)}, restore ${seconds(data.phases.restoreMs)}, prepare ${seconds(data.phases.prepareMs)})`\n : '';\n console.log(` ready in ${seconds(data.readyMs)}${phases}`);\n }\n if (data.snapshot?.buildPhases) {\n const { buildMs, prepareMs, seeds } = data.snapshot.buildPhases;\n const seedsMs = seeds.reduce((total, step) => total + step.ms, 0);\n console.log(\n ` snapshot built ${data.snapshot.builtAt} (build ${(buildMs / 1000).toFixed(1)}s, prepare ${(prepareMs / 1000).toFixed(1)}s, ${seeds.length} seeds ${(seedsMs / 1000).toFixed(1)}s)`\n );\n for (const step of seeds) console.log(` ${step.id} ${step.action} ${(step.ms / 1000).toFixed(1)}s`);\n const ran = data.snapshot.buildPhases.build?.steps.filter((step) => step.ran) ?? [];\n if (ran.length > 0) {\n console.log(` build steps: ${ran.map((step) => `${step.name} ${(step.ms / 1000).toFixed(1)}s`).join(', ')}`);\n }\n }\n if (data.leaseExpiresAt) console.log(` expires ${data.leaseExpiresAt}`);\n if (data.synced) {\n writeRawOutput(` synced ${data.synced.buildOutput ? 'build output' : 'tree'} ${data.synced.tree} at ${data.synced.at}`);\n }\n\n if (Object.keys(data.portUrls).length > 0) {\n console.log('');\n console.log(' URLs:');\n for (const [name, url] of Object.entries(data.portUrls)) writeRawOutput(` ${pad(name, 10)} ${url}`);\n }\n\n if (data.personas.length > 0) {\n console.log('');\n console.log(' Logins:');\n for (const p of data.personas) writeRawOutput(` ${pad(p.name, 14)} ${p.email} ${p.password}`);\n // Absent from an older API.\n if (data.nativeLogin !== undefined) {\n console.log(\n data.nativeLogin\n ? ' (native Login nodes sign these in via authentication.run)'\n : ' (no authentication.run: native Login nodes fail with script_not_declared)'\n );\n }\n }\n\n if (data.signInUrl) {\n console.log('');\n writeRawOutput(` Open sign-in: ${data.signInUrl}`);\n }\n\n const seedIds = Object.keys(data.seedOutputs);\n if (seedIds.length > 0) {\n console.log('');\n console.log(' Seed outputs:');\n for (const id of seedIds) {\n for (const [key, value] of Object.entries(data.seedOutputs[id]!)) {\n writeRawOutput(` ${id}.${key}: ${JSON.stringify(value)}`);\n }\n }\n }\n\n if (data.actions.length > 0) {\n console.log('');\n console.log(' Actions:');\n for (const a of data.actions) console.log(` ${formatActionSignature(a)}`);\n }\n\n if (data.operations.length > 0) {\n console.log('');\n console.log(' Operations:');\n for (const op of data.operations) console.log(` ${formatOperationSignature(op)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// validate — offline, no auth\n// ---------------------------------------------------------------------------\n\n/**\n * `cofactor sandbox validate [file] [--upload]`: offline schema + cross-check,\n * no auth required. `--upload` checks a definition for pushed images (no\n * `setup` or build steps); otherwise one built from a GitHub checkout.\n */\nexport function handleSandboxValidate(argv: string[]): void {\n const file = argv.find((a) => !a.startsWith('-')) ?? './cofactor.sandbox.yaml';\n const json = hasFlag(argv, '--json');\n const delivery = hasFlag(argv, '--upload') ? 'upload' : 'github';\n\n let yamlText: string;\n try {\n yamlText = readFileSync(file, 'utf-8');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (json) {\n writeRawOutput(JSON.stringify({ ok: false, issues: [{ path: '', message }] }, null, 2));\n } else {\n console.error(`Error reading \"${file}\": ${message}`);\n }\n process.exit(1);\n }\n\n const { definition, issues } = validateDefinition(yamlText, delivery);\n\n if (json) {\n writeRawOutput(JSON.stringify({ ok: issues.length === 0, issues }, null, 2));\n process.exit(issues.length === 0 ? 0 : 1);\n }\n\n if (issues.length > 0) {\n for (const issue of issues) console.error(`${issue.path || '(root)'}: ${issue.message}`);\n process.exit(1);\n }\n\n const environmentCount = Object.keys(definition!.environments).length;\n const actionCount = Object.keys(definition!.actions).length;\n const operationCount = Object.keys(definition!.emulation).reduce(\n (sum, pluginId) => sum + pluginOperations(pluginId).length,\n 0\n );\n console.log(`✓ ${file} is valid`);\n console.log(` ${environmentCount} environment(s), ${actionCount} action(s), ${operationCount} operation(s)`);\n}\n\n// ---------------------------------------------------------------------------\n// envs\n// ---------------------------------------------------------------------------\n\n/** The application's sandbox setting, or exit with the API's reason. */\nasync function fetchApplicationSandbox(apiUrl: string, token: string, applicationId: string): Promise<ApplicationSandbox> {\n const res = await apiRequest<ApplicationSandboxResponse>(apiUrl, token, 'GET', `/org/applications/${applicationId}/sandbox`);\n exitOnRefusal(res);\n return res.data;\n}\n\n/**\n * The options each Sandboxes v2 command takes (beyond the global ones the\n * dispatcher strips): a value option, a boolean flag, how many positionals.\n */\nconst V2_ARGS: Record<string, { values: string[]; flags: string[]; positionals: number }> = {\n envs: { values: ['--application', '--property'], flags: ['--json'], positionals: 0 },\n refresh: { values: ['--application', '--property', '--environment'], flags: ['--wait', '--json'], positionals: 0 },\n connect: { values: ['--application', '--property', '--repo', '--dir'], flags: ['--json'], positionals: 0 },\n push: { values: ['--application', '--property', '--definition', '--tag'], flags: ['--wait', '--json'], positionals: 1 },\n claim: { values: ['--application', '--property', '--environment', '--at', '--timeout'], flags: ['--wait', '--json'], positionals: 0 },\n show: { values: [], flags: ['--json'], positionals: 1 },\n run: { values: ['--input', '--json-input'], flags: ['--json'], positionals: 2 },\n exec: { values: ['--timeout'], flags: ['--json'], positionals: 1 },\n sync: { values: ['--to'], flags: ['--build', '--json'], positionals: 2 },\n 'verify-env': { values: ['--application', '--property', '--environment'], flags: ['--json'], positionals: 0 },\n destroy: { values: [], flags: [], positionals: 1 },\n secret: { values: ['--application', '--property', '--environment'], flags: ['--json'], positionals: 2 },\n};\n\n/**\n * Refuse an argument a Sandboxes v2 command doesn't take, rather than ignore\n * it: a `\"--at accept-a\"` quoted into one word would otherwise claim the\n * newest version without a word.\n */\nexport function rejectUnknownSandboxV2Args(command: string, argv: string[]): void {\n const spec = V2_ARGS[command];\n if (!spec) return;\n let positionals = 0;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (spec.values.includes(arg)) {\n i++;\n continue;\n }\n // `--tag=nightly`: only the repeatable options (`getArgValues`) read the `=` form;\n // `getArgValue` would ignore `--at=x`, so that one stays refused. `--debug` traces any command.\n if (['--input', '--tag'].some((value) => spec.values.includes(value) && arg.startsWith(`${value}=`))) continue;\n if (spec.flags.includes(arg) || arg === '--debug') continue;\n if (!arg.startsWith('-') && ++positionals <= spec.positionals) continue;\n if (command === 'secret' && !arg.startsWith('-')) {\n // A rejected positional may be a pasted key: never echo it.\n console.error(\n 'Error: `cofactor sandbox secret` takes no value argument: pipe the value on stdin. If you typed a real key on the command line, rotate it.'\n );\n process.exit(1);\n }\n console.error(`Error: \\`cofactor sandbox ${command}\\` does not take ${JSON.stringify(arg)}. See \\`cofactor sandbox --help\\`.`);\n process.exit(1);\n }\n}\n\n/** The positionals of a Sandboxes v2 command: `argv` without its options and the value options' arguments. */\nexport function v2Positionals(command: string, argv: string[]): string[] {\n const values = V2_ARGS[command]?.values ?? [];\n const result: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (values.includes(arg)) i++;\n else if (!arg.startsWith('-')) result.push(arg);\n }\n return result;\n}\n\n/** `cofactor sandbox envs --application A [--json]`: what the application's definition offers. */\nexport async function handleSandboxEnvs(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const app = await fetchApplicationSandbox(apiUrl, token, applicationId);\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(app, null, 2));\n return;\n }\n printApplicationHuman(app);\n}\n\n/**\n * `cofactor sandbox connect --application A --repo owner/name [--dir D] [--json]`:\n * build the application's sandboxes from a GitHub repository directory,\n * creating the application when `A` is a new name.\n */\nexport async function handleSandboxConnect(argv: string[], apiUrl: string, token: string): Promise<void> {\n const repo = getArgValue(argv, '--repo');\n if (!repo) {\n console.error('Usage: cofactor sandbox connect --application <name|id> --repo <owner/name> [--dir <path>] [--json]');\n process.exit(1);\n }\n const body = { repo, definitionDir: getArgValue(argv, '--dir') ?? '.' };\n // A new application is created only once the repository could connect, so a refusal leaves none behind.\n const checkCanConnect = async () => {\n exitOnRefusal(await apiRequest<Refusal>(apiUrl, token, 'POST', '/org/sandboxes-v2/connect-check', body));\n };\n const applicationId = await resolveOrCreateSandboxApplication(argv, apiUrl, token, checkCanConnect);\n const res = await apiRequest<ApplicationSandboxResponse>(apiUrl, token, 'PUT', `/org/applications/${applicationId}/sandbox`, body);\n exitOnRefusal(res);\n if (hasFlag(argv, '--json')) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n printApplicationHuman(res.data);\n}\n\n// ---------------------------------------------------------------------------\n// claim / show — shared polling\n// ---------------------------------------------------------------------------\n\nconst CLAIM_POLL_INTERVAL_MS = 3_000;\n/** The API's own default for a claim's time limit (`timeoutSeconds`). */\nconst CLAIM_TIMEOUT_SECONDS = 2 * 3600;\n/** Past the claim's time limit, how long to keep polling for the API to report it failed. */\nconst CLAIM_POLL_GRACE_MS = 2 * 60 * 1000;\n\n/**\n * Wait for a claim to settle. The API fails a claim at its time limit\n * (`timeoutSeconds`), so polling outlasts that by `CLAIM_POLL_GRACE_MS`. A\n * timeout or an API error comes back as `{ error }` rather than exiting, so a\n * caller that owns the sandbox (`verify-env`) can still destroy it.\n */\nasync function pollSandboxUntilReady(\n apiUrl: string,\n token: string,\n id: string,\n timeoutSeconds: number\n): Promise<{ data: SandboxData } | { error: string }> {\n const deadline = Date.now() + timeoutSeconds * 1000 + CLAIM_POLL_GRACE_MS;\n let toldUpgrading = false;\n while (Date.now() < deadline) {\n const res = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!res.ok) return { error: res.error ?? 'unknown error' };\n if (res.data.status === 'ready' || res.data.status === 'failed') return { data: res.data };\n if (res.data.upgrading && !toldUpgrading) {\n toldUpgrading = true;\n console.error(' updating this push to the current sandbox platform (once; the claim continues after)...');\n }\n await sleep(CLAIM_POLL_INTERVAL_MS);\n }\n return { error: `timed out waiting for sandbox ${id} to become ready (${Math.round(timeoutSeconds / 60)} min)` };\n}\n\n/** `cofactor sandbox claim --application A [--environment E] [--at V] [--timeout D] [--wait] [--json]`. */\nexport async function handleSandboxClaim(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const environment = getArgValue(argv, '--environment') ?? 'default';\n // Not `--version`: the CLI answers that flag anywhere on the line with its own version.\n const version = getArgValue(argv, '--at');\n const timeout = parseDurationSeconds(getArgValue(argv, '--timeout'), '--timeout');\n const timeoutSeconds = timeout ?? CLAIM_TIMEOUT_SECONDS;\n const wait = hasFlag(argv, '--wait');\n const json = hasFlag(argv, '--json');\n\n const body: Record<string, unknown> = { applicationId, environment };\n if (version) body.version = version;\n // Only when asked: an API from before `timeoutSeconds` refuses the field, and waited the same 2 h.\n if (timeout !== undefined) body.timeoutSeconds = timeout;\n // The API holds the request a few seconds for a warm claim and answers with the ready sandbox; a claim\n // still going comes back as just its id, and is polled below.\n if (wait) body.wait = true;\n\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n code?: string;\n issues?: DefinitionIssue[];\n data: { id: string } | SandboxData;\n }>(apiUrl, token, 'POST', '/org/sandboxes-v2/sandboxes', body);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n // The definition of that version fails validation: say where, as `validate` does.\n if (res.code === 'SANDBOX_V2_DEFINITION_INVALID') printIssues(res.issues, ' ');\n process.exit(1);\n }\n const id = res.data.id;\n\n if (!wait) {\n if (json) {\n writeRawOutput(JSON.stringify({ id }, null, 2));\n } else {\n console.log(`Sandbox claiming: ${id}`);\n console.log(` Poll with: cofactor sandbox show ${id}`);\n }\n return;\n }\n\n const settled = 'status' in res.data && (res.data.status === 'ready' || res.data.status === 'failed') ? res.data : null;\n if (!json && !settled) console.log(`Claiming sandbox ${id}...`);\n const waited = settled ? { data: settled } : await pollSandboxUntilReady(apiUrl, token, id, timeoutSeconds);\n if ('error' in waited) {\n // The sandbox stays yours (its lease still runs): `show` or `destroy` it.\n console.error(`Error: ${waited.error}`);\n process.exit(1);\n }\n const { data } = waited;\n if (json) {\n writeRawOutput(JSON.stringify(data, null, 2));\n } else {\n printSandbox(data);\n }\n if (data.status === 'failed') process.exit(1);\n}\n\n/** `cofactor sandbox show <id> [--json]`. */\nexport async function handleSandboxShow(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Usage: cofactor sandbox show <id> [--json]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n\n const res = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n printSandbox(res.data);\n}\n\n// ---------------------------------------------------------------------------\n// run\n// ---------------------------------------------------------------------------\n\nfunction coerceInputValue(raw: string, type: string): string | number | boolean {\n switch (type) {\n case 'integer': {\n if (!/^-?\\d+$/.test(raw)) throw new Error(`must be an integer, got \"${raw}\"`);\n return Number(raw);\n }\n case 'number': {\n const value = Number(raw);\n if (Number.isNaN(value)) throw new Error(`must be a number, got \"${raw}\"`);\n return value;\n }\n case 'boolean': {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n throw new Error(`must be \"true\" or \"false\", got \"${raw}\"`);\n }\n default:\n // string, persona\n return raw;\n }\n}\n\nfunction buildRunInputFromFlags(argv: string[], propTypes: Record<string, string>): Record<string, unknown> {\n const pairs = getArgValues(argv, '--input');\n const input: Record<string, unknown> = {};\n for (const pair of pairs) {\n const eq = pair.indexOf('=');\n if (eq <= 0) {\n console.error(`Error: invalid --input \"${pair}\". Use key=value.`);\n process.exit(1);\n }\n const key = pair.slice(0, eq);\n const raw = pair.slice(eq + 1);\n const type = propTypes[key];\n if (!type) {\n console.error(`Error: unknown input \"${key}\". Known: ${Object.keys(propTypes).join(', ') || '(none)'}`);\n process.exit(1);\n }\n try {\n input[key] = coerceInputValue(raw, type);\n } catch (error) {\n console.error(`Error: --input ${key}: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n }\n return input;\n}\n\n/** Poll a run every 2 s until it leaves `running`; exits 1 on a poll error or when `timeoutMs` passes. */\nasync function pollRunUntilDone(\n apiUrl: string,\n token: string,\n runId: string,\n timeoutMs: number,\n timeoutNote = ''\n): Promise<RunData> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const pollRes = await apiRequest<RunResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/runs/${runId}`);\n if (!pollRes.ok) {\n console.error(`Error: ${pollRes.error}`);\n process.exit(1);\n }\n if (pollRes.data.status !== 'running') return pollRes.data;\n await sleep(2_000);\n }\n console.error(`Timed out waiting for run ${runId}${timeoutNote}.`);\n process.exit(1);\n}\n\n/** `cofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input '{...}'] [--json]`. */\nexport async function handleSandboxRun(argv: string[], apiUrl: string, token: string): Promise<void> {\n const positional = argv.filter((a) => !a.startsWith('-'));\n const [id, actionRef] = positional;\n if (!id || !actionRef) {\n console.error('Usage: cofactor sandbox run <id> <action|plugin.op> [--input k=v ...] [--json-input \\'{...}\\'] [--json]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n const jsonInput = getArgValue(argv, '--json-input');\n\n const showRes = await apiRequest<SandboxResponse>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n if (!showRes.ok) {\n console.error(`Error: ${showRes.error}`);\n process.exit(1);\n }\n const sandbox = showRes.data;\n const repoAction = sandbox.actions.find((a) => a.slug === actionRef);\n const operation = sandbox.operations.find((op) => op.name === actionRef);\n if (!repoAction && !operation) {\n console.error(`Error: unknown action or operation \"${actionRef}\".`);\n console.error(` Actions: ${sandbox.actions.map((a) => a.slug).join(', ') || '(none)'}`);\n console.error(` Operations: ${sandbox.operations.map((op) => op.name).join(', ') || '(none)'}`);\n process.exit(1);\n }\n\n let input: Record<string, unknown>;\n if (jsonInput !== undefined) {\n try {\n input = JSON.parse(jsonInput) as Record<string, unknown>;\n } catch (error) {\n console.error(`Error: invalid --json-input: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n } else if (repoAction) {\n const propTypes = Object.fromEntries(Object.entries(repoAction.input).map(([key, prop]) => [key, prop.type]));\n input = buildRunInputFromFlags(argv, propTypes);\n } else {\n input = buildRunInputFromFlags(argv, jsonSchemaPropTypes(operation!.inputSchema));\n }\n\n const enqueueRes = await apiRequest<RunEnqueueResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/sandboxes-v2/sandboxes/${id}/runs`,\n { action: actionRef, input }\n );\n if (!enqueueRes.ok) {\n if (enqueueRes.code === 'SANDBOX_V2_ACTION_INVALID_INPUT') {\n console.error(`Error: invalid input for \"${actionRef}\":`);\n for (const issue of enqueueRes.issues ?? []) {\n if (typeof issue === 'string') console.error(` ${issue}`);\n else console.error(` ${(issue.path ?? []).join('.')}: ${issue.message ?? ''}`);\n }\n console.error('');\n console.error('Input schema:');\n writeRawOutput(JSON.stringify(repoAction ? repoAction.input : operation!.inputSchema, null, 2));\n } else {\n console.error(`Error: ${enqueueRes.error}`);\n }\n process.exit(1);\n }\n const runId = enqueueRes.data!.id;\n\n const run = await pollRunUntilDone(apiUrl, token, runId, ((repoAction?.timeout ?? 60) + 60) * 1000);\n\n if (json) {\n writeRawOutput(JSON.stringify(run, null, 2));\n if (run.status === 'failed') process.exit(1);\n return;\n }\n\n if (run.status === 'failed') {\n console.error(`Run failed: ${run.error ?? 'unknown error'}`);\n if (run.stderrTail) {\n console.error('');\n console.error('stderr:');\n console.error(run.stderrTail);\n }\n process.exit(1);\n }\n\n writeRawOutput(JSON.stringify(run.output ?? {}, null, 2));\n}\n\n// ---------------------------------------------------------------------------\n// exec\n// ---------------------------------------------------------------------------\n\nexport type ExecArgs = { id: string; timeoutSeconds?: number; json: boolean; command: string };\n\nconst EXEC_USAGE = 'Usage: cofactor sandbox exec <id> [--timeout <seconds>] [--json] -- <command...>';\n\n/** Parse `exec`'s words: the id and flags (`options`), and the command words after `--` (joined by spaces, like ssh). */\nexport function parseExecArgs(\n options: string[],\n commandWords: string[]\n): { ok: true; args: ExecArgs } | { ok: false; error: string } {\n const command = commandWords.join(' ');\n if (command.trim() === '') return { ok: false, error: `a command is required after \\`--\\`. ${EXEC_USAGE}` };\n const id = v2Positionals('exec', options)[0];\n if (!id) return { ok: false, error: `sandbox id required. ${EXEC_USAGE}` };\n const timeout = getArgValue(options, '--timeout');\n let timeoutSeconds: number | undefined;\n if (timeout !== undefined) {\n timeoutSeconds = /^\\d+$/.test(timeout) ? Number(timeout) : NaN;\n if (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 1 || timeoutSeconds > EXEC_MAX_TIMEOUT_SECONDS) {\n return { ok: false, error: `--timeout must be a whole number of seconds from 1 to ${EXEC_MAX_TIMEOUT_SECONDS}.` };\n }\n }\n return { ok: true, args: { id, timeoutSeconds, json: hasFlag(options, '--json'), command } };\n}\n\n/** The CLI exit code for a finished exec run: the command's own, or 1 when the failure was not the command's. */\nexport function execExitCode(run: Pick<RunData, 'status' | 'exitCode'>): number {\n if (run.status === 'succeeded') return 0;\n return run.exitCode ?? 1;\n}\n\n/** `cofactor sandbox exec <id> [--timeout S] [--json] -- <command...>`: one shell command in a claimed sandbox. */\nexport async function handleSandboxExec(\n options: string[],\n commandWords: string[],\n apiUrl: string,\n token: string\n): Promise<void> {\n const parsed = parseExecArgs(options, commandWords);\n if (!parsed.ok) {\n console.error(`Error: ${parsed.error}`);\n process.exit(1);\n }\n const { id, timeoutSeconds, json, command } = parsed.args;\n\n const enqueueRes = await apiRequest<RunEnqueueResponse>(\n apiUrl,\n token,\n 'POST',\n `/org/sandboxes-v2/sandboxes/${id}/exec`,\n timeoutSeconds === undefined ? { command } : { command, timeoutSeconds }\n );\n if (!enqueueRes.ok) {\n console.error(`Error: ${enqueueRes.error}`);\n process.exit(1);\n }\n const runId = enqueueRes.data!.id;\n\n const run = await pollRunUntilDone(\n apiUrl,\n token,\n runId,\n ((timeoutSeconds ?? EXEC_DEFAULT_TIMEOUT_SECONDS) + 60) * 1000,\n '; the command may still be running in the sandbox'\n );\n\n const code = execExitCode(run);\n if (json) {\n writeRawOutput(JSON.stringify(run, null, 2));\n process.exit(code);\n }\n if (run.status === 'failed' && run.exitCode === null) {\n // Run text goes out raw: console output is brand-rewritten.\n process.stderr.write(`Error: ${run.error ?? 'unknown error'}\\n`);\n if (run.stdoutTail) process.stderr.write(run.stdoutTail);\n if (run.stdoutTail && !run.stdoutTail.endsWith('\\n') && run.stderrTail) process.stderr.write('\\n');\n if (run.stderrTail) process.stderr.write(run.stderrTail);\n process.exit(1);\n }\n if (run.stdoutTail) process.stdout.write(run.stdoutTail);\n if (run.stderrTail) process.stderr.write(run.stderrTail);\n if (code === 124 && run.error) process.stderr.write(`\\n${run.error}\\n`);\n process.exit(code);\n}\n\n// ---------------------------------------------------------------------------\n// destroy\n// ---------------------------------------------------------------------------\n\n/** `cofactor sandbox destroy <id>`. */\nexport async function handleSandboxDestroy(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id] = argv.filter((a) => !a.startsWith('-'));\n if (!id) {\n console.error('Usage: cofactor sandbox destroy <id>');\n process.exit(1);\n }\n const res = await apiRequest<{ ok: boolean; error?: string; data: { id: string } }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/sandboxes-v2/sandboxes/${id}`\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n console.log(`Sandbox ${id} destroying.`);\n}\n\n// ---------------------------------------------------------------------------\n// verify-env\n// ---------------------------------------------------------------------------\n\n/** `cofactor sandbox verify-env --application A [--environment E] [--json]`. */\nexport async function handleSandboxVerifyEnv(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const onlyEnvironment = getArgValue(argv, '--environment');\n const json = hasFlag(argv, '--json');\n // With --json, stdout carries only the result array; progress goes to stderr.\n const log = (line: string) => (json ? console.error(line) : console.log(line));\n\n const app = await fetchApplicationSandbox(apiUrl, token, applicationId);\n if (!app.version || !app.definition) {\n console.error('Error: this application has no version to verify yet: run `cofactor sandbox push` first.');\n process.exit(1);\n }\n const version = app.version;\n const allEnvironments = app.definition.environments.map((e) => e.name);\n if (onlyEnvironment && !allEnvironments.includes(onlyEnvironment)) {\n console.error(`Error: environment \"${onlyEnvironment}\" not found. Known: ${allEnvironments.join(', ')}`);\n process.exit(1);\n }\n const environments = onlyEnvironment ? [onlyEnvironment] : allEnvironments;\n\n type PersonaCheck = { persona: string; ok: boolean; error?: string };\n type Result = { environment: string; ok: boolean; seconds: number; error?: string; personas?: PersonaCheck[] };\n const results: Result[] = [];\n\n // Sequential on purpose — the fleet's placer fails rather than queues.\n for (const environment of environments) {\n log(`\\n=== ${environment} ===`);\n const started = Date.now();\n const claimRes = await apiRequest<{ ok: boolean; error?: string; data: { id: string } }>(\n apiUrl,\n token,\n 'POST',\n '/org/sandboxes-v2/sandboxes',\n { applicationId, environment, version }\n );\n if (!claimRes.ok) {\n const seconds = (Date.now() - started) / 1000;\n console.error(` claim failed: ${claimRes.error}`);\n results.push({ environment, ok: false, seconds, error: claimRes.error });\n continue;\n }\n const id = claimRes.data.id;\n log(` claimed ${id}, waiting...`);\n const waited = await pollSandboxUntilReady(apiUrl, token, id, CLAIM_TIMEOUT_SECONDS);\n const seconds = (Date.now() - started) / 1000;\n if ('error' in waited || waited.data.status !== 'ready') {\n // A timeout or a failed claim still falls through to the destroy below.\n const error = 'error' in waited ? waited.error : (waited.data.error ?? 'failed');\n console.error(` failed: ${error}`);\n results.push({ environment, ok: false, seconds, error });\n } else {\n const { data } = waited;\n log(` ready in ${data.readyMs !== null ? (data.readyMs / 1000).toFixed(1) : '?'}s`);\n for (const [stepId, outputs] of Object.entries(data.seedOutputs)) {\n for (const [key, value] of Object.entries(outputs)) {\n log(` ${stepId}.${key}: ${JSON.stringify(value)}`);\n }\n }\n // Sign every persona in as a Login step would, so a broken login fails here, not in a test.\n const personas: PersonaCheck[] = [];\n if (data.nativeLogin) {\n for (const { name } of data.personas) {\n const checked = await apiRequest<{ ok: boolean; error?: string; data: PersonaCheck }>(\n apiUrl,\n token,\n 'POST',\n `/org/sandboxes-v2/sandboxes/${id}/auth-check`,\n { persona: name },\n // The API waits on the script (up to 60 s) and the sandbox connection.\n { timeoutMs: 90_000 }\n );\n const check = checked.ok ? checked.data : { persona: name, ok: false, error: checked.error ?? 'unknown error' };\n personas.push(check);\n if (check.ok) log(` persona ${name}: signed in`);\n else console.error(` persona ${name}: FAILED\\n${indentLines(check.error ?? '', ' ')}`);\n }\n }\n const failedPersonas = personas.filter((p) => !p.ok).map((p) => p.persona);\n results.push({\n environment,\n ok: failedPersonas.length === 0,\n seconds,\n ...(failedPersonas.length > 0 ? { error: `persona sign-in failed: ${failedPersonas.join(', ')}` } : {}),\n ...(data.nativeLogin ? { personas } : {}),\n });\n }\n log(` destroying ${id}...`);\n await apiRequest(apiUrl, token, 'DELETE', `/org/sandboxes-v2/sandboxes/${id}`);\n }\n\n log('');\n log('Summary:');\n log(` ${pad('Environment', 20)} ${pad('Result', 6)} Seconds`);\n for (const r of results) {\n const errSuffix = r.error ? ` (${r.error})` : '';\n log(` ${pad(r.environment, 20)} ${pad(r.ok ? 'OK' : 'FAIL', 6)} ${r.seconds.toFixed(1)}${errSuffix}`);\n }\n\n if (json) writeRawOutput(JSON.stringify(results, null, 2));\n if (results.some((r) => !r.ok)) process.exit(1);\n}\n","import { createHash, randomBytes } from 'node:crypto';\nimport { parse } from 'yaml';\nimport { z } from 'zod';\nimport { findOperation, getPlugin } from '@chatsdet/emulation';\nimport {\n OBJECT_STORAGE_PORT,\n OBJECT_STORAGE_PORT_NAME,\n OBJECT_STORAGE_PROVIDERS,\n OBJECT_STORAGE_SERVICE_NAMES,\n type ObjectStorageProvider,\n RESERVED_STORAGE_PORTS,\n objectStorageServices,\n} from './object-storage';\nimport { SYNC_PORT, SYNC_PORT_NAME, SYNC_SERVICE_NAME, syncKeySchema } from './sync-protocol';\nimport { emulationParamRefs, emulationReferenceIssues } from './emulation-refs';\n\nexport { emulationConfigFor, emulationSecretRefs, missingEmulationSecret } from './emulation-refs';\n\n// For the API (the helper-layer route presigns each provider's layer; the app URL skips the storage port) and push.\nexport { OBJECT_STORAGE_PORT_NAME, OBJECT_STORAGE_PROVIDERS, type ObjectStorageProvider };\n// For the API (hidden from the app URL like the other implicit ports).\nexport { SYNC_PORT_NAME };\n\n/** The implicit port that carries the emulator's public origin; reserved, never declared. */\nexport const EMULATION_PORT_NAME = 'emulation';\n\n/**\n * The sandbox definition: `cofactor.sandbox.yaml`, at a repository's root or\n * (for a monorepo) under a definition directory. Pure schema and derivations;\n * the guest-side paths live in runtime.ts, so the API can import this module\n * to validate a definition it read from GitHub.\n */\nexport const DEFINITION_FILE = 'cofactor.sandbox.yaml';\n\n/**\n * How an application's code reaches its sandboxes. `github`: Cofactor clones\n * the repository and the definition installs (`setup`) and builds\n * (`lifecycle.build`) it. `upload`: the customer pushes a built image with\n * `cofactor sandbox push`, so the definition has neither.\n */\nexport type Delivery = 'github' | 'upload';\n\n/**\n * An uploaded image's Cofactor files, layered in by `cofactor sandbox push`:\n * the definition and the image's own config (`ImageConfig`). Their presence\n * is what makes a sandbox an uploaded one.\n *\n * A contract: a push keeps its own layer when the API upgrades it to a newer\n * helper (specs/pending/sandboxes-v2-transparent-upgrades.md), so these paths,\n * `/etc/cofactor` and the uid 1000 sandbox user (runtime.ts) don't move\n * without a contract version.\n */\nexport const IMAGE_DIR = '/cofactor';\nexport const IMAGE_DEFINITION_FILE = `${IMAGE_DIR}/${DEFINITION_FILE}`;\nexport const IMAGE_CONFIG_FILE = `${IMAGE_DIR}/image.json`;\n\n/**\n * Where `image.sh` publishes the helper layer of a platform version, in the\n * API's bucket, for `cofactor sandbox push` to add to an uploaded image.\n * Republishing the same version replaces it.\n */\nexport function helperLayerKey(platformVersion: number): string {\n return `sandbox-v2/helper/v${platformVersion}.tar`;\n}\n\n/**\n * Where `image.sh` publishes a provider's object storage server (one binary\n * under /usr/local/bin) for a platform version, next to the helper layer.\n * `cofactor sandbox push` adds it only when the definition declares that\n * provider, so no push carries a server it doesn't use.\n */\nexport function objectStorageLayerKey(provider: ObjectStorageProvider, platformVersion: number): string {\n return `sandbox-v2/object-storage/${provider}/v${platformVersion}.tar`;\n}\n\n/**\n * The PATH the helper (as root) finds programs on, and every child starts\n * from before the image's and the definition's env. A Blackcat rootfs\n * import does not apply the image's Docker ENV.\n */\nexport const BASE_PATH = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';\n\n/**\n * What root runs, and so what the sandbox account (uid 1000, given the\n * WORKDIR) must never own: a WORKDIR at or above any of these (the programs\n * root finds, the libraries and loader they link), or inside a directory\n * root reads its configuration from, would hand it the means to run code\n * as root.\n */\nconst ROOT_RUN_PATHS = [\n ...BASE_PATH.split(':'),\n '/lib',\n '/lib64',\n '/usr/lib',\n '/usr/lib64',\n '/usr/libexec',\n '/etc',\n // Devices and kernel interfaces, and root's own home.\n '/dev',\n '/proc',\n '/sys',\n '/root',\n IMAGE_DIR,\n];\nconst ROOT_CONFIG_DIRS = ['/etc', IMAGE_DIR];\n\n/** Whether the sandbox account may be given `dir` (an image's WORKDIR, as declared or as resolved). */\nexport function isSafeWorkdir(dir: string): boolean {\n // A canonical absolute path: no `.`/`..` segments, repeated or trailing slashes.\n const segments = dir.split('/').slice(1);\n if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) return false;\n if (ROOT_RUN_PATHS.some((path) => path === dir || path.startsWith(`${dir}/`))) return false;\n return !ROOT_CONFIG_DIRS.some((path) => dir.startsWith(`${path}/`));\n}\n\n/**\n * What an uploaded image's own config would have applied. Blackcat's import\n * keeps the filesystem but drops the image's ENV and WORKDIR, so\n * `cofactor sandbox push` records them at `/cofactor/image.json` and the\n * helper runs every service, prepare command and action with them.\n */\nexport const imageConfigSchema = z.strictObject({\n /** Where children run. The helper gives it to the sandbox account, so never `/` or a system directory. */\n workdir: z\n .string()\n .startsWith('/', 'the image needs an absolute WORKDIR')\n .refine(isSafeWorkdir, 'the image needs a WORKDIR of its own, such as /app (not /, nor a system directory)'),\n env: z.record(z.string(), z.string()),\n});\nexport type ImageConfig = z.infer<typeof imageConfigSchema>;\n\n/**\n * Bumped whenever the helper or its contract with the platform layer changes\n * incompatibly. Old layers keep rejecting the new schema until they are\n * rebuilt from a definition of this version, so it is part of both the\n * platform template's identity and the toolchain key.\n *\n * The deploy pipeline publishes a new version's platform image and helper\n * layers (`image.sh`) before each stage's `sst deploy`, so the older notes\n * below that say to republish them by hand describe what is now automatic.\n *\n * 3: `prepareInputSchema` gained a required `copy` field and the `port` param\n * variant gained `live` — an old helper's `strictObject`s reject both, so\n * every layer built from an older platform version (toolchain, base, ready)\n * must be rebuilt from this one before any `prepare` (full or copy) can\n * succeed. Bumping this forces that rebuild via `toolchainKey`/\n * `platformTemplateName`; the platform image itself must also be republished\n * (`packages/sandbox-helper/image.sh`) as part of rolling this out — see\n * specs/pending/sandboxes-v2-ready-snapshots.md.\n *\n * 4: the helper's contract grew `object_storage` (an old helper's\n * `strictObject` rejects the field, and it has neither the implicit storage\n * services nor the `object-storage-front` command). Same rollout as 3: layers\n * rebuild, the platform image (which now carries both providers' servers) is\n * republished, and every existing push must be pushed again — see\n * specs/pending/sandboxes-v2-object-storage.md.\n *\n * 5: Blackcat's Firecracker 1.17 (alpha.68+) moved the snapshot format (6.0.0\n * to 12.0.0) with no migration, so no template built before it restores. The\n * helper's contract is unchanged; the bump only retires every recorded template\n * row and names the rebuilt platform template anew (`clearStaleToolchain`).\n * Republish the platform image and helper layers with `image.sh` for each stage\n * as part of rolling this out — see docs/features/blackcat-execution-platform.md\n * (\"Rebuilding templates\").\n *\n * 6: the guest helper gained the private `auth` command. Native persona login\n * requires its script; rebuild and republish helper layers and\n * ready snapshots so old helpers cannot silently accept the new contract.\n *\n * 8: the helper installs the pinned Node runtime from the official archive.\n * Its tar entries are owned by uid 1001, so root extraction must not preserve\n * that ownership; publish a fresh immutable helper/platform image with the\n * archive extracted as root before enforcing the existing ownership checks.\n *\n * 9: the pinned Playwright install uses distinct empty npm user/global config\n * files. This avoids npm rejecting a shared config path while keeping config\n * outside the uploaded app and deleting it after installation.\n *\n * 10: install Git because pinned test-source preparation failed on a builder\n * with `spawn git ENOENT`.\n *\n * 11: a service whose command isn't found or can't run (exit 127/126) fails the\n * boot at once instead of crash-looping until the health deadline, and an\n * uploaded image's own PATH survives the login profile (`loginShell`). The\n * first bump after pushed images upgrade themselves on first claim\n * (specs/pending/sandboxes-v2-transparent-upgrades.md): its rollout runs that\n * spec's live check on dev before prod.\n *\n * 12: `authentication` is `run` or `emulated`; the emulator gained the\n * authorize endpoint and per-persona identities; every google-oauth\n * definition needs personas. An old helper's `strictObject` rejects\n * `authentication.emulated`, and an old platform image's emulator has no\n * authorize endpoint. Republish the helper layers and the platform image with\n * `image.sh` for each stage as part of rolling this out; the dev live check\n * runs before prod.\n *\n * 13 is unused: the data fast-forward stack below was planned as 13 and\n * landed after 14.\n *\n * 14: the platform emulator gained the `oidc` plugin, the sign-in gate and\n * public-origin dispatch, and creates its CA at start; the helper's\n * validation (`oidc` provider, `{ port: emulation }` params) and `auth`\n * branch changed. Republish the helper layers, the platform image and ready\n * snapshots (docs/features/blackcat-execution-platform.md, \"Rebuilding\n * templates\"); every existing push must be pushed again.\n *\n * The `exec` subcommand needed no bump; see the comment at `case 'exec'` in\n * cli.ts. Remove the API's SANDBOX_V2_EXEC_NEEDS_UPGRADE detection once every\n * live sandbox is on a platform version that has `exec` (15 or later).\n *\n * 15: the guest helper gained `databases` (a top-level block) and the seed\n * step key `operation`, and the `operation <name>` subcommand that runs the\n * built-in `database.fast-forward` — an old helper's strictObjects reject\n * both fields and it has no such command. Same rollout as 3-6: layers\n * rebuild, republish the platform image (`image.sh`) on dev and prod, and\n * re-push every pushed image — see specs/pending/sandboxes-v2-data-fast-forward.md.\n *\n * 16: the platform emulator's Jira plugin serves `api.atlassian.com` and\n * `auth.atlassian.com` (#4494), which the app calls through the OAuth gateway.\n * The v15 image predates that, so its TLS certificate covered only the tenant\n * host and every Jira call from a sandbox failed the hostname check. The\n * emulator is built into the platform image, so a published v15 never picks the\n * change up. Same rollout as 15: the deploy publishes the v16 image, ready\n * snapshots rebuild, and every pushed image is pushed again.\n *\n * 17: every definition gains the implicit public `sync` port and the\n * `cofactor-sync` service (`cofactor-sandbox sync-server`, sync-server.ts),\n * which syncs a claimer's working tree into the copy; `prepareInputSchema`\n * gained a required `sync: {key}`, and the helper gained the `restart`\n * subcommand. An old helper's strictObject rejects `sync`, and an old layer\n * has neither the port nor the service. Same rollout as 15 and 16: layers rebuild,\n * the deploy publishes the v17 platform image and helper layers (`image.sh`),\n * and every pushed image is pushed again or upgraded transparently on its\n * next claim. A definition already at 8 ports stops validating (the `sync`\n * port counts toward Blackcat's cap).\n *\n * 18: a copy's `prepare` keeps its snapshot's personas (`personas` must be\n * empty, and `lifecycle.prepare` still gets the snapshot's); `prepare`\n * prints the sandbox's personas. An old helper rejects the empty `personas`\n * and prints none. Same rollout as 17: layers and ready snapshots rebuild,\n * and the deploy publishes the v18 platform image and helper layers; every\n * pushed image is pushed again or upgraded transparently on its next claim.\n * A v17 sandbox re-prepared after the deploy fails (a v17 helper rejects the\n * empty `personas`); it is transient, as for the earlier bumps.\n *\n * 19: `auth` reports a failing `authentication.run`'s exit code and stderr\n * tail (`AUTH_SCRIPT_FAILURE_PREFIX`), which the API puts into the Login step\n * error and `verify-env`'s persona check. No contract change: a v18 helper\n * just prints no such line, and its failures read as before. Same rollout as\n * 18: the deploy publishes the v19 platform image and helper layers, ready\n * snapshots rebuild, and every pushed image is upgraded on its next claim.\n *\n * 20: add copy-values/copy-sync-key commands, apply public URLs to live-env.json\n * and prepare.json without restarting services, and read emulator origins per use.\n * Shipping requires publishing the v20 platform image and helper layers. The version\n * changes toolchain keys, so ready snapshots rebuild; pushed images receive the new helper.\n */\nexport const PLATFORM_VERSION = 20;\n\n/**\n * The browser layer (`/opt/cofactor-browser`, built by\n * `packages/sandbox-helper/browser/build-layer.sh` from `layer.lock.json`):\n * Chromium (the build the worker runs, so captures match the worker's), Node,\n * playwright-core, ffmpeg, fonts and their libraries, for running a test\n * session's browser inside its own copy.\n *\n * It is not part of the platform image and has no bearing on\n * `PLATFORM_VERSION`: `image.sh` publishes it as a second image\n * (`browserImageTag`), the published platform image plus this one layer, and\n * only an org with the in-sandbox browser flag builds its layers on that image\n * (`platformTemplateName`, `toolchainKey`). Every other org's templates, keys\n * and copies are exactly what they were without it.\n *\n * Bump it whenever the layer changes (the lock, the build, what runs from it),\n * including every Playwright bump: Playwright's `connect()` needs the same\n * version on both sides (browser-layer.test.ts checks the pin, and that\n * the layer's inputs never change under a published version). A bump publishes\n * a new browser image on the next deploy and rebuilds only the flagged orgs'\n * toolchains.\n */\nexport const BROWSER_LAYER_VERSION = 2;\n\n/** The browser layer an application's layers build on: its version, or `null` for the platform image alone. */\nexport type BrowserLayer = number | null;\n\n/** Emulator providers `authentication.emulated` can sign in through. */\nexport const EMULATED_PROVIDER_IDS = ['google-oauth', 'oidc'] as const;\nexport type EmulatedProviderId = (typeof EMULATED_PROVIDER_IDS)[number];\n\n/** What the helper's `auth` command prints before a stable category on stderr; the API logs lines of this shape. */\nexport const AUTH_FAILURE_PREFIX = 'authentication failed: ';\n\n/** How long `authentication.run` may take before the helper kills it. */\nexport const AUTH_SCRIPT_TIMEOUT_MS = 60_000;\n/** How much of a failing `authentication.run`'s stderr (its end) the helper reports. */\nexport const AUTH_SCRIPT_STDERR_TAIL_CHARS = 4000;\n\n/**\n * Before `authentication failed: script_failed`, the helper prints one line\n * with this prefix and the failing `authentication.run`'s exit and stderr\n * tail (scrubbed of the copy's secrets in the guest), so the Login step and\n * `verify-env` can say why. Stdout is never included: it is the session\n * state channel. A v18 helper prints no such line.\n */\nexport const AUTH_SCRIPT_FAILURE_PREFIX = 'authentication script failed: ';\nconst authScriptFailureSchema = z.strictObject({\n exitCode: z.number().int(),\n timedOut: z.boolean(),\n stderr: z.string(),\n});\nexport type AuthScriptFailure = z.infer<typeof authScriptFailureSchema>;\n\nexport function formatAuthScriptFailure(failure: AuthScriptFailure): string {\n return `${AUTH_SCRIPT_FAILURE_PREFIX}${JSON.stringify(failure)}`;\n}\n\n/** The helper's script-failure line from its stderr; null when there is none (or it is malformed). */\nexport function parseAuthScriptFailure(helperStderr: string): AuthScriptFailure | null {\n const line = helperStderr.split('\\n').find((candidate) => candidate.startsWith(AUTH_SCRIPT_FAILURE_PREFIX));\n if (!line) return null;\n try {\n const parsed = authScriptFailureSchema.safeParse(JSON.parse(line.slice(AUTH_SCRIPT_FAILURE_PREFIX.length)));\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\n/** `cofactor-sandbox build`/`action` exit codes its callers act on. */\nexport const EXIT = {\n /** The commit's definition needs another toolchain: build it on a sandbox of that one. */\n TOOLCHAIN_MISMATCH: 3,\n /**\n * The commit declares personas or per-sandbox params (or a resolved\n * environment chain) this prepared sandbox was not given: prepare a new\n * sandbox for it instead.\n */\n PREPARE_MISMATCH: 4,\n /** The action script exited non-zero, or was killed at its timeout. */\n ACTION_FAILED: 5,\n /** The action is unknown, its input failed validation, or the sandbox has not been prepared. */\n INVALID_INPUT: 6,\n /** The action produced no parseable JSON result line, or its result failed the output schema. */\n INVALID_OUTPUT: 7,\n /** Authentication did not return valid, bounded storage state. */\n AUTH_FAILED: 8,\n} as const;\n\n// Every toolchain gets what `build` itself needs to clone and fetch.\nexport const SETUP_PRELUDE = `set -euo pipefail\nexport DEBIAN_FRONTEND=noninteractive\napt-get update -qq\napt-get install -y -qq --no-install-recommends git ca-certificates curl openssl >/dev/null\n`;\n\nconst NAME = /^[a-z][a-z0-9-]{0,31}$/;\nconst PARAM = /^[a-z][a-z0-9_]*$/;\nexport const ENV_NAME = /^[A-Z_][A-Z0-9_]*$/;\n/** An emulated sign-in's start URL: one port param, then a path. */\nconst SIGN_IN_URL = /^\\$\\{params\\.([a-z][a-z0-9_]*)\\}\\/[^\\s$]*$/;\nconst PARAM_REF = /\\$\\{params\\.([a-z][a-z0-9_]*)\\}/g;\n/** Action/operation input and output property names. */\nconst IO_NAME = /^[a-z][a-z0-9_]*$/;\n/** A seed step id, referenced as `${id.key}`. */\nconst STEP_ID = /^[a-z][a-z0-9_]*$/;\n/** A `${stepId.key}` reference, anywhere in a string. */\nconst STEP_REF = /\\$\\{([a-z][a-z0-9_]*)\\.([a-z][a-z0-9_]*)\\}/g;\n/** The whole string is exactly one reference — the value is the raw output, not a string interpolation. */\nconst EXACT_STEP_REF = /^\\$\\{([a-z][a-z0-9_]*)\\.([a-z][a-z0-9_]*)\\}$/;\n/** A bucket name both providers accept: 3–63 lowercase letters, digits, `-` and `.`, starting and ending alphanumeric. */\nconst BUCKET = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\nconst GCS_RESERVED_BUCKETS = new Set(['storage', 'upload', 'download', 'batch']);\n\nconst health = z.union([\n z.strictObject({ command: z.string().min(1) }),\n z.strictObject({ http: z.number().int().min(1).max(65535), path: z.string().startsWith('/') }),\n]);\n\nconst service = z.strictObject({\n run: z.string().min(1),\n health,\n /** Not started until the build lifecycle has completed (it needs the build's output). */\n after_build: z.boolean().default(false),\n /**\n * Runs with per-sandbox values: never started in a layer (whose params are\n * the definition's defaults), started by the first `prepare`, restarted by\n * every later one.\n */\n reload_on_prepare: z.boolean().default(false),\n});\n\n/**\n * A per-sandbox value. A string is a fixed value. `{port}` is the sandbox's\n * public origin for that port; `{secret: true}` is one random value made when\n * the ready snapshot is built and shared by every copy of it (e.g. a\n * session-signing key); it is not unique per claim. Layers, which no one is\n * handed, see a local origin and a placeholder instead.\n *\n * A `port` param may set `live: true`: its value changes every time a\n * sandbox is restored as a fresh copy (each copy gets its own public URLs),\n * but nothing needs to restart for the sandbox's app to pick up the new\n * value — the app is expected to re-read it at request time instead (see\n * `resolveLiveEnv`). `prepare` writes every live param's resolved env to\n * `LIVE_ENV_FILE` (runtime.ts) on every call, and skips restarting\n * `reload_on_prepare` services when nothing else changed (`restartRequired`).\n * Not offered on `secret` params — see the `auth_secret` row of the per-copy\n * work table in specs/pending/sandboxes-v2-ready-snapshots.md for why.\n */\nconst param = z.union([\n z.string(),\n z.strictObject({ port: z.string().regex(NAME), live: z.boolean().optional() }),\n z.strictObject({ secret: z.literal(true) }),\n]);\n\nconst buildStep = z.strictObject({\n name: z.string().regex(NAME),\n run: z.string().min(1),\n /**\n * Git pathspecs over tracked files (`:(glob)` magic is applied), relative to\n * the definition's directory. The step reruns when the blob ids they match\n * change, or when an earlier step ran.\n */\n inputs: z.array(z.string().min(1)).min(1),\n});\n\nconst literalValue = z.union([z.string(), z.number(), z.boolean()]);\n\n/** A `persona` input takes one of the definition's declared persona names, as a string. */\nconst inputProp = z.strictObject({\n type: z.enum(['string', 'number', 'integer', 'boolean', 'persona']),\n description: z.string().optional(),\n required: z.boolean().optional(),\n default: literalValue.optional(),\n enum: z.array(z.union([z.string(), z.number()])).optional(),\n /** Never echoed back by anything that logs or returns action input. */\n sensitive: z.boolean().optional(),\n});\n\nconst outputProp = z.strictObject({\n type: z.enum(['string', 'number', 'integer', 'boolean']),\n description: z.string().optional(),\n});\n\n/** A repository action: a typed script, the one way data gets into a sandbox. */\nconst action = z.strictObject({\n description: z.string().min(1),\n /** Script path, relative to the definition's directory; run via `bash`. */\n run: z.string().min(1),\n input: z.record(z.string().regex(IO_NAME), inputProp).default({}),\n output: z.record(z.string().regex(IO_NAME), outputProp).default({}),\n timeout: z.number().int().min(1).max(1800).default(60),\n});\n\n/** Database engines `databases:` can declare. One member today; the discriminator MySQL extends. */\nconst DATABASE_ENGINES = ['postgres'] as const;\n\n/** The whole `url` value: one `${env.NAME}` reference, resolved by the operation in the guest. */\nexport const ENV_REF = /^\\$\\{env\\.([A-Z_][A-Z0-9_]*)\\}$/;\n\nconst database = z.strictObject({\n engine: z.enum(DATABASE_ENGINES),\n /** `${env.NAME}`: the connection string is the value of NAME in the env an action would see. */\n url: z.string().regex(ENV_REF, 'must be exactly ${env.NAME}, naming an env var or repository secret'),\n /** When the dump was captured, in UTC (`2026-09-30T00:00:00Z`). The first fast-forward shifts from here. */\n as_of: z.iso.datetime(),\n});\n\n/** The built-in operation's name, as a seed's `operation` and as a `sandbox run` ref. */\nexport const FAST_FORWARD = 'database.fast-forward' as const;\n\nconst seedStep = z\n .strictObject({\n id: z.string().regex(STEP_ID),\n /** A declared action's slug, or `<plugin>.<op>` for an emulator operation. */\n action: z.string().min(1).optional(),\n /** A platform built-in operation. */\n operation: z.literal(FAST_FORWARD).optional(),\n input: z.record(z.string(), literalValue).default({}),\n })\n .refine((step) => (step.action === undefined) !== (step.operation === undefined), {\n message: 'a seed step has exactly one of \"action\" or \"operation\"',\n });\n\nconst environment = z.strictObject({\n description: z.string().min(1),\n extends: z.string().regex(NAME).optional(),\n seeds: z.array(seedStep).default([]),\n /** Merged parent-first, this environment's values last. */\n env: z.record(z.string().regex(ENV_NAME), z.string()).default({}),\n});\n\n/**\n * Run by `prepare` after the sandbox's services have restarted with its\n * values. Each param is also in their env as COFACTOR_PARAM_<NAME>.\n */\nconst prepareCommands = z.array(z.string().min(1));\n\n/** Every field both deliveries share; see `definitionSchema` and `githubDefinitionSchema`. */\nconst sharedFields = {\n version: z.literal(1),\n /** The machine every sandbox of this application gets; a promoted layer keeps it. */\n resources: z.strictObject({\n vcpus: z.number().int().min(1).max(32),\n memory_mib: z.number().int().min(1024).max(65536),\n }),\n ports: z.record(\n z.string().regex(NAME),\n z.strictObject({ port: z.number().int().min(1).max(65535), access: z.enum(['public', 'private']) })\n ),\n /**\n * Per-sandbox values, set by `prepare`. Build steps and layers see the\n * layer value (see `param`); `reload_on_prepare` services and prepare\n * commands see the sandbox's own.\n */\n params: z.record(z.string().regex(PARAM), param),\n /** `${params.NAME}` is replaced by the parameter's value. */\n env: z.record(z.string().regex(ENV_NAME), z.string()),\n services: z.record(z.string().regex(NAME), service),\n /**\n * The users a sandbox is seeded with, by name. Cofactor generates each\n * one's password when the snapshot is built (every copy of it shares them)\n * and hands it to `lifecycle.prepare` only (as COFACTOR_PERSONAS, a JSON\n * map of name to email and password).\n */\n personas: z.record(z.string().regex(NAME), z.strictObject({ email: z.email() })).default({}),\n /**\n * How `cofactor-sandbox auth` mints browser state for one declared persona:\n * a private script (`run`), or a sign-in the helper drives itself through a\n * declared emulator (`emulated`).\n */\n authentication: z.union([\n z.strictObject({\n run: z.string().min(1).refine(\n (path) => !path.startsWith('/') && path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'),\n 'must be a relative script path inside the definition directory'\n ),\n }),\n z.strictObject({\n emulated: z.strictObject({\n provider: z.enum(EMULATED_PROVIDER_IDS),\n /** `${params.<port param>}<path>`: the app URL that starts sign-in. */\n sign_in: z.string().regex(SIGN_IN_URL),\n }),\n }),\n ]).optional(),\n /** Typed scripts: the one way data gets into a sandbox (seeds, the CLI, agents). */\n actions: z.record(z.string().regex(NAME), action).default({}),\n /** Emulator plugin id -> its config. Declaring any plugin adds the implicit `emulation` port. */\n emulation: z.record(z.string().regex(NAME), z.record(z.string(), z.unknown())).default({}),\n /** Databases a built-in operation (`database.fast-forward`) works on, by name. */\n databases: z.record(z.string().regex(NAME), database).default({}),\n /**\n * An emulated S3 or GCS server with these buckets, created before the first\n * prepare. Declaring it adds the implicit public `object-storage` port and\n * the services that run the server (`objectStorageServices`), and gives\n * every child the provider's standard client env (`objectStorageEnv`).\n */\n object_storage: z\n .strictObject({\n provider: z.enum(OBJECT_STORAGE_PROVIDERS),\n buckets: z\n .array(\n z\n .string()\n .regex(BUCKET, '3–63 lowercase letters, digits, \"-\" and \".\", starting and ending with a letter or digit')\n // Names the real services refuse too, so an app's buckets carry over.\n .refine((name) => !name.includes('..'), 'no consecutive dots')\n .refine((name) => !/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(name), 'not shaped like an IP address')\n )\n .min(1)\n .refine((buckets) => new Set(buckets).size === buckets.length, 'bucket names must be unique'),\n })\n // The GCS front reads `/<bucket>/…` as an XML-style upload unless it is a\n // JSON API path (`/storage/v1/…`, `/upload/storage/v1/…`): those first\n // segments can't be buckets.\n .refine(\n (storage) => storage.provider !== 'gcs' || !storage.buckets.some((name) => GCS_RESERVED_BUCKETS.has(name)),\n { message: `a GCS bucket can't be named ${[...GCS_RESERVED_BUCKETS].join(', ')}`, path: ['buckets'] }\n )\n .optional(),\n /**\n * Named flavors of the same sandbox (different starting data and config).\n * Must include `default`; cross-checked, not schema-enforced, so the issue\n * carries a path.\n */\n environments: z.record(z.string().regex(NAME), environment),\n};\n\n/** Playwright storage state returned by `cofactor-sandbox auth`. */\nexport const sandboxAuthResultSchema = z.strictObject({\n cookies: z.array(z.strictObject({\n name: z.string(), value: z.string(), domain: z.string(), path: z.string(),\n expires: z.number(), httpOnly: z.boolean(), secure: z.boolean(),\n sameSite: z.enum(['Strict', 'Lax', 'None']),\n })),\n origins: z.array(z.strictObject({\n origin: z.string(),\n localStorage: z.array(z.strictObject({ name: z.string(), value: z.string() })),\n })),\n}).refine(\n (state) => state.cookies.length > 0 || state.origins.some((origin) => origin.localStorage.length > 0),\n 'must contain at least one cookie or local-storage entry'\n);\nexport type SandboxAuthResult = z.infer<typeof sandboxAuthResultSchema>;\nexport const SANDBOX_AUTH_MAX_BYTES = 1024 * 1024;\n\n/** Reject malformed or overlarge helper results without including state values in diagnostics. */\nexport function parseSandboxAuthResult(value: string): SandboxAuthResult | null {\n if (Buffer.byteLength(value, 'utf8') > SANDBOX_AUTH_MAX_BYTES) return null;\n let parsed: unknown;\n try { parsed = JSON.parse(value); } catch { return null; }\n const result = sandboxAuthResultSchema.safeParse(parsed);\n return result.success ? result.data : null;\n}\n\n/**\n * What every delivery's definition has: how the sandbox runs, prepares and\n * seeds. An uploaded image is already built, so its definition is exactly this.\n */\nconst definitionSchema = z.strictObject({\n ...sharedFields,\n lifecycle: z.strictObject({ prepare: prepareCommands }),\n});\n\n/** A GitHub-delivered definition also installs and builds the checkout. */\nconst githubDefinitionSchema = definitionSchema.extend({\n /**\n * Root shell script that installs the toolchain (packages, runtimes,\n * database clusters). With the resources, it keys the toolchain layer.\n */\n setup: z.string().min(1),\n lifecycle: z.strictObject({\n build: z.array(buildStep).min(1),\n prepare: prepareCommands,\n }),\n});\n\n/** What every delivery's definition has; everything after the build takes this. */\nexport type Definition = z.infer<typeof definitionSchema>;\n/** A GitHub-delivered definition: a `Definition` plus how to install and build the checkout. */\nexport type GithubDefinition = z.infer<typeof githubDefinitionSchema>;\nexport type ServiceSpec = Definition['services'][string];\nexport type BuildStep = GithubDefinition['lifecycle']['build'][number];\nexport type ActionDef = Definition['actions'][string];\nexport type SeedStep = Definition['environments'][string]['seeds'][number];\n\nexport type Environment = Definition['environments'][string];\n\n/** What a seed step runs, as the string every record of it keeps (the action-run row, `buildPhases.seeds`). */\nexport function seedStepTarget(step: SeedStep): string {\n // The schema guarantees exactly one of the two is set.\n return step.operation ?? step.action!;\n}\n\n/** One problem found while parsing or cross-checking a definition, with a dotted path to it. */\nexport interface DefinitionIssue {\n path: string;\n message: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nconst PROP_TYPES = ['string', 'number', 'integer', 'boolean', 'persona'] as const;\ntype PropType = (typeof PROP_TYPES)[number];\n\n/** Narrows a duck-typed `prop.type` (from raw YAML) to the four/five declared values. */\nfunction isPropType(value: unknown): value is PropType {\n return typeof value === 'string' && (PROP_TYPES as readonly string[]).includes(value);\n}\n\nfunction typeMatches(type: PropType, value: unknown): boolean {\n // A persona reference is a string; whether it names a declared persona is\n // checked separately (validateActionInput, checkSeedInputAgainstAction).\n if (type === 'string' || type === 'persona') return typeof value === 'string';\n if (type === 'boolean') return typeof value === 'boolean';\n if (type === 'integer') return typeof value === 'number' && Number.isInteger(value);\n return typeof value === 'number';\n}\n\nfunction isExactStepRef(value: unknown): boolean {\n return typeof value === 'string' && EXACT_STEP_REF.test(value);\n}\n\n/**\n * A step's declared outputs, keyed by property name, each mapped to whether\n * it is a primitive (string/number/boolean) that `${id.key}` may reference. A\n * repo action's outputs are always primitive (`outputProp.type` has no other\n * option); an emulator operation's may not be (e.g. `email.sent-emails`'s\n * `emails` array).\n */\ntype StepOutputs = Record<string, Record<string, boolean>>;\n\n/** Every `${stepId.key}` in `value` must reference an already-resolved, primitive output. */\nfunction checkStepReferences(value: unknown, outputs: StepOutputs, path: string, issues: DefinitionIssue[]): void {\n if (typeof value !== 'string') return;\n for (const match of value.matchAll(STEP_REF)) {\n const stepId = match[1]!;\n const key = match[2]!;\n const stepOutputs = outputs[stepId];\n if (!stepOutputs) {\n issues.push({ path, message: `references step \"${stepId}\", which has not run yet (or does not exist)` });\n continue;\n }\n if (!(key in stepOutputs)) {\n issues.push({ path, message: `references undeclared output \"${key}\" of step \"${stepId}\"` });\n continue;\n }\n if (!stepOutputs[key]) {\n issues.push({\n path,\n message: `output \"${key}\" of step \"${stepId}\" is not a string/number/boolean and cannot be referenced`,\n });\n }\n }\n}\n\n/** Resolve `environments[name]`'s parent chain (raw, duck-typed), parent-first. Throws on a cycle. */\nfunction resolveRawChain(environments: Record<string, unknown>, name: string): unknown[] {\n const order: string[] = [];\n let current: string | undefined = name;\n const seen = new Set<string>();\n while (current) {\n if (seen.has(current)) throw new Error(`extends cycle at \"${current}\"`);\n seen.add(current);\n order.unshift(current);\n const env: unknown = environments[current];\n current = isRecord(env) && typeof env.extends === 'string' ? env.extends : undefined;\n }\n return order.flatMap((envName) => {\n const env = environments[envName];\n return isRecord(env) && Array.isArray(env.seeds) ? env.seeds : [];\n });\n}\n\n/**\n * Validate a seed's static (literal, non-referencing) input values against a\n * repo action's declared input properties, and check every `${id.key}`\n * reference against outputs resolved so far.\n */\nfunction checkSeedInputAgainstAction(\n input: unknown,\n declaredAction: unknown,\n outputs: StepOutputs,\n personas: Set<string>,\n path: string,\n issues: DefinitionIssue[]\n): void {\n const given = isRecord(input) ? input : {};\n const declaredInputs = isRecord(declaredAction) && isRecord(declaredAction.input) ? declaredAction.input : {};\n for (const [key, value] of Object.entries(given)) {\n checkStepReferences(value, outputs, `${path}.${key}`, issues);\n if (!(key in declaredInputs)) issues.push({ path: `${path}.${key}`, message: `unknown input \"${key}\"` });\n }\n for (const [name, prop] of Object.entries(declaredInputs)) {\n if (!isRecord(prop)) continue;\n const hasValue = name in given;\n const hasDefault = prop.default !== undefined;\n if (!hasValue && prop.required === true && !hasDefault) {\n issues.push({ path: `${path}.${name}`, message: `missing required input \"${name}\"` });\n continue;\n }\n if (!hasValue) continue;\n const value = given[name];\n if (isExactStepRef(value)) continue; // resolved at claim time; skip the literal type/persona/enum check\n if (isPropType(prop.type) && !typeMatches(prop.type, value)) {\n issues.push({ path: `${path}.${name}`, message: `must be ${String(prop.type)}` });\n } else if (prop.type === 'persona' && typeof value === 'string' && !personas.has(value)) {\n issues.push({\n path: `${path}.${name}`,\n message: `must be a declared persona (one of ${[...personas].join(', ') || '(none declared)'})`,\n });\n } else if (Array.isArray(prop.enum) && !(prop.enum as unknown[]).includes(value)) {\n issues.push({ path: `${path}.${name}`, message: `must be one of ${prop.enum.join(', ')}` });\n }\n }\n}\n\n/** Same as above, but against an emulator operation's zod input schema. */\nfunction checkSeedInputAgainstOperation(\n input: unknown,\n operationInput: z.ZodType,\n outputs: StepOutputs,\n path: string,\n issues: DefinitionIssue[]\n): void {\n const given = isRecord(input) ? input : {};\n let anyReference = false;\n for (const [key, value] of Object.entries(given)) {\n checkStepReferences(value, outputs, `${path}.${key}`, issues);\n if (isExactStepRef(value)) anyReference = true;\n }\n // A step's value can only be validated once resolved; skip the schema check\n // when any input references an earlier step (references are still checked above).\n if (anyReference) return;\n const parsed = operationInput.safeParse(given);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n issues.push({ path: [path, ...issue.path.map(String)].join('.'), message: issue.message });\n }\n }\n}\n\nconst PRIMITIVE_JSON_TYPES = new Set(['string', 'number', 'integer', 'boolean']);\n\n/**\n * Declared output keys of an operation, each mapped to whether it is a\n * primitive `${id.key}` may reference (see `StepOutputs`). Read from the\n * output's JSON Schema, the same representation operation inputs are checked\n * against (packages/emulation operations.test.ts).\n */\nfunction operationOutputShape(outputSchema: z.ZodType): Record<string, boolean> {\n const { properties } = z.toJSONSchema(outputSchema) as { properties?: Record<string, { type?: unknown }> };\n if (!properties) return {};\n return Object.fromEntries(\n Object.entries(properties).map(([key, prop]) => [key, typeof prop.type === 'string' && PRIMITIVE_JSON_TYPES.has(prop.type)])\n );\n}\n\n/**\n * Cross-checks that zod cannot express, run against whatever shape parsed\n * (typed `Definition` when schema validation succeeded, or the raw YAML\n * object otherwise) so every problem is reported in one pass.\n */\nfunction crossCheck(data: unknown, delivery: Delivery, issues: DefinitionIssue[]): void {\n if (!isRecord(data)) return;\n const actions = isRecord(data.actions) ? data.actions : {};\n const emulation = isRecord(data.emulation) ? data.emulation : {};\n const databases = isRecord(data.databases) ? data.databases : {};\n const environments = isRecord(data.environments) ? data.environments : {};\n const ports = isRecord(data.ports) ? data.ports : {};\n const personas = isRecord(data.personas) ? new Set(Object.keys(data.personas)) : new Set<string>();\n const hasEmulation = Object.keys(emulation).length > 0;\n\n if (!('default' in environments)) {\n issues.push({ path: 'environments', message: 'must include a \"default\" environment' });\n }\n\n // emulation plugin ids: every declared plugin must be registered, and its\n // config must match the plugin's `configSchema` when it declares one.\n for (const [pluginId, pluginConfig] of Object.entries(emulation)) {\n const plugin = getPlugin(pluginId);\n if (!plugin) {\n issues.push({ path: `emulation.${pluginId}`, message: `unknown emulation plugin \"${pluginId}\"` });\n continue;\n }\n const parsed = plugin.configSchema?.safeParse(pluginConfig);\n for (const issue of parsed?.error?.issues ?? []) {\n issues.push({ path: ['emulation', pluginId, ...issue.path.map(String)].join('.'), message: issue.message });\n }\n }\n\n // Every Google account is a persona: with none, nobody can be signed in.\n if ('google-oauth' in emulation && personas.size === 0) {\n issues.push({ path: 'emulation.google-oauth', message: 'each persona is a Google account; declare at least one' });\n }\n if ('oidc' in emulation && personas.size === 0) {\n issues.push({ path: 'emulation.oidc', message: 'each persona is an account; declare at least one' });\n }\n\n // authentication.emulated: the provider must be declared, and sign_in must\n // start from one of the definition's port params.\n const authentication = isRecord(data.authentication) ? data.authentication : {};\n if (isRecord(authentication.emulated)) {\n const { provider, sign_in: signIn } = authentication.emulated;\n if (typeof provider === 'string' && !(provider in emulation)) {\n issues.push({ path: 'authentication.emulated.provider', message: `\"${provider}\" is not declared in emulation` });\n }\n const paramName = typeof signIn === 'string' ? SIGN_IN_URL.exec(signIn)?.[1] : undefined;\n if (paramName !== undefined) {\n const declared = isRecord(data.params) ? data.params[paramName] : undefined;\n if (declared === undefined) {\n issues.push({ path: 'authentication.emulated.sign_in', message: `references undeclared param \"${paramName}\"` });\n } else if (!(isRecord(declared) && 'port' in declared)) {\n issues.push({ path: 'authentication.emulated.sign_in', message: `param \"${paramName}\" must be a port param (the copy's public URL)` });\n } else {\n const port = typeof declared.port === 'string' ? ports[declared.port] : undefined;\n if (isRecord(port) && port.access !== 'public') {\n issues.push({ path: 'authentication.emulated.sign_in', message: `param \"${paramName}\" is on a private port; sign-in must start on the copy's public URL` });\n }\n }\n }\n }\n\n // extends: existence and cycles. `broken` collects every environment whose\n // own or inherited extends chain is unresolvable, so the seed-chain pass\n // below skips it (its own seeds get no path to validate against).\n const broken = new Set<string>();\n for (const name of Object.keys(environments)) {\n const env = environments[name];\n if (!isRecord(env) || typeof env.extends !== 'string') continue;\n if (!(env.extends in environments)) {\n issues.push({ path: `environments.${name}.extends`, message: `unknown environment \"${env.extends}\"` });\n broken.add(name);\n continue;\n }\n try {\n resolveRawChain(environments, name);\n } catch (error) {\n issues.push({ path: `environments.${name}.extends`, message: error instanceof Error ? error.message : String(error) });\n broken.add(name);\n }\n }\n\n // env keys.\n for (const [name, env] of Object.entries(environments)) {\n if (!isRecord(env) || !isRecord(env.env)) continue;\n for (const key of Object.keys(env.env)) {\n if (!ENV_NAME.test(key)) issues.push({ path: `environments.${name}.env.${key}`, message: 'not a valid env var name' });\n }\n }\n\n /**\n * Validate one environment's OWN seeds (not its inherited ones), labeled\n * with its declaring environment name and local index, and only once no\n * matter how many descendants extend it. Returns the cumulative outputs and\n * step ids after this environment's own seeds, for a child to build on.\n */\n const resolved = new Map<string, { outputs: StepOutputs; seenIds: Set<string> }>();\n function resolveEnvSeeds(name: string): { outputs: StepOutputs; seenIds: Set<string> } {\n const cached = resolved.get(name);\n if (cached) return cached;\n const env = environments[name];\n let outputs: StepOutputs = {};\n let seenIds = new Set<string>();\n if (isRecord(env) && typeof env.extends === 'string' && env.extends in environments && !broken.has(name)) {\n const parent = resolveEnvSeeds(env.extends);\n outputs = { ...parent.outputs };\n seenIds = new Set(parent.seenIds);\n }\n const ownSeeds = isRecord(env) && Array.isArray(env.seeds) ? env.seeds : [];\n ownSeeds.forEach((step: unknown, index: number) => {\n if (!isRecord(step)) return;\n const path = `environments.${name}.seeds[${index}]`;\n const id = typeof step.id === 'string' ? step.id : undefined;\n if (id) {\n if (seenIds.has(id)) issues.push({ path: `${path}.id`, message: `duplicate step id \"${id}\"` });\n seenIds.add(id);\n }\n const actionRef = typeof step.action === 'string' ? step.action : undefined;\n let declaredOutputs: Record<string, boolean> | undefined;\n if (step.operation === FAST_FORWARD) {\n const given = isRecord(step.input) ? step.input : {};\n checkSeedInputAgainstOperation(step.input, fastForwardInput, outputs, `${path}.input`, issues);\n const named = given.database;\n if (typeof named === 'string' && !isExactStepRef(named) && !Object.hasOwn(databases, named)) {\n issues.push({\n path: `${path}.input.database`,\n message: `unknown database \"${named}\" (declared: ${Object.keys(databases).join(', ') || 'none'})`,\n });\n }\n declaredOutputs = operationOutputShape(fastForwardOutput);\n } else if (actionRef?.includes('.')) {\n const [pluginId, opId] = actionRef.split('.', 2) as [string, string];\n if (!(pluginId in emulation)) {\n issues.push({ path: `${path}.action`, message: `plugin \"${pluginId}\" is not declared in emulation` });\n } else {\n const op = findOperation(pluginId, opId);\n if (!op) {\n issues.push({ path: `${path}.action`, message: `unknown operation \"${actionRef}\"` });\n } else {\n declaredOutputs = operationOutputShape(op.output);\n checkSeedInputAgainstOperation(step.input, op.input, outputs, `${path}.input`, issues);\n }\n }\n } else if (actionRef !== undefined) {\n const declared = actions[actionRef];\n if (!declared) {\n issues.push({ path: `${path}.action`, message: `unknown action \"${actionRef}\"` });\n } else {\n const outputProps = isRecord(declared) && isRecord(declared.output) ? declared.output : {};\n declaredOutputs = Object.fromEntries(Object.keys(outputProps).map((key) => [key, true]));\n checkSeedInputAgainstAction(step.input, declared, outputs, personas, `${path}.input`, issues);\n }\n }\n if (id) outputs = { ...outputs, [id]: declaredOutputs ?? {} };\n });\n const result = { outputs, seenIds };\n resolved.set(name, result);\n return result;\n }\n\n // seed chains: duplicate ids, unknown actions/operations, static input and references.\n for (const name of Object.keys(environments)) {\n if (broken.has(name)) continue; // the cycle/unknown-parent is already reported above\n resolveEnvSeeds(name);\n }\n\n // ports: reserved names, and (with emulation or object storage on) no user\n // port on theirs, plus Blackcat's 8-port cap.\n if (EMULATION_PORT_NAME in ports) {\n issues.push({ path: `ports.${EMULATION_PORT_NAME}`, message: `\"${EMULATION_PORT_NAME}\" is a reserved port name` });\n }\n if (OBJECT_STORAGE_PORT_NAME in ports) {\n issues.push({ path: `ports.${OBJECT_STORAGE_PORT_NAME}`, message: `\"${OBJECT_STORAGE_PORT_NAME}\" is a reserved port name` });\n }\n if (SYNC_PORT_NAME in ports) {\n issues.push({ path: `ports.${SYNC_PORT_NAME}`, message: `\"${SYNC_PORT_NAME}\" is a reserved port name` });\n }\n for (const [portName, portValue] of Object.entries(ports)) {\n if (isRecord(portValue) && portValue.port === SYNC_PORT) {\n issues.push({ path: `ports.${portName}.port`, message: `port ${SYNC_PORT} is reserved for syncing a working tree into the sandbox` });\n }\n }\n const storage = isRecord(data.object_storage) ? data.object_storage : undefined;\n const provider = OBJECT_STORAGE_PROVIDERS.find((name) => name === storage?.provider);\n if (provider) {\n for (const [portName, portValue] of Object.entries(ports)) {\n if (!isRecord(portValue) || typeof portValue.port !== 'number') continue;\n if (RESERVED_STORAGE_PORTS[provider].includes(portValue.port)) {\n issues.push({\n path: `ports.${portName}.port`,\n message: `port ${portValue.port} is reserved for the ${provider} object storage server`,\n });\n }\n }\n }\n if (hasEmulation) {\n for (const [portName, portValue] of Object.entries(ports)) {\n if (portName === EMULATION_PORT_NAME || !isRecord(portValue)) continue;\n if (portValue.port === 80 || portValue.port === 443) {\n issues.push({ path: `ports.${portName}.port`, message: 'ports 80 and 443 are reserved for the emulator' });\n }\n }\n }\n // The `sync` port is always implied, so a definition may declare at most 7 of its own.\n const impliedPortCount =\n Object.keys(ports).filter((p) => p !== EMULATION_PORT_NAME && p !== OBJECT_STORAGE_PORT_NAME && p !== SYNC_PORT_NAME).length +\n (hasEmulation ? 1 : 0) +\n (storage ? 1 : 0) +\n 1;\n if (impliedPortCount > 8) {\n issues.push({\n path: 'ports',\n message: `at most 8 ports are allowed (Blackcat's limit), counting the implicit ones (sync${hasEmulation ? ', emulation' : ''}${storage ? ', object-storage' : ''}); got ${impliedPortCount}`,\n });\n }\n\n const services = isRecord(data.services) ? data.services : {};\n for (const name of [...OBJECT_STORAGE_SERVICE_NAMES, SYNC_SERVICE_NAME]) {\n if (name in services) issues.push({ path: `services.${name}`, message: `\"${name}\" is a reserved service name` });\n }\n\n // build steps: duplicates (kept from before actions/environments existed).\n const lifecycle = isRecord(data.lifecycle) ? data.lifecycle : {};\n if (Array.isArray(lifecycle.build)) {\n const seen = new Set<string>();\n lifecycle.build.forEach((step: unknown, index: number) => {\n if (!isRecord(step) || typeof step.name !== 'string') return;\n if (seen.has(step.name)) {\n issues.push({ path: `lifecycle.build[${index}].name`, message: `build step \"${step.name}\" is declared twice` });\n }\n seen.add(step.name);\n });\n }\n\n // An uploaded image is already built: say so where a build is declared.\n if (delivery === 'upload') {\n if ('setup' in data) {\n issues.push({ path: 'setup', message: 'an uploaded image is already built: remove setup (install it in the image)' });\n }\n if (isRecord(data.lifecycle) && 'build' in data.lifecycle) {\n issues.push({\n path: 'lifecycle.build',\n message: 'an uploaded image is already built: remove lifecycle.build (build it in the image)',\n });\n }\n for (const [name, spec] of Object.entries(services)) {\n if (isRecord(spec) && spec.after_build === true) {\n issues.push({ path: `services.${name}.after_build`, message: 'an uploaded image has no build to wait for' });\n }\n }\n }\n\n // params referring to undeclared ports, and env referring to undeclared params.\n const params = isRecord(data.params) ? data.params : {};\n for (const [name, value] of Object.entries(params)) {\n if (\n isRecord(value) &&\n 'port' in value &&\n typeof value.port === 'string' &&\n !(value.port in ports) &&\n !(storage && value.port === OBJECT_STORAGE_PORT_NAME) &&\n !(hasEmulation && value.port === EMULATION_PORT_NAME)\n ) {\n issues.push({ path: `params.${name}.port`, message: `refers to undeclared port \"${value.port}\"` });\n }\n }\n const env = isRecord(data.env) ? data.env : {};\n for (const [key, template] of Object.entries(env)) {\n if (typeof template !== 'string') continue;\n for (const match of template.matchAll(PARAM_REF)) {\n const paramName = match[1]!;\n if (!(paramName in params)) issues.push({ path: `env.${key}`, message: `references undeclared param \"${paramName}\"` });\n }\n }\n issues.push(...emulationReferenceIssues(emulation, params));\n}\n\n/** The sync server: started like any service that needs neither a build nor a prepare; it refuses requests until the first prepare gives it a key. */\nconst SYNC_SERVICE = {\n run: 'exec /usr/local/bin/cofactor-sandbox sync-server',\n health: { http: SYNC_PORT, path: '/healthz' },\n after_build: false,\n reload_on_prepare: false,\n} as const;\n\n/**\n * Adds what declarations imply: the public `sync` port and the service that\n * serves it (always), the public, port-80 `emulation` port once any plugin is\n * declared, and with `object_storage` the public `object-storage` port and\n * the services that run its server.\n */\nfunction withImplicitPieces<D extends Definition>(definition: D): D {\n let result: D = {\n ...definition,\n ports: { ...definition.ports, [SYNC_PORT_NAME]: { port: SYNC_PORT, access: 'public' } },\n services: { ...definition.services, [SYNC_SERVICE_NAME]: SYNC_SERVICE },\n };\n if (Object.keys(definition.emulation).length > 0) {\n result = { ...result, ports: { ...result.ports, emulation: { port: 80, access: 'public' } } };\n }\n const storage = definition.object_storage;\n if (storage) {\n result = {\n ...result,\n ports: { ...result.ports, [OBJECT_STORAGE_PORT_NAME]: { port: OBJECT_STORAGE_PORT[storage.provider], access: 'public' } },\n services: { ...result.services, ...objectStorageServices(storage.provider) },\n };\n }\n return result;\n}\n\n/**\n * Parse and cross-check a definition delivered by `delivery`. Schema issues\n * (zod collects every one, not just the first) and cross-checks are both\n * returned with a dotted path; cross-checks also run against the raw YAML\n * object when schema parsing failed, so a structural problem (an extends\n * cycle, a duplicate step id) is still reported.\n */\nexport function validateDefinition(\n yamlText: string,\n delivery: 'github'\n): { definition?: GithubDefinition; issues: DefinitionIssue[] };\nexport function validateDefinition(\n yamlText: string,\n delivery: Delivery\n): { definition?: Definition; issues: DefinitionIssue[] };\nexport function validateDefinition(\n yamlText: string,\n delivery: Delivery\n): { definition?: Definition; issues: DefinitionIssue[] } {\n const issues: DefinitionIssue[] = [];\n let raw: unknown;\n try {\n raw = parse(yamlText);\n } catch (error) {\n return { issues: [{ path: '', message: `invalid YAML: ${error instanceof Error ? error.message : String(error)}` }] };\n }\n const parsed = (delivery === 'github' ? githubDefinitionSchema : definitionSchema).safeParse(raw);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n issues.push({ path: issue.path.map(String).join('.') || '(root)', message: issue.message });\n }\n crossCheck(raw, delivery, issues);\n return { issues };\n }\n crossCheck(parsed.data, delivery, issues);\n return { definition: withImplicitPieces(parsed.data), issues };\n}\n\n/**\n * A stored definition's port names in their declared order, whichever way it\n * was delivered. For text validated when it was stored (a claimed sandbox's\n * `definitionYaml`); it does not validate again.\n */\nexport function declaredPortNames(yamlText: string): string[] {\n const ports = z.object({ ports: z.record(z.string(), z.unknown()) }).parse(parse(yamlText)).ports;\n return Object.keys(ports);\n}\n\n/** `validateDefinition`, throwing an error listing every issue (one per line, `path: message`). */\nexport function parseDefinition(yamlText: string, delivery: 'github'): GithubDefinition;\nexport function parseDefinition(yamlText: string, delivery: Delivery): Definition;\nexport function parseDefinition(yamlText: string, delivery: Delivery): Definition {\n const { definition, issues } = validateDefinition(yamlText, delivery);\n if (issues.length > 0) throw new Error(issues.map((issue) => `${issue.path}: ${issue.message}`).join('\\n'));\n return definition!;\n}\n\n/**\n * Identifies the toolchain layer a definition needs: everything fixed when a\n * layer is made (the platform version, the prelude, the machine, the setup\n * script, the ports a template exposes, and the browser layer of the image\n * it starts from). Anything else in the definition is applied per commit and\n * never needs a new toolchain. The guest helper always passes `null`: it only\n * compares its own keys with each other, and the browser layer changes\n * nothing the helper does.\n */\nexport function toolchainKey(definition: GithubDefinition, browserLayer: BrowserLayer): string {\n const hash = createHash('sha256')\n .update(String(PLATFORM_VERSION))\n .update('\\0')\n .update(SETUP_PRELUDE)\n .update('\\0')\n .update(JSON.stringify(definition.resources))\n .update('\\0')\n .update(definition.setup)\n .update('\\0')\n .update(JSON.stringify(Object.entries(definition.ports).sort(([a], [b]) => a.localeCompare(b))));\n // Only a toolchain on the browser image hashes anything more, so every key\n // without the browser layer is exactly what it was before the layer existed.\n if (browserLayer !== null) hash.update('\\0browser-v').update(String(browserLayer));\n return hash.digest('hex');\n}\n\n/** Param values inside layers: fixed values, local port origins and placeholders for secrets. */\nexport function layerParams(definition: Definition): Record<string, string> {\n return Object.fromEntries(\n Object.entries(definition.params).map(([name, value]) => {\n if (typeof value === 'string') return [name, value];\n if ('port' in value) return [name, `http://127.0.0.1:${definition.ports[value.port]!.port}`];\n return [name, `layer-placeholder-${name}`];\n })\n );\n}\n\n/**\n * Exactly what a prepared sandbox was given values for — never more:\n * personas, per-sandbox params, the chosen environment's name, the resolved\n * chain's ENV (not its seeds, which run once at claim time and are Cofactor's\n * to re-run, not the sandbox's own state), the emulation section and the\n * object storage (whose buckets prepare creates). Seeds\n * run after prepare; a PR that only changes seed data does not need a fresh\n * sandbox. A commit that would change any of this needs a newly prepared\n * sandbox instead (EXIT.PREPARE_MISMATCH).\n */\nexport function preparedShape(definition: Definition, environment: string): string {\n const sorted = <T>(record: Record<string, T>) =>\n Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));\n const perSandbox = Object.fromEntries(Object.entries(definition.params).filter(([, value]) => typeof value !== 'string'));\n const env = environmentChain(definition, environment).env;\n return JSON.stringify({\n personas: sorted(definition.personas),\n params: sorted(perSandbox),\n environment,\n env,\n emulation: sorted(definition.emulation),\n object_storage: definition.object_storage && {\n provider: definition.object_storage.provider,\n buckets: [...definition.object_storage.buckets].sort(),\n },\n });\n}\n\n/**\n * A new sandbox's own param values, for `prepare`: its public port origins\n * and fresh secrets. Fixed values keep the definition's.\n */\nexport function sandboxParams(definition: Definition, portUrls: Record<string, string>): Record<string, string> {\n const values: Record<string, string> = {};\n for (const [name, value] of Object.entries(definition.params)) {\n if (typeof value === 'string') continue;\n if ('port' in value) {\n const url = portUrls[value.port];\n if (!url) throw new Error(`the sandbox has no URL for port \"${value.port}\"`);\n values[name] = url;\n } else {\n values[name] = randomBytes(32).toString('hex');\n }\n }\n return values;\n}\n\n/** Replaces every `${params.NAME}` in `template` with that param's value. */\nexport function substituteParams(template: string, values: Record<string, string>): string {\n return template.replace(PARAM_REF, (_, name: string) => values[name]!);\n}\n\nexport function resolveEnv(definition: Definition, params: Record<string, string>): Record<string, string> {\n const values = { ...layerParams(definition), ...params };\n return Object.fromEntries(\n Object.entries(definition.env).map(([key, template]) => [key, substituteParams(template, values)])\n );\n}\n\n/** The names of every `port` param, live or not: every one of these changes on every copy (each copy has its own port URLs). */\nexport function portParamNames(definition: Definition): Set<string> {\n return new Set(\n Object.entries(definition.params)\n .filter(([, value]) => typeof value !== 'string' && 'port' in value)\n .map(([name]) => name)\n );\n}\n\n/** The names of every param declared `live: true` (see `param`'s doc comment) — necessarily a subset of `portParamNames`. */\nfunction liveParamNames(definition: Definition): Set<string> {\n return new Set(\n Object.entries(definition.params)\n .filter(([, value]) => typeof value !== 'string' && 'port' in value && value.live === true)\n .map(([name]) => name)\n );\n}\n\n/**\n * `definition.env` keys whose template references at least one param and\n * every param it references is live: the subset of the resolved env a\n * sandbox's `reload_on_prepare` services never need a restart to pick up,\n * because `prepare` writes it to `LIVE_ENV_FILE` (runtime.ts) on every call\n * regardless of whether it restarts anything. A key that mixes a live and a\n * non-live param reference is deliberately excluded: it still needs a\n * restart whenever the non-live half changes, so the app cannot treat it as\n * always current.\n */\nfunction liveEnvKeys(definition: Definition): Set<string> {\n const live = liveParamNames(definition);\n const keys = new Set<string>();\n for (const [key, template] of Object.entries(definition.env)) {\n const refs = [...template.matchAll(PARAM_REF)].map((match) => match[1]!);\n if (refs.length > 0 && refs.every((name) => live.has(name))) keys.add(key);\n }\n return keys;\n}\n\n/**\n * The resolved env values a sandbox's live params currently determine — what\n * `prepare` writes to `LIVE_ENV_FILE` for the app to re-read at request time\n * instead of waiting for a restart.\n */\nexport function resolveLiveEnv(definition: Definition, params: Record<string, string>): Record<string, string> {\n const keys = liveEnvKeys(definition);\n const resolved = resolveEnv(definition, params);\n return Object.fromEntries(Object.entries(resolved).filter(([key]) => keys.has(key)));\n}\n\n/** Fast copies require live URL env and no baked emulator config that depends on a port URL. */\nexport function copyRestartRequired(definition: Definition): boolean {\n const ports = portParamNames(definition);\n const live = liveParamNames(definition);\n if ([...ports].some((name) => !live.has(name))) return true;\n const liveKeys = liveEnvKeys(definition);\n for (const [key, template] of Object.entries(definition.env)) {\n if ([...template.matchAll(PARAM_REF)].some((match) => ports.has(match[1]!)) && !liveKeys.has(key)) return true;\n }\n return [...emulationParamRefs(definition)].some((name) => ports.has(name));\n}\n\n/**\n * Whether a sandbox's `reload_on_prepare` services must restart for `next`'s\n * values to take effect, given what they were last prepared with (`previous`\n * is `undefined` the first time a sandbox is prepared, which always needs a\n * restart — the services have not started at all yet).\n *\n * The rule is the resolved env, not the raw params: restart iff the\n * environment, the repository secrets, or the emulation env's resolved\n * values differ, or `resolveEnv(definition, previous.params)` and\n * `resolveEnv(definition, next.params)` differ on any key that is NOT in\n * `liveEnvKeys`. Comparing resolved env keys (rather than looping over param\n * names and skipping the live ones) is what a *mixed* key needs: a key\n * templated from both a live and a non-live param is not in `liveEnvKeys`\n * (see its doc comment), so any change to either param — including the live\n * one alone — changes that key's resolved value and correctly forces a\n * restart; a per-param diff that simply skipped live param names would miss\n * this, since it never looks at the key the live param feeds into.\n *\n * Deliberately does not compare `personas` or `emulation.config` (the\n * control-plane token): neither reaches `reload_on_prepare` services' env —\n * `childEnv`'s `for: 'service'` branch never includes personas, and only\n * `emulation.env` (the plugins' env-contract values, currently always `{}`;\n * see `prepareInputSchema`), not `emulation.config`, is merged in. Rotating\n * the emulator's token on every copy is therefore free: it reconfigures the\n * emulator sidecar (`cofactor-sandbox prepare`'s unconditional `/emulation`\n * control call) and reruns `lifecycle.prepare` (a DB write, not a restart),\n * never the app services this function gates.\n */\nexport function restartRequired(\n definition: Definition,\n previous: PrepareInput | undefined,\n next: PrepareInput\n): boolean {\n if (!previous) return true;\n if (previous.environment !== next.environment) return true;\n if (JSON.stringify(previous.secrets) !== JSON.stringify(next.secrets)) return true;\n if (JSON.stringify(previous.emulation?.env ?? {}) !== JSON.stringify(next.emulation?.env ?? {})) return true;\n const live = liveEnvKeys(definition);\n const previousEnv = resolveEnv(definition, previous.params);\n const nextEnv = resolveEnv(definition, next.params);\n for (const key of Object.keys(definition.env)) {\n if (live.has(key)) continue;\n if (previousEnv[key] !== nextEnv[key]) return true;\n }\n return false;\n}\n\n/**\n * Resolve a copy prepare's full param set: every param the sandbox's previous\n * prepare had, with `given` (a copy's own `port` params — every one of them,\n * live or not: each copy has its own port URLs) applied over it. Any param\n * `given` omits, `secret` params among them, keeps the value the sandbox was\n * previously prepared with rather than a freshly generated one — necessary\n * for `auth_secret` (never reissued per copy) and harmless for a `live` port\n * param the caller happened to leave out (there is no real caller that\n * would: `copyPrepareInput` always sends every port param).\n *\n * Throws when `given` names a param the definition does not declare, or a\n * declared param that is not a `port` param (a `secret`, or a literal string)\n * — a copy prepare's caller (`copyPrepareInput`) must never attempt to change\n * one of those; the caller that wants to is not preparing a copy.\n *\n * Whether the result actually needs a restart is `restartRequired`'s call,\n * not this function's: a definition with no `live: true` params sends every\n * port param here exactly as before, and the resolved env then always\n * differs, so nothing here silently defeats an old definition's restart.\n */\nexport function resolveCopyParams(\n definition: Definition,\n previous: PrepareInput,\n given: Record<string, string>\n): Record<string, string> {\n const undeclared = Object.keys(given).filter((name) => !(name in definition.params));\n if (undeclared.length > 0) throw new Error(`not declared params: ${undeclared.join(', ')}`);\n const ports = portParamNames(definition);\n const notPort = Object.keys(given).filter((name) => !ports.has(name));\n if (notPort.length > 0) {\n throw new Error(`a copy prepare may only set port params, got: ${notPort.join(', ')}`);\n }\n return { ...previous.params, ...given };\n}\n\n/** The resolved seeds (parent first) and merged env (parent first, this environment last) of an environment chain. */\nexport interface ResolvedEnvironment {\n seeds: SeedStep[];\n env: Record<string, string>;\n /** The chain's environment names, ancestors first, `name` last. */\n lineage: string[];\n}\n\nexport function environmentChain(definition: Definition, name: string): ResolvedEnvironment {\n const seen = new Set<string>();\n function resolve(envName: string): ResolvedEnvironment {\n if (seen.has(envName)) throw new Error(`environment \"${envName}\" has an extends cycle`);\n seen.add(envName);\n const env = definition.environments[envName];\n if (!env) throw new Error(`unknown environment \"${envName}\"`);\n const parent = env.extends ? resolve(env.extends) : { seeds: [], env: {}, lineage: [] };\n return {\n seeds: [...parent.seeds, ...env.seeds],\n env: { ...parent.env, ...env.env },\n lineage: [...parent.lineage, envName],\n };\n }\n return resolve(name);\n}\n\n/**\n * Resolve one seed step's input against earlier steps' outputs: a value that\n * is exactly `${id.key}` becomes the raw output value, an embedded reference\n * is string-interpolated, and everything else passes through unchanged.\n */\nexport function resolveSeedInput(\n input: Record<string, string | number | boolean>,\n outputs: Record<string, Record<string, unknown>>\n): Record<string, unknown> {\n const resolveRef = (stepId: string, key: string): string | number | boolean => {\n const stepOutputs = outputs[stepId];\n if (!stepOutputs || !(key in stepOutputs)) throw new Error(`no output \"${key}\" from step \"${stepId}\"`);\n const value = stepOutputs[key];\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n throw new Error(`output \"${key}\" of step \"${stepId}\" is not a string/number/boolean and cannot be referenced`);\n }\n return value;\n };\n const resolved: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(input)) {\n if (typeof value !== 'string') {\n resolved[key] = value;\n continue;\n }\n const exact = EXACT_STEP_REF.exec(value);\n if (exact) {\n resolved[key] = resolveRef(exact[1]!, exact[2]!);\n continue;\n }\n resolved[key] = value.replace(STEP_REF, (_match, stepId: string, refKey: string) => String(resolveRef(stepId, refKey)));\n }\n return resolved;\n}\n\n/**\n * Apply an action's declared defaults/required/type/enum to raw input; report\n * every issue found. `personas` is the definition's declared persona names,\n * the only valid values for a `persona`-typed input.\n */\nexport function validateActionInput(\n action: ActionDef,\n input: Record<string, unknown>,\n personas: string[]\n): { values: Record<string, string | number | boolean> } | { issues: string[] } {\n const issues: string[] = [];\n for (const key of Object.keys(input)) {\n if (!(key in action.input)) issues.push(`unknown input \"${key}\"`);\n }\n const personaSet = new Set(personas);\n const values: Record<string, string | number | boolean> = {};\n for (const [name, prop] of Object.entries(action.input)) {\n const given = input[name];\n const value = given === undefined ? prop.default : given;\n if (value === undefined) {\n if (prop.required) issues.push(`missing required input \"${name}\"`);\n continue;\n }\n if (!typeMatches(prop.type, value)) {\n issues.push(`input \"${name}\" must be ${prop.type}`);\n continue;\n }\n if (prop.type === 'persona' && !personaSet.has(value as string)) {\n issues.push(`input \"${name}\" must be a declared persona (one of ${personas.join(', ') || '(none declared)'})`);\n continue;\n }\n if (prop.enum && !prop.enum.includes(value as string | number)) {\n issues.push(`input \"${name}\" must be one of ${prop.enum.join(', ')}`);\n continue;\n }\n values[name] = value as string | number | boolean;\n }\n return issues.length > 0 ? { issues } : { values };\n}\n\n/** v1's action-result contract: the last stdout line that starts with `{`, JSON-parsed as an object. */\nexport function parseActionResult(stdout: string): Record<string, unknown> | undefined {\n const lines = stdout.split('\\n');\n for (let i = lines.length - 1; i >= 0; i--) {\n const line = lines[i]!.trim();\n if (!line.startsWith('{')) continue;\n try {\n const parsed: unknown = JSON.parse(line);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nexport const fastForwardInput = z.strictObject({ database: z.string().regex(NAME) });\nexport const fastForwardOutput = z.strictObject({\n database: z.string(),\n /** Whole days the data moved this run (0: nothing to do). */\n offset_days: z.number().int(),\n tables: z.number().int(),\n rows: z.number().int(),\n /** The data's date before and after (ISO-8601 UTC). */\n from: z.string(),\n to: z.string(),\n});\nexport type FastForwardOutput = z.infer<typeof fastForwardOutput>;\n\nexport const databaseFastForward = {\n name: FAST_FORWARD,\n description:\n \"Moves every date, timestamp and timestamptz column of a declared database forward by the whole days since the data's recorded date, so a restored dump looks current.\",\n input: fastForwardInput,\n output: fastForwardOutput,\n} as const;\n\nexport function isBuiltinOperation(ref: string): boolean {\n return ref === FAST_FORWARD;\n}\n\n/** Validate a run's input against the schema and the definition's declared databases. */\nexport function validateFastForwardInput(\n definition: Definition,\n input: Record<string, unknown>\n): { values: z.infer<typeof fastForwardInput> } | { issues: string[] } {\n const parsed = fastForwardInput.safeParse(input);\n if (!parsed.success) {\n return { issues: parsed.error.issues.map((issue) => `${issue.path.map(String).join('.') || '(root)'}: ${issue.message}`) };\n }\n const name = parsed.data.database;\n if (!Object.hasOwn(definition.databases, name)) {\n return { issues: [`database: unknown database \"${name}\" (declared: ${Object.keys(definition.databases).join(', ') || 'none'})`] };\n }\n return { values: parsed.data };\n}\n\n/**\n * What `cofactor-sandbox action` prints on stdout once the script's result has\n * validated: the result under `output`, plus the script's run time. The one\n * format both the helper (writer) and the API (reader) use.\n */\nexport interface ActionEnvelope {\n output: Record<string, unknown>;\n ms: number;\n}\n\n/**\n * The start of the helper's `default:` usage line. The API recognises a helper\n * without `exec` by this prefix plus exit 2, so it must never change (see the\n * PLATFORM_VERSION note).\n */\nexport const HELPER_USAGE_PREFIX = 'usage: cofactor-sandbox ';\n\n/** Longest command `cofactor-sandbox exec` runs. */\nexport const EXEC_MAX_TIMEOUT_SECONDS = 1800;\n\n/** The timeout `POST .../exec` applies when the caller gives none. */\nexport const EXEC_DEFAULT_TIMEOUT_SECONDS = 300;\n\n/** Longest command the exec route accepts, in characters. */\nexport const EXEC_MAX_COMMAND_LENGTH = 8192;\n\n/** What `cofactor-sandbox exec` reads on stdin. */\nexport const execInputSchema = z.strictObject({\n command: z.string().min(1),\n timeoutSeconds: z.number().int().min(1).max(EXEC_MAX_TIMEOUT_SECONDS),\n});\n\n/**\n * What `cofactor-sandbox exec` prints on stdout (exit 0): the command's own\n * outcome, so its exit codes never collide with the helper's.\n */\nexport const execEnvelopeSchema = z.strictObject({\n exitCode: z.number().int(),\n stdout: z.string(),\n stderr: z.string(),\n timedOut: z.boolean(),\n});\n\nexport function formatActionEnvelope(envelope: ActionEnvelope): string {\n return JSON.stringify(envelope);\n}\n\nexport function parseActionEnvelope(stdout: string): ActionEnvelope | undefined {\n const line = parseActionResult(stdout);\n if (!line || !isRecord(line.output) || typeof line.ms !== 'number') return undefined;\n return { output: line.output, ms: line.ms };\n}\n\n/**\n * What `cofactor-sandbox build` prints on stdout (one JSON line): the commit it\n * built, each `lifecycle.build` step (skipped steps have `ran: false`), and the\n * checkout, reload and service-restart times. Written by the helper, read by\n * the API, which keeps it as a ready snapshot's build timings.\n */\nexport const buildReportSchema = z.object({\n sha: z.string(),\n steps: z.array(z.object({ name: z.string(), ran: z.boolean(), ms: z.number() })),\n checkoutMs: z.number(),\n reloadMs: z.number(),\n restartMs: z.number(),\n totalMs: z.number(),\n});\n\nexport type BuildReport = z.infer<typeof buildReportSchema>;\n\n/** The build report on `build`'s stdout; throws when there is none (the helper always prints one). */\nexport function parseBuildReport(stdout: string): BuildReport {\n const parsed = buildReportSchema.safeParse(parseActionResult(stdout));\n if (!parsed.success) throw new Error(`build printed no valid report: ${parsed.error.message}`);\n return parsed.data;\n}\n\n/** Every declared output key must be present with the right primitive type; extra keys are allowed. */\nexport function validateActionOutput(action: ActionDef, output: Record<string, unknown>): string[] {\n const issues: string[] = [];\n for (const [name, prop] of Object.entries(action.output)) {\n if (!(name in output)) {\n issues.push(`missing output \"${name}\"`);\n continue;\n }\n if (!typeMatches(prop.type, output[name])) issues.push(`output \"${name}\" must be ${prop.type}`);\n }\n return issues;\n}\n\n/** What a sandbox receives at `prepare`: declared params, its environment, persona passwords and the application's secrets. */\nexport const prepareInputSchema = z.strictObject({\n /**\n * `false` for a sandbox's first prepare (a repo base moved to a commit, or\n * the builder a ready snapshot is captured from): `params` is every\n * declared param. `true` for a copy restored from an already-prepared\n * ready snapshot: `params` sets every `port` param (its own URLs — every\n * one of them differs per copy, `live: true` or not; see `portParamNames`)\n * but no `secret` param — those keep the value the snapshot itself was\n * prepared with (`resolveCopyParams`), which is what lets `restartRequired`\n * skip restarting `reload_on_prepare` services when the only params that\n * changed are `live` ones. A copy prepare requires a previous prepare to\n * copy from; there is no default because a caller that gets this wrong\n * needs a loud, specific error, not a silently-full or silently-partial\n * prepare.\n */\n copy: z.boolean(),\n params: z.record(z.string().regex(PARAM), z.string()),\n /**\n * One password per declared persona, generated by Cofactor when the\n * snapshot is built. Empty for a copy (`copy: true`), which keeps its\n * snapshot's, shared by every copy (`lifecycle.prepare` gets them as\n * COFACTOR_PERSONAS). The prepare output echoes the personas the sandbox\n * now has.\n */\n personas: z.record(z.string().regex(NAME), z.strictObject({ password: z.string().min(12) })),\n /**\n * The application's secrets (stored in Cofactor, never in the repo),\n * resolved for the environment chain at claim: runtime env for\n * `reload_on_prepare` services, prepare commands, seeds, actions and\n * `authentication.run`, overriding the definition's env on a name clash.\n * Never injected into build steps or services without `reload_on_prepare`.\n */\n secrets: z.record(z.string().regex(ENV_NAME), z.string()),\n /** The chosen flavor; its resolved chain's seeds ran at claim time. */\n environment: z.string().regex(NAME),\n /**\n * Present iff the definition declares `emulation`. `config` is the\n * `EmulationInstanceConfig` JSON Cofactor built for this sandbox; `env` is\n * the plugins' resolved env-contract values, mapped to this app's env vars.\n */\n emulation: z\n .strictObject({\n config: z.record(z.string(), z.unknown()),\n env: z.record(z.string().regex(ENV_NAME), z.string()),\n })\n .optional(),\n /**\n * The copy's own sync key, fresh on every prepare (a copy never shares its\n * snapshot's): the sync server checks each request's token against it\n * (`verifySyncToken`, sync-protocol.ts). Never in any child's env.\n */\n sync: z.strictObject({ key: syncKeySchema }),\n});\nexport type PrepareInput = z.infer<typeof prepareInputSchema>;\n\n/*\n * Blackcat template names. A template's name is its identity: the jobs and\n * the operator driver find a layer by name, so these are the one definition.\n * A GitHub-delivered application's toolchain and repo base are named by its\n * repository and definition directory, which both fold into their slug.\n */\n\n/**\n * The platform layer for a machine shape. A Blackcat template restores only at\n * the size it was captured at, so there is one per shape in use; the API\n * imports each from the version's one platform image (`platformImageTag`) the\n * first time a toolchain of that shape is built. With a browser layer, it is\n * imported from that layer's image (`browserImageTag`) under its own name.\n */\nexport function platformTemplateName(definition: Definition, browserLayer: BrowserLayer): string {\n const name = `sbv2-platform-v${PLATFORM_VERSION}-${definition.resources.vcpus}c-${definition.resources.memory_mib}m`;\n return browserLayer === null ? name : `${name}-browser-v${browserLayer}`;\n}\n\n/**\n * The platform image's tag in its repository, published once per version by\n * `image.sh`. The repository's tags are immutable, so a version's image never\n * changes under the templates imported from it.\n */\nexport function platformImageTag(platformVersion: number): string {\n return `platform-v${platformVersion}`;\n}\n\n/**\n * The browser image's tag: the platform image of `platformVersion` with the\n * browser layer of `browserLayer` appended, published once per pair by\n * `image.sh` (immutable, like the platform tag).\n */\nexport function browserImageTag(platformVersion: number, browserLayer: number): string {\n return `${platformImageTag(platformVersion)}-browser-v${browserLayer}`;\n}\n\n/**\n * The definition's file, relative to the repo root: `cofactor.sandbox.yaml`\n * at the root, or `<dir>/cofactor.sandbox.yaml` for a monorepo definition.\n * `dir` must be `.` or a relative POSIX path with no leading/trailing slash\n * and no `..` segment.\n */\nexport function definitionFile(dir: string): string {\n if (dir === '.') return DEFINITION_FILE;\n if (dir === '' || dir.startsWith('/') || dir.endsWith('/') || dir.split('/').includes('..')) {\n throw new Error(`invalid definition directory \"${dir}\"`);\n }\n return `${dir}/${DEFINITION_FILE}`;\n}\n\n/** For '.' hashes the repo alone (existing root template names are unchanged); otherwise repo + ':' + dir. */\nfunction repoSlug(repoFullName: string, definitionDir: string): string {\n const key = definitionDir === '.' ? repoFullName : `${repoFullName}:${definitionDir}`;\n return createHash('sha256').update(key).digest('hex').slice(0, 8);\n}\n\nexport function toolchainTemplateName(\n repoFullName: string,\n definitionDir: string,\n definition: GithubDefinition,\n browserLayer: BrowserLayer\n): string {\n return `sbv2-${repoSlug(repoFullName, definitionDir)}-toolchain-${toolchainKey(definition, browserLayer).slice(0, 12)}`;\n}\n\n/**\n * A repo base at `sha`, built on `definition`'s toolchain. The toolchain is in\n * the name so a base built on an older toolchain (a platform bump, a change to\n * setup or resources) never shares a name with its replacement: the rebuild\n * can't adopt the old template, and deleting the old one can't remove the new.\n */\nexport function baseTemplateName(\n repoFullName: string,\n definitionDir: string,\n definition: GithubDefinition,\n browserLayer: BrowserLayer,\n sha: string\n): string {\n return `sbv2-${repoSlug(repoFullName, definitionDir)}-base-${toolchainKey(definition, browserLayer).slice(0, 12)}-${sha.slice(0, 12)}`;\n}\n\n/**\n * A ready snapshot: a version of an application (a commit, for a GitHub-\n * delivered one) built, prepared and seeded for one environment, captured\n * running. Its key on the template row is `readySnapshotKey`. Named by the\n * application, which is one org's: a ready snapshot holds an org's seeded\n * data, so no two orgs ever adopt or delete each other's. `nonce` is fixed\n * when the snapshot's row is first created: a rerun of the same build adopts\n * the template it promoted, but a row created after retention deleted the\n * last one never adopts a template left behind.\n */\nexport function readyTemplateName(applicationId: string, version: string, environment: string, nonce: string): string {\n const slug = createHash('sha256').update(applicationId).digest('hex').slice(0, 8);\n const versionSlug = createHash('sha256').update(version).digest('hex').slice(0, 12);\n return `sbv2-${slug}-ready-${versionSlug}-${environment}-${nonce}`;\n}\n\nexport function readySnapshotKey(version: string, environment: string): string {\n return `${version}/${environment}`;\n}\n\n/** The version a `readySnapshotKey` names. */\nexport function readySnapshotVersion(key: string): string {\n const slash = key.lastIndexOf('/');\n if (slash < 0) throw new Error(`\"${key}\" is not a ready snapshot key`);\n return key.slice(0, slash);\n}\n\n/** A pushed image of an upload-delivered application, imported into Blackcat. */\nexport function uploadTemplateName(applicationId: string, uploadId: string): string {\n const slug = createHash('sha256').update(applicationId).digest('hex').slice(0, 8);\n return `sbv2-${slug}-upload-${uploadId}`;\n}\n\n/**\n * What a pushed image's ready snapshots are compatible with: the helper the\n * CLI layered into it. An upload-delivered application's counterpart of\n * `toolchainKey`; a platform bump makes every older push unusable until it\n * is pushed again.\n */\nexport function uploadCompatibilityKey(platformVersion: number): string {\n return `upload-platform-v${platformVersion}`;\n}\n","/**\n * Built-in object storage (`object_storage` in the definition): one emulated\n * S3 or GCS server per sandbox, run by the supervisor as implicit services so\n * its data is in every snapshot. Pure values, shared by the schema\n * (manifest.ts), the env (runtime.ts) and the guest side\n * (object-storage-server.ts); see specs/pending/sandboxes-v2-object-storage.md.\n */\n\nexport const OBJECT_STORAGE_PROVIDERS = ['s3', 'gcs'] as const;\nexport type ObjectStorageProvider = (typeof OBJECT_STORAGE_PROVIDERS)[number];\n\nexport interface ObjectStorage {\n provider: ObjectStorageProvider;\n buckets: string[];\n}\n\n/** The implicit public port; reserved whether or not storage is declared. */\nexport const OBJECT_STORAGE_PORT_NAME = 'object-storage';\n/** The implicit services' names; reserved whether or not storage is declared. */\nconst OBJECT_STORAGE_SERVICE = 'object-storage';\nconst OBJECT_STORAGE_FRONT_SERVICE = 'object-storage-front';\nexport const OBJECT_STORAGE_SERVICE_NAMES = [OBJECT_STORAGE_SERVICE, OBJECT_STORAGE_FRONT_SERVICE];\n\n/** Where the server keeps buckets: under the helper's state dir, outside any WORKDIR or checkout, owned by the sandbox account. */\nexport const OBJECT_STORAGE_DIR = '/var/lib/cofactor/object-storage';\n\n/** What clients (and the public port) reach: versitygw itself, or the GCS front. */\nexport const OBJECT_STORAGE_PORT: Record<ObjectStorageProvider, number> = { s3: 9000, gcs: 4443 };\n/** fake-gcs-server's own, loopback-only port behind the front. */\nexport const FAKE_GCS_PORT = 4444;\n/** Ports a user port may not take while that provider is declared. */\nexport const RESERVED_STORAGE_PORTS: Record<ObjectStorageProvider, number[]> = {\n s3: [OBJECT_STORAGE_PORT.s3],\n gcs: [OBJECT_STORAGE_PORT.gcs, FAKE_GCS_PORT],\n};\n\n/** Each provider's server, from its per-provider layer (`objectStorageLayerKey`, manifest.ts). */\nexport const OBJECT_STORAGE_BINARY: Record<ObjectStorageProvider, string> = {\n s3: '/usr/local/bin/versitygw',\n gcs: '/usr/local/bin/fake-gcs-server',\n};\n\n/**\n * The S3 root credentials, the same in every sandbox: the server holds only\n * the sandbox's own emulated data, and apps get them through the env.\n */\nexport const S3_ACCESS_KEY = 'cofactor-sandbox';\nexport const S3_SECRET_KEY = 'cofactor-sandbox-secret';\nconst S3_REGION = 'us-east-1';\n/** versitygw's health route. No bucket can be named `_health` (bucket names have no `_`). */\nconst S3_HEALTH_PATH = '/_health';\n\n/**\n * The fixed host fake-gcs-server is told is its public one (`-public-host`):\n * it serves XML-style `/<bucket>/<object>` routes only for that Host, so the\n * front rewrites every request's Host to it, whichever URL the browser used.\n */\nexport const GCS_PUBLIC_HOST = 'storage.cofactor.internal';\nexport const GCS_PROJECT = 'sandbox';\n\nconst SERVICE_DEFAULTS = { after_build: false, reload_on_prepare: false } as const;\n\n/** The implicit services that run the declared provider, started like any service that needs neither a build nor a prepare. */\nexport function objectStorageServices(provider: ObjectStorageProvider) {\n if (provider === 's3') {\n const env = [\n `ROOT_ACCESS_KEY_ID=${S3_ACCESS_KEY}`,\n `ROOT_SECRET_ACCESS_KEY=${S3_SECRET_KEY}`,\n `VGW_REGION=${S3_REGION}`,\n // Browsers upload through presigned URLs on the public port, another\n // origin; without this the preflight 403s (spike, 2026-10-02).\n `VGW_CORS_ALLOW_ORIGIN='*'`,\n `VGW_HEALTH=${S3_HEALTH_PATH}`,\n ].join(' ');\n return {\n [OBJECT_STORAGE_SERVICE]: {\n run: `${env} exec ${OBJECT_STORAGE_BINARY.s3} --port :${OBJECT_STORAGE_PORT.s3} posix ${OBJECT_STORAGE_DIR}`,\n health: { http: OBJECT_STORAGE_PORT.s3, path: S3_HEALTH_PATH },\n ...SERVICE_DEFAULTS,\n },\n };\n }\n return {\n [OBJECT_STORAGE_SERVICE]: {\n run:\n `exec ${OBJECT_STORAGE_BINARY.gcs} -scheme http -host 127.0.0.1 -port ${FAKE_GCS_PORT}` +\n ` -backend filesystem -filesystem-root ${OBJECT_STORAGE_DIR} -public-host ${GCS_PUBLIC_HOST}` +\n // Upload Location headers point at the front (the port clients use), not fake-gcs's own.\n ` -external-url http://127.0.0.1:${OBJECT_STORAGE_PORT.gcs}`,\n health: { http: FAKE_GCS_PORT, path: '/storage/v1/b' },\n ...SERVICE_DEFAULTS,\n },\n [OBJECT_STORAGE_FRONT_SERVICE]: {\n run: 'exec /usr/local/bin/cofactor-sandbox object-storage-front',\n health: { http: OBJECT_STORAGE_PORT.gcs, path: '/storage/v1/b' },\n ...SERVICE_DEFAULTS,\n },\n };\n}\n\n/** The standard client env for the declared provider; a definition's own env overrides it (runtime.ts `childEnv`). */\nexport function objectStorageEnv(storage: ObjectStorage | undefined): Record<string, string> {\n if (!storage) return {};\n if (storage.provider === 's3') {\n return {\n AWS_ENDPOINT_URL_S3: `http://127.0.0.1:${OBJECT_STORAGE_PORT.s3}`,\n AWS_ACCESS_KEY_ID: S3_ACCESS_KEY,\n AWS_SECRET_ACCESS_KEY: S3_SECRET_KEY,\n AWS_REGION: S3_REGION,\n };\n }\n return { STORAGE_EMULATOR_HOST: `http://127.0.0.1:${OBJECT_STORAGE_PORT.gcs}`, GOOGLE_CLOUD_PROJECT: GCS_PROJECT };\n}\n","/**\n * Syncing a working tree into a claimed sandbox: the wire contract between\n * `cofactor sandbox sync` (the CLI), the API that mints a session, and the\n * guest's `cofactor-sandbox sync-server` (sync-server.ts). Pure values and\n * schemas, so all three import the one definition.\n *\n * The CLI talks to the copy directly, over the preview edge, on the implicit\n * public `sync` port. Every request but `/healthz` carries a short-lived\n * token the API minted from the copy's own `sync.key` (its prepare input).\n *\n * @module sandbox-helper/sync-protocol\n * @see ~/.claude/plans/dirty-tree-sandboxes.md (approved 2026-10-13)\n */\nimport { createHash, createHmac, timingSafeEqual } from 'node:crypto';\nimport { z } from 'zod';\n\n/** The implicit public port the sync server answers on; reserved, never declared. */\nexport const SYNC_PORT_NAME = 'sync';\n/** The implicit service that runs the sync server; reserved, never declared. */\nexport const SYNC_SERVICE_NAME = 'cofactor-sync';\n/** The guest port behind `SYNC_PORT_NAME`; a declared port may not take it. */\nexport const SYNC_PORT = 7099;\n\n/** How long a sync token lives: one sync per session. */\nexport const SYNC_TOKEN_TTL_SECONDS = 15 * 60;\n/** Clock skew the guest tolerates between the API that minted a token and itself. */\nconst SYNC_TOKEN_SKEW_SECONDS = 60;\n\n/** The largest apply body the guest reads (`maxRequestBodySize`); the CLI splits a bigger sync into several applies. */\nexport const SYNC_MAX_APPLY_BYTES = 64 * 1024 * 1024;\n\n/** How often a `?build=1` stream sends a heartbeat: well inside the preview edge's 60 s idle timeout. */\nexport const SYNC_HEARTBEAT_MS = 20_000;\n\n/** A sync key: 32 random bytes as hex, one per prepared copy. */\nexport const syncKeySchema = z.string().regex(/^[0-9a-f]{64}$/);\n\nconst mac = (key: string, exp: number) =>\n createHmac('sha256', Buffer.from(key, 'hex')).update(`sync:${exp}`).digest('base64url');\n\n/** `<exp>.<base64url(HMAC-SHA256(key, \"sync:\" + exp))>`, `exp` in Unix seconds. */\nexport function mintSyncToken(key: string, exp: number): string {\n return `${exp}.${mac(key, exp)}`;\n}\n\n/** Why a token was refused, for the 401's body; never says anything about the key. */\nexport type SyncTokenRefusal = 'malformed' | 'expired' | 'too_long' | 'bad_mac';\n\n/**\n * Check a token against the copy's key in constant time. Refuses an expired\n * one, and one whose expiry is further out than a session ever lasts (a\n * token the API never minted).\n */\nexport function verifySyncToken(key: string, token: string, nowSeconds: number): SyncTokenRefusal | null {\n const match = /^(\\d{1,12})\\.([A-Za-z0-9_-]{43})$/.exec(token);\n if (!match) return 'malformed';\n const exp = Number(match[1]);\n if (exp <= nowSeconds) return 'expired';\n if (exp > nowSeconds + SYNC_TOKEN_TTL_SECONDS + SYNC_TOKEN_SKEW_SECONDS) return 'too_long';\n const expected = Buffer.from(mac(key, exp));\n const given = Buffer.from(match[2]!);\n return expected.length === given.length && timingSafeEqual(expected, given) ? null : 'bad_mac';\n}\n\n/** Git's blob id of `bytes`: what `git hash-object` prints, so the CLI can read it straight from an index. */\nexport function gitBlobId(bytes: Uint8Array): string {\n return createHash('sha1').update(`blob ${bytes.length}\\0`).update(bytes).digest('hex');\n}\n\n/**\n * A path relative to the sync root: no leading `/`, no empty, `.` or `..`\n * segment, no `.git` segment, no NUL. Everything the guest writes or deletes\n * passes this first.\n */\nexport function isSafeSyncPath(path: string): boolean {\n if (path === '' || path.length > 4096 || path.includes('\\0') || path.startsWith('/')) return false;\n return path.split('/').every((part) => part !== '' && part !== '.' && part !== '..' && part !== '.git');\n}\n\nconst syncPath = z.string().refine(isSafeSyncPath, 'must be a relative path with no \"..\", \".\" or \".git\" segment');\nconst blobId = z.string().regex(/^[0-9a-f]{40}$/);\n/** Only the executable bit matters, as in git: 0o755 or 0o644. */\nconst fileMode = z.union([z.literal(0o755), z.literal(0o644)]);\n\n/**\n * `POST /sync/plan`: every file the CLI would sync (with its size, so a copy\n * file of another size is needed without being read), and optionally the\n * directory (`--to`) the sync is scoped to. The whole list is one request\n * body, so a tree's list must fit in `SYNC_MAX_APPLY_BYTES`.\n */\nexport const syncPlanRequestSchema = z.strictObject({\n local: z.array(z.strictObject({ path: syncPath, blob: blobId, mode: fileMode, size: z.number().int().nonnegative() })),\n to: syncPath.optional(),\n});\nexport type SyncPlanRequest = z.infer<typeof syncPlanRequestSchema>;\n\n/**\n * The plan: `need` is each local path that is missing in the copy or differs\n * (content or executable bit). `copyOnly` is what the copy has that the CLI\n * didn't list: within `to`, one entry per file; over the whole root, one per\n * file except that a directory holding no local path is one entry ending in\n * `/` with its file count (e.g. `node_modules/`). Plan that directory as `to`\n * to list its files.\n */\nexport const syncPlanResponseSchema = z.strictObject({\n need: z.array(z.string()),\n copyOnly: z.array(z.strictObject({ path: z.string(), files: z.number().int().positive() })),\n});\nexport type SyncPlanResponse = z.infer<typeof syncPlanResponseSchema>;\n\n/**\n * `manifest.json` in an apply body: each file's path, blob id and mode, in the\n * order of the body's `files/<index>` entries, and the paths to delete. A\n * delete of a path the copy doesn't have is a no-op, so applying twice\n * converges.\n */\nexport const syncApplyManifestSchema = z.strictObject({\n files: z.array(z.strictObject({ path: syncPath, blob: blobId, mode: fileMode })),\n delete: z.array(syncPath),\n});\nexport type SyncApplyManifest = z.infer<typeof syncApplyManifestSchema>;\n\n/** The tar entry of the `index`th manifest file (names stay under ustar's 100 bytes whatever the path). */\nexport function syncApplyEntryName(index: number): string {\n return `files/${index}`;\n}\n\n/** What an apply did. */\nexport const syncApplyResultSchema = z.strictObject({\n written: z.number().int().nonnegative(),\n deleted: z.number().int().nonnegative(),\n ms: z.number(),\n});\nexport type SyncApplyResult = z.infer<typeof syncApplyResultSchema>;\n\n/**\n * One NDJSON line of `POST /sync/apply?build=1` (GitHub copies only): the\n * apply, then each build step as it starts and ends and the step's output,\n * then the build's end. A `heartbeat` keeps the preview edge's 60 s idle\n * timeout from closing a quiet step. The last line is `done` or `error`.\n */\nexport const syncBuildEventSchema = z.discriminatedUnion('type', [\n z.strictObject({ type: z.literal('applied'), result: syncApplyResultSchema }),\n z.strictObject({ type: z.literal('step'), name: z.string(), ran: z.boolean(), ms: z.number() }),\n z.strictObject({ type: z.literal('output'), text: z.string() }),\n z.strictObject({ type: z.literal('heartbeat') }),\n z.strictObject({ type: z.literal('done'), restarted: z.array(z.string()), ms: z.number() }),\n z.strictObject({ type: z.literal('error'), message: z.string() }),\n]);\nexport type SyncBuildEvent = z.infer<typeof syncBuildEventSchema>;\n","/**\n * `${params.NAME}` and `${secrets.NAME}` references inside an emulation\n * plugin's config, for plugins that accept them (`configReferences`, e.g.\n * `aws`): a seeded value can be a `{secret: true}` param or one of the\n * application's stored secrets without the value ever being written into the\n * definition.\n *\n * - `validate` checks every reference's shape and that a param is declared\n * (`emulationReferenceIssues`), and refuses references in other plugins,\n * whose domains and env contract the API computes from the raw config.\n * - The API refuses a prepare whose environment lacks a referenced secret\n * (`emulationSecretRefs`, `missingEmulationSecret`) and sends the references\n * unresolved.\n * - The sandbox resolves them at prepare (`emulationConfigFor`), with the same\n * params and secrets its services get, and hands the result only to the\n * emulator's process; prepare.json keeps the references.\n *\n * @module\n */\n\nimport { getPlugin } from '@chatsdet/emulation';\nimport type { Definition, DefinitionIssue, PrepareInput } from './manifest';\n\n/** Any `${...}`: each one inside a reference-accepting plugin's config must be a well-formed reference. */\nconst ANY_REF = /\\$\\{([^}]*)\\}/g;\nconst REFERENCE = /^(?:params\\.([a-z][a-z0-9_]*)|secrets\\.([A-Z_][A-Z0-9_]*))$/;\n/** What makes a string look like an attempted reference, in a plugin that does not accept them. */\nconst REFERENCE_LIKE = /\\$\\{(?:params|secrets)\\./;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Calls `visit` with every string value inside `value` (not object keys) and its dotted path. */\nfunction forEachString(value: unknown, path: string, visit: (text: string, path: string) => void): void {\n if (typeof value === 'string') visit(value, path);\n else if (Array.isArray(value)) value.forEach((item, index) => forEachString(item, `${path}[${index}]`, visit));\n else if (isRecord(value)) for (const [key, item] of Object.entries(value)) forEachString(item, `${path}.${key}`, visit);\n}\n\n/** `value` with every string value (not object keys) replaced by `replace(string)`. */\nfunction mapStrings(value: unknown, replace: (text: string) => string): unknown {\n if (typeof value === 'string') return replace(value);\n if (Array.isArray(value)) return value.map((item) => mapStrings(item, replace));\n if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, mapStrings(item, replace)]));\n return value;\n}\n\nfunction acceptsReferences(pluginId: string): boolean {\n return getPlugin(pluginId)?.configReferences === true;\n}\n\n/** Definition problems with references in the (raw) `emulation` block, given the declared `params`. */\nexport function emulationReferenceIssues(emulation: Record<string, unknown>, params: Record<string, unknown>): DefinitionIssue[] {\n const issues: DefinitionIssue[] = [];\n for (const [pluginId, pluginConfig] of Object.entries(emulation)) {\n const accepts = acceptsReferences(pluginId);\n forEachString(pluginConfig, `emulation.${pluginId}`, (text, path) => {\n if (!accepts) {\n if (REFERENCE_LIKE.test(text)) issues.push({ path, message: `the ${pluginId} emulator does not resolve \\${params.*} or \\${secrets.*}` });\n return;\n }\n for (const match of text.matchAll(ANY_REF)) {\n const reference = REFERENCE.exec(match[1]!);\n if (!reference) issues.push({ path, message: `\"${match[0]}\" is not \\${params.name} or \\${secrets.NAME}` });\n else if (reference[1] !== undefined && !(reference[1] in params)) {\n issues.push({ path, message: `references undeclared param \"${reference[1]}\"` });\n }\n }\n });\n }\n return issues;\n}\n\n/** Params referenced by emulation config values, including nested records and arrays. */\nexport function emulationParamRefs(definition: Definition): Set<string> {\n const names = new Set<string>();\n forEachString(definition.emulation, 'emulation', (text) => {\n for (const match of text.matchAll(ANY_REF)) {\n const param = REFERENCE.exec(match[1]!)?.[1];\n if (param !== undefined) names.add(param);\n }\n });\n return names;\n}\n\n/** The names of the application's stored secrets the definition's emulation configs reference, sorted. */\nexport function emulationSecretRefs(definition: Definition): string[] {\n const names = new Set<string>();\n for (const [pluginId, pluginConfig] of Object.entries(definition.emulation)) {\n if (!acceptsReferences(pluginId)) continue;\n forEachString(pluginConfig, pluginId, (text) => {\n for (const match of text.matchAll(ANY_REF)) {\n const secret = REFERENCE.exec(match[1]!)?.[2];\n if (secret !== undefined) names.add(secret);\n }\n });\n }\n return [...names].sort();\n}\n\n/** The message for an emulation config referencing a secret the application has not set for an environment. */\nexport function missingEmulationSecret(name: string, environment: string): string {\n return `emulation references the secret \"${name}\", which is not set for environment \"${environment}\"; set it with \\`cofactor sandbox secret set ${name} --application <application>\\``;\n}\n\n/**\n * The emulation instance config with every reference in a reference-accepting\n * plugin's config replaced by its value, in one pass (a substituted value is\n * never scanned again). Throws naming a secret that is not set: the API\n * refused such a prepare already, and this keeps the resolution total.\n */\nexport function resolveEmulationConfig(\n config: Record<string, unknown>,\n params: Record<string, string>,\n secrets: Record<string, string>,\n environment: string\n): Record<string, unknown> {\n if (!Array.isArray(config.plugins)) throw new Error('emulation config needs a \"plugins\" array');\n const resolve = (text: string) =>\n text.replace(ANY_REF, (whole, inner: string) => {\n const reference = REFERENCE.exec(inner);\n if (!reference) return whole;\n const [, param, secret] = reference;\n if (param !== undefined) {\n const value = params[param];\n if (value === undefined) throw new Error(`emulation references undeclared param \"${param}\"`);\n return value;\n }\n const value = secrets[secret!];\n if (value === undefined) throw new Error(missingEmulationSecret(secret!, environment));\n return value;\n });\n return {\n ...config,\n plugins: config.plugins.map((plugin: unknown) =>\n isRecord(plugin) && typeof plugin.pluginId === 'string' && acceptsReferences(plugin.pluginId)\n ? { ...plugin, config: mapStrings(plugin.config, resolve) }\n : plugin\n ),\n };\n}\n\n/**\n * The emulator config a prepare hands the emulator: the prepare input's\n * emulation config resolved with every param (the definition's layer values\n * under this prepare's own) and the environment's secrets. Each resolved\n * plugin config is checked against the plugin's schema again (a reference\n * that resolves to, say, non-base64 KMS plaintext), so prepare fails here\n * naming the field instead of the emulator failing to start.\n */\nexport function emulationConfigFor(\n emulation: NonNullable<PrepareInput['emulation']>,\n input: Pick<PrepareInput, 'params' | 'secrets' | 'environment'>,\n layerParamValues: Record<string, string>\n): Record<string, unknown> {\n const resolved = resolveEmulationConfig(emulation.config, { ...layerParamValues, ...input.params }, input.secrets, input.environment);\n for (const plugin of resolved.plugins as unknown[]) {\n if (!isRecord(plugin) || typeof plugin.pluginId !== 'string' || !acceptsReferences(plugin.pluginId)) continue;\n const issue = getPlugin(plugin.pluginId)?.configSchema?.safeParse(plugin.config).error?.issues[0];\n if (issue) {\n throw new Error(`emulation.${[plugin.pluginId, ...issue.path.map(String)].join('.')}: ${issue.message} (once its references are resolved)`);\n }\n }\n return resolved;\n}\n","/**\n * `cofactor sandbox push`: deliver an application's sandboxes as a built\n * image, with no repository access. The team's image (any glibc\n * linux/amd64 image with a WORKDIR) gets Cofactor's layers on top — its\n * sandbox helper as the entrypoint, the declared object storage server —\n * named by digest and never uploaded, and one small layer of its own: the\n * application's `cofactor.sandbox.yaml` and the image's ENV and WORKDIR at\n * /cofactor. The image goes to Cofactor as content-addressed blobs read\n * straight out of `docker save`, and only the ones Cofactor doesn't hold yet\n * are uploaded, so a re-push sends the layers that changed. Cofactor imports\n * it into the sandbox fleet, where it boots until its services are healthy;\n * the definition's environments then sync onto the application.\n *\n * @module\n * @see specs/pending/sandboxes-v2-incremental-push.md\n */\n\nimport process from 'node:process';\nimport { spawnSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { createReadStream, createWriteStream, mkdtempSync, type ReadStream, readFileSync, rmSync } from 'node:fs';\nimport { type FileHandle, open } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport { createGunzip } from 'node:zlib';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getArgValues } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { resolveOrCreateSandboxApplication } from './environment.js';\nimport type { PushEntry } from './sandbox-v2.js';\nimport { syncabilityLineOrSkipped } from './sandbox-push-syncable.js';\nimport {\n type DefinitionIssue,\n IMAGE_CONFIG_FILE,\n IMAGE_DEFINITION_FILE,\n type ImageConfig,\n imageConfigSchema,\n type ObjectStorageProvider,\n validateDefinition,\n} from '@chatsdet/sandbox-helper/manifest';\nimport {\n type ImageBlob,\n type ImageConfigJson,\n MAX_BLOB_BYTES,\n tarEntries,\n tarOf,\n withSandboxLayers,\n} from '@chatsdet/sandbox-helper/image-archive';\n\nconst USAGE = [\n 'Usage: cofactor sandbox push --application <name|id> <image> [--definition <file>] [--tag <tag>]... [--wait] [--json]',\n '',\n 'Tag main builds `main` (or `nightly`): once an application has a push with either tag, its head is the',\n 'newest such push, and only those runs move the visual-review baseline. Other pushes are claimed with --at.',\n].join('\\n');\n\n/** Blobs uploaded at once. */\nconst UPLOAD_CONCURRENCY = 4;\nconst UPLOAD_ATTEMPTS = 3;\nconst HASH_CHUNK_BYTES = 8 * 1024 * 1024;\n\n/** How long `--wait` follows an import (it boots the image until healthy; a big image takes a while). */\nconst IMPORT_TIMEOUT_MS = 50 * 60_000;\nconst IMPORT_POLL_MS = 5_000;\n\ntype ApiResult<T> = { ok: boolean; error?: string; code?: string; issues?: DefinitionIssue[]; data: T };\n\n/** Why a push stopped; printed once, after the temporary files and image are cleaned up. */\nclass PushError extends Error {\n constructor(\n message: string,\n readonly issues: DefinitionIssue[] = []\n ) {\n super(message);\n }\n}\n\nfunction fail(message: string, issues?: DefinitionIssue[]): never {\n throw new PushError(message, issues);\n}\n\n/** Run docker, failing with its stderr. `quiet` keeps its progress off the terminal (for --json). */\nfunction docker(args: string[], options: { quiet: boolean }): string {\n const result = spawnSync('docker', args, {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', options.quiet ? 'pipe' : 'inherit'],\n maxBuffer: 64 * 1024 * 1024,\n });\n if (result.error) fail(`could not run docker (${result.error.message}); is Docker installed and running?`);\n if (result.status !== 0) fail(`docker ${args.slice(0, 2).join(' ')} failed:\\n${result.stderr ?? ''}`.trimEnd());\n return result.stdout;\n}\n\ntype InspectedImage = {\n Architecture: string;\n Os: string;\n Config: { WorkingDir?: string; Env?: string[] | null; User?: string };\n};\n\n/**\n * What the image's own config would have applied when run: its WORKDIR and\n * ENV, which an import into the fleet otherwise drops. Refuses an image the\n * fleet cannot run.\n */\nfunction imageConfigOf(image: string): ImageConfig {\n const result = spawnSync('docker', ['image', 'inspect', image], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });\n if (result.error) fail(`could not run docker (${result.error.message}); is Docker installed and running?`);\n const [inspected] = result.status === 0 ? (JSON.parse(result.stdout) as InspectedImage[]) : [];\n if (!inspected) fail(`Docker has no local image ${image}: build or pull it first`);\n if (inspected.Os !== 'linux' || inspected.Architecture !== 'amd64') {\n fail(\n `${image} is ${inspected.Os}/${inspected.Architecture}; sandboxes run linux/amd64. Rebuild it with\\n` +\n ` docker buildx build --platform linux/amd64 -t ${image} --load <context>`\n );\n }\n // The sandbox boots as root and drops to uid 1000 itself. An image ending on\n // another USER failed to boot only after its whole upload (seen 2026-10-06:\n // EACCES on /etc/cofactor), so it is refused before the upload.\n const user = inspected.Config.User ?? '';\n // The uid is what matters (a group after `:` doesn't change it).\n if (!['', '0', 'root'].includes(user.split(':')[0]!)) {\n fail(\n `${image} runs as USER ${user}; a sandbox image must leave USER as root (the sandbox drops to uid 1000 itself). ` +\n 'Remove the final `USER` line from the Dockerfile, or end it with `USER root`.'\n );\n }\n const env = Object.fromEntries(\n (inspected.Config.Env ?? []).map((entry) => {\n const separator = entry.indexOf('=');\n return [entry.slice(0, separator), entry.slice(separator + 1)];\n })\n );\n const parsed = imageConfigSchema.safeParse({ workdir: inspected.Config.WorkingDir ?? '', env });\n if (!parsed.success) {\n const workdir = inspected.Config.WorkingDir || '(none)';\n fail(\n `${image}'s WORKDIR ${workdir} can't be used: ${parsed.error.issues[0]?.message ?? 'invalid'}. ` +\n 'Set one for your app in its Dockerfile (e.g. `WORKDIR /app`): services, prepare commands and actions run from it, ' +\n 'and the sandbox makes it writable by the app.'\n );\n }\n return parsed.data;\n}\n\n/** A value for a shell command line, quoted only when it needs to be. */\nfunction shellWord(value: string): string {\n return /^[\\w@%+=:,./-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", `'\\\\''`)}'`;\n}\n\nasync function apiOrFail<T>(result: Promise<ApiResult<T>>): Promise<T> {\n const res = await result;\n if (!res.ok) fail(res.error ?? 'unknown error', res.issues);\n return res.data;\n}\n\nconst mib = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`;\n\n/**\n * `size` bytes of the saved archive from `offset`, read at that position.\n * Not `openAsBlob`: Node's (v22) wraps a file's size at 4 GiB, and its\n * slices past that read nothing, so a `docker save` over 4 GiB lost its\n * manifest.json, which comes last (seen 2026-10-22). Short only at the\n * file's end, which `tarEntries` reports as a truncated archive; every entry\n * it returns lies before the archive's end block, so reading one is never short.\n */\nasync function readAt(file: FileHandle, offset: number, size: number): Promise<Uint8Array> {\n const bytes = new Uint8Array(size);\n for (let at = 0; at < size; ) {\n const { bytesRead } = await file.read(bytes, at, size - at, offset + at);\n if (bytesRead === 0) return bytes.subarray(0, at);\n at += bytesRead;\n }\n return bytes;\n}\n\n/**\n * `sha256:<hex>` of a range of the saved archive, read a chunk at a time by\n * position. Not a stream: under Bun, streams of a file-backed blob never\n * settled when several ran at once, and the process exited 0 mid-push (seen\n * 2026-10-04).\n */\nasync function digestOf(file: FileHandle, offset: number, size: number): Promise<string> {\n const hash = createHash('sha256');\n for (let at = 0; at < size; at += HASH_CHUNK_BYTES) {\n hash.update(await readAt(file, offset + at, Math.min(HASH_CHUNK_BYTES, size - at)));\n }\n return `sha256:${hash.digest('hex')}`;\n}\n\n/**\n * A blob of the image as the API names it, and a fresh request body for\n * uploading it (a retry needs a new one).\n */\ntype Part = ImageBlob & { body: () => Uint8Array<ArrayBuffer> | ReadStream };\n\n/** A blob held in memory: the config, the push's own layer. */\nfunction partOf(bytes: Uint8Array<ArrayBuffer>): Part {\n return { digest: `sha256:${createHash('sha256').update(bytes).digest('hex')}`, size: bytes.length, body: () => bytes };\n}\n\n/**\n * PUT a blob to its presigned URL, again after a pause when the network or\n * storage fails. A refusal (4xx: a checksum mismatch, an expired URL) won't\n * change on a retry; a push run again uploads only what is still missing.\n */\nasync function putBlob(url: string, headers: Record<string, string>, part: Part): Promise<void> {\n const digest = part.digest;\n for (let attempt = 1; ; attempt++) {\n let response: Response;\n try {\n // A length, not chunked: S3 refuses a chunked presigned PUT.\n const init: RequestInit & { duplex: 'half' } = {\n method: 'PUT',\n headers: { ...headers, 'content-length': String(part.size) },\n body: part.body() as BodyInit,\n duplex: 'half',\n };\n response = await fetch(url, init);\n } catch (error) {\n if (attempt >= UPLOAD_ATTEMPTS) throw error;\n await new Promise((resolve) => setTimeout(resolve, 2_000 * attempt));\n continue;\n }\n if (response.ok) return;\n const detail = `${response.status}: ${(await response.text()).slice(0, 300)}`;\n if (response.status < 500 || attempt >= UPLOAD_ATTEMPTS) {\n fail(`uploading ${digest} was refused (HTTP ${detail}); run the push again to upload what is still missing`);\n }\n await new Promise((resolve) => setTimeout(resolve, 2_000 * attempt));\n }\n}\n\n/**\n * The image's config and layers as `docker save` wrote them, from its\n * manifest.json (Docker writes one in its OCI layout too). Layer paths are\n * whatever the layout uses; each is named by the digest of its bytes.\n */\nasync function savedImage(archive: string) {\n const file = await open(archive);\n try {\n const entries = await tarEntries((offset, length) => readAt(file, offset, length)).catch((error: unknown) =>\n fail(`reading the saved image: ${error instanceof Error ? error.message : String(error)}`)\n );\n const byName = new Map(entries.map((entry) => [entry.name.replace(/^\\.\\//, ''), entry]));\n const entryOf = (name: string) => {\n const entry = byName.get(name);\n if (!entry) fail(`the saved image has no ${name}; is this a \\`docker save\\` archive?`);\n return entry;\n };\n const json = async (name: string) => {\n const entry = entryOf(name);\n return JSON.parse(new TextDecoder().decode(await readAt(file, entry.offset, entry.size))) as unknown;\n };\n // A layer uploads straight from its range of the archive: not a blob\n // slice, which under Bun hangs with several uploads at once (seen\n // 2026-10-04), and under Node reads nothing past 4 GiB (see `readAt`).\n const layerOf = async (name: string): Promise<Part> => {\n const entry = entryOf(name);\n // Before hashing it: a layer too large to upload fails at once, not after a multi-GB read.\n const tooLarge = (bytes: number) => fail(`a layer of the image is ${(bytes / 1024 ** 3).toFixed(1)} GB; a push takes at most 5 GB per layer. Slim the image.`);\n if (entry.size > MAX_BLOB_BYTES) tooLarge(entry.size);\n const range = { start: entry.offset, end: entry.offset + entry.size - 1 };\n // Docker 29 saves from its containerd image store with gzip-compressed layers; Cofactor takes the\n // uncompressed tar the image config's diff_ids name. Unpack beside the archive (removed with it); this\n // needs disk for every unpacked layer until the push ends.\n const [first, second] = await readAt(file, entry.offset, 2);\n if (first === 0x1f && second === 0x8b) {\n const unpacked = join(dirname(archive), `layer-${entry.offset}.tar`);\n await pipeline(createReadStream(archive, range), createGunzip(), createWriteStream(unpacked)).catch((error: unknown) =>\n fail(`unpacking a compressed layer of the image: ${error instanceof Error ? error.message : String(error)}`)\n );\n const plain = await open(unpacked);\n try {\n const { size } = await plain.stat();\n if (size > MAX_BLOB_BYTES) tooLarge(size);\n return { digest: await digestOf(plain, 0, size), size, body: () => createReadStream(unpacked) };\n } finally {\n await plain.close();\n }\n }\n return { digest: await digestOf(file, entry.offset, entry.size), size: entry.size, body: () => createReadStream(archive, range) };\n };\n const [manifest] = (await json('manifest.json')) as { Config: string; Layers: string[] }[];\n if (!manifest) fail('the saved image lists no image');\n // One at a time: see `digestOf`.\n const layers: Part[] = [];\n for (const name of manifest.Layers) layers.push(await layerOf(name));\n return { config: (await json(manifest.Config)) as ImageConfigJson, layers };\n } finally {\n await file.close();\n }\n}\n\n/**\n * The image a push sends, from the team's saved image: its layers, then\n * Cofactor's (`cofactorLayers`, by digest), then the push's own layer with\n * what the helper reads from /cofactor; and its config, rewritten to name\n * those layers and run the helper. `blobs` holds the bytes of every blob the\n * push may have to upload (all but Cofactor's).\n */\nexport async function pushedImage(\n archive: string,\n cofactorLayers: ImageBlob[],\n definitionYaml: string,\n config: ImageConfig\n): Promise<{ config: ImageBlob; layers: ImageBlob[]; blobs: Map<string, Part> }> {\n const saved = await savedImage(archive);\n const own = partOf(\n tarOf([\n { name: IMAGE_DEFINITION_FILE.slice(1), data: new TextEncoder().encode(definitionYaml) },\n { name: IMAGE_CONFIG_FILE.slice(1), data: new TextEncoder().encode(JSON.stringify(config)) },\n ])\n );\n const added = [...cofactorLayers, own];\n const pushed = withSandboxLayers(saved.config, added, new Date().toISOString());\n const imageConfig = partOf(new TextEncoder().encode(JSON.stringify(pushed)));\n const blob = ({ digest, size }: ImageBlob) => ({ digest, size });\n return {\n config: blob(imageConfig),\n layers: [...saved.layers, ...added].map(blob),\n blobs: new Map([imageConfig, ...saved.layers, own].map((part) => [part.digest, part])),\n };\n}\n\n/** `cofactor sandbox push --application A <image> [--definition F] [--tag T]... [--wait] [--json]`. */\nexport async function handleSandboxPush(argv: string[], apiUrl: string, token: string): Promise<void> {\n try {\n await push(argv, apiUrl, token);\n } catch (error) {\n if (!(error instanceof PushError)) throw error;\n console.error(`Error: ${error.message}`);\n for (const issue of error.issues) console.error(` ${issue.path || '(root)'}: ${issue.message}`);\n process.exit(1);\n }\n}\n\nasync function push(argv: string[], apiUrl: string, token: string): Promise<void> {\n const valueFlags = new Set(['--application', '--property', '--definition', '--tag', '--env', '--org', '--token', '--api-url', '--app-url']);\n const positional = argv.filter((arg, i) => !arg.startsWith('-') && !valueFlags.has(argv[i - 1] ?? ''));\n const image = positional[0];\n if (!image) fail(`an image is required\\n${USAGE}`);\n const definitionFile = getArgValue(argv, '--definition') ?? './cofactor.sandbox.yaml';\n const tags = getArgValues(argv, '--tag');\n // The server's rule, checked before a multi-minute build.\n const badTag = tags.find((tag) => !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(tag));\n if (badTag !== undefined) fail(`--tag ${badTag}: use letters, digits, '.', '_' and '-' (at most 64, starting with a letter or digit)`);\n if (tags.length > 10) fail('a push takes at most 10 tags');\n const wait = hasFlag(argv, '--wait');\n const json = hasFlag(argv, '--json');\n const log = (line: string) => (json ? console.error(line) : console.log(line));\n\n let definitionYaml: string;\n try {\n definitionYaml = readFileSync(definitionFile, 'utf8');\n } catch (error) {\n fail(`reading ${definitionFile}: ${error instanceof Error ? error.message : String(error)}`);\n }\n const { definition, issues } = validateDefinition(definitionYaml, 'upload');\n if (!definition || issues.length > 0) fail(`${definitionFile} is not a valid definition for pushed images:`, issues);\n const storageProvider = definition.object_storage?.provider;\n const applicationId = await resolveOrCreateSandboxApplication(argv, apiUrl, token);\n const config = imageConfigOf(image);\n // Advisory only: whether `cofactor sandbox sync` can write into this image. Never stops the push.\n log(syncabilityLineOrSkipped({ image, workdir: config.workdir, definitionFile }));\n\n const work = mkdtempSync(join(tmpdir(), 'cofactor-push-'));\n const cleanUp = () => rmSync(work, { recursive: true, force: true });\n // A push interrupted mid-save or mid-upload would otherwise leave a multi-GB archive behind.\n const interrupted = (signal: NodeJS.Signals) => {\n cleanUp();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', interrupted);\n process.once('SIGTERM', interrupted);\n // Red until the push is through: if the event loop ever drains mid-push again\n // (it did under Bun, 2026-10-04), the process exits 1, not a silent 0.\n process.exitCode = 1;\n try {\n // Cofactor's layers for this version, by digest: added to the image, never uploaded.\n const ours = await apiOrFail(\n apiRequest<ApiResult<{ platformVersion: number; helper?: ImageBlob; objectStorage: Record<ObjectStorageProvider, ImageBlob> }>>(\n apiUrl,\n token,\n 'GET',\n '/org/sandboxes-v2/helper-layer'\n )\n );\n // A Cofactor deployed before incremental pushes answers with download URLs instead: the CLI\n // ships to npm ahead of a Cofactor's deploy (prod ran hundreds of commits behind, 2026-10-02).\n if (!ours.helper) fail('this Cofactor takes pushes from an older cofactor CLI; use the version it ships with');\n const cofactorLayers = [ours.helper, ...(storageProvider ? [ours.objectStorage[storageProvider]] : [])];\n\n const archive = join(work, 'image.tar');\n log('Saving the image...');\n docker(['save', '--output', archive, image], { quiet: json });\n const pushing = await pushedImage(archive, cofactorLayers, definitionYaml, config);\n const { layers } = pushing;\n\n const created = await apiOrFail(\n apiRequest<ApiResult<{ id: string; uploads: { digest: string; url: string; headers: Record<string, string> }[] }>>(\n apiUrl,\n token,\n 'POST',\n `/org/applications/${applicationId}/sandbox/uploads`,\n { definitionYaml, platformVersion: ours.platformVersion, tags, image: { config: pushing.config, layers } }\n )\n );\n const partsByDigest = pushing.blobs;\n const uploadBytes = created.uploads.reduce((total, upload) => total + (partsByDigest.get(upload.digest)?.size ?? 0), 0);\n const imageBytes = layers.reduce((total, layer) => total + layer.size, 0);\n log(`Pushing ${created.id}: uploading ${created.uploads.length} of ${layers.length + 1} blobs (${mib(uploadBytes)}); ${mib(imageBytes - uploadBytes)} already in Cofactor`);\n const queue = [...created.uploads];\n let sent = 0;\n await Promise.all(\n Array.from({ length: UPLOAD_CONCURRENCY }, async () => {\n for (let next = queue.shift(); next; next = queue.shift()) {\n const part = partsByDigest.get(next.digest);\n if (!part) fail(`Cofactor asked for ${next.digest}, which this image doesn't have`);\n await putBlob(next.url, next.headers, part);\n sent += part.size;\n if (part.size > 10 * 1024 * 1024) log(` ${mib(sent)} of ${mib(uploadBytes)}`);\n }\n })\n );\n rmSync(archive);\n let pushed = await apiOrFail(\n apiRequest<ApiResult<PushEntry>>(\n apiUrl,\n token,\n 'POST',\n `/org/applications/${applicationId}/sandbox/uploads/${created.id}/complete`\n )\n );\n\n if (wait) {\n log('Importing (Cofactor boots the image until its services are healthy)...');\n const deadline = Date.now() + IMPORT_TIMEOUT_MS;\n const importStarted = Date.now();\n let reported = importStarted;\n while ((pushed.status === 'importing' || pushed.status === 'uploading') && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, IMPORT_POLL_MS));\n // A large image imports for minutes: say it is still going.\n if (Date.now() - reported >= 60_000) {\n reported = Date.now();\n log(` still importing (${Math.round((reported - importStarted) / 60_000)} min)`);\n }\n pushed = await apiOrFail(\n apiRequest<ApiResult<PushEntry>>(apiUrl, token, 'GET', `/org/applications/${applicationId}/sandbox/uploads/${created.id}`)\n );\n }\n }\n const uploaded = { blobs: created.uploads.length, of: layers.length + 1, bytes: uploadBytes, reusedBytes: imageBytes - uploadBytes };\n\n const application = shellWord(getArgValue(argv, '--application') ?? getArgValue(argv, '--property') ?? applicationId);\n // The dispatcher strips the global options from `argv`; the hints must carry them.\n const target = ['--env', '--org']\n .map((flag) => {\n const value = getArgValue(process.argv, flag);\n return value ? ` ${flag} ${shellWord(value)}` : '';\n })\n .join('');\n if (json) {\n writeRawOutput(JSON.stringify({ ...pushed, uploaded }, null, 2));\n } else {\n console.log(`Push ${pushed.id}: ${pushed.status}${pushed.tags.length > 0 ? ` [${pushed.tags.join(', ')}]` : ''}`);\n if (pushed.status === 'ready') {\n console.log(` Claim it: cofactor sandbox claim --application ${application} --environment default --wait${target}`);\n } else if (pushed.status !== 'failed') {\n console.log(` Importing; follow it with: cofactor sandbox envs --application ${application}${target}`);\n }\n }\n if (pushed.status === 'failed') fail(`push ${pushed.id} failed:\\n${pushed.error ?? 'no reason given'}`);\n process.exitCode = 0;\n } finally {\n process.off('SIGINT', interrupted);\n process.off('SIGTERM', interrupted);\n cleanUp();\n }\n}\n","/**\n * Whether a pushed image can take `cofactor sandbox sync`: the rule is that a\n * syncable image has the definition file at its WORKDIR (`COPY . .` gives you\n * that), so the definition directory's files sit at WORKDIR in the same\n * layout. `push` checks it on the local image and reports; it never fails a\n * push.\n *\n * The anchor is the user's own copy at `<WORKDIR>/cofactor.sandbox.yaml`, never\n * the copy `push` itself adds under `/cofactor`. A sample of the directory's\n * tracked files then says how current the image is: one that isn't in the\n * image is fine (compiled sources aren't shipped), one that differs means the\n * image is older than the working tree, which is fine too.\n *\n * @module\n */\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { gitBlobId } from '@chatsdet/sandbox-helper/sync-protocol';\n\nexport type DockerRun = (args: string[]) => { status: number | null; stdout: string; stderr: string };\n\nconst realDocker: DockerRun = (args) => {\n const result = spawnSync('docker', args, { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });\n if (result.error) return { status: null, stdout: '', stderr: result.error.message };\n return { status: result.status, stdout: result.stdout, stderr: result.stderr };\n};\n\n/** How many tracked files the check samples. */\nconst SAMPLE = 20;\n\n/** Up to `SAMPLE` paths spread evenly over `paths` (sorted), so one big directory doesn't fill the sample. */\nexport function sampleOf(paths: string[]): string[] {\n const sorted = [...paths].sort();\n if (sorted.length <= SAMPLE) return sorted;\n return Array.from({ length: SAMPLE }, (_, i) => sorted[Math.floor((i * sorted.length) / SAMPLE)]!);\n}\n\nexport const SYNCABLE_RULE = 'a syncable image has your definition file at its WORKDIR, as `COPY . .` gives you';\n\n/**\n * The one line `push` prints. `definitionDir` is the directory holding the\n * definition being pushed; `definitionFile` its path there.\n */\nexport function syncabilityLine(params: {\n image: string;\n workdir: string;\n definitionDir: string;\n definitionFile: string;\n docker?: DockerRun;\n}): string {\n const docker = params.docker ?? realDocker;\n const git = spawnSync('git', ['ls-files', '-s', '-z'], { cwd: params.definitionDir, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });\n if (git.status !== 0) return 'Sync: not checked (the definition is not in a git repository).';\n const tracked = git.stdout\n .split('\\0')\n .flatMap((entry) => /^(100644|100755) [0-9a-f]+ \\d\\t(.*)$/s.exec(entry)?.[2] ?? [])\n .filter((path) => path !== 'cofactor.sandbox.yaml')\n // A tracked file deleted in the working tree has nothing to compare.\n .filter((path) => existsSync(join(params.definitionDir, path)));\n\n const temp = mkdtempSync(join(tmpdir(), 'cofactor-syncable-'));\n const created = docker(['create', params.image]);\n if (created.status !== 0) {\n rmSync(temp, { recursive: true, force: true });\n return `Sync: not checked (docker create failed: ${created.stderr.trim()}).`;\n }\n const container = created.stdout.trim();\n let copies = 0;\n try {\n /** The image's file at WORKDIR/`path` (a symlink followed inside the image), or null when there is no file there. */\n const copyOut = (path: string): Uint8Array | null => {\n const target = join(temp, String(copies++));\n if (docker(['cp', '-L', `${container}:${params.workdir}/${path}`, target]).status !== 0) return null;\n // A directory at that path is not the file.\n return statSync(target).isFile() ? readFileSync(target) : null;\n };\n const anchor = copyOut('cofactor.sandbox.yaml');\n if (!anchor) {\n return `Sync: not syncable: cofactor.sandbox.yaml isn't at ${params.workdir}/cofactor.sandbox.yaml (${SYNCABLE_RULE}).`;\n }\n const sample = sampleOf(tracked);\n let differ = 0;\n let missing = 0;\n for (const path of sample) {\n const bytes = copyOut(path);\n if (!bytes) missing += 1;\n else if (gitBlobId(bytes) !== gitBlobId(readFileSync(join(params.definitionDir, path)))) differ += 1;\n }\n const anchorOlder = gitBlobId(anchor) !== gitBlobId(readFileSync(params.definitionFile));\n const notes = [\n ...(anchorOlder || differ > 0 ? ['the image is older than your working tree, which is fine'] : []),\n ...(missing > 0 ? [`${missing} of ${sample.length} sampled files aren't in the image (fine for sources it doesn't ship)`] : []),\n ];\n return `Sync: syncable${notes.length > 0 ? ` (${notes.join('; ')})` : ''}.`;\n } finally {\n docker(['rm', container]);\n rmSync(temp, { recursive: true, force: true });\n }\n}\n\n/**\n * `syncabilityLine` for the definition at `definitionFile`, as `push` prints\n * it. The check is advice the plan says never stops a push, so a failure of\n * the check itself (docker, git, a file it can't read) is reported as \"not\n * checked\" instead of failing the push.\n */\nexport function syncabilityLineOrSkipped(params: { image: string; workdir: string; definitionFile: string; docker?: DockerRun }): string {\n try {\n const definitionFile = resolve(params.definitionFile);\n return syncabilityLine({ ...params, definitionFile, definitionDir: dirname(definitionFile) });\n } catch (error) {\n return `Sync: not checked (${error instanceof Error ? error.message : String(error)}).`;\n }\n}\n","/**\n * A pushed image as content-addressed blobs, and the small tar reader and\n * writer the push and sync share.\n *\n * `cofactor sandbox push` reads the layers out of a `docker save` archive,\n * uploads only the blobs Cofactor doesn't hold yet, and describes the image\n * as an {@link ImageBlobs}: its config and its layers in order. The API's\n * import hands Blackcat those blobs by URL; Blackcat's node reads them and\n * loads them as the `docker save` archive they make up.\n *\n * @module sandbox-helper/image-archive\n * @see specs/pending/sandboxes-v2-incremental-push.md\n */\nimport { z } from 'zod';\n\n/** One presigned PUT carries a blob, and S3 takes at most 5 GB in one. */\nexport const MAX_BLOB_BYTES = 5 * 1024 ** 3;\n\n/** A blob by content: `sha256:<hex>` of its bytes, and their length. */\nexport const ImageBlob = z.object({\n digest: z.string().regex(/^sha256:[0-9a-f]{64}$/),\n size: z.number().int().nonnegative(),\n});\nexport type ImageBlob = z.infer<typeof ImageBlob>;\n\n/** An image: its config blob, and its layer blobs bottom to top. */\nexport const ImageBlobs = z.object({\n config: ImageBlob,\n // Docker allows at most 127 layers; each costs a storage lookup per push.\n layers: z.array(ImageBlob).min(1).max(128),\n});\nexport type ImageBlobs = z.infer<typeof ImageBlobs>;\n\n/**\n * An image config as Docker writes it, in the parts a push rewrites: the\n * layer diff ids, the history, and the run settings. Everything else is kept\n * as is.\n */\nexport type ImageConfigJson = {\n config?: Record<string, unknown>;\n rootfs: { type: string; diff_ids: string[] };\n history?: Record<string, unknown>[];\n} & Record<string, unknown>;\n\n/**\n * A pushed image's config: the team image's own config with `added` layers on\n * top (Cofactor's, then the push's own), booting Cofactor's helper instead of\n * the image's entrypoint. Each added layer is an uncompressed tar, so its\n * digest is also its diff id. `cofactor sandbox push` makes a push's config\n * with this, and the API makes an upgraded push's (`withoutSandboxLayers`).\n */\nexport function withSandboxLayers(team: ImageConfigJson, added: ImageBlob[], created: string): ImageConfigJson {\n return {\n ...team,\n rootfs: { ...team.rootfs, diff_ids: [...team.rootfs.diff_ids, ...added.map((layer) => layer.digest)] },\n history: [...(team.history ?? []), ...added.map(() => ({ created, created_by: 'cofactor sandbox push' }))],\n config: { ...team.config, Entrypoint: ['/usr/local/bin/cofactor-sandbox', 'boot'], Cmd: [] },\n };\n}\n\n/**\n * The team image's config inside a pushed one: the last `added` layers (the\n * ones {@link withSandboxLayers} put on) and their history entries removed.\n * Its run settings stay the helper's, which {@link withSandboxLayers} sets again.\n */\nexport function withoutSandboxLayers(pushed: ImageConfigJson, added: number): ImageConfigJson {\n const history = pushed.history ?? [];\n if (pushed.rootfs.diff_ids.length <= added || history.length < added) {\n throw new Error(`the image config has fewer than ${added} layers Cofactor added`);\n }\n return {\n ...pushed,\n rootfs: { ...pushed.rootfs, diff_ids: pushed.rootfs.diff_ids.slice(0, -added) },\n history: history.slice(0, -added),\n };\n}\n\nconst BLOCK = 512;\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** A ustar header for a regular file (names here are short: a digest and a suffix). */\nfunction tarHeader(name: string, size: number, mode = 0o644): Uint8Array {\n if (encoder.encode(name).length > 100) throw new Error(`tar entry name too long: ${name}`);\n const header = new Uint8Array(BLOCK);\n const put = (offset: number, length: number, value: string) => header.set(encoder.encode(value).slice(0, length), offset);\n const octal = (offset: number, length: number, value: number) =>\n put(offset, length, `${value.toString(8).padStart(length - 1, '0')}\\0`);\n put(0, 100, name);\n octal(100, 8, mode);\n octal(108, 8, 0);\n octal(116, 8, 0);\n // Sizes past 8 GiB need the base-256 form; tarOf writes small files.\n octal(124, 12, size);\n octal(136, 12, 0);\n put(148, 8, ' ');\n put(156, 1, '0');\n put(257, 6, 'ustar\\0');\n put(263, 2, '00');\n const checksum = header.reduce((sum, byte) => sum + byte, 0);\n put(148, 8, `${checksum.toString(8).padStart(6, '0')}\\0 `);\n return header;\n}\n\n/** The zero bytes that round an entry of `size` up to a whole block. */\nfunction tarPadding(size: number): Uint8Array {\n return new Uint8Array((BLOCK - (size % BLOCK)) % BLOCK);\n}\n\n/** Two zero blocks: the end of an archive. */\nconst TAR_END = new Uint8Array(BLOCK * 2);\n\n/** A complete tar of small in-memory files, in order. */\nexport function tarOf(files: { name: string; data: Uint8Array; mode?: number }[]): Uint8Array<ArrayBuffer> {\n const parts = files.flatMap((file) => [tarHeader(file.name, file.data.length, file.mode), file.data, tarPadding(file.data.length)]);\n parts.push(TAR_END);\n const out = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));\n let at = 0;\n for (const part of parts) {\n out.set(part, at);\n at += part.length;\n }\n return out;\n}\n\ntype TarEntry = { name: string; offset: number; size: number };\n\nconst field = (block: Uint8Array, offset: number, length: number) =>\n decoder.decode(block.subarray(offset, offset + length)).replace(/\\0.*$/s, '');\n\n/**\n * The regular files of a tar on disk, with where each one's bytes are, read\n * header by header so a multi-GB archive is never held in memory. Follows the\n * pax and GNU long-name records `docker save` may write. The archive ends at\n * its zero block; running out of bytes before it means the archive (or the\n * reader) came up short, and fails rather than returning the entries read so\n * far: a reader that wrapped at 4 GiB once lost a `docker save`'s\n * manifest.json, which comes last (seen 2026-10-22).\n */\nexport async function tarEntries(read: (offset: number, length: number) => Promise<Uint8Array>): Promise<TarEntry[]> {\n const entries: TarEntry[] = [];\n let longName: string | null = null;\n for (let offset = 0; ; ) {\n const block = await read(offset, BLOCK);\n if (block.length < BLOCK) throw new Error(`the tar ends at byte ${offset + block.length}, before its end-of-archive block; it is truncated`);\n if (block.every((byte) => byte === 0)) return entries;\n const size = Number.parseInt(field(block, 124, 12).trim() || '0', 8);\n // A negative or unreadable size would never advance `offset` (a tar can come from a sync's client).\n if (!Number.isSafeInteger(size) || size < 0) throw new Error(`tar entry at ${offset} has an invalid size`);\n const type = field(block, 156, 1) || '0';\n const prefix = field(block, 345, 155);\n const name = longName ?? (prefix ? `${prefix}/${field(block, 0, 100)}` : field(block, 0, 100));\n const data = offset + BLOCK;\n longName = null;\n if (type === 'x' || type === 'L') {\n const text = decoder.decode(await read(data, size)).replace(/\\0+$/, '');\n longName = type === 'L' ? text : (/(?:^|\\n)\\d+ path=([^\\n]*)\\n/.exec(text)?.[1] ?? null);\n } else if (type === '0') {\n entries.push({ name, offset: data, size });\n }\n offset = data + size + tarPadding(size).length;\n }\n}\n","/**\n * `cofactor sandbox sync`: make a claimed sandbox's files match the local\n * working tree, uncommitted and untracked changes included, by talking to the\n * copy's own sync server (sync-server.ts in @chatsdet/sandbox-helper) over its\n * public `sync` port with a token the API mints. The claimer then rebuilds\n * and restarts what they choose (`exec`, `cofactor-sandbox restart`), or a\n * GitHub copy reruns its changed build steps with `--build`.\n *\n * sync <id> [--build] [--json] the working tree: the repo (GitHub), or the\n * directory holding ./cofactor.sandbox.yaml (pushed)\n * sync <id> <local-dir> --to <path> [--json]\n * mirror build output (ignore rules don't apply)\n * into <path> under the sandbox's sync root\n *\n * The local file list comes from a temporary git index (`git add -A` into a\n * copy of the user's index), so the user's own index is never touched, and\n * its `write-tree` is the identity the sync records.\n *\n * @module\n */\nimport { spawnSync } from 'node:child_process';\nimport process from 'node:process';\nimport { copyFileSync, existsSync, lstatSync, mkdtempSync, readFileSync, realpathSync, rmSync, type Stats } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join, relative, resolve, sep } from 'node:path';\nimport { hasFlag, getArgValue } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { tarOf } from '@chatsdet/sandbox-helper/image-archive';\nimport {\n SYNC_MAX_APPLY_BYTES,\n type SyncApplyManifest,\n type SyncApplyResult,\n type SyncPlanResponse,\n gitBlobId,\n syncApplyEntryName,\n syncApplyResultSchema,\n syncBuildEventSchema,\n syncPlanResponseSchema,\n} from '@chatsdet/sandbox-helper/sync-protocol';\n\nexport type LocalFile = { path: string; blob: string; mode: 0o644 | 0o755; size: number };\n\n/** What the API's `POST .../sync-session` returns. */\ntype SyncSession = {\n url: string;\n token: string;\n expiresAt: string;\n delivery: 'github' | 'upload';\n version: string;\n};\n\nclass SyncError extends Error {}\n\nfunction git(args: string[], options: { cwd: string; env?: NodeJS.ProcessEnv; input?: string; okCodes?: number[] }): string {\n const result = spawnSync('git', args, {\n cwd: options.cwd,\n env: options.env ?? process.env,\n input: options.input,\n encoding: 'utf8',\n maxBuffer: 1 << 30, // Node defaults to 1 MiB; this repo's `ls-files -s -z` alone is 1.2 MB\n });\n if (result.error) throw new SyncError(`git ${args[0]} failed: ${result.error.message}`);\n if (!(options.okCodes ?? [0]).includes(result.status ?? -1)) {\n throw new SyncError(`git ${args[0]} failed (${result.signal ?? result.status}): ${result.stderr.trim()}`);\n }\n return result.stdout;\n}\n\nconst toPosix = (path: string) => path.split(sep).join('/');\n\n/** The repository holding `dir`: its top level and its absolute git directory. */\nexport function repositoryOf(dir: string): { top: string; gitDir: string } {\n const top = git(['rev-parse', '--show-toplevel'], { cwd: dir }).trim();\n const gitDir = git(['rev-parse', '--absolute-git-dir'], { cwd: dir }).trim();\n return { top: realpathSync(top), gitDir };\n}\n\n/**\n * `ls-files -s -z` entries: regular files as `LocalFile`s (relative to\n * `strip`, sized from the working tree under `top`), and the paths it skips\n * (symlinks, submodules).\n */\nfunction indexEntries(output: string, strip: string, top: string): { files: LocalFile[]; skipped: string[] } {\n const files: LocalFile[] = [];\n const skipped: string[] = [];\n for (const entry of output.split('\\0')) {\n if (entry === '') continue;\n const match = /^(\\d{6}) ([0-9a-f]+) \\d\\t(.*)$/s.exec(entry);\n if (!match) throw new SyncError(`unexpected git ls-files line: ${entry}`);\n const [, mode, blob, repoPath] = match;\n const path = strip === '' ? repoPath! : repoPath!.slice(strip.length + 1);\n // An entry with no file here (skip-worktree in a sparse checkout) is skipped like a symlink.\n const onDisk = mode === '100644' || mode === '100755' ? lstatOrNull(join(top, repoPath!)) : null;\n if (onDisk?.isFile()) files.push({ path, blob: blob!, mode: mode === '100755' ? 0o755 : 0o644, size: onDisk.size });\n else skipped.push(path);\n }\n return { files, skipped };\n}\n\n/**\n * The working tree under `root` as git would commit it: tracked and untracked\n * files, ignored ones left out. Built in a temporary index (a copy of the\n * user's, so unchanged files aren't re-hashed), never the user's own.\n * `tree` is that index's tree id for `root` (`write-tree --prefix` for a\n * subdirectory), which a later commit matches as `commit:<prefix>`.\n * `untracked` is what isn't tracked yet, so the CLI can say so.\n */\nexport function workingTree(root: string): {\n files: LocalFile[];\n skipped: string[];\n untracked: Set<string>;\n tree: string;\n repo: string;\n prefix: string;\n env: NodeJS.ProcessEnv;\n cleanup: () => void;\n} {\n const { top, gitDir } = repositoryOf(root);\n const prefix = toPosix(relative(top, realpathSync(root)));\n const temp = mkdtempSync(join(tmpdir(), 'cofactor-sync-'));\n const cleanup = () => rmSync(temp, { recursive: true, force: true });\n try {\n const index = join(temp, 'index');\n const env = { ...process.env, GIT_INDEX_FILE: index };\n const userIndex = join(gitDir, 'index');\n if (existsSync(userIndex)) copyFileSync(userIndex, index);\n else if (git(['rev-parse', '--verify', '-q', 'HEAD'], { cwd: top, okCodes: [0, 1] }).trim() !== '') git(['read-tree', 'HEAD'], { cwd: top, env });\n const pathspec = prefix === '' ? '.' : `:(literal)${prefix}`;\n git(['add', '-A', '--', pathspec], { cwd: top, env });\n const entries = indexEntries(git(['ls-files', '-s', '-z', '--', pathspec], { cwd: top, env }), prefix, top);\n const files = withRawBlobs(entries.files, top, prefix);\n const skipped = entries.skipped;\n const tree = git(prefix === '' ? ['write-tree'] : ['write-tree', `--prefix=${prefix}/`], { cwd: top, env }).trim();\n const untracked = new Set(\n git(['ls-files', '--others', '--exclude-standard', '-z', '--', pathspec], { cwd: top })\n .split('\\0')\n .filter(Boolean)\n .map((path) => (prefix === '' ? path : path.slice(prefix.length + 1)))\n );\n return { files, skipped, untracked, tree, repo: top, prefix, env, cleanup };\n } catch (error) {\n cleanup();\n throw error;\n }\n}\n\n/**\n * Build output under `dir`, mirrored as is (ignore rules don't apply), each\n * path under `to`. `tree` is its tree id, recorded with `buildOutput: true`.\n */\nexport function buildOutput(dir: string, to: string): { files: LocalFile[]; skipped: string[]; tree: string } {\n const { gitDir } = repositoryOf(dir);\n const temp = mkdtempSync(join(tmpdir(), 'cofactor-sync-'));\n try {\n const env = { ...process.env, GIT_INDEX_FILE: join(temp, 'index'), GIT_DIR: gitDir, GIT_WORK_TREE: realpathSync(dir) };\n git(['add', '-A', '-f', '--', '.'], { cwd: dir, env });\n const entries = indexEntries(git(['ls-files', '-s', '-z'], { cwd: dir, env }), '', realpathSync(dir));\n const files = withRawBlobs(entries.files, realpathSync(dir), '', env);\n const skipped = entries.skipped;\n const tree = git(['write-tree'], { cwd: dir, env }).trim();\n return { files: files.map((file) => ({ ...file, path: `${to}/${file.path}` })), skipped: skipped.map((path) => `${to}/${path}`), tree };\n } finally {\n rmSync(temp, { recursive: true, force: true });\n }\n}\n\n/** The attributes through which git can run a file's bytes through a filter before storing them. */\nconst FILTER_ATTRIBUTES = ['filter', 'text', 'eol', 'ident', 'working-tree-encoding'];\n\n/**\n * The blob ids of the files' bytes as they are on disk. A clean filter\n * (autocrlf, `.gitattributes` `text`/`eol`/`filter`, LFS) makes the index's\n * blob differ from them, and the copy must get the bytes the app runs\n * locally, so every file git could filter is hashed as it is: all of them\n * with `core.autocrlf` on (`input`, or any of git's true spellings),\n * otherwise those an attribute names. `tree` stays the index's: what a\n * commit of these files would have.\n */\nfunction withRawBlobs(files: LocalFile[], top: string, prefix: string, env?: NodeJS.ProcessEnv): LocalFile[] {\n if (files.length === 0) return files;\n const options = { cwd: top, ...(env ? { env } : {}) };\n const repoPaths = files.map((file) => (prefix === '' ? file.path : `${prefix}/${file.path}`));\n const autocrlf = git(['config', '--get', 'core.autocrlf'], { ...options, okCodes: [0, 1] }).trim();\n const converts =\n autocrlf === 'input' ||\n (autocrlf !== '' && git(['config', '--get', '--type=bool', 'core.autocrlf'], options).trim() === 'true');\n let filtered: Set<string>;\n if (converts) {\n filtered = new Set(repoPaths);\n } else {\n // NUL-separated triples: path, attribute, value.\n const out = git(['check-attr', '-z', '--stdin', ...FILTER_ATTRIBUTES], { ...options, input: repoPaths.map((path) => `${path}\\0`).join('') }).split('\\0');\n filtered = new Set();\n for (let i = 0; i + 2 < out.length; i += 3) if (out[i + 2] !== 'unspecified') filtered.add(out[i]!);\n }\n // Read here, a file at a time: any path name works, and only these files are read.\n return files.map((file, i) =>\n filtered.has(repoPaths[i]!) ? { ...file, blob: gitBlobId(readFileSync(join(top, repoPaths[i]!))) } : file\n );\n}\n\nfunction lstatOrNull(path: string): Stats | null {\n try {\n return lstatSync(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw error;\n }\n}\n\n/** Run `fn` over `items` in chunks small enough for one command line. */\nfunction chunked<T>(items: T[], fn: (chunk: T[]) => void): void {\n for (let i = 0; i < items.length; i += 200) fn(items.slice(i, i + 200));\n}\n\n/** Of `paths` (relative to the repo top), the ones local ignore rules match (a trailing `/` marks a directory). */\nfunction ignoredPaths(repo: string, env: NodeJS.ProcessEnv, paths: string[]): Set<string> {\n if (paths.length === 0) return new Set();\n // Exit 1: none of them is ignored.\n const out = git(['check-ignore', '-z', '--stdin'], { cwd: repo, env, input: paths.map((p) => `${p}\\0`).join(''), okCodes: [0, 1] });\n return new Set(out.split('\\0').filter(Boolean));\n}\n\n/** Every path, relative to the repo top, that any local commit ever had under `pathspecs`. */\nfunction everTracked(repo: string, pathspecs: string[]): Set<string> {\n const seen = new Set<string>();\n if (pathspecs.length === 0) return seen;\n if (git(['rev-parse', '--verify', '-q', 'HEAD'], { cwd: repo, okCodes: [0, 1] }).trim() === '') return seen;\n chunked(pathspecs, (chunk) => {\n const out = git(['log', '--all', '--format=', '--name-only', '-z', '--', ...chunk.map((p) => `:(literal)${p}`)], { cwd: repo });\n for (const path of out.split('\\0')) {\n const trimmed = path.replace(/^\\n+/, '');\n if (trimmed) seen.add(trimmed);\n }\n });\n return seen;\n}\n\nexport type KeptEntry = { path: string; files: number; why: 'ignored here' | 'never tracked here' };\n\n/**\n * Which of the copy's extra files a working-tree sync deletes: a path is\n * deleted only when it isn't present locally (every `copyOnly` path isn't),\n * isn't ignored locally, and some local commit once had a file at that path.\n * A collapsed directory (`dir/`) is listed file by file (`listDir`, a plan\n * scoped to it) and each file judged on its own history. Everything else is\n * kept, with why.\n */\nexport async function chooseDeletions(params: {\n copyOnly: SyncPlanResponse['copyOnly'];\n repo: string;\n /** The sync root's path in the repo, '' at the top. */\n prefix: string;\n /** The temporary index's env, where the working tree's files are all tracked. */\n env: NodeJS.ProcessEnv;\n listDir: (dir: string) => Promise<string[]>;\n}): Promise<{ delete: string[]; kept: KeptEntry[] }> {\n const repoPath = (path: string) => (params.prefix === '' ? path : `${params.prefix}/${path}`);\n const ignored = ignoredPaths(params.repo, params.env, params.copyOnly.map((entry) => repoPath(entry.path)));\n const candidates = params.copyOnly.filter((entry) => !ignored.has(repoPath(entry.path)));\n const history = everTracked(params.repo, candidates.map((entry) => repoPath(entry.path)));\n const kept: KeptEntry[] = params.copyOnly\n .filter((entry) => ignored.has(repoPath(entry.path)))\n .map((entry) => ({ ...entry, why: 'ignored here' as const }));\n const remove: string[] = [];\n for (const entry of candidates) {\n if (!entry.path.endsWith('/')) {\n if (history.has(repoPath(entry.path))) remove.push(entry.path);\n else kept.push({ ...entry, why: 'never tracked here' });\n continue;\n }\n const tracked = [...history].some((path) => path.startsWith(repoPath(entry.path)));\n const listed = tracked ? await params.listDir(entry.path.slice(0, -1)) : [];\n // Each file of the directory is judged on its own: not ignored here, and once tracked.\n const ignoredInside = ignoredPaths(params.repo, params.env, listed.map(repoPath));\n const files = listed.filter((path) => !ignoredInside.has(repoPath(path)) && history.has(repoPath(path)));\n remove.push(...files);\n if (files.length < entry.files) kept.push({ path: entry.path, files: entry.files - files.length, why: 'never tracked here' });\n }\n return { delete: remove.sort(), kept: kept.sort((a, b) => a.path.localeCompare(b.path)) };\n}\n\n/** The files to send, split into apply bodies under the copy's limit; deletions ride the first. */\nexport function applyBatches(files: Array<LocalFile & { bytes: Uint8Array }>, remove: string[]): Array<Uint8Array<ArrayBuffer>> {\n const budget = SYNC_MAX_APPLY_BYTES - 1024 * 1024;\n // What a file adds to a body: its tar header, its bytes padded to a block, and its manifest entry.\n const cost = (file: LocalFile & { bytes: Uint8Array }) => 512 + Math.ceil(file.bytes.length / 512) * 512 + 100 + file.path.length;\n const batches: Array<Array<LocalFile & { bytes: Uint8Array }>> = [[]];\n let size = 0;\n for (const file of files) {\n if (cost(file) > budget) throw new SyncError(`${file.path} is ${Math.ceil(file.bytes.length / 1024 / 1024)} MiB; a sync sends files of at most ${budget / 1024 / 1024} MiB`);\n if (size + cost(file) > budget && batches.at(-1)!.length > 0) {\n batches.push([]);\n size = 0;\n }\n batches.at(-1)!.push(file);\n size += cost(file);\n }\n return batches.map((batch, index) => {\n const manifest: SyncApplyManifest = {\n files: batch.map(({ path, blob, mode }) => ({ path, blob, mode })),\n delete: index === 0 ? remove : [],\n };\n return tarOf([\n { name: 'manifest.json', data: new TextEncoder().encode(JSON.stringify(manifest)) },\n ...batch.map((file, i) => ({ name: syncApplyEntryName(i), data: file.bytes })),\n ]);\n });\n}\n\nasync function copyRequest(session: SyncSession, path: string, init: { body: BodyInit; contentType: string }): Promise<Response> {\n let response: Response;\n try {\n response = await fetch(`${session.url}${path}`, {\n method: 'POST',\n headers: { authorization: `Bearer ${session.token}`, 'content-type': init.contentType },\n body: init.body,\n });\n } catch (error) {\n throw new SyncError(`could not reach the sandbox for ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (!response.ok) {\n const text = await response.text();\n let message = text;\n try {\n message = (JSON.parse(text) as { error?: string }).error ?? text;\n } catch {\n // Not the sync server's JSON (the edge's own page): show it as is.\n }\n throw new SyncError(`the sandbox refused ${path} (${response.status}): ${message.slice(0, 2000)}`);\n }\n return response;\n}\n\nasync function plan(session: SyncSession, local: LocalFile[], to?: string): Promise<SyncPlanResponse> {\n const body = JSON.stringify(to === undefined ? { local } : { local, to });\n // The whole list is one request, under the copy's body limit.\n if (Buffer.byteLength(body) > SYNC_MAX_APPLY_BYTES) {\n throw new SyncError(\n `the file list (${local.length} files) is over the ${SYNC_MAX_APPLY_BYTES / 1024 / 1024} MiB a sync can plan in one request; ignore what the sandbox doesn't need (.gitignore)`\n );\n }\n const response = await copyRequest(session, '/sync/plan', { body, contentType: 'application/json' });\n return parseAnswer(syncPlanResponseSchema, await response.text(), '/sync/plan');\n}\n\n/** Parse a sync server answer, or fail naming what came back (an edge page, a cut stream). */\nfunction parseAnswer<T>(schema: { safeParse(value: unknown): { success: true; data: T } | { success: false } }, text: string, path: string): T {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n throw new SyncError(`the sandbox answered ${path} with something other than JSON: ${text.slice(0, 300)}`);\n }\n const parsed = schema.safeParse(value);\n if (!parsed.success) throw new SyncError(`the sandbox answered ${path} unexpectedly: ${text.slice(0, 300)}`);\n return parsed.data;\n}\n\n/** Apply each batch; the last runs the build when asked, streaming its progress to stderr. */\nasync function apply(session: SyncSession, batches: Array<Uint8Array<ArrayBuffer>>, build: boolean, json: boolean) {\n let written = 0;\n let deleted = 0;\n let restarted: string[] = [];\n const steps: Array<{ name: string; ran: boolean; ms: number }> = [];\n for (const [index, body] of batches.entries()) {\n const last = index === batches.length - 1;\n const response = await copyRequest(session, `/sync/apply${build && last ? '?build=1' : ''}`, { body, contentType: 'application/x-tar' });\n if (!(build && last)) {\n const result: SyncApplyResult = parseAnswer(syncApplyResultSchema, await response.text(), '/sync/apply');\n written += result.written;\n deleted += result.deleted;\n continue;\n }\n let buffer = '';\n const decoder = new TextDecoder();\n let finished = false;\n for await (const chunk of response.body!) {\n buffer += decoder.decode(chunk, { stream: true });\n let newline = buffer.indexOf('\\n');\n while (newline >= 0) {\n const event = parseAnswer(syncBuildEventSchema, buffer.slice(0, newline), '/sync/apply?build=1');\n buffer = buffer.slice(newline + 1);\n newline = buffer.indexOf('\\n');\n if (event.type === 'applied') {\n written += event.result.written;\n deleted += event.result.deleted;\n } else if (event.type === 'output') {\n if (!json) process.stderr.write(event.text);\n } else if (event.type === 'step') {\n steps.push({ name: event.name, ran: event.ran, ms: event.ms });\n if (!json) process.stderr.write(` build step ${event.name}: ${event.ran ? `ran in ${(event.ms / 1000).toFixed(1)} s` : 'unchanged'}\\n`);\n } else if (event.type === 'done') {\n restarted = event.restarted;\n finished = true;\n } else if (event.type === 'error') {\n throw new SyncError(`the build failed after the files were synced: ${event.message}`);\n }\n }\n }\n if (!finished) throw new SyncError('the build stream ended early; check the copy with `cofactor sandbox exec <id> -- cofactor-sandbox status`');\n }\n return { written, deleted, restarted, steps };\n}\n\nconst SYNC_USAGE =\n 'Usage: cofactor sandbox sync <id> [--build] [--json]\\n cofactor sandbox sync <id> <local-dir> --to <path-in-sandbox> [--json]';\n\n/** How many written paths a human-readable sync lists before summarizing (untracked ones are always listed). */\nconst LIST_LIMIT = 20;\n\n/** `cofactor sandbox sync`: see the module comment. `positionals` are the id and the optional local dir. */\nexport async function handleSandboxSync(positionals: string[], argv: string[], apiUrl: string, token: string): Promise<void> {\n const [id, localDir] = positionals;\n const to = getArgValue(argv, '--to');\n const build = hasFlag(argv, '--build');\n const json = hasFlag(argv, '--json');\n const fail = (message: string): never => {\n console.error(`Error: ${message}`);\n process.exit(1);\n };\n if (!id) return fail(`sandbox id required.\\n${SYNC_USAGE}`);\n if ((localDir === undefined) !== (to === undefined)) return fail(`a local directory and --to go together.\\n${SYNC_USAGE}`);\n if (to !== undefined && build) return fail('--build reruns the build of a working-tree sync; build output is already built (`sync <id> <dir> --to <path>`).');\n if (to !== undefined && (to === '' || to.startsWith('/') || to.split('/').some((part) => part === '' || part === '.' || part === '..'))) {\n return fail('--to is a path under the sandbox\\'s sync root, like `static` or `frontend/dist` (no leading `/`, no `..`).');\n }\n // Before a session is minted: the file list and the synced identity come from git.\n if (localDir !== undefined && (!existsSync(localDir) || !lstatSync(localDir).isDirectory())) return fail(`${localDir} is not a directory`);\n if (!isInsideRepo(to === undefined ? process.cwd() : resolve(localDir!))) {\n return fail('run this inside a git repository: the files to sync and the synced tree come from git.');\n }\n\n const started = Date.now();\n const sessionRes = await apiRequest<{ ok: boolean; error?: string; data: SyncSession }>(\n apiUrl,\n token,\n 'POST',\n `/org/sandboxes-v2/sandboxes/${id}/sync-session`\n );\n if (!sessionRes.ok) return fail(sessionRes.error ?? 'could not start a sync');\n const session = sessionRes.data;\n if (build && session.delivery === 'upload') {\n return fail('a pushed image has no build to rerun: sync, then run your build with `cofactor sandbox exec <id> -- <command>`.');\n }\n\n try {\n let local: { files: LocalFile[]; skipped: string[]; tree: string; untracked: Set<string>; root: string };\n let decide: (planned: SyncPlanResponse) => Promise<{ delete: string[]; kept: KeptEntry[] }>;\n let cleanup = () => {};\n if (to !== undefined) {\n const dir = resolve(localDir!);\n const output = buildOutput(dir, to);\n local = { ...output, untracked: new Set(), root: dir };\n // A mirror: everything the copy has under --to that the directory doesn't.\n decide = async (planned) => ({ delete: planned.copyOnly.map((entry) => entry.path), kept: [] });\n } else {\n // GitHub: the whole repository is the copy's checkout. Pushed: the definition's directory is its WORKDIR.\n const root = session.delivery === 'github' ? repositoryOf(process.cwd()).top : process.cwd();\n if (session.delivery === 'upload' && !existsSync(join(root, 'cofactor.sandbox.yaml'))) {\n return fail('run this from the directory holding cofactor.sandbox.yaml: a pushed image\\'s WORKDIR mirrors it.');\n }\n const tree = workingTree(root);\n cleanup = tree.cleanup;\n local = { ...tree, root };\n decide = (planned) =>\n chooseDeletions({\n copyOnly: planned.copyOnly,\n repo: tree.repo,\n prefix: tree.prefix,\n env: tree.env,\n listDir: async (dir) => (await plan(session, [], dir)).copyOnly.map((entry) => entry.path),\n });\n }\n\n try {\n const planStarted = Date.now();\n const planned = await plan(session, local.files, to);\n // A path that is a symlink or submodule here exists locally: never a deletion.\n // A symlinked directory here is reported as `dir/` by the copy.\n const skipped = new Set(local.skipped.flatMap((path) => [path, `${path}/`]));\n const { delete: remove, kept } = await decide({ ...planned, copyOnly: planned.copyOnly.filter((entry) => !skipped.has(entry.path)) });\n const planMs = Date.now() - planStarted;\n const need = new Set(planned.need);\n const sending = local.files.filter((file) => need.has(file.path));\n\n if (!json) {\n writeRawOutput(`Syncing ${local.root} into sandbox ${id} (${session.delivery === 'github' ? 'GitHub' : 'pushed image'}, version ${session.version.slice(0, 12)})`);\n writeRawOutput(` plan: ${sending.length} to write, ${remove.length} to delete, ${local.files.length - sending.length} unchanged (${(planMs / 1000).toFixed(1)} s)`);\n const untracked = sending.filter((file) => local.untracked.has(file.path));\n const tracked = sending.filter((file) => !local.untracked.has(file.path));\n if (sending.length > 0) writeRawOutput(' write:');\n for (const file of tracked.slice(0, LIST_LIMIT)) writeRawOutput(` ${file.path}`);\n if (tracked.length > LIST_LIMIT) writeRawOutput(` ...and ${tracked.length - LIST_LIMIT} more`);\n for (const file of untracked) writeRawOutput(` ${file.path} (untracked)`);\n if (remove.length > 0) writeRawOutput(' delete:');\n for (const path of remove) writeRawOutput(` ${path}`);\n if (kept.length > 0) {\n writeRawOutput(' kept (only in the sandbox):');\n for (const entry of kept.slice(0, LIST_LIMIT)) {\n writeRawOutput(` ${entry.path}${entry.files > 1 ? ` (${entry.files} files)` : ''}, ${entry.why}`);\n }\n if (kept.length > LIST_LIMIT) writeRawOutput(` ...and ${kept.length - LIST_LIMIT} more`);\n }\n if (local.skipped.length > 0) writeRawOutput(` not synced (symlinks and submodules): ${local.skipped.join(', ')}`);\n }\n\n const applyStarted = Date.now();\n const batches =\n sending.length === 0 && remove.length === 0 && !build\n ? []\n : applyBatches(\n sending.map((file) => ({ ...file, bytes: readFileSync(join(local.root, to === undefined ? file.path : file.path.slice(to.length + 1))) })),\n remove\n );\n const applied = await apply(session, batches, build, json);\n const applyMs = Date.now() - applyStarted;\n const ms = Date.now() - started;\n\n const recorded = await apiRequest<{ ok: boolean; error?: string }>(apiUrl, token, 'POST', `/org/sandboxes-v2/sandboxes/${id}/syncs`, {\n tree: local.tree,\n buildOutput: to !== undefined,\n written: applied.written,\n deleted: applied.deleted,\n ms,\n });\n if (!recorded.ok) throw new SyncError(`the files were synced, but recording the sync failed: ${recorded.error}`);\n\n if (json) {\n writeRawOutput(\n JSON.stringify(\n {\n id,\n tree: local.tree,\n buildOutput: to !== undefined,\n written: sending.map((file) => file.path),\n deleted: remove,\n kept,\n build: build ? { steps: applied.steps, restarted: applied.restarted } : null,\n ms: { total: ms, plan: planMs, apply: applyMs },\n },\n null,\n 2\n )\n );\n return;\n }\n writeRawOutput(` applied: ${applied.written} written, ${applied.deleted} deleted (${(applyMs / 1000).toFixed(1)} s)`);\n if (build) writeRawOutput(` restarted: ${applied.restarted.join(', ') || 'nothing'}`);\n writeRawOutput(`Synced ${to === undefined ? 'tree' : 'build output'} ${local.tree} in ${(ms / 1000).toFixed(1)} s.`);\n if (!build) {\n writeRawOutput(` Restart what runs it: cofactor sandbox exec ${id} -- cofactor-sandbox restart <service>`);\n }\n const shown = await apiRequest<{ ok: boolean; data?: { portUrls: Record<string, string> } }>(apiUrl, token, 'GET', `/org/sandboxes-v2/sandboxes/${id}`);\n for (const [name, url] of Object.entries(shown.data?.portUrls ?? {})) writeRawOutput(` ${name.padEnd(10)} ${url}`);\n } finally {\n cleanup();\n }\n } catch (error) {\n if (error instanceof SyncError) return fail(error.message);\n throw error;\n }\n}\n\nfunction isInsideRepo(dir: string): boolean {\n return git(['rev-parse', '--is-inside-work-tree'], { cwd: dir, okCodes: [0, 128] }).trim() === 'true';\n}\n","/**\n * `cofactor sandbox refresh`: move an environment's head (the default\n * branch's head commit, or a pushed application's head push) to now, so\n * flow runs, the designer and unpinned claims use it without waiting for the\n * 6-hourly background refresh.\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport { resolveApplicationId } from './environment.js';\n\ntype HeadStatus = 'ready' | 'building' | 'failed';\n\ntype RefreshData = {\n /** Server time of the request; sent back while polling, so only a failure after it is this refresh's. */\n requestedAt: number;\n /** The head before the refresh: `status` is the environment's readiness (`warming` / `none` have no version). */\n before: { version: string | null; status: 'ready' | 'warming' | 'stale' | 'none' };\n head: { version: string; status: HeadStatus; error: string | null };\n};\n\ntype StatusResponse = { ok: boolean; error?: string; data: { status: HeadStatus; error: string | null; isHead: boolean } };\n\ntype RefreshResponse = { ok: boolean; error?: string; code?: string; data: RefreshData };\n\nconst POLL_INTERVAL_MS = 5_000;\n/** A snapshot build (seeds included) can take a while; the API's own build limit is longer. */\nconst WAIT_TIMEOUT_MS = 50 * 60_000;\n\n/** A version as people read it: a short commit, or a push id. */\nfunction shortVersion(version: string): string {\n return /^[0-9a-f]{40}$/.test(version) ? version.slice(0, 12) : version;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** `cofactor sandbox refresh --application A [--environment E] [--wait] [--json]`. */\nexport async function handleSandboxRefresh(argv: string[], apiUrl: string, token: string): Promise<void> {\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n const environment = getArgValue(argv, '--environment') ?? 'default';\n const wait = hasFlag(argv, '--wait');\n const json = hasFlag(argv, '--json');\n\n const refresh = async (): Promise<RefreshData> => {\n const res = await apiRequest<RefreshResponse>(apiUrl, token, 'POST', `/org/sandboxes-v2/applications/${applicationId}/refresh`, {\n environment,\n });\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n return res.data;\n };\n\n const first = await refresh();\n let latest = first;\n if (wait && first.head.status === 'building') {\n // Polls only read. A snapshot that finishes without becoming the head (a build of the\n // same commit that was already queued for a claim does not carry the request) is asked\n // for once more, which marks it.\n let askedAgain = false;\n const deadline = Date.now() + WAIT_TIMEOUT_MS;\n while (latest.head.status === 'building' && Date.now() < deadline) {\n await sleep(POLL_INTERVAL_MS);\n const query = new URLSearchParams({ environment, version: latest.head.version, requestedAt: String(latest.requestedAt) });\n const res = await apiRequest<StatusResponse>(\n apiUrl,\n token,\n 'GET',\n `/org/sandboxes-v2/applications/${applicationId}/refresh-status?${query}`\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n if (res.data.status === 'ready' && !res.data.isHead && !askedAgain) {\n askedAgain = true;\n latest = await refresh();\n } else if (res.data.status !== 'building') {\n latest = { ...latest, head: { ...latest.head, status: res.data.status, error: res.data.error } };\n }\n }\n }\n\n if (json) {\n writeRawOutput(JSON.stringify({ environment, before: first.before, head: latest.head }, null, 2));\n } else {\n const { before } = first;\n const { head } = latest;\n const unchanged = before.version === head.version && head.status === 'ready' && before.status === 'ready';\n if (unchanged) {\n console.log(`Environment \"${environment}\" is already at head ${shortVersion(head.version)}.`);\n } else {\n console.log(`Environment \"${environment}\":`);\n console.log(` old head ${before.version ? `${shortVersion(before.version)} (${before.status})` : `(none: ${before.status})`}`);\n console.log(` new head ${shortVersion(head.version)} (${head.status})`);\n if (head.error) console.log(` error ${head.error}`);\n if (head.status === 'building') {\n console.log(\n wait\n ? ' still building: timed out waiting. Check `cofactor sandbox envs`.'\n : ' The head advances when the snapshot is ready. Follow it with `--wait`, or `cofactor sandbox envs`.'\n );\n }\n }\n }\n if (latest.head.status === 'failed' || (wait && latest.head.status === 'building')) process.exit(1);\n}\n","/**\n * `cofactor sandbox warm`: keep a Sandboxes v2 environment warm. Staff only\n * (superadmin): warming holds capacity on the sandbox fleet.\n *\n * warm list every environment kept warm on the stage\n * warm set --application A --environment E --slots N [--tag T]\n * warm show --application A --environment E\n * warm clear --application A --environment E\n *\n * The org acted in is the one the session is signed in to (`--org` picks which\n * of your sessions, as everywhere). `--target-org <id>` names another org\n * instead; its application must then be given by id, since names resolve\n * within the session's own org. `--json` prints the API's data unchanged.\n *\n * @module\n * @see specs/pending/sandboxes-v2-warm-snapshots.md\n */\n\nimport process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, getTargetOrg, UUID_RE } from './cli-helpers.js';\nimport { resolveApplicationId } from './environment.js';\nimport { writeRawOutput } from './brand.js';\nimport { decodeJwtPayload } from './jwt.js';\n\ntype WarmHeld = { version: string | null; warmFree: number } | null;\n\ntype WarmView = {\n orgId: string;\n applicationId: string;\n environment: string;\n state: 'warming' | 'warm' | 'switching' | 'failed';\n slots: number;\n tag: string | null;\n current: WarmHeld;\n next: WarmHeld;\n /** Set only by `set`, when the row saved but its live state could not be read: `current` and `next` are then unknown, not empty. */\n liveUnavailable?: string;\n since: string;\n lastError: string | null;\n};\n\ntype WarmListed = Pick<WarmView, 'orgId' | 'applicationId' | 'environment' | 'state' | 'slots' | 'tag'> & {\n currentReservationId: string | null;\n nextReservationId: string | null;\n retiringReservationId: string | null;\n};\n\ntype WarmResponse<T> = { ok: boolean; error?: string; data: T };\n\n/** What the loop's state is called to people: `warming` is not `Filling` in the readiness chip's sense. */\nconst STATE_LABELS: Record<WarmView['state'], string> = {\n warming: 'Filling',\n warm: 'On',\n switching: 'Switching',\n failed: 'Needs attention',\n};\n\nconst USAGE = [\n 'Usage: cofactor sandbox warm <command>',\n ' list Every environment kept warm on this stage',\n ' set --application <a> --environment <e> --slots <1-12> [--tag <t>] Turn keep warm on, or change its slots or tag',\n ' show --application <a> --environment <e> State, slots, and the free warm slots now',\n ' clear --application <a> --environment <e> Turn it off and release the capacity',\n ' --target-org <id> Act in another org (the application must then be an id); default: your session org',\n ' --json Print the API data as JSON',\n].join('\\n');\n\nconst COMMANDS: Record<string, { values: string[] }> = {\n list: { values: [] },\n set: { values: ['--application', '--environment', '--slots', '--tag', '--target-org'] },\n show: { values: ['--application', '--environment', '--target-org'] },\n clear: { values: ['--application', '--environment', '--target-org'] },\n};\n\n/** `warm list|set|show|clear` is this file's; anything else after `warm` is the v1 `warm <template-id>`. */\nexport function isSandboxV2WarmCommand(first: string | undefined): boolean {\n return first !== undefined && Object.hasOwn(COMMANDS, first);\n}\n\nfunction fail(message: string): never {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\n/** Refuse an argument the command doesn't take, as the other v2 commands do. */\nfunction rejectUnknownWarmArgs(command: string, argv: string[]): void {\n const spec = COMMANDS[command]!;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (spec.values.includes(arg)) {\n i++;\n continue;\n }\n if (arg === '--json' || arg === '--debug') continue;\n fail(`\\`cofactor sandbox warm ${command}\\` does not take ${JSON.stringify(arg)}.\\n${USAGE}`);\n }\n}\n\n/** The org the command acts in, and the application in it. */\nasync function resolveTarget(argv: string[], apiUrl: string, token: string): Promise<{ orgId: string; applicationId: string; environment: string }> {\n const environment = getArgValue(argv, '--environment');\n if (!environment) fail('--environment <name> is required.');\n const targetOrg = getTargetOrg(argv);\n if (targetOrg) {\n if (!UUID_RE.test(targetOrg)) fail('--target-org takes an organization id.');\n const application = getArgValue(argv, '--application');\n if (!application || !UUID_RE.test(application)) fail('With --target-org, --application must be the application id.');\n return { orgId: targetOrg, applicationId: application, environment };\n }\n const sessionOrg = decodeJwtPayload(token)?.org_id;\n if (typeof sessionOrg !== 'string') fail('Cannot tell which org this token is for: pass --target-org <id>.');\n return { orgId: sessionOrg, applicationId: await resolveApplicationId(argv, apiUrl, token), environment };\n}\n\nconst warmPath = (target: { orgId: string; applicationId: string; environment: string }) =>\n `/superadmin/sandboxes-v2/orgs/${target.orgId}/applications/${target.applicationId}/environments/${encodeURIComponent(target.environment)}/warm`;\n\nfunction printHeld(label: string, held: WarmHeld): void {\n if (!held) return;\n console.log(` ${label}: ${held.version ?? 'unknown version'} (${held.warmFree} warm slot${held.warmFree === 1 ? '' : 's'} free now)`);\n}\n\nfunction printWarm(view: WarmView): void {\n const tag = view.tag ? `, tag ${view.tag}` : '';\n console.log(`Keep warm: ${STATE_LABELS[view.state]} (${view.slots} slot${view.slots === 1 ? '' : 's'}${tag})`);\n console.log(` application ${view.applicationId}, environment ${view.environment}, org ${view.orgId}`);\n if (view.liveUnavailable) console.log(` Saved, but live warm state could not be read, so warm slots are unknown: ${view.liveUnavailable}`);\n printHeld('Serving claims', view.current);\n printHeld('Warming next', view.next);\n console.log(` Since: ${new Date(view.since).toLocaleString()}`);\n if (view.lastError) console.log(` ${view.state === 'failed' ? 'Error' : 'Last error'}: ${view.lastError}`);\n}\n\nfunction printList(rows: WarmListed[]): void {\n if (rows.length === 0) {\n console.log('No environment is kept warm on this stage.');\n return;\n }\n for (const row of rows) {\n const reservations = [row.currentReservationId, row.nextReservationId, row.retiringReservationId].filter(Boolean).join(' ');\n console.log(\n `${STATE_LABELS[row.state].padEnd(15)} ${row.slots} slot${row.slots === 1 ? ' ' : 's'} org ${row.orgId} application ${row.applicationId} ${row.environment}${\n row.tag ? ` tag ${row.tag}` : ''\n }${reservations ? ` reservations ${reservations}` : ''}`\n );\n }\n}\n\nfunction output<T>(argv: string[], res: WarmResponse<T>, print: (data: T) => void): void {\n if (!res.ok) fail(res.error ?? 'request failed');\n if (hasFlag(argv, '--json')) writeRawOutput(JSON.stringify(res.data, null, 2));\n else print(res.data);\n}\n\n/** The PUT body from `--slots` and `--tag` (null when `--tag` is absent), checked here so a bad value fails before the request. */\nexport function parseSetBody(slotsArg: string, tag: string | null): { slots: number; tag?: string } {\n const slots = Number(slotsArg);\n if (slotsArg.trim() === '' || !Number.isInteger(slots) || slots < 1 || slots > 12) {\n throw new Error('--slots must be a whole number from 1 to 12');\n }\n if (tag === '') throw new Error('--tag must not be empty');\n return tag === null ? { slots } : { slots, tag };\n}\n\n/** `cofactor sandbox warm <list|set|show|clear>`: see the file comment. */\nexport async function handleSandboxWarm(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [command, ...rest] = argv;\n if (!command || !isSandboxV2WarmCommand(command)) {\n console.error(command ? `Unknown warm command: ${command}\\n${USAGE}` : USAGE);\n process.exit(1);\n }\n rejectUnknownWarmArgs(command, rest);\n\n if (command === 'list') {\n output(rest, await apiRequest<WarmResponse<WarmListed[]>>(apiUrl, token, 'GET', '/superadmin/sandboxes-v2/warm'), printList);\n return;\n }\n const target = await resolveTarget(rest, apiUrl, token);\n if (command === 'show') {\n output(rest, await apiRequest<WarmResponse<WarmView>>(apiUrl, token, 'GET', warmPath(target)), printWarm);\n } else if (command === 'clear') {\n output(rest, await apiRequest<WarmResponse<{ cleared: true }>>(apiUrl, token, 'DELETE', warmPath(target)), () =>\n console.log(`Keep warm cleared for ${target.environment}; its capacity is released.`)\n );\n } else {\n const slotsArg = getArgValue(rest, '--slots');\n if (slotsArg === undefined) fail('--slots <1-12> is required.');\n // `--tag` as the last argument has no value: an empty tag, refused, rather than silently dropped.\n const tagArg = rest.includes('--tag') ? (getArgValue(rest, '--tag') ?? '') : null;\n let body: ReturnType<typeof parseSetBody>;\n try {\n body = parseSetBody(slotsArg, tagArg);\n } catch (error) {\n return fail(error instanceof Error ? error.message : String(error));\n }\n output(rest, await apiRequest<WarmResponse<WarmView>>(apiUrl, token, 'PUT', warmPath(target), body), printWarm);\n }\n}\n","/**\n * `cofactor sandbox secret set|list|rm`: an application's third-party secrets\n * (API keys and the like) for its sandboxes, for all environments or one.\n * A value comes only from stdin so it never lands in shell history or `ps`,\n * and no command prints it.\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, stripOneTrailingNewline } from './cli-helpers.js';\nimport { resolveApplicationId } from './environment.js';\nimport { writeRawOutput } from './brand.js';\nimport { v2Positionals } from './sandbox-v2.js';\nimport { ENV_NAME } from '@chatsdet/sandbox-helper/manifest';\n\ntype SecretEntry = { name: string; environment: string | null; updatedAt: string };\n\ntype SecretResponse<T> = { ok: boolean; error?: string; data: T };\n\n/** `all environments` or `environment <name>`. */\nexport function scopeText(environment: string | null): string {\n return environment === null ? 'all environments' : `environment ${environment}`;\n}\n\n/** The sentence `set` and `rm` print: `... for all environments of shop` / `... for environment \"staging\" of shop`. */\nfunction scopeSentence(environment: string | null, application: string): string {\n return environment === null ? `for all environments of ${application}` : `for environment \"${environment}\" of ${application}`;\n}\n\nconst base = (applicationId: string, name: string) => `/org/applications/${applicationId}/sandbox/secrets/${encodeURIComponent(name)}`;\n\n/** The request that sets one secret; `environment` is omitted (never empty) when application-wide. */\nexport function setSecretRequest(applicationId: string, name: string, environment: string | null, value: string) {\n return {\n method: 'PUT' as const,\n path: base(applicationId, name),\n body: { value, ...(environment === null ? {} : { environment }) },\n };\n}\n\n/** The request that removes one secret; `environment` is omitted (never empty) when application-wide. */\nexport function rmSecretRequest(applicationId: string, name: string, environment: string | null) {\n const path = base(applicationId, name);\n return { method: 'DELETE' as const, path: environment === null ? path : `${path}?environment=${encodeURIComponent(environment)}` };\n}\n\n/** Why `set` cannot read a value from this stdin, or null when it can. */\nexport function stdinRefusal(isTTY: boolean | undefined): string | null {\n return isTTY ? 'pipe the value on stdin, e.g. printf %s \"$KEY\" | cofactor sandbox secret set NAME --application A' : null;\n}\n\nfunction fail(message: string): never {\n console.error(`Error: ${message}`);\n process.exit(1);\n}\n\n/** The stdin text as the secret value, or the reason it cannot be one. */\nexport function decodeSecretValue(bytes: Uint8Array): { value: string } | { error: string } {\n let text: string;\n try {\n text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n } catch {\n return { error: 'the value must be UTF-8 text' };\n }\n const value = stripOneTrailingNewline(text);\n return value === '' ? { error: 'stdin was empty: is the variable set?' } : { value };\n}\n\nasync function readStdin(): Promise<Uint8Array> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks);\n}\n\n/** `environment` as given: omitted means application-wide; a missing or empty value is a mistake, not \"all\". */\nfunction environmentArg(argv: string[]): string | null {\n const index = argv.indexOf('--environment');\n if (index === -1) return null;\n const environment = argv[index + 1];\n if (environment === undefined || environment === '' || environment.startsWith('-')) {\n fail('--environment needs an environment name; omit it for all environments');\n }\n return environment;\n}\n\nfunction printUsage(): never {\n console.error('Usage: cofactor sandbox secret set <NAME> --application <name|id> [--environment <e>] [--json] (value on stdin)');\n console.error(' cofactor sandbox secret list --application <name|id> [--json]');\n console.error(' cofactor sandbox secret rm <NAME> --application <name|id> [--environment <e>] [--json]');\n process.exit(1);\n}\n\nexport async function handleSandboxSecret(argv: string[], apiUrl: string, token: string): Promise<void> {\n const [action, name, ...extra] = v2Positionals('secret', argv);\n if (action !== 'set' && action !== 'list' && action !== 'rm') printUsage();\n const environment = environmentArg(argv);\n if (action === 'list') {\n if (name !== undefined) fail('`secret list` takes no name');\n if (environment !== null) fail('`secret list` shows every scope; --environment is for set and rm');\n } else {\n if (!name || extra.length > 0) printUsage();\n // The server's name rule (ENV_NAME). Never echo the name: it may be a pasted value.\n if (!ENV_NAME.test(name)) fail('NAME must be an environment variable name (A–Z, 0–9, _)');\n }\n const json = hasFlag(argv, '--json');\n const application = getArgValue(argv, '--application') ?? getArgValue(argv, '--property') ?? '';\n\n if (action === 'set') {\n // Refuse before any network call so an interactive terminal never hangs on stdin.\n const refusal = stdinRefusal(process.stdin.isTTY);\n if (refusal) {\n console.error(`Error: ${refusal}`);\n process.exit(1);\n }\n }\n\n // Read and check the value before any network call.\n let value: string | undefined;\n if (action === 'set') {\n const decoded = decodeSecretValue(await readStdin());\n if ('error' in decoded) fail(decoded.error);\n value = decoded.value;\n }\n\n const applicationId = await resolveApplicationId(argv, apiUrl, token);\n\n if (action === 'list') {\n const res = await apiRequest<SecretResponse<{ secrets: SecretEntry[] }>>(\n apiUrl, token, 'GET', `/org/applications/${applicationId}/sandbox/secrets`\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n const { secrets } = res.data;\n if (secrets.length === 0) {\n console.log(`No secrets set for ${application}.`);\n return;\n }\n const rows = secrets.map((s) => [s.name, scopeText(s.environment), s.updatedAt]);\n const nameWidth = Math.max('NAME'.length, ...rows.map((r) => r[0]!.length));\n const scopeWidth = Math.max('SCOPE'.length, ...rows.map((r) => r[1]!.length));\n const line = (cells: string[]) => `${cells[0]!.padEnd(nameWidth)} ${cells[1]!.padEnd(scopeWidth)} ${cells[2]}`;\n console.log(line(['NAME', 'SCOPE', 'UPDATED']));\n for (const row of rows) console.log(line(row));\n return;\n }\n\n const request = value === undefined ? rmSecretRequest(applicationId, name!, environment) : setSecretRequest(applicationId, name!, environment, value);\n const res = await apiRequest<SecretResponse<{ name: string; environment: string | null }>>(\n apiUrl, token, request.method, request.path, 'body' in request ? request.body : undefined\n );\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n if (json) {\n writeRawOutput(JSON.stringify(res.data, null, 2));\n return;\n }\n console.log(`Secret ${name} ${action === 'set' ? 'set' : 'removed'} ${scopeSentence(environment, application)}.`);\n}\n"],"mappings":"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AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,wBAAwB,aAAa,EAAE,KAAK,IAAI,CAAC;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAMA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,QAAM,CAAC,cAAc,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,IACrD;AAAA,MACE;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,IACxB;AAAA,IACA;AAAA,MACE;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,IACxB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,aAAa,IAAI;AACpB,YAAQ,MAAM,UAAU,aAAa,KAAK,EAAE;AAC5C,IAAAD,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,aAAa,IAAI;AACpB,YAAQ,MAAM,4BAA4B,aAAa,KAAK,EAAE;AAC9D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,EAAE,WAAW,aAAa,MAAM,WAAW,aAAa,KAAK,GAAG,MAAM,CAAC,CAAC;AACtG;AAAA,EACF;AAEA,QAAM,YAAY,aAAa,QAAQ,CAAC;AACxC,QAAM,YAAY,aAAa,QAAQ,CAAC;AAExC,UAAQ,IAAI,YAAY;AACxB,MAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,IAAI,UAAU;AAAA,EACxB,OAAO;AACL,YAAQ,IAAI,KAAKC,KAAI,MAAM,EAAE,CAAC,IAAIA,KAAI,QAAQ,EAAE,CAAC,IAAIA,KAAI,UAAU,EAAE,CAAC,IAAIA,KAAI,WAAW,EAAE,CAAC,aAAa;AACzG,eAAW,KAAK,WAAW;AACzB,cAAQ,IAAI,KAAKA,KAAI,EAAE,IAAI,EAAE,CAAC,IAAIA,KAAI,EAAE,MAAM,EAAE,CAAC,IAAIA,KAAI,EAAE,QAAQ,EAAE,CAAC,IAAIA,KAAI,EAAE,oBAAoB,KAAK,EAAE,CAAC,IAAI,EAAE,qBAAqB,GAAG,EAAE,kBAAkB,IAAI,EAAE,MAAM,KAAK,iBAAiB,EAAE;AAAA,IACpM;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,YAAY;AACxB,MAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,IAAI,UAAU;AAAA,EACxB,OAAO;AACL,YAAQ,IAAI,KAAKA,KAAI,MAAM,EAAE,CAAC,IAAIA,KAAI,UAAU,EAAE,CAAC,IAAIA,KAAI,WAAW,EAAE,CAAC,IAAIA,KAAI,UAAU,EAAE,CAAC,IAAIA,KAAI,YAAY,EAAE,CAAC,UAAU;AAC/H,eAAW,KAAK,WAAW;AACzB,YAAM,SAAS,EAAE,YAAY,YAAY;AAGzC,YAAM,UACJ,EAAE,YAAY,mBAAmB,oBAAqB,EAAE,WAAW;AACrE,cAAQ,IAAI,KAAKA,KAAI,EAAE,IAAI,EAAE,CAAC,IAAIA,KAAI,EAAE,QAAQ,EAAE,CAAC,IAAIA,KAAI,SAAS,EAAE,CAAC,IAAIA,KAAI,QAAQ,EAAE,CAAC,IAAIA,KAAI,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,WAAW,EAAE,SAAS,IAAI,GAAG,EAAE;AAAA,IACtK;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,2EAA2E;AACvF,YAAQ,IAAI,kFAAkF;AAAA,EAChG;AACF;AAEA,eAAe,eAAe,MAAgB,QAAgB,OAA8B;AAC1F,QAAM,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAClD,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,IAAI;AACP,UAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,EACF;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,EAAE;AAAA,EACzD;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAD,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,IAAI,IAAI;AACd,UAAQ,IAAI,aAAa,EAAE,IAAI,EAAE;AACjC,UAAQ,IAAI,iBAAiB,EAAE,EAAE,EAAE;AACnC,UAAQ,IAAI,iBAAiB,EAAE,qBAAqB,GAAG,EAAE,kBAAkB,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE;AACtG,MAAI,EAAE,aAAc,SAAQ,IAAI,iBAAiB,EAAE,YAAY,EAAE;AACjE,UAAQ,IAAI,iBAAiB,EAAE,MAAM,EAAE;AACvC,UAAQ,IAAI,iBAAiB,EAAE,oBAAoB,sBAAsB,EAAE;AAC3E,UAAQ,IAAI,iBAAiB,WAAW,EAAE,SAAS,CAAC,EAAE;AACtD,UAAQ,IAAI,iBAAiB,WAAW,EAAE,SAAS,CAAC,EAAE;AAEtD,MAAI,EAAE,gBAAgB,EAAE,aAAa,SAAS,GAAG;AAC/C,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,kBAAkB;AAC9B,eAAW,KAAK,EAAE,cAAc;AAC9B,YAAM,QAAQ,CAAC,EAAE,WAAW,aAAa,YAAY,EAAE,YAAY,cAAc,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC9G,qBAAe,OAAO,EAAE,GAAG,KAAK,KAAK,IAAI,EAAE,cAAc,WAAM,EAAE,WAAW,KAAK,EAAE,EAAE;AAAA,IACvF;AAAA,EACF;AACF;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,CAAC,UAAU,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAC1D,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,4EAA4E;AAC1F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,QAAQ,CAAC;AAC/B,MAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,IAAI,qBAAqB;AACjC;AAAA,EACF;AAEA,UAAQ,IAAI,GAAGC,KAAI,QAAQ,EAAE,CAAC,IAAIA,KAAI,YAAY,EAAE,CAAC,IAAIA,KAAI,QAAQ,EAAE,CAAC,UAAU;AAClF,aAAW,KAAK,WAAW;AACzB,YAAQ,IAAI,GAAGA,KAAI,EAAE,cAAc,EAAE,CAAC,IAAIA,KAAI,EAAE,UAAU,EAAE,CAAC,IAAIA,KAAI,EAAE,QAAQ,EAAE,CAAC,IAAI,WAAW,EAAE,SAAS,CAAC,EAAE;AAAA,EACjH;AACF;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAGnC,MAAI,WAAW,CAAC,MAAM,QAAQ;AAC5B,UAAM,oBAAoB,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK;AACtD;AAAA,EACF;AACA,MAAI,WAAW,CAAC,MAAM,YAAY;AAChC,UAAM,wBAAwB,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK;AAC1D;AAAA,EACF;AAEA,QAAM,aAAa,WAAW,CAAC;AAC/B,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,4EAA4E;AAC1F,IAAAD,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,QAAQ,CAAC;AAC3B,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,qBAAqB;AACjC;AAAA,EACF;AAEA,UAAQ,IAAI,YAAY;AACxB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,aAAa,KAAK,WAAW,MAAM,KAAK;AACrD,UAAM,aAAa,KAAK,aAAa,OAAO;AAC5C,YAAQ,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,UAAU,EAAE;AAClD,QAAI,KAAK,WAAW,SAAS;AAC3B,cAAQ,IAAI,YAAY,KAAK,WAAW,OAAO,EAAE;AAAA,IACnD;AACA,QAAI,KAAK,WAAW,OAAO;AACzB,cAAQ,IAAI,mBAAmB,KAAK,WAAW,KAAK,EAAE;AAAA,IACxD;AACA,QAAI,KAAK,UAAU,SAAS,GAAG;AAC7B,cAAQ,IAAI,uBAAuB,KAAK,UAAU,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,IAC3F;AAAA,EACF;AACA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,2CAA2C;AACzD;AAEA,eAAe,oBAAoB,MAAgB,QAAgB,OAA8B;AAC/F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,SAAS,WAAW,CAAC;AAE3B,MAAI,CAAC,cAAc,CAAC,QAAQ;AAC1B,YAAQ,MAAM,4GAA4G;AAC1H,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU,cAAc,MAAM;AAAA,EACtF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,2BAA2B,MAAM,EAAE;AACjD;AAEA,eAAe,wBAAwB,MAAgB,QAAgB,OAA8B;AACnG,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,SAAS,WAAW,CAAC;AAE3B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,QAAQ;AAC1B,YAAQ,MAAM,+CAA+C;AAC7D,YAAQ,MAAM,8GAA8G;AAC5H,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,YAAY,MAAM,UAAU,KAAK;AAChD,QAAM,UAAU,YAAY,MAAM,WAAW;AAC7C,QAAM,kBAAkB,YAAY,MAAM,iBAAiB;AAC3D,QAAM,kBAAkB,YAAY,MAAM,qBAAqB;AAE/D,MAAI;AACJ,MAAI,iBAAiB;AAEnB,QAAI;AACF,qBAAe,KAAK,MAAM,eAAe;AAAA,IAC3C,QAAQ;AACN,UAAI;AACF,uBAAe,KAAK,MAAME,cAAa,iBAAiB,OAAO,CAAC;AAAA,MAClE,SAAS,GAAG;AACV,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,gBAAQ,MAAM,qCAAqC,eAAe,MAAM,GAAG,EAAE;AAC7E,QAAAF,UAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAgC;AAAA,IACpC,QAAQ,WAAW,WAAW,WAAW;AAAA,IACzC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,IACvC,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,EAC/C;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU,cAAc,MAAM;AAAA,IAAa;AAAA,EACnG;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AACA,UAAQ,IAAI,6BAA6B,MAAM,KAAK,IAAI,MAAM,MAAM,GAAG;AACzE;AAEA,eAAe,iBAAiB,MAAgB,QAAgB,OAA8B;AAC5F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,8EAA8E;AAC5F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,IAAI,IAAI;AACd,QAAM,SAAS,EAAE,gBAAgB,CAAC;AAClC,QAAM,WAAW,EAAE,kBAAkB,CAAC;AACtC,QAAM,0BAA0B,EAAE,2BAA2B,CAAC;AAC9D,QAAM,4BAA4B,IAAI,IAAI,uBAAuB;AAEjE,MAAI,MAAM;AACR;AAAA,MAAe,KAAK;AAAA,QAChB,EAAE,cAAc,QAAQ,gBAAgB,UAAU,wBAAwB;AAAA,QAC1E;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,IAAI,6CAA6C;AACzD;AAAA,EACF;AAEA,UAAQ,IAAI,gBAAgB;AAC5B,aAAW,QAAQ,QAAQ;AACzB,UAAM,QAAQ,SAAS,KAAK,GAAG;AAC/B,UAAM,UAAU,KAAK,YACjB,0BAA0B,IAAI,KAAK,GAAG,IACpC,kEACA,cACD,SAAS;AACd,UAAM,WAAW,KAAK,WAAW,gBAAgB;AACjD,UAAM,YAAY,KAAK,YAAY,iBAAiB;AACpD,YAAQ,IAAI,KAAK,KAAK,GAAG,GAAG,QAAQ,GAAG,SAAS,EAAE;AAClD,QAAI,KAAK,YAAa,gBAAe,OAAO,KAAK,WAAW,EAAE;AAC9D,YAAQ,IAAI,cAAc,OAAO,EAAE;AAAA,EACrC;AACF;AAEO,IAAM,mBAAmB,CAAC,KAAa,WAAmB;AAAA,EAC/D,gBAAgB,EAAE,CAAC,GAAG,GAAG,MAAM;AAAA,EAC/B,WAAW,CAAC;AACd;AAEO,IAAM,qBAAqB,CAAC,SAAiB;AAAA,EAClD,gBAAgB,CAAC;AAAA,EACjB,WAAW,CAAC,GAAG;AACjB;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,YAAY,QAAQ,MAAM,eAAe;AAC/C,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,MAAM,WAAW,CAAC;AACxB,MAAI,QAAQ,WAAW,CAAC;AAExB,MAAI,CAAC,cAAc,CAAC,KAAK;AACvB,YAAQ,MAAM,sCAAsC;AACpD,YAAQ,MAAM,8DAA8D;AAC5E,YAAQ,MAAM,yGAAyG;AACvH,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,WAAW;AACb,UAAM,SAAmB,CAAC;AAC1B,qBAAiB,SAASA,UAAQ,MAAO,QAAO,KAAK,KAAe;AACpE,YAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,EAAE,KAAK;AAAA,EACvD;AAEA,MAAI,UAAU,QAAW;AACvB,YAAQ,MAAM,8DAA8D;AAC5E,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAMA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAS,+BAA+B,UAAU;AAAA,IAAW;AAAA,MAC1E,GAAG,iBAAiB,KAAK,KAAK;AAAA,IAChC;AAAA,IACA,EAAE,WAAW,IAAO;AAAA,EACtB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,eAAe,GAAG,EAAE;AAClC;AAEA,eAAe,kBAAkB,MAAgB,QAAgB,OAA8B;AAC7F,QAAM,CAAC,YAAY,GAAG,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAC;AACnE,MAAI,CAAC,cAAc,CAAC,KAAK;AACvB,YAAQ,MAAM,wDAAwD;AACtE,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC,mBAAmB,GAAG;AAAA,IACtB,EAAE,WAAW,IAAO;AAAA,EACtB;AACA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,iBAAiB,GAAG,EAAE;AACpC;AAEA,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,CAAC,YAAY,OAAO,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAEnE,MAAI,CAAC,cAAc,CAAC,SAAS;AAC3B,YAAQ,MAAM,uCAAuC;AACrD,YAAQ,MAAM,iDAAiD,eAAe,KAAK,GAAG,CAAC,GAAG;AAC1F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,kBAAkB,OAAO;AACtC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,wBAAwB,OAAO,sBAAsB,eAAe,KAAK,IAAI,CAAC,EAAE;AAC9F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,wBAAwB,IAAI,EAAE;AAC5C;AAEA,eAAe,kBAAkB,MAAgB,QAAgB,OAA8B;AAC7F,QAAM,CAAC,YAAY,QAAQ,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAEpE,MAAI,CAAC,cAAc,aAAa,QAAW;AACzC,YAAQ,MAAM,wCAAwC;AACtD,YAAQ,MAAM,2DAA2D;AACzE,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,QAAM,eAAe,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,IAAI,OAAO;AACxE,MAAI,CAAC,OAAO,UAAU,YAAY,GAAG;AACnC,YAAQ,MAAM,yBAAyB,QAAQ,oCAAoC;AACnF,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC,EAAE,aAAa;AAAA,EACjB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,iCAAiC,YAAY,EAAE;AAC7D;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAGnC,QAAM,eAAe,WAAW,CAAC,KAAK,YAAY,MAAM,YAAY;AACpE,QAAM,WAAW,MAAM,eAAe,QAAQ,OAAO,gBAAgB,MAAS;AAC9E,MAAI,CAAC,SAAU,CAAAA,UAAQ,KAAK,CAAC;AAG7B,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,mBAAmB,SAAS,EAAE;AAAA,EACtD;AACA,MAAI,CAAC,SAAS,MAAM,CAAC,SAAS,OAAO,QAAQ;AAC3C,YAAQ,MAAM,uCAAuC,SAAS,IAAI,mCAAmC;AACrG,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,SAAS,MAAM,CAAC;AACnC,QAAM,SAAS,YAAY,MAAM,UAAU,KAAK,WAAW,iBAAiB;AAC5E,QAAM,OAAO,YAAY,MAAM,QAAQ,KAAK,WAAW,YAAY,WAAW,aAAa,MAAM,GAAG,EAAE,IAAI,KAAK,SAAS;AACxH,QAAM,eAAe,YAAY,MAAM,gBAAgB,KAAK,WAAW,WAAW;AAElF,QAAM,UAAU,YAAY,MAAM,QAAQ;AAC1C,QAAM,eAAe,UAAU,kBAAkB,OAAO,IAAI;AAC5D,MAAI,WAAW,CAAC,cAAc;AAC5B,YAAQ,MAAM,0BAA0B,OAAO,sBAAsB,eAAe,KAAK,IAAI,CAAC,EAAE;AAChG,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAgC;AAAA,IACpC;AAAA,IACA,YAAY,SAAS;AAAA,IACrB,oBAAoB,WAAW;AAAA,IAC/B;AAAA,EACF;AACA,MAAI,aAAc,MAAK,eAAe;AACtC,MAAI,WAAW,UAAW,MAAK,QAAQ,WAAW;AAClD,MAAI,aAAc,MAAK,eAAe;AAEtC,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,IAA+B;AAAA,EACxD;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,UAAQ,IAAI,qBAAqB,IAAI,KAAK,EAAE,EAAE;AAC9C,UAAQ,IAAI,eAAe,SAAS,IAAI,EAAE;AAC1C,UAAQ,IAAI,iBAAiB,IAAI,KAAK,qBAAqB,GAAG,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,MAAM,KAAK,QAAQ,EAAE;AAC3H,UAAQ,IAAI,aAAa,IAAI,KAAK,MAAM,EAAE;AAC1C,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,2CAA2C;AACvD,UAAQ,IAAI,6BAA6B,IAAI,KAAK,EAAE,EAAE;AACxD;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,sFAAsF;AACpG,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,YAAY,MAAM,QAAQ,KAAK,YAAY,KAAK,IAAI,CAAC;AAClE,QAAM,MAAM,SAAS,YAAY,MAAM,OAAO,KAAK,KAAK,EAAE;AAC1D,QAAM,YAAY,QAAQ,MAAM,cAAc;AAC9C,QAAM,YAAY,QAAQ,MAAM,WAAW;AAC3C,QAAM,aAAa,gBAAgB,YAAY,MAAM,eAAe,CAAC;AACrE,QAAM,kBAAkB,qBAAqB,IAAI;AAEjD,QAAM,OAAgC;AAAA,IACpC;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,YAAY,CAAC;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,IAA+B;AAAA,EACxD;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,UAAQ,IAAI,qBAAqB,IAAI,KAAK,EAAE,EAAE;AAC9C,UAAQ,IAAI,aAAa,IAAI,KAAK,MAAM,EAAE;AAC1C,MAAI,UAAW,SAAQ,IAAI,mCAAmC;AAC9D,MAAI,eAAe,OAAO;AACxB,YAAQ,IAAI,aAAa,UAAU,KAAK,gBAAgB,MAAM,qBAAqB;AAAA,EACrF;AACA,UAAQ,IAAI,cAAc,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,SAAS,IAAI,GAAG,EAAE;AACvF;AAoBA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,aAAa,SAAS,YAAY,MAAM,WAAW,KAAK,OAAO,EAAE;AAEvE,MAAI,CAAC,YAAY;AACf,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,MAAM,gDAAgD;AAC9D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MACb;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,EAC3C;AAEF,QAAM,QAAQ,MAAM,OAAO;AAC3B,MAAI,CAAC,MAAM,MAAM,CAAC,MAAM,MAAM;AAC5B,QAAI,MAAM;AACR,qBAAe,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,IAC/C,OAAO;AACL,cAAQ,MAAM,UAAU,MAAM,SAAS,wBAAwB,EAAE;AAAA,IACnE;AACA,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,oBAAI,IAAI,CAAC,SAAS,CAAC;AACtC,QAAM,eAAe,oBAAI,IAAI,CAAC,UAAU,cAAc,UAAU,CAAC;AAEjE,MAAI,UAAU,MAAM;AACpB,MAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,MAAM,KAAK,CAAC,aAAa,IAAI,QAAQ,MAAM,GAAG;AAChF,UAAM,WAAW,KAAK,IAAI,IAAI,aAAa;AAC3C,QAAI,CAAC,KAAM,SAAQ,IAAI,mCAAmC,QAAQ,MAAM,MAAM;AAC9E,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAI,CAAC;AAC5C,YAAM,OAAO,MAAM,OAAO;AAC1B,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,MAAM;AAC1B,gBAAQ,MAAM,0BAA0B,KAAK,SAAS,eAAe,EAAE;AACvE,QAAAA,UAAQ,KAAK,CAAC;AAAA,MAChB;AACA,gBAAU,KAAK;AACf,UAAI,WAAW,IAAI,QAAQ,MAAM,KAAK,aAAa,IAAI,QAAQ,MAAM,EAAG;AAAA,IAC1E;AAAA,EACF;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,EAAE,IAAI,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC;AAAA,EACrE,OAAO;AACL,YAAQ,IAAI,WAAW,QAAQ,UAAU,YAAY,QAAQ,KAAK,QAAQ,UAAU,EAAE;AACtF,YAAQ,IAAI,aAAa,QAAQ,MAAM,EAAE;AACzC,QAAI,QAAQ,IAAK,gBAAe,UAAU,QAAQ,GAAG,EAAE;AACvD,QAAI,QAAQ,UAAW,SAAQ,IAAI,cAAc,WAAW,QAAQ,SAAS,CAAC,EAAE;AAChF,QAAI,QAAQ,WAAW,WAAW;AAChC,cAAQ,IAAI,0CAA0C;AACtD,cAAQ,IAAI,uEAAuE;AAAA,IACrF,WAAW,QAAQ,aAAa,IAAI,QAAQ,MAAM,GAAG;AACnD,cAAQ,IAAI,wCAAwC;AAAA,IACtD,WAAW,MAAM;AACf,cAAQ,IAAI,8EAA8E;AAAA,IAC5F,OAAO;AACL,cAAQ,IAAI,iEAAiE,aAAa,SAAS;AAAA,IACrG;AAAA,EACF;AAEA,MAAI,aAAa,IAAI,QAAQ,MAAM,EAAG,CAAAA,UAAQ,KAAK,CAAC;AACpD,MAAI,QAAQ,QAAQ,WAAW,UAAW,CAAAA,UAAQ,KAAK,CAAC;AAC1D;AAaO,SAAS,cAAc,QAAiC;AAC7D,QAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACpE,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,CAAC,OAAO,IAAI,SAAS,EAAE,IAAI,KAAK,MAAM,GAAI;AAChD,UAAM,QAAQ,OAAO,YAAY;AACjC,QAAI,QAAgC;AACpC,QAAI,MAAM,WAAW,YAAY,EAAG,SAAQ;AAAA,aACnC,MAAM,WAAW,QAAQ,EAAG,SAAQ;AAAA,aACpC,MAAM,WAAW,IAAI,KAAK,MAAM,SAAS,WAAW,EAAG,SAAQ;AAAA,aAC/D,MAAM,WAAW,IAAI,KAAK,MAAM,SAAS,SAAS,EAAG,SAAQ;AAAA,aAC7D,MAAM,WAAW,IAAI,EAAG,SAAQ;AACzC,WAAO,EAAE,MAAM,QAAQ,MAAM;AAAA,EAC/B,CAAC;AACH;AAGO,SAAS,kBAAkB,GAA2B;AAC3D,SAAO,EAAE,UAAU,gBAAgB,EAAE,UAAU,YAAY,EAAE,UAAU;AACzE;AAaO,SAAS,uBAAuB,eAA+B;AACpE,QAAM,cAAc,cAAc,MAAM,sBAAsB;AAC9D,MAAI,YAAa,QAAO,YAAY,CAAC;AACrC,QAAM,iBAAiB,cAAc,MAAM,YAAY;AACvD,MAAI,eAAgB,QAAO,eAAe,CAAC;AAC3C,SAAO;AACT;AAEA,eAAe,iBACb,QACA,OACA,YACAG,UACA,MACiB;AACjB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC,EAAE,SAAAA,UAAS,KAAK;AAAA,EAClB;AACA,MAAI,CAAC,IAAI,GAAI,QAAO,sBAAsB,IAAI,SAAS,SAAS;AAChE,SAAO,IAAI,MAAM,QAAQ;AAC3B;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAInC,QAAM,eAAe,QAAQ,MAAM,iBAAiB;AACpD,QAAM,yBAAyB,QAAQ,MAAM,6BAA6B;AAE1E,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,yEAAyE;AACvF,IAAAH,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,sBAAsB,UAAU,KAAK;AAIjD,QAAM,YAAY,MAAM;AAAA,IACtB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU;AAAA,IAAW,CAAC;AAAA,EAC9E;AACA,MAAI,CAAC,UAAU,IAAI;AACjB,YAAQ,MAAM,UAAU,UAAU,KAAK,EAAE;AACzC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,aAAa,UAAU,KAAK;AAClC,UAAQ,IAAI,4BAA4B,UAAU,EAAE;AAGpD,QAAMI,oBAAmB;AACzB,QAAM,aAAa;AACnB,QAAM,0BAA0B;AAChC,QAAM,iBAAiB;AACvB,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI,cAAc;AAClB,MAAI;AACJ,MAAI,mBAAmB;AACvB,MAAI,SAAyB,CAAC;AAC9B,MAAI,gBAAgB;AAEpB,SAAO,KAAK,IAAI,IAAI,YAAY,YAAY;AAC1C,UAAM,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAASD,iBAAgB,CAAC;AAEpE,UAAM,OAAO,MAAM;AAAA,MACjB;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO,+BAA+B,UAAU;AAAA,MAC/D;AAAA,MACA,EAAE,WAAW,IAAO;AAAA,IACtB;AACA,QAAI,CAAC,KAAK,GAAI;AAEd,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,KAAK,OAAO,MAAM,aAAa,GAAI;AACnD,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,cAAc,MAAM,cAAc,GAAI,CAAC;AACjF,UAAM,SAAS,OAAQ,KAAK,KAAK,SAAqD,UAAU,EAAE;AAClG,gBAAa,KAAK,KAAK,SAAoC;AAC3D,UAAM,YAAY,YAAY,KAAK,KAAK,YAAY,KAAK,KAAK,UAAU,SAAS;AACjF,UAAM,QAAQ,UAAU,SAAS,KAAK,CAAC,UAAU,SAAS,aAAa;AACvE,UAAM,eAAe,YAAY,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO,SAAS;AAC9E,UAAM,aAAa,cAAc,YAAY;AAC7C,UAAM,SAAS,WAAW,OAAO,iBAAiB;AAGlD,UAAM,MAAM,YAAY,KAAK,GAAG,OAAO,cAAc,GAAG,SAAS;AACjE,IAAAJ,UAAQ,OAAO;AAAA,MACb,QAAQ,GAAG,aAAa,MAAM,UAAU,QAAQ,OAAO,SAAS,iBAAiB,WAAW,MAAM,gBAAgB,OAAO,MAAM;AAAA,IACjI;AAEA,QAAI,WAAW,aAAa,OAAO;AACjC,MAAAA,UAAQ,OAAO,MAAM,IAAI;AACzB,oBAAc;AACd;AAAA,IACF;AACA,QAAI,WAAW,YAAY,WAAW,cAAc;AAClD,MAAAA,UAAQ,OAAO,MAAM,IAAI;AACzB,oBAAc;AACd;AAAA,IACF;AAKA,UAAM,aACJ,CAAC,SACD,OAAO,SAAS,KAChB,MAAM,oBAAoB;AAC5B,QAAI,YAAY;AACd,yBAAmB;AACnB,MAAAA,UAAQ,OAAO,MAAM,IAAI;AACzB,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,2BAA2B,OAAO,qBAAqB;AACnE,iBAAW,KAAK,YAAY;AAC1B,gBAAQ,IAAI,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE;AAAA,MACrD;AACA,cAAQ,IAAI,EAAE;AAKd,YAAM,qBAAqB;AAC3B,YAAM,UAAU,OAAO,MAAM,GAAG,kBAAkB;AAClD,YAAM,WAAW,OAAO,SAAS,QAAQ;AACzC,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,QAAQ;AAAA,UAAI,CAAC,MACX,iBAAiB,QAAQ,OAAO,YAAY,uBAAuB,EAAE,IAAI,GAAG,cAAc,EACvF,KAAK,CAAC,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,QACjC;AAAA,MACF;AACA,iBAAW,EAAE,GAAG,KAAK,KAAK,SAAS;AACjC,cAAM,OAAO,KAAK,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,CAAC,cAAc,EAAE,KAAK,QAAQ;AACzE,gBAAQ,IAAI,SAAS,EAAE,IAAI,KAAK,EAAE,KAAK,OAAO;AAC9C,gBAAQ,IAAI,OAAO,QAAQ,WAAW,EAAE;AACxC,gBAAQ,IAAI,EAAE;AAAA,MAChB;AACA,UAAI,WAAW,GAAG;AAChB,gBAAQ;AAAA,UACN,OAAO,QAAQ,yBAAyB,aAAa,IAAI,KAAK,GAAG,4CAAuC,UAAU;AAAA,QACpH;AACA,gBAAQ,IAAI,EAAE;AAAA,MAChB;AAIA,UAAI,CAAC,eAAe;AAClB,iBAAS,MAAM,oBAAoB,QAAQ,OAAO,UAAU;AAC5D,wBAAgB;AAAA,MAClB;AACA,YAAM,aAAa,WAAW,KAAK,CAAC,MAAM,uBAAuB,EAAE,IAAI,MAAM,aAAa;AAC1F,UAAI,YAAY,UAAU,gBAAgB,OAAO,SAAS,GAAG;AAC3D,cAAM,kBAAkB,OACrB,IAAI,CAAC,OAAO;AAAA,UACX,OAAO,EAAE;AAAA,UACT,SAAS,EAAE;AAAA,UACX,WAAW,OAAO,KAAK,CAAC,MAAM,uBAAuB,EAAE,IAAI,MAAM,EAAE,OAAO;AAAA,QAC5E,EAAE,EACD,OAAO,CAAC,MAAM,EAAE,SAAS;AAC5B,YAAI,gBAAgB,SAAS,GAAG;AAC9B,qBAAW,MAAM,iBAAiB;AAChC,oBAAQ;AAAA,cACN,yDAAyD,GAAG,OAAO,aAAa,GAAG,KAAK,SAAS,GAAG,UAAW,KAAK;AAAA,YACtH;AAAA,UACF;AACA,kBAAQ,IAAI,EAAE;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,WAAW;AAC7B,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,wDAAmD,eAAe,SAAS,IAAI;AAC7F,QAAI,UAAW,SAAQ,MAAM,mBAAmB,SAAS,EAAE;AAC3D,YAAQ,MAAM,mBAAmB,UAAU,EAAE;AAC7C,YAAQ,MAAM,8CAA8C,UAAU,EAAE;AACxE,YAAQ,MAAM,uCAAuC,UAAU,mBAAmB;AAClF,YAAQ,MAAM,8CAA8C,UAAU,iBAAiB;AACvF,QAAI,wBAAwB;AAC1B,YAAM,WAAW,QAAQ,OAAO,QAAQ,+BAA+B,UAAU,YAAY;AAC7F,cAAQ,MAAM,6CAA6C;AAAA,IAC7D,OAAO;AAGL,cAAQ,MAAM,wFAAwF;AACtG,cAAQ,MAAM,4BAA4B;AAC1C,cAAQ,MAAM,4CAA4C,UAAU,EAAE;AAAA,IACxE;AACA,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAMA,QAAM,kBAAkB,4DAAuD,UAAU;AACzF,aAAW,UAAU,CAAC,kBAAkB,mBAAmB,GAAG;AAC5D,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,+BAA+B,UAAU,cAAc,MAAM;AAAA,MAC7D,EAAE,QAAQ,aAAa,SAAS,gBAAgB;AAAA,IAClD,EAAE,MAAM,MAAM;AAAA,IAEd,CAAC;AAAA,EACH;AAIA,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC;AAAA,MACE,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF;AACA,MAAI,CAAC,YAAY,IAAI;AACnB,YAAQ,MAAM,kCAAkC,YAAY,KAAK,EAAE;AACnE,YAAQ,MAAM,YAAY,UAAU,+BAA+B;AACnE,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,cAAc;AACjB,UAAM,WAAW,QAAQ,OAAO,QAAQ,+BAA+B,UAAU,YAAY;AAC7F,YAAQ,IAAI,qCAAqC;AAAA,EACnD,OAAO;AACL,YAAQ,IAAI,+BAA+B,UAAU,gBAAgB,YAAY,OAAO,SAAS,KAAK,EAAE,GAAG;AAAA,EAC7G;AAMA,QAAM,gBAAgB,MAAM;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,EAC3C,EAAE,MAAM,OAAO,EAAE,IAAI,MAAM,EAAiD;AAC5E,QAAM,iBAAiB,cAAc,MAAM;AAC3C,QAAM,WAAW,mBAAmB;AAEpC,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,EAAE,YAAY,UAAU,QAAQ,gBAAgB,YAAY,UAAU,GAAG,MAAM,CAAC,CAAC;AAC/G;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,YAAQ,IAAI,oBAAoB;AAChC;AAAA,EACF;AAIA,UAAQ,IAAI,uCAAuC,UAAU,oBAAoB;AACjF,UAAQ,IAAI,iCAAiC,kBAAkB,SAAS,IAAI;AAE5E,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,EAC3C,EAAE,MAAM,OAAO,EAAE,IAAI,MAAM,EAAoD;AAC/E,QAAM,WAAW,aAAa,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO;AACvD,UAAM,IAAI,GAAG,YAAY,UAAU;AACnC,WAAO,MAAM,eAAe,MAAM;AAAA,EACpC,CAAC;AACD,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAQ,IAAI,uDAAuD;AACnE,eAAW,QAAQ,SAAS;AAC1B,cAAQ,IAAI,OAAO,KAAK,KAAK,KAAK,KAAK,EAAE,GAAG;AAAA,IAC9C;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,uGAAuG;AACnH,YAAQ,IAAI,sEAAsE;AAAA,EACpF;AACA,EAAAA,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,kBAAkB,MAAgB,QAAgB,OAA8B;AAC7F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AACzB,QAAM,aAAa,YAAY,MAAM,UAAU;AAE/C,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,YAAQ,MAAM,wGAAwG;AACtH,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU,cAAc,IAAI;AAAA,EACnF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AACd,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,IAAS;AAChD,kBAAc,YAAY,IAAI,KAAK,WAAW,IAAI,OAAO;AACzD,YAAQ,IAAI,cAAc,UAAU,EAAE;AAAA,EACxC,OAAO;AACL,IAAAA,UAAQ,OAAO,MAAM,IAAI,KAAK,WAAW,EAAE;AAAA,EAC7C;AACF;AAEA,eAAe,qBAAqB,MAAgB,QAAgB,OAA8B;AAChG,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AACzB,QAAM,WAAW,WAAW,CAAC;AAC7B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,UAAU;AACrC,YAAQ,MAAM,6FAA6F;AAC3G,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,mBAAmB,CAAC,kBAAkB,cAAc,kBAAkB,gBAAgB,eAAe,sBAAsB,MAAM;AACvI,MAAI,CAAC,iBAAiB,SAAS,IAAI,GAAG;AACpC,YAAQ,MAAM,iCAAiC,IAAI,mBAAmB,iBAAiB,KAAK,IAAI,CAAC,EAAE;AACnG,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,kBAAkB,YAAY,MAAM,QAAQ;AAClD,MAAI,SAAS,UAAU,CAAC,iBAAiB;AACvC,YAAQ,MAAM,+GAA+G;AAC7H,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,mBAAmB,CAAC,gBAAgB,WAAW,qBAAqB,GAAG;AACzE,UAAM,gBAAgB,gBAAgB,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AAC1E,YAAQ;AAAA,MACN;AAAA,wCACyC,aAAa;AAAA,IACxD;AACA,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,cAAUE,cAAa,UAAU,OAAO;AAAA,EAC1C,SAAS,KAAK;AACZ,YAAQ,MAAM,uBAAuB,GAAG,EAAE;AAC1C,IAAAF,UAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAK9C,QAAM,MAAM,MAAM;AAAA,IAKhB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,IAAc;AAAA,MAC3E,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,IAC/C;AAAA,IACA,EAAE,WAAW,IAAO;AAAA,EACtB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,MAAM;AACvB,QAAM,SAAS,SAAS,aAAa,gBAAgB,SAAS,YAAY,eAAe;AACzF,UAAQ,IAAI,sBAAsB,IAAI,KAAK,QAAQ,KAAK,QAAQ,MAAM,UAAU,MAAM,EAAE;AACxF,MAAI,SAAS,cAAc,IAAI,MAAM,cAAc;AACjD,YAAQ,IAAI,mCAAmC,IAAI,KAAK,YAAY,EAAE;AAAA,EACxE;AACF;AAEA,eAAe,YAAY,MAAgB,QAAgB,OAA8B;AACvF,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,SAAS,QAAQ,MAAM,WAAW;AACxC,QAAM,aAAa,WAAW,CAAC;AAE/B,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,wEAAwE;AACtF,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,gDAAgD;AAO5D,QAAM,qBAAqB;AAE3B,MAAI,iBAAiB;AACrB,MAAI,cAA2E;AAE/E,QAAM,SAAS,MAAM,SAAS,GAAG,MAAM,+BAA+B,UAAU,UAAU;AAAA,IACxF,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EACb,CAAC;AAED,MAAI,OAAO,IAAI;AAEb,UAAM,OAAQ,MAAM,OAAO,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AAG3D,QAAI,MAAM,MAAM,KAAK,MAAM;AACzB,uBAAiB;AACjB,oBAAc,KAAK;AAAA,IACrB,WAAW,QAAQ,CAAC,KAAK,IAAI;AAC3B,cAAQ,MAAM,UAAU,KAAK,SAAS,cAAc,EAAE;AACtD,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AACL,UAAM,EAAE,QAAQ,IAAI;AAGpB,UAAM,kBACJ,QAAQ,SAAS,aAChB,QAAQ,SAAS,iBAAiB,QAAQ,WAAW,OAAO,QAAQ,WAAW;AAClF,QAAI,CAAC,iBAAiB;AACpB,UAAI,kBAAkB,OAAO,EAAG,eAAc,OAAO;AACrD,cAAQ,MAAM,UAAU,QAAQ,OAAO,EAAE;AACzC,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,kBAAkB,aAAa;AACjC,QAAI,MAAM;AACR,qBAAe,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AACnD;AAAA,IACF;AAEA,QAAI,YAAY,QAAQ;AACtB,MAAAA,UAAQ,OAAO,MAAM,YAAY,MAAM;AACvC,UAAI,CAAC,YAAY,OAAO,SAAS,IAAI,EAAG,CAAAA,UAAQ,OAAO,MAAM,IAAI;AAAA,IACnE;AACA,QAAI,YAAY,QAAQ;AACtB,MAAAA,UAAQ,OAAO,MAAM,WAAW,YAAY,MAAM,EAAE;AACpD,UAAI,CAAC,YAAY,OAAO,SAAS,IAAI,EAAG,CAAAA,UAAQ,OAAO,MAAM,IAAI;AAAA,IACnE;AAEA,QAAI,YAAY,aAAa,GAAG;AAC9B,cAAQ,IAAI,iBAAiB;AAAA,IAC/B,OAAO;AACL,cAAQ,MAAM,+BAA+B,YAAY,QAAQ,EAAE;AACnE,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AACA;AAAA,EACF;AAGA,MAAI,QAAQ;AACV,YAAQ,IAAI,qDAAqD;AACjE,YAAQ,IAAI,gCAAgC,UAAU,EAAE;AACxD;AAAA,EACF;AAEA,UAAQ,IAAI,yCAAyC;AAErD,QAAM,wBAAwB;AAC9B,QAAMI,oBAAmB;AACzB,QAAM,YAAY,KAAK,IAAI;AAE3B,SAAO,KAAK,IAAI,IAAI,YAAY,uBAAuB;AACrD,UAAM,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAASD,iBAAgB,CAAC;AAEpE,UAAM,OAAO,MAAM;AAAA,MACjB;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO,+BAA+B,UAAU;AAAA,MAC/D;AAAA,MACA,EAAE,WAAW,IAAO;AAAA,IACtB;AAEA,QAAI,CAAC,KAAK,GAAI;AAEd,UAAM,UAAU,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI;AAC1D,UAAM,eAAe,YAAY,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO,SAAS;AAC9E,UAAM,YAAY,YAAY,KAAK,KAAK,YAAY,KAAK,KAAK,UAAU,SAAS;AACjF,UAAM,QAAQ,UAAU,SAAS,KAAK,CAAC,UAAU,SAAS,aAAa;AAGvE,UAAM,kBAAkB,aAAa,MAAM,KAAK,KAAK,CAAC,GAAG,UAAU,eAAe,IAAI;AACtF,UAAM,gBAAgB,aAAa,MAAM,cAAc,KAAK,CAAC,GAAG;AAChE,IAAAJ,UAAQ,OAAO,MAAM,QAAQ,OAAO,kBAAkB,cAAc,cAAc,YAAY,UAAU,QAAQ,OAAO,SAAS,IAAI;AAIpI,QAAI,OAAO;AACT,MAAAA,UAAQ,OAAO,MAAM,IAAI;AACzB,cAAQ,IAAI,iBAAiB;AAC7B;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,UAAQ,OAAO,MAAM,IAAI;AACzB,UAAQ,MAAM,6EAA6E;AAC3F,UAAQ,MAAM,gCAAgC,UAAU,EAAE;AAC1D,UAAQ,MAAM,yBAAyB,UAAU,mBAAmB;AACpE,EAAAA,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,iBAAiB,MAAgB,QAAgB,OAA8B;AAC5F,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAMG,WAAU,YAAY,MAAM,WAAW;AAC7C,QAAM,cAAc,YAAY,MAAM,WAAW;AAIjD,QAAM,WAAW,KAAK,OAAO,CAAC,GAAG,GAAG,QAAQ;AAC1C,QAAI,MAAM,SAAU,QAAO;AAC3B,QAAI,MAAM,eAAe,MAAM,YAAa,QAAO;AACnD,QAAI,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,eAAe,IAAI,IAAI,CAAC,MAAM,aAAc,QAAO;AAChF,WAAO;AAAA,EACT,CAAC;AAID,QAAM,eAAe,SAAS,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACjE,QAAM,aAAa,gBAAgB,IAAI,SAAS,YAAY,IAAI;AAChE,QAAM,UAAU,gBAAgB,IAAI,SAAS,MAAM,eAAe,CAAC,EAAE,KAAK,GAAG,IAAI;AAEjF,MAAI,CAAC,cAAc,CAAC,SAAS;AAC3B,YAAQ,MAAM,2IAA2I;AACzJ,IAAAH,UAAQ,KAAK,CAAC;AAAA,EAChB;AAMA,QAAM,YAAY,cAAc,KAAK,IAAI,SAAS,aAAa,EAAE,GAAG,GAAI,IAAI;AAE5E,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU;AAAA,IAChE,EAAE,SAAS,GAAIG,WAAU,EAAE,SAAAA,SAAQ,IAAI,CAAC,EAAG;AAAA,IAC3C,EAAE,UAAU;AAAA,EACd;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAH,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,QAAQ;AACnB,IAAAA,UAAQ,OAAO,MAAM,IAAI,KAAK,MAAM;AACpC,QAAI,CAAC,IAAI,KAAK,OAAO,SAAS,IAAI,EAAG,CAAAA,UAAQ,OAAO,MAAM,IAAI;AAAA,EAChE;AACA,MAAI,IAAI,KAAK,QAAQ;AACnB,IAAAA,UAAQ,OAAO,MAAM,WAAW,IAAI,KAAK,MAAM,EAAE;AACjD,QAAI,CAAC,IAAI,KAAK,OAAO,SAAS,IAAI,EAAG,CAAAA,UAAQ,OAAO,MAAM,IAAI;AAAA,EAChE;AACA,MAAI,IAAI,KAAK,aAAa,GAAG;AAC3B,YAAQ,IAAI,cAAc,IAAI,KAAK,QAAQ,EAAE;AAAA,EAC/C;AACF;AAEA,eAAe,kBAAkB,MAAgB,QAAgB,OAA8B;AAC7F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,8EAA8E;AAC5F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,IAC/D;AAAA,IACA,EAAE,WAAW,IAAO;AAAA,EACtB;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MAAM,oBAAoB,QAAQ,OAAO,UAAU;AAClE,QAAM,OAAO,EAAE,GAAG,IAAI,MAAM,OAAO;AAEnC,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC5C;AAAA,EACF;AAEA,QAAM,IAAI;AAEV,UAAQ,IAAI,kBAAkB;AAC9B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,EAAE,QAAQ,GAAG;AACrD,YAAQ,IAAI,KAAK,GAAG,KAAK,KAAK,EAAE;AAAA,EAClC;AAEA,MAAI,EAAE,WAAW;AACf,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,mBAAmB;AAC/B,YAAQ,IAAI,qBAAqB,EAAE,UAAU,cAAc,EAAE;AAC7D,YAAQ,IAAI,iBAAiB,EAAE,UAAU,cAAc,SAAS,EAAE;AAClE,YAAQ,IAAI,uBAAuB,EAAE,UAAU,oBAAoB,SAAS,EAAE;AAC9E,YAAQ,IAAI,WAAW,EAAE,UAAU,IAAI,EAAE;AACzC,QAAI,EAAE,UAAU,QAAS,gBAAe,cAAc,EAAE,UAAU,OAAO,EAAE;AAAA,EAC7E;AAEA,MAAI,EAAE,WAAW;AACf,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,sBAAsB;AAClC,YAAQ,IAAI,YAAY,EAAE,UAAU,KAAK,EAAE;AAC3C,YAAQ,IAAI,gBAAgB,EAAE,UAAU,SAAS,EAAE;AACnD,QAAI,EAAE,UAAU,UAAW,SAAQ,IAAI,gBAAgB,EAAE,UAAU,SAAS,EAAE;AAC9E,QAAI,EAAE,UAAU,mBAAmB,KAAM,SAAQ,IAAI,cAAc,EAAE,UAAU,cAAc,GAAG;AAChG,QAAI,EAAE,UAAU,UAAW,SAAQ,IAAI,gBAAgB,EAAE,UAAU,SAAS,EAAE;AAAA,EAChF;AAEA,QAAM,WAA4C;AAAA,IAChD,CAAC,qBAAqB,EAAE,MAAM;AAAA,IAC9B,CAAC,cAAc,EAAE,GAAG;AAAA,IACpB,CAAC,cAAc,EAAE,IAAI;AAAA,IACrB,CAAC,gBAAgB,EAAE,WAAW;AAAA,IAC9B,CAAC,cAAc,EAAE,SAAS;AAAA,EAC5B;AACA,MAAI,EAAE,UAAW,UAAS,OAAO,GAAG,GAAG,CAAC,kBAAkB,EAAE,SAAS,CAAC;AAEtE,aAAW,CAAC,OAAO,MAAM,KAAK,UAAU;AACtC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,OAAO,KAAK,MAAM;AAC9B,QAAI,WAAW,QAAQ;AACrB,qBAAe,UAAU,OAAO,OAAO,KAAK,OAAO,IAAI,MAAM,EAAE,KAAK,OAAO,KAAK,EAAE;AAAA,IACpF,OAAO;AACL,UAAI,OAAO,OAAQ,gBAAe,OAAO,MAAM;AAC/C,UAAI,OAAO,OAAQ,gBAAe,aAAa,OAAO,MAAM,EAAE;AAAA,IAChE;AAAA,EACF;AACF;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAE/B,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,4EAA4E;AAC1F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU;AAAA,EAClE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,YAAY,UAAU,yBAAyB;AAC7D;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAE/B,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,yEAAyE;AACvF,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAU,+BAA+B,UAAU;AAAA,EACpE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,YAAY,UAAU,WAAW;AAC/C;AAsCA,eAAe,iBAAiB,MAAgB,QAAgB,OAA8B;AAC5F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,8EAA8E;AAC5F,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,QAAQ,CAAC;AAC7B,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,uCAAuC;AACnD;AAAA,EACF;AAEA,UAAQ,IAAI,YAAY,QAAQ,MAAM,IAAI;AAC1C,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,UAAU,KAAK;AAChC,YAAQ,IAAI,KAAKC,KAAI,EAAE,MAAM,EAAE,CAAC,IAAIA,KAAI,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE;AAAA,EACjE;AACF;AAEA,eAAe,gBAAgB,MAAgB,QAAgB,OAA8B;AAC3F,QAAM,aAAa,KAAK,OAAO,CAACK,OAAM,CAACA,GAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AACzB,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,YAAQ,MAAM,6FAA6F;AAC3G,IAAAN,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU,YAAY,IAAI;AAAA,EACjF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,IAAI,IAAI;AACd,UAAQ,IAAI,WAAW,EAAE,IAAI,EAAE;AAC/B,UAAQ,IAAI,kBAAkB,EAAE,KAAK,EAAE;AACvC,iBAAe,kBAAkB,EAAE,eAAe,QAAQ,EAAE;AAC5D,UAAQ,IAAI,kBAAkB,EAAE,QAAQ,QAAQ,EAAE;AAClD,UAAQ,IAAI,kBAAkB,EAAE,OAAO,EAAE;AACzC,UAAQ,IAAI,kBAAkB,EAAE,SAAS,IAAI;AAC7C,UAAQ,IAAI,iBAAiB;AAC7B,UAAQ,IAAI,OAAO,KAAK,UAAU,EAAE,aAAa,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,QAAQ,CAAC,EAAE;AACtF,MAAI,EAAE,cAAc;AAClB,YAAQ,IAAI,kBAAkB;AAC9B,YAAQ,IAAI,OAAO,KAAK,UAAU,EAAE,cAAc,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,QAAQ,CAAC,EAAE;AAAA,EACzF;AAGA,MAAI,EAAE,gBAAgB,EAAE,aAAa,SAAS,GAAG;AAC/C,YAAQ,IAAI,mCAAmC;AAC/C,YAAQ,IAAI,OAAO,KAAK,UAAU,EAAE,cAAc,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,QAAQ,CAAC,EAAE;AAAA,EACzF;AACA,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,OAAO,EAAE,cAAc,MAAM,IAAI,EAAE,KAAK,QAAQ,CAAC,EAAE;AACjE;AAEA,eAAe,mBAAmB,MAAgB,QAAgB,OAA8B;AAC9F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AACzB,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,YAAQ,MAAM,0GAA0G;AACxH,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,YAAY,MAAM,UAAU;AAC/C,QAAM,kBAAkB,YAAY,MAAM,gBAAgB;AAC1D,QAAM,mBAAmB,YAAY,MAAM,iBAAiB;AAC5D,QAAM,6BAA6B,YAAY,MAAM,4BAA4B;AACjF,QAAM,mBAAmB,YAAY,MAAM,iBAAiB;AAC5D,QAAM,QAAQ,YAAY,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM,GAAG,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAC7G,QAAM,cAAc,YAAY,MAAM,eAAe;AACrD,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,aAAa,YAAY,MAAM,WAAW;AAChD,QAAM,gBAAgB,QAAQ,MAAM,kBAAkB;AACtD,QAAM,uBAAuB,YAAY,MAAM,uBAAuB;AACtE,QAAM,mBAAmB,YAAY,MAAM,qBAAqB;AAChE,QAAM,iBAAiB,QAAQ,MAAM,oBAAoB;AACzD,QAAM,oBAAoB,YAAY,MAAM,mBAAmB;AAE/D,MAAI,oBAAoB,qBAAqB,YAAY,qBAAqB,UAAU;AACtF,YAAQ,MAAM,yDAAyD;AACvE,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,qBAAqB,CAAC,UAAU,KAAK,iBAAiB,GAAG;AAC3D,YAAQ,MAAM,6CAA6C;AAC3D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,oCAAoC;AAClD,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,iBAAiB;AACpB,YAAQ,MAAM,0CAA0C;AACxD,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,oBAAgBE,cAAa,YAAY,OAAO;AAAA,EAClD,SAAS,KAAK;AACZ,YAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,IAAAF,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,kBAAc,KAAK,MAAME,cAAa,iBAAiB,OAAO,CAAC;AAAA,EACjE,SAAS,KAAK;AACZ,YAAQ,MAAM,oCAAoC,GAAG,EAAE;AACvD,IAAAF,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI,kBAAkB;AACpB,QAAI;AACF,qBAAe,KAAK,MAAME,cAAa,kBAAkB,OAAO,CAAC;AAAA,IACnE,SAAS,KAAK;AACZ,cAAQ,MAAM,qCAAqC,GAAG,EAAE;AACxD,MAAAF,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,4BAA4B;AAC9B,QAAI;AACF,+BAAyB,KAAK,MAAME,cAAa,4BAA4B,OAAO,CAAC;AAAA,IACvF,SAAS,KAAK;AACZ,cAAQ,MAAM,gDAAgD,GAAG,EAAE;AACnE,MAAAF,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAOA,MAAI;AACJ,MAAI,kBAAkB;AACpB,QAAI;AACF,qBAAe,KAAK,MAAME,cAAa,kBAAkB,OAAO,CAAC;AAAA,IACnE,SAAS,KAAK;AACZ,cAAQ,MAAM,qCAAqC,GAAG,EAAE;AACxD,MAAAF,UAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,CAAC,MAAM,QAAQ,YAAY,GAAG;AAChC,cAAQ,MAAM,kEAAkE;AAChF,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,OAAgC;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACvB,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,IACvC,GAAI,yBAAyB,EAAE,uBAAuB,IAAI,CAAC;AAAA,IAC3D,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,IACvC,GAAI,uBAAuB,EAAE,qBAAqB,IAAI,CAAC;AAAA,IACvD,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,IAC/C,GAAI,iBAAiB,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAAA,IACjD,GAAI,oBAAoB,EAAE,gBAAgB,SAAS,mBAAmB,EAAE,EAAE,IAAI,CAAC;AAAA,IAC/E,GAAI,aAAa,EAAE,WAAW,SAAS,YAAY,EAAE,EAAE,IAAI,CAAC;AAAA,IAC5D,GAAI,gBAAgB,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAAA,EACnD;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU,YAAY,IAAI;AAAA,IAAI;AAAA,EACrF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,UAAQ,IAAI,WAAW,IAAI,YAAY,cAAc,MAAM,0BAA0B,KAAK,IAAI;AAChG;AAEA,eAAe,mBAAmB,MAAgB,QAAgB,OAA8B;AAC9F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AAEzB,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,YAAQ,MAAM,gGAAgG;AAC9G,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAU,+BAA+B,UAAU,YAAY,IAAI;AAAA,EACpF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,WAAW,IAAI,YAAY;AACzC;AAEA,eAAe,oBAAoB,MAAgB,QAAgB,OAA8B;AAC/F,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,WAAW,CAAC;AACzB,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,MAAM;AACxB,YAAQ,MAAM,2GAA2G;AACzH,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,SAAkC,CAAC;AAEvC,QAAM,aAAa,YAAY,MAAM,eAAe;AACpD,MAAI,YAAY;AACd,QAAI;AACF,eAAS,KAAK,MAAME,cAAa,YAAY,OAAO,CAAC;AAAA,IACvD,SAAS,KAAK;AACZ,cAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,MAAAF,UAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,MAAM,aAAa,KAAK,IAAI,CAAC,GAAG;AACxC,YAAM,CAAC,KAAK,GAAG,UAAU,IAAI,KAAK,IAAI,CAAC,EAAG,MAAM,GAAG;AACnD,UAAI,OAAO,WAAW,SAAS,GAAG;AAChC,eAAO,GAAG,IAAI,WAAW,KAAK,GAAG;AAAA,MACnC;AACA;AAAA,IACF;AAAA,EACF;AAcA,QAAM,WAAW,QAAQ,MAAM,SAAS;AACxC,QAAM,aAAa,YAAY,MAAM,cAAc;AACnD,MAAI;AACJ,MAAI,YAAY;AACd,UAAM,SAAS,OAAO,UAAU;AAChC,QAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC3C,cAAQ,MAAM,uDAAuD,UAAU,GAAG;AAClF,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,CAAC,UAAU;AAGb,cAAQ,MAAM,+CAA+C;AAC7D,MAAAA,UAAQ,KAAK,CAAC;AAAA,IAChB;AACA,yBAAqB;AAAA,EACvB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ,+BAA+B,UAAU,YAAY,IAAI;AAAA,IAAY;AAAA,MAC1F;AAAA,MACA,QAAQ;AAAA,MACR,GAAI,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,MAClC,GAAI,uBAAuB,SAAY,EAAE,mBAAmB,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,OAAO,IAAI;AAIjB,MAAI,UAAU;AACZ,YAAQ,IAAI,qBAAqB,IAAI,8BAA8B;AACnE,YAAQ,IAAI,cAAc,KAAK,WAAW,EAAE;AAC5C,YAAQ;AAAA,MACN,oDAAoD,UAAU,IAAI,KAAK,WAAW;AAAA,IACpF;AACA,YAAQ;AAAA,MACN,uDAAuD,UAAU,IAAI,KAAK,WAAW;AAAA,IACvF;AACA;AAAA,EACF;AACA,QAAM,aAAa,KAAK,WAAW,cAAc,WAAW,KAAK,WAAW,WAAW,WAAW,IAAI,KAAK,MAAM;AACjH,UAAQ,IAAI,GAAG,UAAU,YAAY,IAAI,KAAK,KAAK,MAAM,KAAK,KAAK,cAAc,CAAC,YAAY,KAAK,YAAY,GAAG,GAAG;AACrH,MAAI,KAAK,QAAQ;AACf,YAAQ,IAAI,WAAW,KAAK,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC,EAAE;AAAA,EAC/D;AACA,MAAI,KAAK,cAAc;AACrB,YAAQ,IAAI,eAAe,KAAK,YAAY,6BAA6B;AAAA,EAC3E;AACA,MAAI,KAAK,WAAW,YAAY,KAAK,QAAQ;AAG3C,UAAM,OAAO,KAAK,OAAO,MAAM,IAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,KAAK,IAAI;AAC/E,YAAQ,IAAI;AAAA,EAA4B,IAAI,EAAE;AAAA,EAChD;AACA,MAAI,KAAK,OAAO;AACd,YAAQ,IAAI,UAAU,KAAK,KAAK,EAAE;AAAA,EACpC;AACF;AAEA,eAAe,uBAAuB,MAAgB,QAAgB,OAA8B;AAClG,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,oFAAoF;AAClG,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAO,+BAA+B,UAAU;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,QAAQ,CAAC;AAC3B,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,oBAAoB;AAChC;AAAA,EACF;AAEA,UAAQ,IAAI,aAAa;AACzB,aAAW,KAAK,OAAO;AACrB,UAAM,OAAO,aAAa,EAAE,MAAM,KAAK,IAAI,EAAE,MAAM;AACnD,UAAM,MAAM,EAAE,eAAe,QAAQ,EAAE,eAAe,SAAY,GAAG,EAAE,UAAU,OAAO;AACxF,YAAQ,IAAI,KAAK,IAAI,IAAIC,KAAI,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,IAAIA,KAAI,EAAE,QAAQ,CAAC,CAAC,IAAIA,KAAI,EAAE,QAAQ,EAAE,CAAC,IAAIA,KAAI,KAAK,CAAC,CAAC,IAAI,WAAW,EAAE,SAAS,CAAC,EAAE;AAAA,EACzI;AACF;AAGA,SAAS,qBAAqB,MAA6B;AACzD,QAAM,OAAO,aAAa,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM;AACzD,QAAM,MAAM,KAAK,eAAe,QAAQ,KAAK,eAAe,SAAY,GAAG,KAAK,UAAU,OAAO;AACjG,UAAQ,IAAI,GAAG,IAAI,IAAI,KAAK,MAAM,KAAK,GAAG,UAAU,KAAK,YAAY,GAAG,GAAG;AAC3E,UAAQ,IAAI,cAAc,KAAK,EAAE,EAAE;AACnC,MAAI,KAAK,QAAQ;AACf,YAAQ,IAAI,WAAW,KAAK,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC,EAAE;AAAA,EAC/D;AACA,MAAI,KAAK,QAAQ;AACf,UAAM,OAAO,KAAK,OAAO,MAAM,IAAO,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,KAAK,IAAI;AAC/E,QAAI,KAAM,SAAQ,IAAI;AAAA,EAA4B,IAAI,EAAE;AAAA,EAC1D;AACA,MAAI,KAAK,OAAO;AACd,YAAQ,IAAI,UAAU,KAAK,KAAK,EAAE;AAAA,EACpC;AACF;AAOA,eAAe,yBACb,MACA,QACA,OACe;AACf,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,cAAc,WAAW,CAAC;AAChC,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,aAAa;AAC/B,YAAQ;AAAA,MACN;AAAA,IACF;AACA,IAAAD,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU,uBAAuB,WAAW;AAAA,EAC7E;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,uBAAqB,IAAI,IAAK;AAChC;AAGA,eAAe,4BACb,MACA,QACA,OACe;AACf,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AACxD,QAAM,aAAa,WAAW,CAAC;AAC/B,QAAM,cAAc,WAAW,CAAC;AAChC,QAAM,OAAO,QAAQ,MAAM,QAAQ;AAEnC,MAAI,CAAC,cAAc,CAAC,aAAa;AAC/B,YAAQ;AAAA,MACN;AAAA,IACF;AACA,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU,uBAAuB,WAAW;AAAA,EAC7E;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,YAAQ,MAAM,UAAU,IAAI,KAAK,EAAE;AACnC,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,CAAC;AAChD;AAAA,EACF;AAEA,uBAAqB,IAAI,IAAK;AAChC;AAoBA,SAAS,cAAc,MAKrB;AACA,MAAI,OAAO;AACX,MAAI;AACJ,QAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,MAAI,KAAK,MAAM,CAAC,KAAK,MAAM,GAAG,EAAE,EAAE,SAAS,GAAG,GAAG;AAC/C,WAAO,KAAK,MAAM,GAAG,EAAE;AACvB,WAAO,KAAK,MAAM,KAAK,CAAC;AAAA,EAC1B;AACA,MAAI;AACJ,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,QAAQ,IAAI;AACd,mBAAe,KAAK,MAAM,QAAQ,CAAC;AACnC,WAAO,KAAK,MAAM,GAAG,KAAK;AAAA,EAC5B;AACA,MAAI;AACJ,QAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,MAAI,KAAK,IAAI;AACX,aAAS,KAAK,MAAM,KAAK,CAAC;AAC1B,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AACA,SAAO,EAAE,MAAM,cAAc,MAAM,QAAQ,aAAa;AAC1D;AAEA,eAAe,eAAe,MAAgB,QAAgB,OAA8B;AAC1F,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,aAAa,YAAY,MAAM,YAAY;AACjD,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,gCAAgC;AAC9C,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,EAC3C;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AACzD,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,MAAM;AACR,mBAAe,KAAK,UAAU,OAAO,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC;AAC1D;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,mCAAmC;AAC/C;AAAA,EACF;AACA,aAAW,KAAK,OAAO;AACrB,UAAM,OAAO,EAAE,OAAO,KAAK,EAAE,IAAI,MAAM;AACvC,UAAM,SAAS,EAAE,eAAe,IAAI,EAAE,YAAY,KAAK;AACvD,YAAQ,IAAI,KAAK,EAAE,WAAW,WAAM,EAAE,oBAAoB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,WAAW,EAAE,EAAE,GAAG;AAAA,EAC1G;AACF;AAEA,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,aAAa,YAAY,MAAM,YAAY;AACjD,QAAM,uBAAuB,YAAY,MAAM,2BAA2B;AAC1E,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,SAAS,YAAY,MAAM,UAAU;AAC3C,QAAM,eAAe,YAAY,MAAM,gBAAgB;AACvD,QAAM,cAAc,YAAY,MAAM,gBAAgB;AAEtD,MAAI,CAAC,cAAc,CAAC,sBAAsB;AACxC,YAAQ,MAAM,+DAA+D;AAC7E,YAAQ,MAAM,kGAAkG;AAChH,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAgH,OAAO,cAAc,IAAI,IAAI,CAAC;AAEpJ,QAAM,OAAgC,EAAE,qBAAqB;AAC7D,QAAM,YAAY,QAAQ,OAAO;AACjC,QAAM,cAAc,UAAU,OAAO;AACrC,QAAM,cAAc,gBAAgB,OAAO;AAC3C,MAAI,cAAc,OAAW,MAAK,OAAO;AACzC,MAAI,gBAAgB,OAAW,MAAK,SAAS;AAC7C,MAAI,gBAAgB,OAAW,MAAK,eAAe;AACnD,MAAI,gBAAgB,OAAW,MAAK,cAAc;AAElD,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU;AAAA,IACzC;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,KAAM,gBAAe,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,QACnD,SAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AAC9D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAM,gBAAe,KAAK,UAAU,OAAO,MAAM,MAAM,CAAC,CAAC;AAAA,MACxD,SAAQ,IAAI,2BAA2B,UAAU,UAAU,OAAO,MAAM,EAAE,GAAG;AACpF;AAEA,eAAe,iBAAiB,MAAgB,QAAgB,OAA8B;AAC5F,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,aAAa,YAAY,MAAM,YAAY;AACjD,QAAM,SAAS,YAAY,MAAM,WAAW;AAC5C,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,SAAS,YAAY,MAAM,UAAU;AAC3C,QAAM,eAAe,YAAY,MAAM,gBAAgB;AACvD,QAAM,cAAc,YAAY,MAAM,gBAAgB;AAEtD,MAAI,CAAC,cAAc,CAAC,QAAQ;AAC1B,YAAQ,MAAM,+CAA+C;AAC7D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAgC,CAAC;AACvC,MAAI,SAAS,OAAW,MAAK,OAAO;AACpC,MAAI,WAAW,OAAW,MAAK,SAAS;AACxC,MAAI,iBAAiB,OAAW,MAAK,eAAe;AACpD,MAAI,gBAAgB,OAAW,MAAK,cAAc;AAElD,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU,UAAU,MAAM;AAAA,IACzD;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,KAAM,gBAAe,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,QACnD,SAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AAC9D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAM,gBAAe,KAAK,UAAU,OAAO,MAAM,MAAM,CAAC,CAAC;AAAA,MACxD,SAAQ,IAAI,qBAAqB,MAAM,EAAE;AAChD;AAEA,eAAe,iBAAiB,MAAgB,QAAgB,OAA8B;AAC5F,QAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,QAAM,aAAa,YAAY,MAAM,YAAY;AACjD,QAAM,SAAS,YAAY,MAAM,WAAW;AAC5C,MAAI,CAAC,cAAc,CAAC,QAAQ;AAC1B,YAAQ,MAAM,+CAA+C;AAC7D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,UAAU,UAAU,MAAM;AAAA,EAC3D;AACA,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,KAAM,gBAAe,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,QACnD,SAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AAC9D,IAAAA,UAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,KAAM,gBAAe,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,MACnD,SAAQ,IAAI,qBAAqB,MAAM,kBAAkB,UAAU,EAAE;AAC5E;AAEA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,CAACO,SAAQ,GAAG,IAAI,IAAI;AAC1B,UAAQA,SAAQ;AAAA,IACd,KAAK;AACH,YAAM,eAAe,MAAM,QAAQ,KAAK;AACxC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK;AACH,YAAM,iBAAiB,MAAM,QAAQ,KAAK;AAC1C;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,YAAM,iBAAiB,MAAM,QAAQ,KAAK;AAC1C;AAAA,IACF;AACE,cAAQ;AAAA,QACN,wBAAwBA,WAAU,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,MAK5C;AACA,MAAAP,UAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAMA,eAAsB,WAAW,MAA+B;AAE9D,QAAM,EAAE,SAAS,YAAY,IAC3B,KAAK,CAAC,MAAM,SAAS,iBAAiB,IAAI,IAAI,EAAE,SAAS,MAAM,aAAa,CAAC,EAAE;AACjF,QAAM,CAAC,YAAY,GAAG,IAAI,IAAI;AAE9B,MAAI,CAAC,cAAc,eAAe,UAAU,QAAQ,SAAS,UAAU,IAAI,GAAG;AAC5E,cAAU;AACV;AAAA,EACF;AAIA,MAAI,eAAe,SAAS;AAC1B,QAAI,KAAK,CAAC,MAAM,MAAM;AACpB,YAAM,SAAS,KAAK,MAAM,CAAC;AAC3B,YAAMQ,WAAU,mBAAmB,MAAM;AACzC,YAAMC,QAAO,QAAQ,QAAQ,QAAQ;AACrC,iBAAW,2BAA2BD,UAASC,KAAI;AACnD;AAAA,IACF;AACA,UAAM,UAAU,mBAAmB,IAAI;AACvC,UAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,eAAW,wBAAwB,SAAS,IAAI;AAChD;AAAA,EACF;AAIA,MAAI,eAAe,YAAY;AAC7B,0BAAsB,IAAI;AAC1B;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,MAAM,IAAI,MAAM,cAAc,OAAO;AAKrD,QAAM,cAAc,oBAAI,IAAI,CAAC,SAAS,aAAa,aAAa,WAAW,OAAO,CAAC;AACnF,QAAM,OAAO,KAAK,OAAO,CAAC,KAAK,MAAM;AACnC,QAAI,YAAY,IAAI,GAAG,EAAG,QAAO;AACjC,QAAI,IAAI,KAAK,YAAY,IAAI,KAAK,IAAI,CAAC,CAAC,EAAG,QAAO;AAClD,WAAO;AAAA,EACT,CAAC;AACD,6BAA2B,YAAY,IAAI;AAE3C,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,eAAe,MAAM,QAAQ,KAAK;AACxC;AAAA,IACF,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,gBAAgB,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK;AACH,YAAM,gBAAgB,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,gBAAgB,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK;AAEH,UAAI,uBAAuB,KAAK,CAAC,CAAC,EAAG,OAAM,kBAAkB,MAAM,QAAQ,KAAK;AAAA,UAC3E,OAAM,WAAW,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK,QAAQ;AACX,YAAM,CAAC,YAAY,GAAG,QAAQ,IAAI;AAClC,UAAI,eAAe,YAAY;AAC7B,cAAM,kBAAkB,UAAU,QAAQ,KAAK;AAAA,MACjD,OAAO;AACL,gBAAQ,MAAM,wBAAwB,UAAU,mBAAmB;AACnE,QAAAT,UAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAAA,IACA,KAAK,YAAY;AACf,YAAM,CAACO,SAAQ,GAAG,UAAU,IAAI;AAChC,UAAIA,YAAW,OAAO;AACpB,cAAM,kBAAkB,YAAY,QAAQ,KAAK;AAAA,MACnD,WAAWA,YAAW,UAAU;AAC9B,cAAM,qBAAqB,YAAY,QAAQ,KAAK;AAAA,MACtD,OAAO;AACL,gBAAQ,MAAM,4BAA4BA,OAAM,0BAA0B;AAC1E,QAAAP,UAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,YAAM,CAAC,cAAc,GAAG,UAAU,IAAI;AACtC,UAAI,iBAAiB,QAAQ;AAC3B,cAAM,iBAAiB,YAAY,QAAQ,KAAK;AAAA,MAClD,WAAW,iBAAiB,OAAO;AACjC,cAAM,gBAAgB,YAAY,QAAQ,KAAK;AAAA,MACjD,WAAW,iBAAiB,SAAS;AACnC,cAAM,kBAAkB,YAAY,QAAQ,KAAK;AAAA,MACnD,OAAO;AACL,gBAAQ,MAAM,0BAA0B,YAAY,kCAAkC;AACtF,QAAAA,UAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAAA,IACA,KAAK;AACH,YAAM,gBAAgB,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK,UAAU;AACb,YAAM,CAAC,WAAW,GAAG,UAAU,IAAI;AACnC,cAAQ,WAAW;AAAA,QACjB,KAAK;AACH,gBAAM,iBAAiB,YAAY,QAAQ,KAAK;AAChD;AAAA,QACF,KAAK;AACH,gBAAM,gBAAgB,YAAY,QAAQ,KAAK;AAC/C;AAAA,QACF,KAAK;AACH,gBAAM,mBAAmB,YAAY,QAAQ,KAAK;AAClD;AAAA,QACF,KAAK;AACH,gBAAM,mBAAmB,YAAY,QAAQ,KAAK;AAClD;AAAA,QACF,KAAK;AACH,gBAAM,oBAAoB,YAAY,QAAQ,KAAK;AACnD;AAAA,QACF,KAAK;AACH,gBAAM,uBAAuB,YAAY,QAAQ,KAAK;AACtD;AAAA,QACF,KAAK,aAAa;AAChB,gBAAM,CAAC,SAAS,GAAG,QAAQ,IAAI;AAC/B,cAAI,YAAY,OAAO;AACrB,kBAAM,yBAAyB,UAAU,QAAQ,KAAK;AAAA,UACxD,WAAW,YAAY,UAAU;AAC/B,kBAAM,4BAA4B,UAAU,QAAQ,KAAK;AAAA,UAC3D,OAAO;AACL,oBAAQ,MAAM,kCAAkC,OAAO,0BAA0B;AACjF,YAAAA,UAAQ,KAAK,CAAC;AAAA,UAChB;AACA;AAAA,QACF;AAAA,QACA;AACE,kBAAQ,MAAM,+BAA+B,SAAS,mFAAmF;AACzI,UAAAA,UAAQ,KAAK,CAAC;AAAA,MAClB;AACA;AAAA,IACF;AAAA,IACA,KAAK;AACH,YAAM,YAAY,MAAM,QAAQ,KAAK;AACrC;AAAA,IACF,KAAK;AACH,YAAM,iBAAiB,MAAM,QAAQ,KAAK;AAC1C;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,MAAM,QAAQ,KAAK;AAC3C;AAAA,IACF,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,MAAM,QAAQ,KAAK;AAC3C;AAAA,IACF,KAAK;AACH,YAAM,qBAAqB,MAAM,QAAQ,KAAK;AAC9C;AAAA,IACF,KAAK;AACH,YAAM,qBAAqB,MAAM,QAAQ,KAAK;AAC9C;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,MAAM,QAAQ,KAAK;AAC3C;AAAA,IACF,KAAK;AACH,YAAM,oBAAoB,MAAM,QAAQ,KAAK;AAC7C;AAAA,IACF,KAAK;AACH,YAAM,mBAAmB,MAAM,QAAQ,KAAK;AAC5C;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,MAAM,QAAQ,KAAK;AAC3C;AAAA,IACF,KAAK;AACH,YAAM,iBAAiB,MAAM,QAAQ,KAAK;AAC1C;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,MAAM,aAAa,QAAQ,KAAK;AACxD;AAAA,IACF,KAAK;AACH,YAAM,kBAAkB,cAAc,QAAQ,IAAI,GAAG,MAAM,QAAQ,KAAK;AACxE;AAAA,IACF,KAAK;AACH,YAAM,uBAAuB,MAAM,QAAQ,KAAK;AAChD;AAAA,IACF,KAAK;AACH,YAAM,qBAAqB,MAAM,QAAQ,KAAK;AAC9C;AAAA,IACF,KAAK;AACH,YAAM,gBAAgB,MAAM,QAAQ,KAAK;AACzC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF;AACE,cAAQ,MAAM,wBAAwB,UAAU,EAAE;AAClD,gBAAU;AACV,MAAAA,UAAQ,KAAK,CAAC;AAAA,EAClB;AACF;","names":["process","readFileSync","action","process","process","service","process","process","process","readFileSync","createHash","z","z","z","z","z","isRecord","z","provider","env","z","z","z","z","pad","resolve","process","push","readFileSync","environment","seconds","process","spawnSync","createHash","mkdtempSync","readFileSync","rmSync","tmpdir","dirname","join","readFileSync","docker","git","readFileSync","z","spawnSync","createHash","resolve","join","dirname","process","readFileSync","mkdtempSync","tmpdir","rmSync","spawnSync","process","existsSync","mkdtempSync","readFileSync","rmSync","tmpdir","join","resolve","spawnSync","process","output","join","mkdtempSync","tmpdir","rmSync","existsSync","readFileSync","decoder","fail","resolve","process","shortVersion","sleep","resolve","environment","process","process","USAGE","fail","process","environment","process","environment","fail","process","action","res","process","pad","readFileSync","service","POLL_INTERVAL_MS","resolve","a","action","section","json"]}
|