@tickernelz/paperclip-pro-adapter-utils 2026.925.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 +37 -0
- package/dist/acpx-engine/cli.d.ts +2 -0
- package/dist/acpx-engine/cli.d.ts.map +1 -0
- package/dist/acpx-engine/cli.js +128 -0
- package/dist/acpx-engine/cli.js.map +1 -0
- package/dist/acpx-engine/composed-run-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/composed-run-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/composed-run-characterization.test.js +739 -0
- package/dist/acpx-engine/composed-run-characterization.test.js.map +1 -0
- package/dist/acpx-engine/constants.d.ts +20 -0
- package/dist/acpx-engine/constants.d.ts.map +1 -0
- package/dist/acpx-engine/constants.js +37 -0
- package/dist/acpx-engine/constants.js.map +1 -0
- package/dist/acpx-engine/execute-identity.test.d.ts +2 -0
- package/dist/acpx-engine/execute-identity.test.d.ts.map +1 -0
- package/dist/acpx-engine/execute-identity.test.js +218 -0
- package/dist/acpx-engine/execute-identity.test.js.map +1 -0
- package/dist/acpx-engine/execute.d.ts +325 -0
- package/dist/acpx-engine/execute.d.ts.map +1 -0
- package/dist/acpx-engine/execute.js +4448 -0
- package/dist/acpx-engine/execute.js.map +1 -0
- package/dist/acpx-engine/execute.test.d.ts +2 -0
- package/dist/acpx-engine/execute.test.d.ts.map +1 -0
- package/dist/acpx-engine/execute.test.js +6694 -0
- package/dist/acpx-engine/execute.test.js.map +1 -0
- package/dist/acpx-engine/index.d.ts +6 -0
- package/dist/acpx-engine/index.d.ts.map +1 -0
- package/dist/acpx-engine/index.js +6 -0
- package/dist/acpx-engine/index.js.map +1 -0
- package/dist/acpx-engine/local-process-control.d.ts +5 -0
- package/dist/acpx-engine/local-process-control.d.ts.map +1 -0
- package/dist/acpx-engine/local-process-control.js +39 -0
- package/dist/acpx-engine/local-process-control.js.map +1 -0
- package/dist/acpx-engine/local-process-control.test.d.ts +2 -0
- package/dist/acpx-engine/local-process-control.test.d.ts.map +1 -0
- package/dist/acpx-engine/local-process-control.test.js +38 -0
- package/dist/acpx-engine/local-process-control.test.js.map +1 -0
- package/dist/acpx-engine/operator-stop.test.d.ts +2 -0
- package/dist/acpx-engine/operator-stop.test.d.ts.map +1 -0
- package/dist/acpx-engine/operator-stop.test.js +107 -0
- package/dist/acpx-engine/operator-stop.test.js.map +1 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.d.ts +2 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.d.ts.map +1 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.js +151 -0
- package/dist/acpx-engine/remote-spawn-smoke.test.js.map +1 -0
- package/dist/acpx-engine/run-contracts.d.ts +413 -0
- package/dist/acpx-engine/run-contracts.d.ts.map +1 -0
- package/dist/acpx-engine/run-contracts.js +20 -0
- package/dist/acpx-engine/run-contracts.js.map +1 -0
- package/dist/acpx-engine/run-contracts.test.d.ts +2 -0
- package/dist/acpx-engine/run-contracts.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-contracts.test.js +57 -0
- package/dist/acpx-engine/run-contracts.test.js.map +1 -0
- package/dist/acpx-engine/run-coordinator.d.ts +57 -0
- package/dist/acpx-engine/run-coordinator.d.ts.map +1 -0
- package/dist/acpx-engine/run-coordinator.js +63 -0
- package/dist/acpx-engine/run-coordinator.js.map +1 -0
- package/dist/acpx-engine/run-coordinator.test.d.ts +2 -0
- package/dist/acpx-engine/run-coordinator.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-coordinator.test.js +160 -0
- package/dist/acpx-engine/run-coordinator.test.js.map +1 -0
- package/dist/acpx-engine/run-fault-matrix.test.d.ts +2 -0
- package/dist/acpx-engine/run-fault-matrix.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-fault-matrix.test.js +829 -0
- package/dist/acpx-engine/run-fault-matrix.test.js.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.d.ts +30 -0
- package/dist/acpx-engine/run-resource-ledger.d.ts.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.js +148 -0
- package/dist/acpx-engine/run-resource-ledger.js.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.test.d.ts +2 -0
- package/dist/acpx-engine/run-resource-ledger.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-resource-ledger.test.js +154 -0
- package/dist/acpx-engine/run-resource-ledger.test.js.map +1 -0
- package/dist/acpx-engine/run-site-host.d.ts +73 -0
- package/dist/acpx-engine/run-site-host.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-host.js +65 -0
- package/dist/acpx-engine/run-site-host.js.map +1 -0
- package/dist/acpx-engine/run-site-host.test.d.ts +2 -0
- package/dist/acpx-engine/run-site-host.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-host.test.js +98 -0
- package/dist/acpx-engine/run-site-host.test.js.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.d.ts +142 -0
- package/dist/acpx-engine/run-site-sandbox.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.js +278 -0
- package/dist/acpx-engine/run-site-sandbox.js.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.test.d.ts +2 -0
- package/dist/acpx-engine/run-site-sandbox.test.d.ts.map +1 -0
- package/dist/acpx-engine/run-site-sandbox.test.js +275 -0
- package/dist/acpx-engine/run-site-sandbox.test.js.map +1 -0
- package/dist/acpx-engine/session-codec.d.ts +3 -0
- package/dist/acpx-engine/session-codec.d.ts.map +1 -0
- package/dist/acpx-engine/session-codec.js +49 -0
- package/dist/acpx-engine/session-codec.js.map +1 -0
- package/dist/acpx-engine/session-reuse-store.d.ts +62 -0
- package/dist/acpx-engine/session-reuse-store.d.ts.map +1 -0
- package/dist/acpx-engine/session-reuse-store.js +134 -0
- package/dist/acpx-engine/session-reuse-store.js.map +1 -0
- package/dist/acpx-engine/session-reuse-store.test.d.ts +2 -0
- package/dist/acpx-engine/session-reuse-store.test.d.ts.map +1 -0
- package/dist/acpx-engine/session-reuse-store.test.js +134 -0
- package/dist/acpx-engine/session-reuse-store.test.js.map +1 -0
- package/dist/acpx-engine/settlement-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/settlement-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-characterization.test.js +880 -0
- package/dist/acpx-engine/settlement-characterization.test.js.map +1 -0
- package/dist/acpx-engine/settlement-sequence.d.ts +77 -0
- package/dist/acpx-engine/settlement-sequence.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-sequence.js +119 -0
- package/dist/acpx-engine/settlement-sequence.js.map +1 -0
- package/dist/acpx-engine/settlement-sequence.test.d.ts +2 -0
- package/dist/acpx-engine/settlement-sequence.test.d.ts.map +1 -0
- package/dist/acpx-engine/settlement-sequence.test.js +272 -0
- package/dist/acpx-engine/settlement-sequence.test.js.map +1 -0
- package/dist/acpx-engine/spawn-smoke.test.d.ts +2 -0
- package/dist/acpx-engine/spawn-smoke.test.d.ts.map +1 -0
- package/dist/acpx-engine/spawn-smoke.test.js +184 -0
- package/dist/acpx-engine/spawn-smoke.test.js.map +1 -0
- package/dist/acpx-engine/startup-cancellation.d.ts +8 -0
- package/dist/acpx-engine/startup-cancellation.d.ts.map +1 -0
- package/dist/acpx-engine/startup-cancellation.js +92 -0
- package/dist/acpx-engine/startup-cancellation.js.map +1 -0
- package/dist/acpx-engine/startup-cancellation.test.d.ts +2 -0
- package/dist/acpx-engine/startup-cancellation.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-cancellation.test.js +115 -0
- package/dist/acpx-engine/startup-cancellation.test.js.map +1 -0
- package/dist/acpx-engine/startup-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/startup-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-characterization.test.js +742 -0
- package/dist/acpx-engine/startup-characterization.test.js.map +1 -0
- package/dist/acpx-engine/startup-timing.d.ts +419 -0
- package/dist/acpx-engine/startup-timing.d.ts.map +1 -0
- package/dist/acpx-engine/startup-timing.js +616 -0
- package/dist/acpx-engine/startup-timing.js.map +1 -0
- package/dist/acpx-engine/startup-timing.test.d.ts +2 -0
- package/dist/acpx-engine/startup-timing.test.d.ts.map +1 -0
- package/dist/acpx-engine/startup-timing.test.js +619 -0
- package/dist/acpx-engine/startup-timing.test.js.map +1 -0
- package/dist/acpx-engine/turn-characterization.test.d.ts +2 -0
- package/dist/acpx-engine/turn-characterization.test.d.ts.map +1 -0
- package/dist/acpx-engine/turn-characterization.test.js +566 -0
- package/dist/acpx-engine/turn-characterization.test.js.map +1 -0
- package/dist/acpx-engine/turn-sequence.d.ts +54 -0
- package/dist/acpx-engine/turn-sequence.d.ts.map +1 -0
- package/dist/acpx-engine/turn-sequence.js +75 -0
- package/dist/acpx-engine/turn-sequence.js.map +1 -0
- package/dist/acpx-engine/turn-sequence.test.d.ts +2 -0
- package/dist/acpx-engine/turn-sequence.test.d.ts.map +1 -0
- package/dist/acpx-engine/turn-sequence.test.js +177 -0
- package/dist/acpx-engine/turn-sequence.test.js.map +1 -0
- package/dist/acpx-engine/ui.d.ts +3 -0
- package/dist/acpx-engine/ui.d.ts.map +1 -0
- package/dist/acpx-engine/ui.js +162 -0
- package/dist/acpx-engine/ui.js.map +1 -0
- package/dist/billing.d.ts +2 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +16 -0
- package/dist/billing.js.map +1 -0
- package/dist/billing.test.d.ts +2 -0
- package/dist/billing.test.d.ts.map +1 -0
- package/dist/billing.test.js +14 -0
- package/dist/billing.test.js.map +1 -0
- package/dist/bridge-transport-contract.d.ts +43 -0
- package/dist/bridge-transport-contract.d.ts.map +1 -0
- package/dist/bridge-transport-contract.js +33 -0
- package/dist/bridge-transport-contract.js.map +1 -0
- package/dist/chat-file-delivery.d.ts +10 -0
- package/dist/chat-file-delivery.d.ts.map +1 -0
- package/dist/chat-file-delivery.js +38 -0
- package/dist/chat-file-delivery.js.map +1 -0
- package/dist/chat-file-delivery.test.d.ts +2 -0
- package/dist/chat-file-delivery.test.d.ts.map +1 -0
- package/dist/chat-file-delivery.test.js +92 -0
- package/dist/chat-file-delivery.test.js.map +1 -0
- package/dist/command-managed-runtime.d.ts +162 -0
- package/dist/command-managed-runtime.d.ts.map +1 -0
- package/dist/command-managed-runtime.js +456 -0
- package/dist/command-managed-runtime.js.map +1 -0
- package/dist/command-managed-runtime.test.d.ts +2 -0
- package/dist/command-managed-runtime.test.d.ts.map +1 -0
- package/dist/command-managed-runtime.test.js +908 -0
- package/dist/command-managed-runtime.test.js.map +1 -0
- package/dist/command-redaction.d.ts +15 -0
- package/dist/command-redaction.d.ts.map +1 -0
- package/dist/command-redaction.js +97 -0
- package/dist/command-redaction.js.map +1 -0
- package/dist/command-redaction.test.d.ts +2 -0
- package/dist/command-redaction.test.d.ts.map +1 -0
- package/dist/command-redaction.test.js +80 -0
- package/dist/command-redaction.test.js.map +1 -0
- package/dist/duplex-frame-codec.d.ts +124 -0
- package/dist/duplex-frame-codec.d.ts.map +1 -0
- package/dist/duplex-frame-codec.js +134 -0
- package/dist/duplex-frame-codec.js.map +1 -0
- package/dist/duplex-frame-codec.test.d.ts +2 -0
- package/dist/duplex-frame-codec.test.d.ts.map +1 -0
- package/dist/duplex-frame-codec.test.js +172 -0
- package/dist/duplex-frame-codec.test.js.map +1 -0
- package/dist/duplex-observability.d.ts +220 -0
- package/dist/duplex-observability.d.ts.map +1 -0
- package/dist/duplex-observability.js +248 -0
- package/dist/duplex-observability.js.map +1 -0
- package/dist/duplex-observability.test.d.ts +2 -0
- package/dist/duplex-observability.test.d.ts.map +1 -0
- package/dist/duplex-observability.test.js +55 -0
- package/dist/duplex-observability.test.js.map +1 -0
- package/dist/env-bindings.d.ts +17 -0
- package/dist/env-bindings.d.ts.map +1 -0
- package/dist/env-bindings.js +98 -0
- package/dist/env-bindings.js.map +1 -0
- package/dist/env-bindings.test.d.ts +2 -0
- package/dist/env-bindings.test.d.ts.map +1 -0
- package/dist/env-bindings.test.js +66 -0
- package/dist/env-bindings.test.js.map +1 -0
- package/dist/exclude-patterns.d.ts +4 -0
- package/dist/exclude-patterns.d.ts.map +1 -0
- package/dist/exclude-patterns.js +26 -0
- package/dist/exclude-patterns.js.map +1 -0
- package/dist/execution-target-sandbox.test.d.ts +2 -0
- package/dist/execution-target-sandbox.test.d.ts.map +1 -0
- package/dist/execution-target-sandbox.test.js +6522 -0
- package/dist/execution-target-sandbox.test.js.map +1 -0
- package/dist/execution-target-stdin-race.test.d.ts +2 -0
- package/dist/execution-target-stdin-race.test.d.ts.map +1 -0
- package/dist/execution-target-stdin-race.test.js +1842 -0
- package/dist/execution-target-stdin-race.test.js.map +1 -0
- package/dist/execution-target.d.ts +537 -0
- package/dist/execution-target.d.ts.map +1 -0
- package/dist/execution-target.js +3877 -0
- package/dist/execution-target.js.map +1 -0
- package/dist/execution-target.test.d.ts +2 -0
- package/dist/execution-target.test.d.ts.map +1 -0
- package/dist/execution-target.test.js +346 -0
- package/dist/execution-target.test.js.map +1 -0
- package/dist/git-workspace-sync.d.ts +193 -0
- package/dist/git-workspace-sync.d.ts.map +1 -0
- package/dist/git-workspace-sync.js +786 -0
- package/dist/git-workspace-sync.js.map +1 -0
- package/dist/git-workspace-sync.test.d.ts +2 -0
- package/dist/git-workspace-sync.test.d.ts.map +1 -0
- package/dist/git-workspace-sync.test.js +830 -0
- package/dist/git-workspace-sync.test.js.map +1 -0
- package/dist/github-launcher-environment.test.d.ts +2 -0
- package/dist/github-launcher-environment.test.d.ts.map +1 -0
- package/dist/github-launcher-environment.test.js +285 -0
- package/dist/github-launcher-environment.test.js.map +1 -0
- package/dist/github-launcher.d.ts +8 -0
- package/dist/github-launcher.d.ts.map +1 -0
- package/dist/github-launcher.js +127 -0
- package/dist/github-launcher.js.map +1 -0
- package/dist/github-launcher.test.d.ts +2 -0
- package/dist/github-launcher.test.d.ts.map +1 -0
- package/dist/github-launcher.test.js +166 -0
- package/dist/github-launcher.test.js.map +1 -0
- package/dist/http2-bridge-server.d.ts +465 -0
- package/dist/http2-bridge-server.d.ts.map +1 -0
- package/dist/http2-bridge-server.js +1087 -0
- package/dist/http2-bridge-server.js.map +1 -0
- package/dist/http2-bridge-server.test.d.ts +2 -0
- package/dist/http2-bridge-server.test.d.ts.map +1 -0
- package/dist/http2-bridge-server.test.js +2129 -0
- package/dist/http2-bridge-server.test.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/local-process-sandbox.d.ts +41 -0
- package/dist/local-process-sandbox.d.ts.map +1 -0
- package/dist/local-process-sandbox.js +432 -0
- package/dist/local-process-sandbox.js.map +1 -0
- package/dist/local-process-sandbox.test.d.ts +2 -0
- package/dist/local-process-sandbox.test.d.ts.map +1 -0
- package/dist/local-process-sandbox.test.js +399 -0
- package/dist/local-process-sandbox.test.js.map +1 -0
- package/dist/log-redaction.d.ts +9 -0
- package/dist/log-redaction.d.ts.map +1 -0
- package/dist/log-redaction.js +88 -0
- package/dist/log-redaction.js.map +1 -0
- package/dist/login-capability.d.ts +88 -0
- package/dist/login-capability.d.ts.map +1 -0
- package/dist/login-capability.js +78 -0
- package/dist/login-capability.js.map +1 -0
- package/dist/login-capability.test.d.ts +2 -0
- package/dist/login-capability.test.d.ts.map +1 -0
- package/dist/login-capability.test.js +78 -0
- package/dist/login-capability.test.js.map +1 -0
- package/dist/login-pty-transport.d.ts +80 -0
- package/dist/login-pty-transport.d.ts.map +1 -0
- package/dist/login-pty-transport.js +75 -0
- package/dist/login-pty-transport.js.map +1 -0
- package/dist/login-pty-transport.test.d.ts +2 -0
- package/dist/login-pty-transport.test.d.ts.map +1 -0
- package/dist/login-pty-transport.test.js +185 -0
- package/dist/login-pty-transport.test.js.map +1 -0
- package/dist/login-runner-lifecycle.d.ts +70 -0
- package/dist/login-runner-lifecycle.d.ts.map +1 -0
- package/dist/login-runner-lifecycle.js +52 -0
- package/dist/login-runner-lifecycle.js.map +1 -0
- package/dist/login-runner-lifecycle.test.d.ts +2 -0
- package/dist/login-runner-lifecycle.test.d.ts.map +1 -0
- package/dist/login-runner-lifecycle.test.js +122 -0
- package/dist/login-runner-lifecycle.test.js.map +1 -0
- package/dist/mcp-isolation.integration.test.d.ts +2 -0
- package/dist/mcp-isolation.integration.test.d.ts.map +1 -0
- package/dist/mcp-isolation.integration.test.js +202 -0
- package/dist/mcp-isolation.integration.test.js.map +1 -0
- package/dist/paperclip-runner-permissions.d.ts +110 -0
- package/dist/paperclip-runner-permissions.d.ts.map +1 -0
- package/dist/paperclip-runner-permissions.js +150 -0
- package/dist/paperclip-runner-permissions.js.map +1 -0
- package/dist/paperclip-runner-permissions.test.d.ts +2 -0
- package/dist/paperclip-runner-permissions.test.d.ts.map +1 -0
- package/dist/paperclip-runner-permissions.test.js +47 -0
- package/dist/paperclip-runner-permissions.test.js.map +1 -0
- package/dist/published-exports.test.d.ts +2 -0
- package/dist/published-exports.test.d.ts.map +1 -0
- package/dist/published-exports.test.js +17 -0
- package/dist/published-exports.test.js.map +1 -0
- package/dist/remote-execution-env.d.ts +2 -0
- package/dist/remote-execution-env.d.ts.map +1 -0
- package/dist/remote-execution-env.js +46 -0
- package/dist/remote-execution-env.js.map +1 -0
- package/dist/remote-managed-runtime.d.ts +45 -0
- package/dist/remote-managed-runtime.d.ts.map +1 -0
- package/dist/remote-managed-runtime.js +183 -0
- package/dist/remote-managed-runtime.js.map +1 -0
- package/dist/remote-managed-runtime.test.d.ts +2 -0
- package/dist/remote-managed-runtime.test.d.ts.map +1 -0
- package/dist/remote-managed-runtime.test.js +294 -0
- package/dist/remote-managed-runtime.test.js.map +1 -0
- package/dist/runner-connectivity.d.ts +61 -0
- package/dist/runner-connectivity.d.ts.map +1 -0
- package/dist/runner-connectivity.js +81 -0
- package/dist/runner-connectivity.js.map +1 -0
- package/dist/runner-connectivity.test.d.ts +2 -0
- package/dist/runner-connectivity.test.d.ts.map +1 -0
- package/dist/runner-connectivity.test.js +183 -0
- package/dist/runner-connectivity.test.js.map +1 -0
- package/dist/runtime-progress.d.ts +48 -0
- package/dist/runtime-progress.d.ts.map +1 -0
- package/dist/runtime-progress.js +99 -0
- package/dist/runtime-progress.js.map +1 -0
- package/dist/runtime-progress.test.d.ts +2 -0
- package/dist/runtime-progress.test.d.ts.map +1 -0
- package/dist/runtime-progress.test.js +240 -0
- package/dist/runtime-progress.test.js.map +1 -0
- package/dist/sandbox-callback-bridge-body.d.ts +12 -0
- package/dist/sandbox-callback-bridge-body.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge-body.js +39 -0
- package/dist/sandbox-callback-bridge-body.js.map +1 -0
- package/dist/sandbox-callback-bridge-body.test.d.ts +2 -0
- package/dist/sandbox-callback-bridge-body.test.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge-body.test.js +31 -0
- package/dist/sandbox-callback-bridge-body.test.js.map +1 -0
- package/dist/sandbox-callback-bridge.d.ts +268 -0
- package/dist/sandbox-callback-bridge.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge.js +2357 -0
- package/dist/sandbox-callback-bridge.js.map +1 -0
- package/dist/sandbox-callback-bridge.test.d.ts +2 -0
- package/dist/sandbox-callback-bridge.test.d.ts.map +1 -0
- package/dist/sandbox-callback-bridge.test.js +3253 -0
- package/dist/sandbox-callback-bridge.test.js.map +1 -0
- package/dist/sandbox-file-sync.test.d.ts +2 -0
- package/dist/sandbox-file-sync.test.d.ts.map +1 -0
- package/dist/sandbox-file-sync.test.js +419 -0
- package/dist/sandbox-file-sync.test.js.map +1 -0
- package/dist/sandbox-install-command.d.ts +2 -0
- package/dist/sandbox-install-command.d.ts.map +1 -0
- package/dist/sandbox-install-command.js +47 -0
- package/dist/sandbox-install-command.js.map +1 -0
- package/dist/sandbox-install-command.test.d.ts +2 -0
- package/dist/sandbox-install-command.test.d.ts.map +1 -0
- package/dist/sandbox-install-command.test.js +22 -0
- package/dist/sandbox-install-command.test.js.map +1 -0
- package/dist/sandbox-managed-runtime.d.ts +417 -0
- package/dist/sandbox-managed-runtime.d.ts.map +1 -0
- package/dist/sandbox-managed-runtime.js +1337 -0
- package/dist/sandbox-managed-runtime.js.map +1 -0
- package/dist/sandbox-managed-runtime.test.d.ts +2 -0
- package/dist/sandbox-managed-runtime.test.d.ts.map +1 -0
- package/dist/sandbox-managed-runtime.test.js +3746 -0
- package/dist/sandbox-managed-runtime.test.js.map +1 -0
- package/dist/sandbox-run-log-stream.d.ts +44 -0
- package/dist/sandbox-run-log-stream.d.ts.map +1 -0
- package/dist/sandbox-run-log-stream.js +203 -0
- package/dist/sandbox-run-log-stream.js.map +1 -0
- package/dist/sandbox-shell.d.ts +3 -0
- package/dist/sandbox-shell.d.ts.map +1 -0
- package/dist/sandbox-shell.js +7 -0
- package/dist/sandbox-shell.js.map +1 -0
- package/dist/server-utils-env.test.d.ts +2 -0
- package/dist/server-utils-env.test.d.ts.map +1 -0
- package/dist/server-utils-env.test.js +15 -0
- package/dist/server-utils-env.test.js.map +1 -0
- package/dist/server-utils.d.ts +617 -0
- package/dist/server-utils.d.ts.map +1 -0
- package/dist/server-utils.js +3258 -0
- package/dist/server-utils.js.map +1 -0
- package/dist/server-utils.test.d.ts +2 -0
- package/dist/server-utils.test.d.ts.map +1 -0
- package/dist/server-utils.test.js +3047 -0
- package/dist/server-utils.test.js.map +1 -0
- package/dist/session-compaction.d.ts +25 -0
- package/dist/session-compaction.d.ts.map +1 -0
- package/dist/session-compaction.js +165 -0
- package/dist/session-compaction.js.map +1 -0
- package/dist/skill-library-manifest.d.ts +6 -0
- package/dist/skill-library-manifest.d.ts.map +1 -0
- package/dist/skill-library-manifest.js +53 -0
- package/dist/skill-library-manifest.js.map +1 -0
- package/dist/skill-library-manifest.test.d.ts +2 -0
- package/dist/skill-library-manifest.test.d.ts.map +1 -0
- package/dist/skill-library-manifest.test.js +84 -0
- package/dist/skill-library-manifest.test.js.map +1 -0
- package/dist/skills-staging-follow-symlinks.test.d.ts +2 -0
- package/dist/skills-staging-follow-symlinks.test.d.ts.map +1 -0
- package/dist/skills-staging-follow-symlinks.test.js +126 -0
- package/dist/skills-staging-follow-symlinks.test.js.map +1 -0
- package/dist/ssh-fixture.test.d.ts +2 -0
- package/dist/ssh-fixture.test.d.ts.map +1 -0
- package/dist/ssh-fixture.test.js +832 -0
- package/dist/ssh-fixture.test.js.map +1 -0
- package/dist/ssh.d.ts +124 -0
- package/dist/ssh.d.ts.map +1 -0
- package/dist/ssh.js +1667 -0
- package/dist/ssh.js.map +1 -0
- package/dist/sync-operation-schedule.d.ts +17 -0
- package/dist/sync-operation-schedule.d.ts.map +1 -0
- package/dist/sync-operation-schedule.js +66 -0
- package/dist/sync-operation-schedule.js.map +1 -0
- package/dist/sync-operation-schedule.test.d.ts +2 -0
- package/dist/sync-operation-schedule.test.d.ts.map +1 -0
- package/dist/sync-operation-schedule.test.js +130 -0
- package/dist/sync-operation-schedule.test.js.map +1 -0
- package/dist/test-support/mcp-isolation-harness.d.ts +23 -0
- package/dist/test-support/mcp-isolation-harness.d.ts.map +1 -0
- package/dist/test-support/mcp-isolation-harness.js +67 -0
- package/dist/test-support/mcp-isolation-harness.js.map +1 -0
- package/dist/types.d.ts +780 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace-restore-merge.d.ts +77 -0
- package/dist/workspace-restore-merge.d.ts.map +1 -0
- package/dist/workspace-restore-merge.js +396 -0
- package/dist/workspace-restore-merge.js.map +1 -0
- package/dist/workspace-restore-merge.test.d.ts +2 -0
- package/dist/workspace-restore-merge.test.d.ts.map +1 -0
- package/dist/workspace-restore-merge.test.js +438 -0
- package/dist/workspace-restore-merge.test.js.map +1 -0
- package/dist/workspace-restore-teardown.d.ts +29 -0
- package/dist/workspace-restore-teardown.d.ts.map +1 -0
- package/dist/workspace-restore-teardown.js +34 -0
- package/dist/workspace-restore-teardown.js.map +1 -0
- package/dist/workspace-restore-teardown.test.d.ts +2 -0
- package/dist/workspace-restore-teardown.test.d.ts.map +1 -0
- package/dist/workspace-restore-teardown.test.js +71 -0
- package/dist/workspace-restore-teardown.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/LICENSE +191 -0
- package/node_modules/@agentclientprotocol/sdk/README.md +70 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.d.ts +1803 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.js +1869 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js +5036 -0
- package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.d.ts +89 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.js +490 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js +507 -0
- package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.d.ts +26 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js +96 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js +77 -0
- package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js +232 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js +132 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.d.ts +9 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js +221 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js +86 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js +112 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js +88 -0
- package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.d.ts +33 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js +435 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js +1380 -0
- package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.d.ts +603 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js +1076 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js +917 -0
- package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.d.ts +22 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js +64 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js +45 -0
- package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.d.ts +17 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js +426 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js +499 -0
- package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.d.ts +37 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js +474 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js +535 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.d.ts +11 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.js +69 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js +82 -0
- package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.d.ts +183 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js +207 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.d.ts +51 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js +52 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.d.ts +6219 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js +3 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.d.ts +15215 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js +3084 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.d.ts +30 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js +98 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js +93 -0
- package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js +261 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js +536 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.d.ts +2 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js +88 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js +53 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js +992 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.d.ts +81 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.js +386 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.js +1116 -0
- package/node_modules/@agentclientprotocol/sdk/dist/server.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.d.ts +4 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.js +91 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js +126 -0
- package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.d.ts +35 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.js +115 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js +195 -0
- package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.d.ts +8 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js +51 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.d.ts +24 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js +163 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.d.ts +9 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js +74 -0
- package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.d.ts +957 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js +1484 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js +1287 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.d.ts +946 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js +1046 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.d.ts +42 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js +43 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.d.ts +6208 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js +3 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.d.ts +33267 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js +3199 -0
- package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.d.ts +14 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js +357 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.d.ts +54 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js +253 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js +836 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.d.ts +13 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js +78 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.d.ts +1 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js +42 -0
- package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js.map +1 -0
- package/node_modules/@agentclientprotocol/sdk/package.json +104 -0
- package/node_modules/@agentclientprotocol/sdk/schema/schema.json +8967 -0
- package/node_modules/@agentclientprotocol/sdk/schema/v2/schema.unstable.json +10242 -0
- package/node_modules/@clack/core/LICENSE +9 -0
- package/node_modules/@clack/core/README.md +22 -0
- package/node_modules/@clack/core/dist/index.d.mts +503 -0
- package/node_modules/@clack/core/dist/index.mjs +1003 -0
- package/node_modules/@clack/core/package.json +63 -0
- package/node_modules/@clack/prompts/LICENSE +9 -0
- package/node_modules/@clack/prompts/README.md +396 -0
- package/node_modules/@clack/prompts/dist/index.d.mts +949 -0
- package/node_modules/@clack/prompts/dist/index.mjs +1423 -0
- package/node_modules/@clack/prompts/package.json +67 -0
- package/node_modules/@esbuild/linux-x64/README.md +3 -0
- package/node_modules/@esbuild/linux-x64/bin/esbuild +0 -0
- package/node_modules/@esbuild/linux-x64/package.json +20 -0
- package/node_modules/acpx/LICENSE +21 -0
- package/node_modules/acpx/README.md +424 -0
- package/node_modules/acpx/dist/cli-CxsS1XQp.js +197 -0
- package/node_modules/acpx/dist/cli-CxsS1XQp.js.map +1 -0
- package/node_modules/acpx/dist/cli.d.ts +14 -0
- package/node_modules/acpx/dist/cli.d.ts.map +1 -0
- package/node_modules/acpx/dist/cli.js +2683 -0
- package/node_modules/acpx/dist/cli.js.map +1 -0
- package/node_modules/acpx/dist/client-DwNyFauq.d.ts +192 -0
- package/node_modules/acpx/dist/client-DwNyFauq.d.ts.map +1 -0
- package/node_modules/acpx/dist/flags-Bjq_XQ4-.js +260 -0
- package/node_modules/acpx/dist/flags-Bjq_XQ4-.js.map +1 -0
- package/node_modules/acpx/dist/flows-Db_LZKu9.js +1920 -0
- package/node_modules/acpx/dist/flows-Db_LZKu9.js.map +1 -0
- package/node_modules/acpx/dist/flows.d.ts +318 -0
- package/node_modules/acpx/dist/flows.d.ts.map +1 -0
- package/node_modules/acpx/dist/flows.js +2 -0
- package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js +6210 -0
- package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js.map +1 -0
- package/node_modules/acpx/dist/output-CjdF5rHk.js +4593 -0
- package/node_modules/acpx/dist/output-CjdF5rHk.js.map +1 -0
- package/node_modules/acpx/dist/runtime.d.ts +472 -0
- package/node_modules/acpx/dist/runtime.d.ts.map +1 -0
- package/node_modules/acpx/dist/runtime.js +1711 -0
- package/node_modules/acpx/dist/runtime.js.map +1 -0
- package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts +278 -0
- package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts.map +1 -0
- package/node_modules/acpx/package.json +129 -0
- package/node_modules/acpx/skills/acpx/SKILL.md +617 -0
- package/node_modules/b4a/LICENSE +201 -0
- package/node_modules/b4a/README.md +153 -0
- package/node_modules/b4a/browser.js +546 -0
- package/node_modules/b4a/index.d.ts +144 -0
- package/node_modules/b4a/index.js +193 -0
- package/node_modules/b4a/lib/ascii.js +31 -0
- package/node_modules/b4a/lib/base64.js +64 -0
- package/node_modules/b4a/lib/hex.js +51 -0
- package/node_modules/b4a/lib/latin1.js +31 -0
- package/node_modules/b4a/lib/utf16le.js +40 -0
- package/node_modules/b4a/lib/utf8.js +144 -0
- package/node_modules/b4a/package.json +54 -0
- package/node_modules/b4a/react-native.js +5 -0
- package/node_modules/b4a/runtime.d.ts +14 -0
- package/node_modules/bare-events/LICENSE +201 -0
- package/node_modules/bare-events/README.md +25 -0
- package/node_modules/bare-events/global.d.ts +15 -0
- package/node_modules/bare-events/global.js +5 -0
- package/node_modules/bare-events/index.d.ts +89 -0
- package/node_modules/bare-events/index.js +397 -0
- package/node_modules/bare-events/lib/errors.js +23 -0
- package/node_modules/bare-events/package.json +63 -0
- package/node_modules/bare-events/web.d.ts +79 -0
- package/node_modules/bare-events/web.js +417 -0
- package/node_modules/bare-fs/CMakeLists.txt +13 -0
- package/node_modules/bare-fs/LICENSE +201 -0
- package/node_modules/bare-fs/README.md +33 -0
- package/node_modules/bare-fs/binding.c +2913 -0
- package/node_modules/bare-fs/binding.js +1 -0
- package/node_modules/bare-fs/index.d.ts +1502 -0
- package/node_modules/bare-fs/index.js +2855 -0
- package/node_modules/bare-fs/lib/constants.d.ts +50 -0
- package/node_modules/bare-fs/lib/constants.js +49 -0
- package/node_modules/bare-fs/lib/errors.d.ts +13 -0
- package/node_modules/bare-fs/lib/errors.js +55 -0
- package/node_modules/bare-fs/package.json +74 -0
- package/node_modules/bare-fs/prebuilds/android-arm/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-ia32/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/android-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/darwin-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/darwin-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-arm64-simulator/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/ios-x64-simulator/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/linux-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/linux-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/win32-arm64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/prebuilds/win32-x64/bare-fs.bare +0 -0
- package/node_modules/bare-fs/promises.d.ts +480 -0
- package/node_modules/bare-fs/promises.js +119 -0
- package/node_modules/bare-path/CMakeLists.txt +13 -0
- package/node_modules/bare-path/LICENSE +201 -0
- package/node_modules/bare-path/NOTICE +28 -0
- package/node_modules/bare-path/README.md +19 -0
- package/node_modules/bare-path/binding.c +106 -0
- package/node_modules/bare-path/binding.js +1 -0
- package/node_modules/bare-path/index.d.ts +27 -0
- package/node_modules/bare-path/index.js +9 -0
- package/node_modules/bare-path/lib/constants.js +11 -0
- package/node_modules/bare-path/lib/posix.js +263 -0
- package/node_modules/bare-path/lib/shared.js +81 -0
- package/node_modules/bare-path/lib/win32.js +564 -0
- package/node_modules/bare-path/package.json +47 -0
- package/node_modules/bare-path/prebuilds/android-arm/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-ia32/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/android-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/darwin-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/darwin-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-arm64-simulator/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/ios-x64-simulator/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/linux-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/linux-x64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/win32-arm64/bare-path.bare +0 -0
- package/node_modules/bare-path/prebuilds/win32-x64/bare-path.bare +0 -0
- package/node_modules/bare-stream/LICENSE +201 -0
- package/node_modules/bare-stream/README.md +354 -0
- package/node_modules/bare-stream/global.d.ts +31 -0
- package/node_modules/bare-stream/global.js +15 -0
- package/node_modules/bare-stream/index.d.ts +235 -0
- package/node_modules/bare-stream/index.js +493 -0
- package/node_modules/bare-stream/package.json +75 -0
- package/node_modules/bare-stream/promises.js +3 -0
- package/node_modules/bare-stream/web.d.ts +152 -0
- package/node_modules/bare-stream/web.js +607 -0
- package/node_modules/bare-url/CMakeLists.txt +36 -0
- package/node_modules/bare-url/LICENSE +201 -0
- package/node_modules/bare-url/README.md +33 -0
- package/node_modules/bare-url/binding.c +422 -0
- package/node_modules/bare-url/binding.js +1 -0
- package/node_modules/bare-url/global.d.ts +24 -0
- package/node_modules/bare-url/global.js +2 -0
- package/node_modules/bare-url/index.d.ts +96 -0
- package/node_modules/bare-url/index.js +676 -0
- package/node_modules/bare-url/lib/errors.d.ts +10 -0
- package/node_modules/bare-url/lib/errors.js +33 -0
- package/node_modules/bare-url/lib/url-search-params.d.ts +66 -0
- package/node_modules/bare-url/lib/url-search-params.js +276 -0
- package/node_modules/bare-url/package.json +54 -0
- package/node_modules/bare-url/prebuilds/android-arm/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-ia32/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/android-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/darwin-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/darwin-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-arm64-simulator/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/ios-x64-simulator/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/linux-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/linux-x64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/win32-arm64/bare-url.bare +0 -0
- package/node_modules/bare-url/prebuilds/win32-x64/bare-url.bare +0 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1172 -0
- package/node_modules/commander/index.js +21 -0
- package/node_modules/commander/lib/argument.js +147 -0
- package/node_modules/commander/lib/command.js +2790 -0
- package/node_modules/commander/lib/error.js +36 -0
- package/node_modules/commander/lib/help.js +731 -0
- package/node_modules/commander/lib/option.js +377 -0
- package/node_modules/commander/lib/suggestSimilar.js +99 -0
- package/node_modules/commander/package-support.json +19 -0
- package/node_modules/commander/package.json +64 -0
- package/node_modules/commander/typings/index.d.ts +1113 -0
- package/node_modules/esbuild/LICENSE.md +21 -0
- package/node_modules/esbuild/README.md +3 -0
- package/node_modules/esbuild/bin/esbuild +223 -0
- package/node_modules/esbuild/install.js +300 -0
- package/node_modules/esbuild/lib/main.d.ts +720 -0
- package/node_modules/esbuild/lib/main.js +2536 -0
- package/node_modules/esbuild/package.json +74 -0
- package/node_modules/events-universal/LICENSE +201 -0
- package/node_modules/events-universal/README.md +17 -0
- package/node_modules/events-universal/bare.js +1 -0
- package/node_modules/events-universal/default.js +1 -0
- package/node_modules/events-universal/index.js +1 -0
- package/node_modules/events-universal/package.json +39 -0
- package/node_modules/events-universal/react-native.js +1 -0
- package/node_modules/fast-fifo/LICENSE +21 -0
- package/node_modules/fast-fifo/README.md +78 -0
- package/node_modules/fast-fifo/fixed-size.js +39 -0
- package/node_modules/fast-fifo/index.js +48 -0
- package/node_modules/fast-fifo/package.json +28 -0
- package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/index.js +111 -0
- package/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
- package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
- package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
- package/node_modules/fast-string-truncated-width/license +21 -0
- package/node_modules/fast-string-truncated-width/package.json +35 -0
- package/node_modules/fast-string-truncated-width/readme.md +59 -0
- package/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/node_modules/fast-string-width/dist/index.js +14 -0
- package/node_modules/fast-string-width/license +21 -0
- package/node_modules/fast-string-width/package.json +34 -0
- package/node_modules/fast-string-width/readme.md +42 -0
- package/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/node_modules/fast-wrap-ansi/README.md +26 -0
- package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/node_modules/fast-wrap-ansi/lib/main.js +218 -0
- package/node_modules/fast-wrap-ansi/package.json +51 -0
- package/node_modules/sisteransi/license +21 -0
- package/node_modules/sisteransi/package.json +34 -0
- package/node_modules/sisteransi/readme.md +113 -0
- package/node_modules/sisteransi/src/index.js +58 -0
- package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
- package/node_modules/skillflag/LICENSE +21 -0
- package/node_modules/skillflag/README.md +44 -0
- package/node_modules/skillflag/dist/bin/skill-install.d.ts +3 -0
- package/node_modules/skillflag/dist/bin/skill-install.d.ts.map +1 -0
- package/node_modules/skillflag/dist/bin/skill-install.js +6 -0
- package/node_modules/skillflag/dist/bin/skill-install.js.map +1 -0
- package/node_modules/skillflag/dist/bin/skillflag.d.ts +3 -0
- package/node_modules/skillflag/dist/bin/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/bin/skillflag.js +25 -0
- package/node_modules/skillflag/dist/bin/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/core/digest.d.ts +2 -0
- package/node_modules/skillflag/dist/core/digest.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/digest.js +15 -0
- package/node_modules/skillflag/dist/core/digest.js.map +1 -0
- package/node_modules/skillflag/dist/core/errors.d.ts +6 -0
- package/node_modules/skillflag/dist/core/errors.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/errors.js +13 -0
- package/node_modules/skillflag/dist/core/errors.js.map +1 -0
- package/node_modules/skillflag/dist/core/export.d.ts +2 -0
- package/node_modules/skillflag/dist/core/export.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/export.js +28 -0
- package/node_modules/skillflag/dist/core/export.js.map +1 -0
- package/node_modules/skillflag/dist/core/list.d.ts +21 -0
- package/node_modules/skillflag/dist/core/list.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/list.js +71 -0
- package/node_modules/skillflag/dist/core/list.js.map +1 -0
- package/node_modules/skillflag/dist/core/paths.d.ts +15 -0
- package/node_modules/skillflag/dist/core/paths.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/paths.js +149 -0
- package/node_modules/skillflag/dist/core/paths.js.map +1 -0
- package/node_modules/skillflag/dist/core/show.d.ts +2 -0
- package/node_modules/skillflag/dist/core/show.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/show.js +8 -0
- package/node_modules/skillflag/dist/core/show.js.map +1 -0
- package/node_modules/skillflag/dist/core/skillflag.d.ts +3 -0
- package/node_modules/skillflag/dist/core/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/skillflag.js +2 -0
- package/node_modules/skillflag/dist/core/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/core/tar.d.ts +16 -0
- package/node_modules/skillflag/dist/core/tar.d.ts.map +1 -0
- package/node_modules/skillflag/dist/core/tar.js +140 -0
- package/node_modules/skillflag/dist/core/tar.js.map +1 -0
- package/node_modules/skillflag/dist/index.d.ts +5 -0
- package/node_modules/skillflag/dist/index.d.ts.map +1 -0
- package/node_modules/skillflag/dist/index.js +3 -0
- package/node_modules/skillflag/dist/index.js.map +1 -0
- package/node_modules/skillflag/dist/install/cli.d.ts +57 -0
- package/node_modules/skillflag/dist/install/cli.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/cli.js +653 -0
- package/node_modules/skillflag/dist/install/cli.js.map +1 -0
- package/node_modules/skillflag/dist/install/copy.d.ts +2 -0
- package/node_modules/skillflag/dist/install/copy.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/copy.js +19 -0
- package/node_modules/skillflag/dist/install/copy.js.map +1 -0
- package/node_modules/skillflag/dist/install/errors.d.ts +6 -0
- package/node_modules/skillflag/dist/install/errors.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/errors.js +13 -0
- package/node_modules/skillflag/dist/install/errors.js.map +1 -0
- package/node_modules/skillflag/dist/install/extract.d.ts +3 -0
- package/node_modules/skillflag/dist/install/extract.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/extract.js +76 -0
- package/node_modules/skillflag/dist/install/extract.js.map +1 -0
- package/node_modules/skillflag/dist/install/install.d.ts +21 -0
- package/node_modules/skillflag/dist/install/install.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/install.js +35 -0
- package/node_modules/skillflag/dist/install/install.js.map +1 -0
- package/node_modules/skillflag/dist/install/resolve.d.ts +12 -0
- package/node_modules/skillflag/dist/install/resolve.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/resolve.js +119 -0
- package/node_modules/skillflag/dist/install/resolve.js.map +1 -0
- package/node_modules/skillflag/dist/install/validate.d.ts +7 -0
- package/node_modules/skillflag/dist/install/validate.d.ts.map +1 -0
- package/node_modules/skillflag/dist/install/validate.js +28 -0
- package/node_modules/skillflag/dist/install/validate.js.map +1 -0
- package/node_modules/skillflag/dist/shared/frontmatter.d.ts +2 -0
- package/node_modules/skillflag/dist/shared/frontmatter.d.ts.map +1 -0
- package/node_modules/skillflag/dist/shared/frontmatter.js +28 -0
- package/node_modules/skillflag/dist/shared/frontmatter.js.map +1 -0
- package/node_modules/skillflag/dist/skillflag.d.ts +29 -0
- package/node_modules/skillflag/dist/skillflag.d.ts.map +1 -0
- package/node_modules/skillflag/dist/skillflag.js +230 -0
- package/node_modules/skillflag/dist/skillflag.js.map +1 -0
- package/node_modules/skillflag/dist/utils/collections.d.ts +3 -0
- package/node_modules/skillflag/dist/utils/collections.d.ts.map +1 -0
- package/node_modules/skillflag/dist/utils/collections.js +18 -0
- package/node_modules/skillflag/dist/utils/collections.js.map +1 -0
- package/node_modules/skillflag/package.json +78 -0
- package/node_modules/skillflag/skills/skillflag/SKILL.md +144 -0
- package/node_modules/streamx/LICENSE +21 -0
- package/node_modules/streamx/README.md +550 -0
- package/node_modules/streamx/index.d.ts +198 -0
- package/node_modules/streamx/index.js +1314 -0
- package/node_modules/streamx/lib/errors.d.ts +13 -0
- package/node_modules/streamx/lib/errors.js +47 -0
- package/node_modules/streamx/package.json +51 -0
- package/node_modules/streamx/runtime.d.ts +55 -0
- package/node_modules/tar-stream/LICENSE +21 -0
- package/node_modules/tar-stream/README.md +183 -0
- package/node_modules/tar-stream/constants.js +14 -0
- package/node_modules/tar-stream/extract.js +418 -0
- package/node_modules/tar-stream/headers.js +322 -0
- package/node_modules/tar-stream/index.d.ts +60 -0
- package/node_modules/tar-stream/index.js +2 -0
- package/node_modules/tar-stream/pack.js +287 -0
- package/node_modules/tar-stream/package.json +50 -0
- package/node_modules/teex/LICENSE +21 -0
- package/node_modules/teex/README.md +31 -0
- package/node_modules/teex/example.js +16 -0
- package/node_modules/teex/index.js +52 -0
- package/node_modules/teex/package.json +26 -0
- package/node_modules/teex/test.js +68 -0
- package/node_modules/text-decoder/LICENSE +201 -0
- package/node_modules/text-decoder/README.md +36 -0
- package/node_modules/text-decoder/index.js +64 -0
- package/node_modules/text-decoder/lib/pass-through-decoder.js +19 -0
- package/node_modules/text-decoder/lib/utf8-decoder.js +185 -0
- package/node_modules/text-decoder/package.json +39 -0
- package/node_modules/tsx/LICENSE +21 -0
- package/node_modules/tsx/README.md +32 -0
- package/node_modules/tsx/dist/cjs/api/index.cjs +2 -0
- package/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
- package/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
- package/node_modules/tsx/dist/cjs/api/index.mjs +2 -0
- package/node_modules/tsx/dist/cjs/index.cjs +1 -0
- package/node_modules/tsx/dist/cli.mjs +55 -0
- package/node_modules/tsx/dist/client-XItNFmsq.cjs +1 -0
- package/node_modules/tsx/dist/esm/api/index.cjs +2 -0
- package/node_modules/tsx/dist/esm/api/index.d.cts +37 -0
- package/node_modules/tsx/dist/esm/api/index.d.mts +37 -0
- package/node_modules/tsx/dist/esm/api/index.mjs +2 -0
- package/node_modules/tsx/dist/esm/index.mjs +2 -0
- package/node_modules/tsx/dist/index-Bqjv9TxC.mjs +1 -0
- package/node_modules/tsx/dist/index-DE3OBZuV.mjs +14 -0
- package/node_modules/tsx/dist/lexer-CGfpMDSb.cjs +3 -0
- package/node_modules/tsx/dist/lexer-CanA6ArK.mjs +3 -0
- package/node_modules/tsx/dist/loader.mjs +2 -0
- package/node_modules/tsx/dist/package-Dj0mHsMt.mjs +1 -0
- package/node_modules/tsx/dist/patch-repl.cjs +1 -0
- package/node_modules/tsx/dist/preflight.cjs +1 -0
- package/node_modules/tsx/dist/register-B1c7OH6V.cjs +23 -0
- package/node_modules/tsx/dist/register-nyXW-TH3.mjs +2 -0
- package/node_modules/tsx/dist/repl.mjs +4 -0
- package/node_modules/tsx/dist/require-CBjy4Foe.mjs +10 -0
- package/node_modules/tsx/dist/require-CVaYn9z3.cjs +1 -0
- package/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
- package/node_modules/tsx/dist/temporary-directory-Du7LpLp9.mjs +1 -0
- package/node_modules/tsx/package.json +70 -0
- package/node_modules/zod/LICENSE +21 -0
- package/node_modules/zod/README.md +218 -0
- package/node_modules/zod/compile.cjs +96 -0
- package/node_modules/zod/compile.d.cts +1 -0
- package/node_modules/zod/compile.d.ts +1 -0
- package/node_modules/zod/compile.js +52 -0
- package/node_modules/zod/index.cjs +52 -0
- package/node_modules/zod/index.d.cts +3 -0
- package/node_modules/zod/index.d.ts +3 -0
- package/node_modules/zod/index.js +7 -0
- package/node_modules/zod/locales/index.cjs +36 -0
- package/node_modules/zod/locales/index.d.cts +1 -0
- package/node_modules/zod/locales/index.d.ts +1 -0
- package/node_modules/zod/locales/index.js +1 -0
- package/node_modules/zod/locales/package.json +7 -0
- package/node_modules/zod/mini/index.cjs +51 -0
- package/node_modules/zod/mini/index.d.cts +3 -0
- package/node_modules/zod/mini/index.d.ts +3 -0
- package/node_modules/zod/mini/index.js +3 -0
- package/node_modules/zod/mini/package.json +7 -0
- package/node_modules/zod/package.json +147 -0
- package/node_modules/zod/src/compile.ts +56 -0
- package/node_modules/zod/src/index.ts +7 -0
- package/node_modules/zod/src/locales/index.ts +1 -0
- package/node_modules/zod/src/mini/index.ts +3 -0
- package/node_modules/zod/src/v3/ZodError.ts +340 -0
- package/node_modules/zod/src/v3/benchmarks/datetime.ts +58 -0
- package/node_modules/zod/src/v3/benchmarks/discriminatedUnion.ts +80 -0
- package/node_modules/zod/src/v3/benchmarks/index.ts +59 -0
- package/node_modules/zod/src/v3/benchmarks/ipv4.ts +57 -0
- package/node_modules/zod/src/v3/benchmarks/object.ts +69 -0
- package/node_modules/zod/src/v3/benchmarks/primitives.ts +162 -0
- package/node_modules/zod/src/v3/benchmarks/realworld.ts +63 -0
- package/node_modules/zod/src/v3/benchmarks/string.ts +55 -0
- package/node_modules/zod/src/v3/benchmarks/union.ts +80 -0
- package/node_modules/zod/src/v3/errors.ts +13 -0
- package/node_modules/zod/src/v3/external.ts +6 -0
- package/node_modules/zod/src/v3/helpers/enumUtil.ts +17 -0
- package/node_modules/zod/src/v3/helpers/errorUtil.ts +8 -0
- package/node_modules/zod/src/v3/helpers/parseUtil.ts +176 -0
- package/node_modules/zod/src/v3/helpers/partialUtil.ts +34 -0
- package/node_modules/zod/src/v3/helpers/typeAliases.ts +2 -0
- package/node_modules/zod/src/v3/helpers/util.ts +224 -0
- package/node_modules/zod/src/v3/index.ts +4 -0
- package/node_modules/zod/src/v3/locales/en.ts +124 -0
- package/node_modules/zod/src/v3/standard-schema.ts +113 -0
- package/node_modules/zod/src/v3/tests/Mocker.ts +54 -0
- package/node_modules/zod/src/v3/tests/all-errors.test.ts +157 -0
- package/node_modules/zod/src/v3/tests/anyunknown.test.ts +28 -0
- package/node_modules/zod/src/v3/tests/array.test.ts +71 -0
- package/node_modules/zod/src/v3/tests/async-parsing.test.ts +388 -0
- package/node_modules/zod/src/v3/tests/async-refinements.test.ts +46 -0
- package/node_modules/zod/src/v3/tests/base.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/bigint.test.ts +55 -0
- package/node_modules/zod/src/v3/tests/branded.test.ts +53 -0
- package/node_modules/zod/src/v3/tests/catch.test.ts +220 -0
- package/node_modules/zod/src/v3/tests/coerce.test.ts +133 -0
- package/node_modules/zod/src/v3/tests/complex.test.ts +70 -0
- package/node_modules/zod/src/v3/tests/custom.test.ts +31 -0
- package/node_modules/zod/src/v3/tests/date.test.ts +32 -0
- package/node_modules/zod/src/v3/tests/deepmasking.test.ts +186 -0
- package/node_modules/zod/src/v3/tests/default.test.ts +112 -0
- package/node_modules/zod/src/v3/tests/description.test.ts +33 -0
- package/node_modules/zod/src/v3/tests/discriminated-unions.test.ts +315 -0
- package/node_modules/zod/src/v3/tests/enum.test.ts +80 -0
- package/node_modules/zod/src/v3/tests/error.test.ts +586 -0
- package/node_modules/zod/src/v3/tests/firstparty.test.ts +87 -0
- package/node_modules/zod/src/v3/tests/firstpartyschematypes.test.ts +21 -0
- package/node_modules/zod/src/v3/tests/function.test.ts +261 -0
- package/node_modules/zod/src/v3/tests/generics.test.ts +48 -0
- package/node_modules/zod/src/v3/tests/instanceof.test.ts +37 -0
- package/node_modules/zod/src/v3/tests/intersection.test.ts +128 -0
- package/node_modules/zod/src/v3/tests/language-server.source.ts +76 -0
- package/node_modules/zod/src/v3/tests/language-server.test.ts +206 -0
- package/node_modules/zod/src/v3/tests/literal.test.ts +36 -0
- package/node_modules/zod/src/v3/tests/map.test.ts +110 -0
- package/node_modules/zod/src/v3/tests/masking.test.ts +4 -0
- package/node_modules/zod/src/v3/tests/mocker.test.ts +19 -0
- package/node_modules/zod/src/v3/tests/nan.test.ts +24 -0
- package/node_modules/zod/src/v3/tests/nativeEnum.test.ts +87 -0
- package/node_modules/zod/src/v3/tests/nullable.test.ts +42 -0
- package/node_modules/zod/src/v3/tests/number.test.ts +176 -0
- package/node_modules/zod/src/v3/tests/object-augmentation.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/object-in-es5-env.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/object.test.ts +431 -0
- package/node_modules/zod/src/v3/tests/optional.test.ts +42 -0
- package/node_modules/zod/src/v3/tests/parseUtil.test.ts +23 -0
- package/node_modules/zod/src/v3/tests/parser.test.ts +41 -0
- package/node_modules/zod/src/v3/tests/partials.test.ts +243 -0
- package/node_modules/zod/src/v3/tests/pickomit.test.ts +111 -0
- package/node_modules/zod/src/v3/tests/pipeline.test.ts +29 -0
- package/node_modules/zod/src/v3/tests/preprocess.test.ts +186 -0
- package/node_modules/zod/src/v3/tests/primitive.test.ts +440 -0
- package/node_modules/zod/src/v3/tests/promise.test.ts +90 -0
- package/node_modules/zod/src/v3/tests/readonly.test.ts +114 -0
- package/node_modules/zod/src/v3/tests/record.test.ts +171 -0
- package/node_modules/zod/src/v3/tests/recursive.test.ts +197 -0
- package/node_modules/zod/src/v3/tests/refine.test.ts +313 -0
- package/node_modules/zod/src/v3/tests/safeparse.test.ts +27 -0
- package/node_modules/zod/src/v3/tests/set.test.ts +142 -0
- package/node_modules/zod/src/v3/tests/standard-schema.test.ts +83 -0
- package/node_modules/zod/src/v3/tests/string.test.ts +922 -0
- package/node_modules/zod/src/v3/tests/transformer.test.ts +233 -0
- package/node_modules/zod/src/v3/tests/tuple.test.ts +90 -0
- package/node_modules/zod/src/v3/tests/unions.test.ts +57 -0
- package/node_modules/zod/src/v3/tests/validations.test.ts +133 -0
- package/node_modules/zod/src/v3/tests/void.test.ts +15 -0
- package/node_modules/zod/src/v3/types.ts +5138 -0
- package/node_modules/zod/src/v4/classic/checks.ts +33 -0
- package/node_modules/zod/src/v4/classic/coerce.ts +27 -0
- package/node_modules/zod/src/v4/classic/compat.ts +78 -0
- package/node_modules/zod/src/v4/classic/deep-partial.ts +76 -0
- package/node_modules/zod/src/v4/classic/errors.ts +97 -0
- package/node_modules/zod/src/v4/classic/external.ts +57 -0
- package/node_modules/zod/src/v4/classic/from-json-schema.ts +944 -0
- package/node_modules/zod/src/v4/classic/in-out.ts +45 -0
- package/node_modules/zod/src/v4/classic/index.ts +5 -0
- package/node_modules/zod/src/v4/classic/iso.ts +20 -0
- package/node_modules/zod/src/v4/classic/parse.ts +88 -0
- package/node_modules/zod/src/v4/classic/schemas.ts +2937 -0
- package/node_modules/zod/src/v4/classic/tests/anyunknown.test.ts +26 -0
- package/node_modules/zod/src/v4/classic/tests/apply.test.ts +86 -0
- package/node_modules/zod/src/v4/classic/tests/array.test.ts +264 -0
- package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +402 -0
- package/node_modules/zod/src/v4/classic/tests/async-parsing.test.ts +381 -0
- package/node_modules/zod/src/v4/classic/tests/async-refinements.test.ts +68 -0
- package/node_modules/zod/src/v4/classic/tests/base.test.ts +7 -0
- package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +89 -0
- package/node_modules/zod/src/v4/classic/tests/brand.test.ts +106 -0
- package/node_modules/zod/src/v4/classic/tests/catch.test.ts +604 -0
- package/node_modules/zod/src/v4/classic/tests/coalesce.test.ts +20 -0
- package/node_modules/zod/src/v4/classic/tests/codec-examples.test.ts +572 -0
- package/node_modules/zod/src/v4/classic/tests/codec.test.ts +704 -0
- package/node_modules/zod/src/v4/classic/tests/coerce.test.ts +160 -0
- package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +374 -0
- package/node_modules/zod/src/v4/classic/tests/credit-card.test.ts +76 -0
- package/node_modules/zod/src/v4/classic/tests/custom.test.ts +40 -0
- package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +985 -0
- package/node_modules/zod/src/v4/classic/tests/date.test.ts +92 -0
- package/node_modules/zod/src/v4/classic/tests/datetime.test.ts +316 -0
- package/node_modules/zod/src/v4/classic/tests/deep-partial.test.ts +380 -0
- package/node_modules/zod/src/v4/classic/tests/default-locale.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/default.test.ts +455 -0
- package/node_modules/zod/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
- package/node_modules/zod/src/v4/classic/tests/description.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/detached-methods.test.ts +221 -0
- package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +942 -0
- package/node_modules/zod/src/v4/classic/tests/enum.test.ts +305 -0
- package/node_modules/zod/src/v4/classic/tests/error-utils.test.ts +917 -0
- package/node_modules/zod/src/v4/classic/tests/error.test.ts +1108 -0
- package/node_modules/zod/src/v4/classic/tests/file.test.ts +96 -0
- package/node_modules/zod/src/v4/classic/tests/firstparty.test.ts +179 -0
- package/node_modules/zod/src/v4/classic/tests/fix-json-issue.test.ts +26 -0
- package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +1467 -0
- package/node_modules/zod/src/v4/classic/tests/function.test.ts +391 -0
- package/node_modules/zod/src/v4/classic/tests/generics.test.ts +72 -0
- package/node_modules/zod/src/v4/classic/tests/global-config.test.ts +32 -0
- package/node_modules/zod/src/v4/classic/tests/hash.test.ts +67 -0
- package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
- package/node_modules/zod/src/v4/classic/tests/in-out.test.ts +159 -0
- package/node_modules/zod/src/v4/classic/tests/index.test.ts +958 -0
- package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +319 -0
- package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +127 -0
- package/node_modules/zod/src/v4/classic/tests/intersection.test.ts +334 -0
- package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +215 -0
- package/node_modules/zod/src/v4/classic/tests/jitless-allows-eval.test.ts +39 -0
- package/node_modules/zod/src/v4/classic/tests/json.test.ts +109 -0
- package/node_modules/zod/src/v4/classic/tests/lazy.test.ts +262 -0
- package/node_modules/zod/src/v4/classic/tests/literal.test.ts +128 -0
- package/node_modules/zod/src/v4/classic/tests/locales_ka.test.ts +29 -0
- package/node_modules/zod/src/v4/classic/tests/locales_ro.test.ts +24 -0
- package/node_modules/zod/src/v4/classic/tests/map.test.ts +342 -0
- package/node_modules/zod/src/v4/classic/tests/nan.test.ts +21 -0
- package/node_modules/zod/src/v4/classic/tests/nested-refine.test.ts +168 -0
- package/node_modules/zod/src/v4/classic/tests/nonoptional.test.ts +101 -0
- package/node_modules/zod/src/v4/classic/tests/nullable.test.ts +22 -0
- package/node_modules/zod/src/v4/classic/tests/number.test.ts +401 -0
- package/node_modules/zod/src/v4/classic/tests/object.test.ts +1006 -0
- package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +235 -0
- package/node_modules/zod/src/v4/classic/tests/optional.test.ts +366 -0
- package/node_modules/zod/src/v4/classic/tests/partial.test.ts +497 -0
- package/node_modules/zod/src/v4/classic/tests/pickomit.test.ts +211 -0
- package/node_modules/zod/src/v4/classic/tests/pipe.test.ts +101 -0
- package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +144 -0
- package/node_modules/zod/src/v4/classic/tests/preprocess-types.test.ts +44 -0
- package/node_modules/zod/src/v4/classic/tests/preprocess.test.ts +351 -0
- package/node_modules/zod/src/v4/classic/tests/primitive.test.ts +175 -0
- package/node_modules/zod/src/v4/classic/tests/promise.test.ts +81 -0
- package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
- package/node_modules/zod/src/v4/classic/tests/prototypes.test.ts +23 -0
- package/node_modules/zod/src/v4/classic/tests/readonly.test.ts +184 -0
- package/node_modules/zod/src/v4/classic/tests/record.test.ts +885 -0
- package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +707 -0
- package/node_modules/zod/src/v4/classic/tests/redos.test.ts +68 -0
- package/node_modules/zod/src/v4/classic/tests/refine.test.ts +693 -0
- package/node_modules/zod/src/v4/classic/tests/registries.test.ts +243 -0
- package/node_modules/zod/src/v4/classic/tests/set.test.ts +190 -0
- package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +153 -0
- package/node_modules/zod/src/v4/classic/tests/string-formats.test.ts +125 -0
- package/node_modules/zod/src/v4/classic/tests/string.test.ts +1440 -0
- package/node_modules/zod/src/v4/classic/tests/stringbool.test.ts +124 -0
- package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +812 -0
- package/node_modules/zod/src/v4/classic/tests/to-json-schema-methods.test.ts +438 -0
- package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +4733 -0
- package/node_modules/zod/src/v4/classic/tests/transform.test.ts +378 -0
- package/node_modules/zod/src/v4/classic/tests/tuple.test.ts +511 -0
- package/node_modules/zod/src/v4/classic/tests/union.test.ts +310 -0
- package/node_modules/zod/src/v4/classic/tests/url.test.ts +13 -0
- package/node_modules/zod/src/v4/classic/tests/validate.test.ts +645 -0
- package/node_modules/zod/src/v4/classic/tests/validations.test.ts +283 -0
- package/node_modules/zod/src/v4/classic/tests/void.test.ts +12 -0
- package/node_modules/zod/src/v4/core/api.ts +1875 -0
- package/node_modules/zod/src/v4/core/checks.ts +1266 -0
- package/node_modules/zod/src/v4/core/compile.ts +2313 -0
- package/node_modules/zod/src/v4/core/core.ts +230 -0
- package/node_modules/zod/src/v4/core/doc.ts +52 -0
- package/node_modules/zod/src/v4/core/errors.ts +543 -0
- package/node_modules/zod/src/v4/core/index.ts +19 -0
- package/node_modules/zod/src/v4/core/json-schema-generator.ts +131 -0
- package/node_modules/zod/src/v4/core/json-schema-processors.ts +934 -0
- package/node_modules/zod/src/v4/core/json-schema.ts +148 -0
- package/node_modules/zod/src/v4/core/memoizer.ts +331 -0
- package/node_modules/zod/src/v4/core/parse.ts +309 -0
- package/node_modules/zod/src/v4/core/regexes.ts +217 -0
- package/node_modules/zod/src/v4/core/registries.ts +105 -0
- package/node_modules/zod/src/v4/core/schemas.ts +5192 -0
- package/node_modules/zod/src/v4/core/standard-schema.ts +159 -0
- package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
- package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +1004 -0
- package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
- package/node_modules/zod/src/v4/core/tests/compile-global.test.ts +89 -0
- package/node_modules/zod/src/v4/core/tests/compile-off-by-default.test.ts +26 -0
- package/node_modules/zod/src/v4/core/tests/compile-stats.test.ts +34 -0
- package/node_modules/zod/src/v4/core/tests/compile.test.ts +2036 -0
- package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
- package/node_modules/zod/src/v4/core/tests/extend.test.ts +59 -0
- package/node_modules/zod/src/v4/core/tests/index.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/locales/be.test.ts +124 -0
- package/node_modules/zod/src/v4/core/tests/locales/el.test.ts +214 -0
- package/node_modules/zod/src/v4/core/tests/locales/en.test.ts +22 -0
- package/node_modules/zod/src/v4/core/tests/locales/es.test.ts +180 -0
- package/node_modules/zod/src/v4/core/tests/locales/fr.test.ts +72 -0
- package/node_modules/zod/src/v4/core/tests/locales/he.test.ts +379 -0
- package/node_modules/zod/src/v4/core/tests/locales/hr.test.ts +163 -0
- package/node_modules/zod/src/v4/core/tests/locales/nl.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/locales/parity.test.ts +42 -0
- package/node_modules/zod/src/v4/core/tests/locales/ru.test.ts +128 -0
- package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
- package/node_modules/zod/src/v4/core/tests/locales/tk.test.ts +69 -0
- package/node_modules/zod/src/v4/core/tests/locales/tr.test.ts +69 -0
- package/node_modules/zod/src/v4/core/tests/locales/uz.test.ts +105 -0
- package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/record-constructor.test.ts +125 -0
- package/node_modules/zod/src/v4/core/tests/recursive-tuples.test.ts +45 -0
- package/node_modules/zod/src/v4/core/tests/treeshake.test.ts +51 -0
- package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +133 -0
- package/node_modules/zod/src/v4/core/to-json-schema.ts +857 -0
- package/node_modules/zod/src/v4/core/util.ts +1280 -0
- package/node_modules/zod/src/v4/core/versions.ts +5 -0
- package/node_modules/zod/src/v4/core/visit.ts +193 -0
- package/node_modules/zod/src/v4/core/zsf.ts +323 -0
- package/node_modules/zod/src/v4/index.ts +4 -0
- package/node_modules/zod/src/v4/locales/ar.ts +120 -0
- package/node_modules/zod/src/v4/locales/az.ts +116 -0
- package/node_modules/zod/src/v4/locales/be.ts +188 -0
- package/node_modules/zod/src/v4/locales/bg.ts +133 -0
- package/node_modules/zod/src/v4/locales/bn.ts +123 -0
- package/node_modules/zod/src/v4/locales/ca.ts +121 -0
- package/node_modules/zod/src/v4/locales/ckb.ts +140 -0
- package/node_modules/zod/src/v4/locales/cs.ts +123 -0
- package/node_modules/zod/src/v4/locales/da.ts +128 -0
- package/node_modules/zod/src/v4/locales/de.ts +121 -0
- package/node_modules/zod/src/v4/locales/el.ts +124 -0
- package/node_modules/zod/src/v4/locales/en.ts +136 -0
- package/node_modules/zod/src/v4/locales/eo.ts +123 -0
- package/node_modules/zod/src/v4/locales/es.ts +145 -0
- package/node_modules/zod/src/v4/locales/fa.ts +131 -0
- package/node_modules/zod/src/v4/locales/fi.ts +126 -0
- package/node_modules/zod/src/v4/locales/fr-CA.ts +121 -0
- package/node_modules/zod/src/v4/locales/fr.ts +139 -0
- package/node_modules/zod/src/v4/locales/gu.ts +123 -0
- package/node_modules/zod/src/v4/locales/he.ts +253 -0
- package/node_modules/zod/src/v4/locales/hi.ts +126 -0
- package/node_modules/zod/src/v4/locales/hr.ts +135 -0
- package/node_modules/zod/src/v4/locales/hu.ts +122 -0
- package/node_modules/zod/src/v4/locales/hy.ts +175 -0
- package/node_modules/zod/src/v4/locales/id.ts +120 -0
- package/node_modules/zod/src/v4/locales/index.ts +63 -0
- package/node_modules/zod/src/v4/locales/is.ts +124 -0
- package/node_modules/zod/src/v4/locales/it.ts +121 -0
- package/node_modules/zod/src/v4/locales/ja.ts +119 -0
- package/node_modules/zod/src/v4/locales/ka.ts +128 -0
- package/node_modules/zod/src/v4/locales/kh.ts +7 -0
- package/node_modules/zod/src/v4/locales/km.ts +124 -0
- package/node_modules/zod/src/v4/locales/kn.ts +126 -0
- package/node_modules/zod/src/v4/locales/ko.ts +126 -0
- package/node_modules/zod/src/v4/locales/lt.ts +243 -0
- package/node_modules/zod/src/v4/locales/mk.ts +123 -0
- package/node_modules/zod/src/v4/locales/ms.ts +120 -0
- package/node_modules/zod/src/v4/locales/ne.ts +128 -0
- package/node_modules/zod/src/v4/locales/nl.ts +126 -0
- package/node_modules/zod/src/v4/locales/nn.ts +121 -0
- package/node_modules/zod/src/v4/locales/no.ts +121 -0
- package/node_modules/zod/src/v4/locales/ota.ts +122 -0
- package/node_modules/zod/src/v4/locales/pl.ts +123 -0
- package/node_modules/zod/src/v4/locales/ps.ts +131 -0
- package/node_modules/zod/src/v4/locales/pt-BR.ts +161 -0
- package/node_modules/zod/src/v4/locales/pt.ts +159 -0
- package/node_modules/zod/src/v4/locales/ro.ts +132 -0
- package/node_modules/zod/src/v4/locales/ru.ts +188 -0
- package/node_modules/zod/src/v4/locales/sk.ts +123 -0
- package/node_modules/zod/src/v4/locales/sl.ts +123 -0
- package/node_modules/zod/src/v4/locales/sv.ts +124 -0
- package/node_modules/zod/src/v4/locales/ta.ts +123 -0
- package/node_modules/zod/src/v4/locales/tg.ts +135 -0
- package/node_modules/zod/src/v4/locales/th.ts +124 -0
- package/node_modules/zod/src/v4/locales/tk.ts +113 -0
- package/node_modules/zod/src/v4/locales/tr.ts +116 -0
- package/node_modules/zod/src/v4/locales/ua.ts +7 -0
- package/node_modules/zod/src/v4/locales/uk.ts +122 -0
- package/node_modules/zod/src/v4/locales/ur.ts +124 -0
- package/node_modules/zod/src/v4/locales/uz.ts +120 -0
- package/node_modules/zod/src/v4/locales/vi.ts +122 -0
- package/node_modules/zod/src/v4/locales/yo.ts +129 -0
- package/node_modules/zod/src/v4/locales/zh-CN.ts +121 -0
- package/node_modules/zod/src/v4/locales/zh-TW.ts +120 -0
- package/node_modules/zod/src/v4/mini/checks.ts +33 -0
- package/node_modules/zod/src/v4/mini/coerce.ts +27 -0
- package/node_modules/zod/src/v4/mini/deep-partial.ts +73 -0
- package/node_modules/zod/src/v4/mini/external.ts +52 -0
- package/node_modules/zod/src/v4/mini/in-out.ts +45 -0
- package/node_modules/zod/src/v4/mini/index.ts +3 -0
- package/node_modules/zod/src/v4/mini/iso.ts +66 -0
- package/node_modules/zod/src/v4/mini/parse.ts +16 -0
- package/node_modules/zod/src/v4/mini/schemas.ts +2051 -0
- package/node_modules/zod/src/v4/mini/tests/apply.test.ts +57 -0
- package/node_modules/zod/src/v4/mini/tests/assignability.test.ts +190 -0
- package/node_modules/zod/src/v4/mini/tests/brand.test.ts +94 -0
- package/node_modules/zod/src/v4/mini/tests/checks.test.ts +158 -0
- package/node_modules/zod/src/v4/mini/tests/codec.test.ts +582 -0
- package/node_modules/zod/src/v4/mini/tests/computed.test.ts +30 -0
- package/node_modules/zod/src/v4/mini/tests/cyclic-data.test.ts +84 -0
- package/node_modules/zod/src/v4/mini/tests/deep-partial.test.ts +54 -0
- package/node_modules/zod/src/v4/mini/tests/error.test.ts +41 -0
- package/node_modules/zod/src/v4/mini/tests/functions.test.ts +14 -0
- package/node_modules/zod/src/v4/mini/tests/index.test.ts +1146 -0
- package/node_modules/zod/src/v4/mini/tests/number.test.ts +109 -0
- package/node_modules/zod/src/v4/mini/tests/object.test.ts +292 -0
- package/node_modules/zod/src/v4/mini/tests/prototypes.test.ts +43 -0
- package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +341 -0
- package/node_modules/zod/src/v4/mini/tests/standard-schema.test.ts +50 -0
- package/node_modules/zod/src/v4/mini/tests/string.test.ts +396 -0
- package/node_modules/zod/src/v4-mini/index.ts +3 -0
- package/node_modules/zod/v3/ZodError.cjs +167 -0
- package/node_modules/zod/v3/ZodError.d.cts +164 -0
- package/node_modules/zod/v3/ZodError.d.ts +164 -0
- package/node_modules/zod/v3/ZodError.js +143 -0
- package/node_modules/zod/v3/errors.cjs +36 -0
- package/node_modules/zod/v3/errors.d.cts +5 -0
- package/node_modules/zod/v3/errors.d.ts +5 -0
- package/node_modules/zod/v3/errors.js +9 -0
- package/node_modules/zod/v3/external.cjs +41 -0
- package/node_modules/zod/v3/external.d.cts +6 -0
- package/node_modules/zod/v3/external.d.ts +6 -0
- package/node_modules/zod/v3/external.js +6 -0
- package/node_modules/zod/v3/helpers/enumUtil.cjs +21 -0
- package/node_modules/zod/v3/helpers/enumUtil.d.cts +8 -0
- package/node_modules/zod/v3/helpers/enumUtil.d.ts +8 -0
- package/node_modules/zod/v3/helpers/enumUtil.js +1 -0
- package/node_modules/zod/v3/helpers/errorUtil.cjs +28 -0
- package/node_modules/zod/v3/helpers/errorUtil.d.cts +9 -0
- package/node_modules/zod/v3/helpers/errorUtil.d.ts +9 -0
- package/node_modules/zod/v3/helpers/errorUtil.js +6 -0
- package/node_modules/zod/v3/helpers/parseUtil.cjs +143 -0
- package/node_modules/zod/v3/helpers/parseUtil.d.cts +78 -0
- package/node_modules/zod/v3/helpers/parseUtil.d.ts +78 -0
- package/node_modules/zod/v3/helpers/parseUtil.js +109 -0
- package/node_modules/zod/v3/helpers/partialUtil.cjs +21 -0
- package/node_modules/zod/v3/helpers/partialUtil.d.cts +8 -0
- package/node_modules/zod/v3/helpers/partialUtil.d.ts +8 -0
- package/node_modules/zod/v3/helpers/partialUtil.js +1 -0
- package/node_modules/zod/v3/helpers/typeAliases.cjs +21 -0
- package/node_modules/zod/v3/helpers/typeAliases.d.cts +2 -0
- package/node_modules/zod/v3/helpers/typeAliases.d.ts +2 -0
- package/node_modules/zod/v3/helpers/typeAliases.js +1 -0
- package/node_modules/zod/v3/helpers/util.cjs +156 -0
- package/node_modules/zod/v3/helpers/util.d.cts +85 -0
- package/node_modules/zod/v3/helpers/util.d.ts +85 -0
- package/node_modules/zod/v3/helpers/util.js +133 -0
- package/node_modules/zod/v3/index.cjs +52 -0
- package/node_modules/zod/v3/index.d.cts +4 -0
- package/node_modules/zod/v3/index.d.ts +4 -0
- package/node_modules/zod/v3/index.js +4 -0
- package/node_modules/zod/v3/locales/en.cjs +112 -0
- package/node_modules/zod/v3/locales/en.d.cts +3 -0
- package/node_modules/zod/v3/locales/en.d.ts +3 -0
- package/node_modules/zod/v3/locales/en.js +109 -0
- package/node_modules/zod/v3/package.json +7 -0
- package/node_modules/zod/v3/standard-schema.cjs +21 -0
- package/node_modules/zod/v3/standard-schema.d.cts +102 -0
- package/node_modules/zod/v3/standard-schema.d.ts +102 -0
- package/node_modules/zod/v3/standard-schema.js +1 -0
- package/node_modules/zod/v3/types.cjs +3798 -0
- package/node_modules/zod/v3/types.d.cts +1034 -0
- package/node_modules/zod/v3/types.d.ts +1034 -0
- package/node_modules/zod/v3/types.js +3697 -0
- package/node_modules/zod/v4/classic/checks.cjs +53 -0
- package/node_modules/zod/v4/classic/checks.d.cts +1 -0
- package/node_modules/zod/v4/classic/checks.d.ts +1 -0
- package/node_modules/zod/v4/classic/checks.js +1 -0
- package/node_modules/zod/v4/classic/coerce.cjs +66 -0
- package/node_modules/zod/v4/classic/coerce.d.cts +17 -0
- package/node_modules/zod/v4/classic/coerce.d.ts +17 -0
- package/node_modules/zod/v4/classic/coerce.js +17 -0
- package/node_modules/zod/v4/classic/compat.cjs +80 -0
- package/node_modules/zod/v4/classic/compat.d.cts +48 -0
- package/node_modules/zod/v4/classic/compat.d.ts +48 -0
- package/node_modules/zod/v4/classic/compat.js +31 -0
- package/node_modules/zod/v4/classic/deep-partial.cjs +58 -0
- package/node_modules/zod/v4/classic/deep-partial.d.cts +14 -0
- package/node_modules/zod/v4/classic/deep-partial.d.ts +14 -0
- package/node_modules/zod/v4/classic/deep-partial.js +13 -0
- package/node_modules/zod/v4/classic/errors.cjs +102 -0
- package/node_modules/zod/v4/classic/errors.d.cts +29 -0
- package/node_modules/zod/v4/classic/errors.d.ts +29 -0
- package/node_modules/zod/v4/classic/errors.js +57 -0
- package/node_modules/zod/v4/classic/external.cjs +97 -0
- package/node_modules/zod/v4/classic/external.d.cts +19 -0
- package/node_modules/zod/v4/classic/external.d.ts +19 -0
- package/node_modules/zod/v4/classic/external.js +17 -0
- package/node_modules/zod/v4/classic/from-json-schema.cjs +926 -0
- package/node_modules/zod/v4/classic/from-json-schema.d.cts +12 -0
- package/node_modules/zod/v4/classic/from-json-schema.d.ts +12 -0
- package/node_modules/zod/v4/classic/from-json-schema.js +881 -0
- package/node_modules/zod/v4/classic/in-out.cjs +82 -0
- package/node_modules/zod/v4/classic/in-out.d.cts +8 -0
- package/node_modules/zod/v4/classic/in-out.d.ts +8 -0
- package/node_modules/zod/v4/classic/in-out.js +36 -0
- package/node_modules/zod/v4/classic/index.cjs +52 -0
- package/node_modules/zod/v4/classic/index.d.cts +3 -0
- package/node_modules/zod/v4/classic/index.d.ts +3 -0
- package/node_modules/zod/v4/classic/index.js +4 -0
- package/node_modules/zod/v4/classic/iso.cjs +68 -0
- package/node_modules/zod/v4/classic/iso.d.cts +7 -0
- package/node_modules/zod/v4/classic/iso.d.ts +7 -0
- package/node_modules/zod/v4/classic/iso.js +15 -0
- package/node_modules/zod/v4/classic/package.json +7 -0
- package/node_modules/zod/v4/classic/parse.cjs +63 -0
- package/node_modules/zod/v4/classic/parse.d.cts +44 -0
- package/node_modules/zod/v4/classic/parse.d.ts +44 -0
- package/node_modules/zod/v4/classic/parse.js +16 -0
- package/node_modules/zod/v4/classic/schemas.cjs +1676 -0
- package/node_modules/zod/v4/classic/schemas.d.cts +816 -0
- package/node_modules/zod/v4/classic/schemas.d.ts +816 -0
- package/node_modules/zod/v4/classic/schemas.js +1535 -0
- package/node_modules/zod/v4/core/api.cjs +1269 -0
- package/node_modules/zod/v4/core/api.d.cts +335 -0
- package/node_modules/zod/v4/core/api.d.ts +335 -0
- package/node_modules/zod/v4/core/api.js +1107 -0
- package/node_modules/zod/v4/core/checks.cjs +561 -0
- package/node_modules/zod/v4/core/checks.d.cts +294 -0
- package/node_modules/zod/v4/core/checks.d.ts +294 -0
- package/node_modules/zod/v4/core/checks.js +516 -0
- package/node_modules/zod/v4/core/compile.cjs +1908 -0
- package/node_modules/zod/v4/core/compile.d.cts +65 -0
- package/node_modules/zod/v4/core/compile.d.ts +65 -0
- package/node_modules/zod/v4/core/compile.js +1858 -0
- package/node_modules/zod/v4/core/core.cjs +167 -0
- package/node_modules/zod/v4/core/core.d.cts +82 -0
- package/node_modules/zod/v4/core/core.d.ts +82 -0
- package/node_modules/zod/v4/core/core.js +141 -0
- package/node_modules/zod/v4/core/doc.cjs +61 -0
- package/node_modules/zod/v4/core/doc.d.cts +16 -0
- package/node_modules/zod/v4/core/doc.d.ts +16 -0
- package/node_modules/zod/v4/core/doc.js +38 -0
- package/node_modules/zod/v4/core/errors.cjs +317 -0
- package/node_modules/zod/v4/core/errors.d.cts +225 -0
- package/node_modules/zod/v4/core/errors.d.ts +225 -0
- package/node_modules/zod/v4/core/errors.js +267 -0
- package/node_modules/zod/v4/core/index.cjs +70 -0
- package/node_modules/zod/v4/core/index.d.cts +19 -0
- package/node_modules/zod/v4/core/index.d.ts +19 -0
- package/node_modules/zod/v4/core/index.js +19 -0
- package/node_modules/zod/v4/core/json-schema-generator.cjs +122 -0
- package/node_modules/zod/v4/core/json-schema-generator.d.cts +65 -0
- package/node_modules/zod/v4/core/json-schema-generator.d.ts +65 -0
- package/node_modules/zod/v4/core/json-schema-generator.js +99 -0
- package/node_modules/zod/v4/core/json-schema-processors.cjs +929 -0
- package/node_modules/zod/v4/core/json-schema-processors.d.cts +65 -0
- package/node_modules/zod/v4/core/json-schema-processors.d.ts +65 -0
- package/node_modules/zod/v4/core/json-schema-processors.js +843 -0
- package/node_modules/zod/v4/core/json-schema.cjs +21 -0
- package/node_modules/zod/v4/core/json-schema.d.cts +89 -0
- package/node_modules/zod/v4/core/json-schema.d.ts +89 -0
- package/node_modules/zod/v4/core/json-schema.js +1 -0
- package/node_modules/zod/v4/core/memoizer.cjs +360 -0
- package/node_modules/zod/v4/core/memoizer.d.cts +15 -0
- package/node_modules/zod/v4/core/memoizer.d.ts +15 -0
- package/node_modules/zod/v4/core/memoizer.js +311 -0
- package/node_modules/zod/v4/core/package.json +7 -0
- package/node_modules/zod/v4/core/parse.cjs +233 -0
- package/node_modules/zod/v4/core/parse.d.cts +52 -0
- package/node_modules/zod/v4/core/parse.d.ts +52 -0
- package/node_modules/zod/v4/core/parse.js +175 -0
- package/node_modules/zod/v4/core/regexes.cjs +209 -0
- package/node_modules/zod/v4/core/regexes.d.cts +90 -0
- package/node_modules/zod/v4/core/regexes.d.ts +90 -0
- package/node_modules/zod/v4/core/regexes.js +156 -0
- package/node_modules/zod/v4/core/registries.cjs +75 -0
- package/node_modules/zod/v4/core/registries.d.cts +35 -0
- package/node_modules/zod/v4/core/registries.d.ts +35 -0
- package/node_modules/zod/v4/core/registries.js +51 -0
- package/node_modules/zod/v4/core/schemas.cjs +2669 -0
- package/node_modules/zod/v4/core/schemas.d.cts +1273 -0
- package/node_modules/zod/v4/core/schemas.d.ts +1273 -0
- package/node_modules/zod/v4/core/schemas.js +2606 -0
- package/node_modules/zod/v4/core/standard-schema.cjs +21 -0
- package/node_modules/zod/v4/core/standard-schema.d.cts +126 -0
- package/node_modules/zod/v4/core/standard-schema.d.ts +126 -0
- package/node_modules/zod/v4/core/standard-schema.js +1 -0
- package/node_modules/zod/v4/core/to-json-schema.cjs +681 -0
- package/node_modules/zod/v4/core/to-json-schema.d.cts +154 -0
- package/node_modules/zod/v4/core/to-json-schema.d.ts +154 -0
- package/node_modules/zod/v4/core/to-json-schema.js +651 -0
- package/node_modules/zod/v4/core/util.cjs +949 -0
- package/node_modules/zod/v4/core/util.d.cts +278 -0
- package/node_modules/zod/v4/core/util.d.ts +278 -0
- package/node_modules/zod/v4/core/util.js +859 -0
- package/node_modules/zod/v4/core/versions.cjs +27 -0
- package/node_modules/zod/v4/core/versions.d.cts +5 -0
- package/node_modules/zod/v4/core/versions.d.ts +5 -0
- package/node_modules/zod/v4/core/versions.js +5 -0
- package/node_modules/zod/v4/core/visit.cjs +214 -0
- package/node_modules/zod/v4/core/visit.d.cts +30 -0
- package/node_modules/zod/v4/core/visit.d.ts +30 -0
- package/node_modules/zod/v4/core/visit.js +169 -0
- package/node_modules/zod/v4/index.cjs +51 -0
- package/node_modules/zod/v4/index.d.cts +3 -0
- package/node_modules/zod/v4/index.d.ts +3 -0
- package/node_modules/zod/v4/index.js +4 -0
- package/node_modules/zod/v4/locales/ar.cjs +138 -0
- package/node_modules/zod/v4/locales/ar.d.cts +5 -0
- package/node_modules/zod/v4/locales/ar.d.ts +4 -0
- package/node_modules/zod/v4/locales/ar.js +111 -0
- package/node_modules/zod/v4/locales/az.cjs +137 -0
- package/node_modules/zod/v4/locales/az.d.cts +5 -0
- package/node_modules/zod/v4/locales/az.d.ts +4 -0
- package/node_modules/zod/v4/locales/az.js +110 -0
- package/node_modules/zod/v4/locales/be.cjs +195 -0
- package/node_modules/zod/v4/locales/be.d.cts +5 -0
- package/node_modules/zod/v4/locales/be.d.ts +4 -0
- package/node_modules/zod/v4/locales/be.js +168 -0
- package/node_modules/zod/v4/locales/bg.cjs +152 -0
- package/node_modules/zod/v4/locales/bg.d.cts +5 -0
- package/node_modules/zod/v4/locales/bg.d.ts +4 -0
- package/node_modules/zod/v4/locales/bg.js +125 -0
- package/node_modules/zod/v4/locales/bn.cjs +140 -0
- package/node_modules/zod/v4/locales/bn.d.cts +5 -0
- package/node_modules/zod/v4/locales/bn.d.ts +4 -0
- package/node_modules/zod/v4/locales/bn.js +113 -0
- package/node_modules/zod/v4/locales/ca.cjs +139 -0
- package/node_modules/zod/v4/locales/ca.d.cts +5 -0
- package/node_modules/zod/v4/locales/ca.d.ts +4 -0
- package/node_modules/zod/v4/locales/ca.js +112 -0
- package/node_modules/zod/v4/locales/ckb.cjs +160 -0
- package/node_modules/zod/v4/locales/ckb.d.cts +5 -0
- package/node_modules/zod/v4/locales/ckb.d.ts +4 -0
- package/node_modules/zod/v4/locales/ckb.js +133 -0
- package/node_modules/zod/v4/locales/cs.cjs +143 -0
- package/node_modules/zod/v4/locales/cs.d.cts +5 -0
- package/node_modules/zod/v4/locales/cs.d.ts +4 -0
- package/node_modules/zod/v4/locales/cs.js +116 -0
- package/node_modules/zod/v4/locales/da.cjs +147 -0
- package/node_modules/zod/v4/locales/da.d.cts +5 -0
- package/node_modules/zod/v4/locales/da.d.ts +4 -0
- package/node_modules/zod/v4/locales/da.js +120 -0
- package/node_modules/zod/v4/locales/de.cjs +140 -0
- package/node_modules/zod/v4/locales/de.d.cts +5 -0
- package/node_modules/zod/v4/locales/de.d.ts +4 -0
- package/node_modules/zod/v4/locales/de.js +113 -0
- package/node_modules/zod/v4/locales/el.cjs +139 -0
- package/node_modules/zod/v4/locales/el.d.cts +5 -0
- package/node_modules/zod/v4/locales/el.d.ts +4 -0
- package/node_modules/zod/v4/locales/el.js +112 -0
- package/node_modules/zod/v4/locales/en.cjs +152 -0
- package/node_modules/zod/v4/locales/en.d.cts +5 -0
- package/node_modules/zod/v4/locales/en.d.ts +4 -0
- package/node_modules/zod/v4/locales/en.js +125 -0
- package/node_modules/zod/v4/locales/eo.cjs +141 -0
- package/node_modules/zod/v4/locales/eo.d.cts +5 -0
- package/node_modules/zod/v4/locales/eo.d.ts +4 -0
- package/node_modules/zod/v4/locales/eo.js +114 -0
- package/node_modules/zod/v4/locales/es.cjs +163 -0
- package/node_modules/zod/v4/locales/es.d.cts +5 -0
- package/node_modules/zod/v4/locales/es.d.ts +4 -0
- package/node_modules/zod/v4/locales/es.js +136 -0
- package/node_modules/zod/v4/locales/fa.cjs +146 -0
- package/node_modules/zod/v4/locales/fa.d.cts +5 -0
- package/node_modules/zod/v4/locales/fa.d.ts +4 -0
- package/node_modules/zod/v4/locales/fa.js +119 -0
- package/node_modules/zod/v4/locales/fi.cjs +144 -0
- package/node_modules/zod/v4/locales/fi.d.cts +5 -0
- package/node_modules/zod/v4/locales/fi.d.ts +4 -0
- package/node_modules/zod/v4/locales/fi.js +117 -0
- package/node_modules/zod/v4/locales/fr-CA.cjs +139 -0
- package/node_modules/zod/v4/locales/fr-CA.d.cts +5 -0
- package/node_modules/zod/v4/locales/fr-CA.d.ts +4 -0
- package/node_modules/zod/v4/locales/fr-CA.js +112 -0
- package/node_modules/zod/v4/locales/fr.cjs +156 -0
- package/node_modules/zod/v4/locales/fr.d.cts +5 -0
- package/node_modules/zod/v4/locales/fr.d.ts +4 -0
- package/node_modules/zod/v4/locales/fr.js +129 -0
- package/node_modules/zod/v4/locales/gu.cjs +140 -0
- package/node_modules/zod/v4/locales/gu.d.cts +5 -0
- package/node_modules/zod/v4/locales/gu.d.ts +4 -0
- package/node_modules/zod/v4/locales/gu.js +113 -0
- package/node_modules/zod/v4/locales/he.cjs +248 -0
- package/node_modules/zod/v4/locales/he.d.cts +5 -0
- package/node_modules/zod/v4/locales/he.d.ts +4 -0
- package/node_modules/zod/v4/locales/he.js +221 -0
- package/node_modules/zod/v4/locales/hi.cjs +138 -0
- package/node_modules/zod/v4/locales/hi.d.cts +5 -0
- package/node_modules/zod/v4/locales/hi.d.ts +4 -0
- package/node_modules/zod/v4/locales/hi.js +111 -0
- package/node_modules/zod/v4/locales/hr.cjs +153 -0
- package/node_modules/zod/v4/locales/hr.d.cts +5 -0
- package/node_modules/zod/v4/locales/hr.d.ts +4 -0
- package/node_modules/zod/v4/locales/hr.js +126 -0
- package/node_modules/zod/v4/locales/hu.cjs +140 -0
- package/node_modules/zod/v4/locales/hu.d.cts +5 -0
- package/node_modules/zod/v4/locales/hu.d.ts +4 -0
- package/node_modules/zod/v4/locales/hu.js +113 -0
- package/node_modules/zod/v4/locales/hy.cjs +185 -0
- package/node_modules/zod/v4/locales/hy.d.cts +5 -0
- package/node_modules/zod/v4/locales/hy.d.ts +4 -0
- package/node_modules/zod/v4/locales/hy.js +158 -0
- package/node_modules/zod/v4/locales/id.cjs +138 -0
- package/node_modules/zod/v4/locales/id.d.cts +5 -0
- package/node_modules/zod/v4/locales/id.d.ts +4 -0
- package/node_modules/zod/v4/locales/id.js +111 -0
- package/node_modules/zod/v4/locales/index.cjs +152 -0
- package/node_modules/zod/v4/locales/index.d.cts +63 -0
- package/node_modules/zod/v4/locales/index.d.ts +63 -0
- package/node_modules/zod/v4/locales/index.js +63 -0
- package/node_modules/zod/v4/locales/is.cjs +141 -0
- package/node_modules/zod/v4/locales/is.d.cts +5 -0
- package/node_modules/zod/v4/locales/is.d.ts +4 -0
- package/node_modules/zod/v4/locales/is.js +114 -0
- package/node_modules/zod/v4/locales/it.cjs +140 -0
- package/node_modules/zod/v4/locales/it.d.cts +5 -0
- package/node_modules/zod/v4/locales/it.d.ts +4 -0
- package/node_modules/zod/v4/locales/it.js +113 -0
- package/node_modules/zod/v4/locales/ja.cjs +139 -0
- package/node_modules/zod/v4/locales/ja.d.cts +5 -0
- package/node_modules/zod/v4/locales/ja.d.ts +4 -0
- package/node_modules/zod/v4/locales/ja.js +112 -0
- package/node_modules/zod/v4/locales/ka.cjs +144 -0
- package/node_modules/zod/v4/locales/ka.d.cts +5 -0
- package/node_modules/zod/v4/locales/ka.d.ts +4 -0
- package/node_modules/zod/v4/locales/ka.js +117 -0
- package/node_modules/zod/v4/locales/kh.cjs +12 -0
- package/node_modules/zod/v4/locales/kh.d.cts +5 -0
- package/node_modules/zod/v4/locales/kh.d.ts +5 -0
- package/node_modules/zod/v4/locales/kh.js +5 -0
- package/node_modules/zod/v4/locales/km.cjs +142 -0
- package/node_modules/zod/v4/locales/km.d.cts +5 -0
- package/node_modules/zod/v4/locales/km.d.ts +4 -0
- package/node_modules/zod/v4/locales/km.js +115 -0
- package/node_modules/zod/v4/locales/kn.cjs +143 -0
- package/node_modules/zod/v4/locales/kn.d.cts +5 -0
- package/node_modules/zod/v4/locales/kn.d.ts +4 -0
- package/node_modules/zod/v4/locales/kn.js +116 -0
- package/node_modules/zod/v4/locales/ko.cjs +143 -0
- package/node_modules/zod/v4/locales/ko.d.cts +5 -0
- package/node_modules/zod/v4/locales/ko.d.ts +4 -0
- package/node_modules/zod/v4/locales/ko.js +116 -0
- package/node_modules/zod/v4/locales/lt.cjs +234 -0
- package/node_modules/zod/v4/locales/lt.d.cts +5 -0
- package/node_modules/zod/v4/locales/lt.d.ts +4 -0
- package/node_modules/zod/v4/locales/lt.js +207 -0
- package/node_modules/zod/v4/locales/mk.cjs +141 -0
- package/node_modules/zod/v4/locales/mk.d.cts +5 -0
- package/node_modules/zod/v4/locales/mk.d.ts +4 -0
- package/node_modules/zod/v4/locales/mk.js +114 -0
- package/node_modules/zod/v4/locales/ms.cjs +139 -0
- package/node_modules/zod/v4/locales/ms.d.cts +5 -0
- package/node_modules/zod/v4/locales/ms.d.ts +4 -0
- package/node_modules/zod/v4/locales/ms.js +112 -0
- package/node_modules/zod/v4/locales/ne.cjs +138 -0
- package/node_modules/zod/v4/locales/ne.d.cts +5 -0
- package/node_modules/zod/v4/locales/ne.d.ts +4 -0
- package/node_modules/zod/v4/locales/ne.js +111 -0
- package/node_modules/zod/v4/locales/nl.cjs +142 -0
- package/node_modules/zod/v4/locales/nl.d.cts +5 -0
- package/node_modules/zod/v4/locales/nl.d.ts +4 -0
- package/node_modules/zod/v4/locales/nl.js +115 -0
- package/node_modules/zod/v4/locales/nn.cjs +140 -0
- package/node_modules/zod/v4/locales/nn.d.cts +5 -0
- package/node_modules/zod/v4/locales/nn.d.ts +4 -0
- package/node_modules/zod/v4/locales/nn.js +113 -0
- package/node_modules/zod/v4/locales/no.cjs +140 -0
- package/node_modules/zod/v4/locales/no.d.cts +5 -0
- package/node_modules/zod/v4/locales/no.d.ts +4 -0
- package/node_modules/zod/v4/locales/no.js +113 -0
- package/node_modules/zod/v4/locales/ota.cjs +141 -0
- package/node_modules/zod/v4/locales/ota.d.cts +5 -0
- package/node_modules/zod/v4/locales/ota.d.ts +4 -0
- package/node_modules/zod/v4/locales/ota.js +114 -0
- package/node_modules/zod/v4/locales/package.json +7 -0
- package/node_modules/zod/v4/locales/pl.cjs +141 -0
- package/node_modules/zod/v4/locales/pl.d.cts +5 -0
- package/node_modules/zod/v4/locales/pl.d.ts +4 -0
- package/node_modules/zod/v4/locales/pl.js +114 -0
- package/node_modules/zod/v4/locales/ps.cjs +146 -0
- package/node_modules/zod/v4/locales/ps.d.cts +5 -0
- package/node_modules/zod/v4/locales/ps.d.ts +4 -0
- package/node_modules/zod/v4/locales/ps.js +119 -0
- package/node_modules/zod/v4/locales/pt-BR.cjs +170 -0
- package/node_modules/zod/v4/locales/pt-BR.d.cts +5 -0
- package/node_modules/zod/v4/locales/pt-BR.d.ts +4 -0
- package/node_modules/zod/v4/locales/pt-BR.js +143 -0
- package/node_modules/zod/v4/locales/pt.cjs +169 -0
- package/node_modules/zod/v4/locales/pt.d.cts +5 -0
- package/node_modules/zod/v4/locales/pt.d.ts +4 -0
- package/node_modules/zod/v4/locales/pt.js +142 -0
- package/node_modules/zod/v4/locales/ro.cjs +149 -0
- package/node_modules/zod/v4/locales/ro.d.cts +5 -0
- package/node_modules/zod/v4/locales/ro.d.ts +4 -0
- package/node_modules/zod/v4/locales/ro.js +122 -0
- package/node_modules/zod/v4/locales/ru.cjs +195 -0
- package/node_modules/zod/v4/locales/ru.d.cts +5 -0
- package/node_modules/zod/v4/locales/ru.d.ts +4 -0
- package/node_modules/zod/v4/locales/ru.js +168 -0
- package/node_modules/zod/v4/locales/sk.cjs +143 -0
- package/node_modules/zod/v4/locales/sk.d.cts +5 -0
- package/node_modules/zod/v4/locales/sk.d.ts +4 -0
- package/node_modules/zod/v4/locales/sk.js +116 -0
- package/node_modules/zod/v4/locales/sl.cjs +141 -0
- package/node_modules/zod/v4/locales/sl.d.cts +5 -0
- package/node_modules/zod/v4/locales/sl.d.ts +4 -0
- package/node_modules/zod/v4/locales/sl.js +114 -0
- package/node_modules/zod/v4/locales/sv.cjs +142 -0
- package/node_modules/zod/v4/locales/sv.d.cts +5 -0
- package/node_modules/zod/v4/locales/sv.d.ts +4 -0
- package/node_modules/zod/v4/locales/sv.js +115 -0
- package/node_modules/zod/v4/locales/ta.cjs +142 -0
- package/node_modules/zod/v4/locales/ta.d.cts +5 -0
- package/node_modules/zod/v4/locales/ta.d.ts +4 -0
- package/node_modules/zod/v4/locales/ta.js +115 -0
- package/node_modules/zod/v4/locales/tg.cjs +144 -0
- package/node_modules/zod/v4/locales/tg.d.cts +5 -0
- package/node_modules/zod/v4/locales/tg.d.ts +4 -0
- package/node_modules/zod/v4/locales/tg.js +117 -0
- package/node_modules/zod/v4/locales/th.cjs +142 -0
- package/node_modules/zod/v4/locales/th.d.cts +5 -0
- package/node_modules/zod/v4/locales/th.d.ts +4 -0
- package/node_modules/zod/v4/locales/th.js +115 -0
- package/node_modules/zod/v4/locales/tk.cjs +134 -0
- package/node_modules/zod/v4/locales/tk.d.cts +5 -0
- package/node_modules/zod/v4/locales/tk.d.ts +4 -0
- package/node_modules/zod/v4/locales/tk.js +107 -0
- package/node_modules/zod/v4/locales/tr.cjs +137 -0
- package/node_modules/zod/v4/locales/tr.d.cts +5 -0
- package/node_modules/zod/v4/locales/tr.d.ts +4 -0
- package/node_modules/zod/v4/locales/tr.js +110 -0
- package/node_modules/zod/v4/locales/ua.cjs +12 -0
- package/node_modules/zod/v4/locales/ua.d.cts +5 -0
- package/node_modules/zod/v4/locales/ua.d.ts +5 -0
- package/node_modules/zod/v4/locales/ua.js +5 -0
- package/node_modules/zod/v4/locales/uk.cjs +140 -0
- package/node_modules/zod/v4/locales/uk.d.cts +5 -0
- package/node_modules/zod/v4/locales/uk.d.ts +4 -0
- package/node_modules/zod/v4/locales/uk.js +113 -0
- package/node_modules/zod/v4/locales/ur.cjs +142 -0
- package/node_modules/zod/v4/locales/ur.d.cts +5 -0
- package/node_modules/zod/v4/locales/ur.d.ts +4 -0
- package/node_modules/zod/v4/locales/ur.js +115 -0
- package/node_modules/zod/v4/locales/uz.cjs +140 -0
- package/node_modules/zod/v4/locales/uz.d.cts +5 -0
- package/node_modules/zod/v4/locales/uz.d.ts +4 -0
- package/node_modules/zod/v4/locales/uz.js +113 -0
- package/node_modules/zod/v4/locales/vi.cjs +140 -0
- package/node_modules/zod/v4/locales/vi.d.cts +5 -0
- package/node_modules/zod/v4/locales/vi.d.ts +4 -0
- package/node_modules/zod/v4/locales/vi.js +113 -0
- package/node_modules/zod/v4/locales/yo.cjs +139 -0
- package/node_modules/zod/v4/locales/yo.d.cts +5 -0
- package/node_modules/zod/v4/locales/yo.d.ts +4 -0
- package/node_modules/zod/v4/locales/yo.js +112 -0
- package/node_modules/zod/v4/locales/zh-CN.cjs +141 -0
- package/node_modules/zod/v4/locales/zh-CN.d.cts +5 -0
- package/node_modules/zod/v4/locales/zh-CN.d.ts +4 -0
- package/node_modules/zod/v4/locales/zh-CN.js +114 -0
- package/node_modules/zod/v4/locales/zh-TW.cjs +139 -0
- package/node_modules/zod/v4/locales/zh-TW.d.cts +5 -0
- package/node_modules/zod/v4/locales/zh-TW.d.ts +4 -0
- package/node_modules/zod/v4/locales/zh-TW.js +112 -0
- package/node_modules/zod/v4/mini/checks.cjs +54 -0
- package/node_modules/zod/v4/mini/checks.d.cts +1 -0
- package/node_modules/zod/v4/mini/checks.d.ts +1 -0
- package/node_modules/zod/v4/mini/checks.js +1 -0
- package/node_modules/zod/v4/mini/coerce.cjs +71 -0
- package/node_modules/zod/v4/mini/coerce.d.cts +7 -0
- package/node_modules/zod/v4/mini/coerce.d.ts +7 -0
- package/node_modules/zod/v4/mini/coerce.js +22 -0
- package/node_modules/zod/v4/mini/deep-partial.cjs +58 -0
- package/node_modules/zod/v4/mini/deep-partial.d.cts +14 -0
- package/node_modules/zod/v4/mini/deep-partial.d.ts +14 -0
- package/node_modules/zod/v4/mini/deep-partial.js +13 -0
- package/node_modules/zod/v4/mini/external.cjs +95 -0
- package/node_modules/zod/v4/mini/external.d.cts +16 -0
- package/node_modules/zod/v4/mini/external.d.ts +16 -0
- package/node_modules/zod/v4/mini/external.js +16 -0
- package/node_modules/zod/v4/mini/in-out.cjs +82 -0
- package/node_modules/zod/v4/mini/in-out.d.cts +8 -0
- package/node_modules/zod/v4/mini/in-out.d.ts +8 -0
- package/node_modules/zod/v4/mini/in-out.js +36 -0
- package/node_modules/zod/v4/mini/index.cjs +51 -0
- package/node_modules/zod/v4/mini/index.d.cts +3 -0
- package/node_modules/zod/v4/mini/index.d.ts +3 -0
- package/node_modules/zod/v4/mini/index.js +3 -0
- package/node_modules/zod/v4/mini/iso.cjs +83 -0
- package/node_modules/zod/v4/mini/iso.d.cts +22 -0
- package/node_modules/zod/v4/mini/iso.d.ts +22 -0
- package/node_modules/zod/v4/mini/iso.js +34 -0
- package/node_modules/zod/v4/mini/package.json +7 -0
- package/node_modules/zod/v4/mini/parse.cjs +37 -0
- package/node_modules/zod/v4/mini/parse.d.cts +1 -0
- package/node_modules/zod/v4/mini/parse.d.ts +1 -0
- package/node_modules/zod/v4/mini/parse.js +1 -0
- package/node_modules/zod/v4/mini/schemas.cjs +1147 -0
- package/node_modules/zod/v4/mini/schemas.d.cts +480 -0
- package/node_modules/zod/v4/mini/schemas.d.ts +480 -0
- package/node_modules/zod/v4/mini/schemas.js +1002 -0
- package/node_modules/zod/v4/package.json +7 -0
- package/node_modules/zod/v4-mini/index.cjs +51 -0
- package/node_modules/zod/v4-mini/index.d.cts +3 -0
- package/node_modules/zod/v4-mini/index.d.ts +3 -0
- package/node_modules/zod/v4-mini/index.js +3 -0
- package/node_modules/zod/v4-mini/package.json +7 -0
- package/package.json +50 -0
|
@@ -0,0 +1,2357 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import http2 from "node:http2";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { decodeSandboxBridgeBody, encodeSandboxBridgeBody, sandboxBridgeBodyCodecSource, sandboxBridgeEnvelopeLimit, } from "./sandbox-callback-bridge-body.js";
|
|
7
|
+
import { runWithoutActiveStep, runWithRuntimeParent, } from "./acpx-engine/startup-timing.js";
|
|
8
|
+
import { preferredShellForSandbox, shellCommandArgs } from "./sandbox-shell.js";
|
|
9
|
+
const DEFAULT_BRIDGE_TOKEN_BYTES = 24;
|
|
10
|
+
const DEFAULT_BRIDGE_POLL_INTERVAL_MS = 100;
|
|
11
|
+
const DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
12
|
+
const DEFAULT_BRIDGE_STOP_TIMEOUT_MS = 2_000;
|
|
13
|
+
const DEFAULT_BRIDGE_MAX_QUEUE_DEPTH = 64;
|
|
14
|
+
// A `BridgeBodyReservation` owner (`http2-bridge-server.ts`) now bounds the
|
|
15
|
+
// process-wide total of live request and response body bytes at
|
|
16
|
+
// `HTTP2_BRIDGE_MAX_PROCESS_BODY_BYTES` (1 GiB), so this per-body limit can
|
|
17
|
+
// rise to the same ceiling `MAX_ATTACHMENT_BYTES`
|
|
18
|
+
// (`server/src/attachment-types.ts`) already accepts, with no unbounded
|
|
19
|
+
// growth in process memory.
|
|
20
|
+
//
|
|
21
|
+
// The attachment upload route carries its file inside a multipart body, so a
|
|
22
|
+
// file at the exact `MAX_ATTACHMENT_BYTES` ceiling needs more than
|
|
23
|
+
// `MAX_ATTACHMENT_BYTES` raw bytes to cross the bridge: the multipart
|
|
24
|
+
// boundary line, each part's `Content-Disposition` and `Content-Type`
|
|
25
|
+
// headers, and the small JSON metadata field this route also accepts all add
|
|
26
|
+
// bytes on top of the file content the server measures. This headroom
|
|
27
|
+
// covers that framing with a wide margin, so a valid maximum-size attachment
|
|
28
|
+
// never fails at the bridge before the server ever sees it.
|
|
29
|
+
const BRIDGE_MULTIPART_FRAMING_HEADROOM_BYTES = 64 * 1024;
|
|
30
|
+
const DEFAULT_BRIDGE_MAX_BODY_BYTES = 10 * 1024 * 1024 + BRIDGE_MULTIPART_FRAMING_HEADROOM_BYTES;
|
|
31
|
+
// Per-iteration timeout for one poll-loop client call. A healthy control-plane
|
|
32
|
+
// round trip finishes in well under one second, so 10s is far above a normal
|
|
33
|
+
// iteration and never false-fires on a slow-but-live call. It is also well
|
|
34
|
+
// under the in-sandbox 30s response deadline
|
|
35
|
+
// (PAPERCLIP_BRIDGE_RESPONSE_TIMEOUT_MS), so the host loop fails fast and writes
|
|
36
|
+
// 503 responses before the in-sandbox client gives up. A silently unresponsive
|
|
37
|
+
// sandbox channel makes a client call hang with no reject; this timeout turns
|
|
38
|
+
// that hang into a caught error, so the poll loop can back off and retry while
|
|
39
|
+
// the watchdog below decides when to fail the queued requests.
|
|
40
|
+
const DEFAULT_BRIDGE_ITERATION_TIMEOUT_MS = 10_000;
|
|
41
|
+
// Watchdog backstop for a hang that the per-iteration timeout does not catch
|
|
42
|
+
// (for example many slow-but-under-timeout calls, or a stall outside the awaited
|
|
43
|
+
// calls). It is larger than one iteration timeout, so a single slow iteration
|
|
44
|
+
// never trips it, and it stays under the in-sandbox 30s response deadline.
|
|
45
|
+
const DEFAULT_BRIDGE_WATCHDOG_TIMEOUT_MS = 20_000;
|
|
46
|
+
// Grace period the recovery path gives an aborted in-flight handler to finalize
|
|
47
|
+
// its own response. The recovery path aborts the handler, then waits this long.
|
|
48
|
+
// A cooperating handler threads the abort signal into its work, rejects, and
|
|
49
|
+
// writes its own response inside the grace, so its accurate result wins. A
|
|
50
|
+
// handler that ignores the signal and never settles does not write inside the
|
|
51
|
+
// grace; the recovery path then writes a non-retryable 504 backstop, so the
|
|
52
|
+
// request never strands with no response. The grace is well under the in-sandbox
|
|
53
|
+
// 30s response deadline, so the backstop lands before the in-sandbox client
|
|
54
|
+
// gives up.
|
|
55
|
+
const DEFAULT_BRIDGE_ABORTED_HANDLER_GRACE_MS = 5_000;
|
|
56
|
+
// The recovery path retries the 504 backstop write this many times before it
|
|
57
|
+
// gives up. A single transient write failure, or one that exceeds the iteration
|
|
58
|
+
// timeout, then does not strand the caller with no terminal response.
|
|
59
|
+
const MAX_BACKSTOP_WRITE_ATTEMPTS = 3;
|
|
60
|
+
// The delay between two 504 backstop write attempts. It is short, so all retries
|
|
61
|
+
// finish well under the in-sandbox 30s response deadline.
|
|
62
|
+
const BACKSTOP_WRITE_RETRY_MS = 50;
|
|
63
|
+
// Backoff cap between poll-loop retries after a transient iteration failure.
|
|
64
|
+
// The cap keeps a recovering loop probing often enough to resume before the
|
|
65
|
+
// in-sandbox 30s response deadline strands queued callers, while the
|
|
66
|
+
// exponential ramp below it keeps a hard-down channel from burning an exec
|
|
67
|
+
// call every poll interval.
|
|
68
|
+
const MAX_TRANSIENT_ITERATION_BACKOFF_MS = 5_000;
|
|
69
|
+
const REMOTE_WRITE_BASE64_CHUNK_SIZE = 32 * 1024;
|
|
70
|
+
export const SANDBOX_CALLBACK_BRIDGE_ENTRYPOINT = "paperclip-bridge-server.mjs";
|
|
71
|
+
const SANDBOX_EXEC_CHANNEL_ENV = "PAPERCLIP_SANDBOX_EXEC_CHANNEL";
|
|
72
|
+
const SANDBOX_EXEC_CHANNEL_BRIDGE = "bridge";
|
|
73
|
+
// The bridge modes the generated gateway supports. The file mode polls a
|
|
74
|
+
// request/response queue on disk. The http2 mode runs one Node HTTP/2 client
|
|
75
|
+
// session directly on stdin/stdout, after it sends the one READY line the
|
|
76
|
+
// host readiness gate expects. The generated `.mjs` selects the mode from
|
|
77
|
+
// `PAPERCLIP_API_BRIDGE_MODE`. The generated gateway rejects every other
|
|
78
|
+
// value with a fixed startup error, including the retired duplex transport.
|
|
79
|
+
// HTTP/2 is the preferred transport. `queue_v1` is the soft-deprecated fallback.
|
|
80
|
+
const SANDBOX_CALLBACK_BRIDGE_FILE_MODE = "queue_v1";
|
|
81
|
+
/** The active non-file transport mode. */
|
|
82
|
+
export const SANDBOX_CALLBACK_BRIDGE_HTTP2_MODE = "http2_v1";
|
|
83
|
+
/** Span name that wraps one Paperclip-API callback request — read the request,
|
|
84
|
+
* write the response, and remove the request file. */
|
|
85
|
+
const CALLBACK_BRIDGE_RELAY_REQUEST_SPAN = "sandbox.callbackBridge.relayRequest";
|
|
86
|
+
/** Span name for a failed or hung bridge worker. The worker runs a throwing
|
|
87
|
+
* function under this span through `input.runtimeSpan`, so the failure lands in
|
|
88
|
+
* the run trace. The run and the orchestrator then see the hang, not only
|
|
89
|
+
* stdout. */
|
|
90
|
+
const CALLBACK_BRIDGE_WORKER_FAILED_SPAN = "sandbox.callbackBridge.workerFailed";
|
|
91
|
+
export const DEFAULT_SANDBOX_CALLBACK_BRIDGE_MAX_BODY_BYTES = DEFAULT_BRIDGE_MAX_BODY_BYTES;
|
|
92
|
+
// Routes the in-sandbox heartbeat skill is documented to call. The server
|
|
93
|
+
// still enforces actor-level permissions on top of this allowlist; the list
|
|
94
|
+
// exists to bound the surface area a compromised CLI could reach via the
|
|
95
|
+
// reverse bridge. Keep this in sync with the Paperclip skill in
|
|
96
|
+
// `skills/paperclip/SKILL.md` and `references/api-reference.md`.
|
|
97
|
+
export const DEFAULT_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST = [
|
|
98
|
+
// Runtime capability authentication is independently checked by the controller.
|
|
99
|
+
{ method: "POST", path: /^\/runtime-tools\/github\/credentials$/ },
|
|
100
|
+
// Identity, inbox, agent self-management
|
|
101
|
+
{ method: "GET", path: /^\/api\/agents\/me$/ },
|
|
102
|
+
{ method: "GET", path: /^\/api\/agents\/me\/inbox-lite$/ },
|
|
103
|
+
{ method: "GET", path: /^\/api\/agents\/me\/inbox\/mine$/ },
|
|
104
|
+
{ method: "GET", path: /^\/api\/agents\/[^/]+$/ },
|
|
105
|
+
{ method: "GET", path: /^\/api\/agents\/[^/]+\/skills$/ },
|
|
106
|
+
{ method: "POST", path: /^\/api\/agents\/[^/]+\/skills\/sync$/ },
|
|
107
|
+
{ method: "PATCH", path: /^\/api\/agents\/[^/]+\/instructions-path$/ },
|
|
108
|
+
// Read-only schema discovery for validated control-plane requests.
|
|
109
|
+
{ method: "GET", path: /^\/api\/openapi\.json$/ },
|
|
110
|
+
// Company-level reads used to discover work and context
|
|
111
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+$/ },
|
|
112
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/dashboard$/ },
|
|
113
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/agents$/ },
|
|
114
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/issues$/ },
|
|
115
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/projects$/ },
|
|
116
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/goals$/ },
|
|
117
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/org$/ },
|
|
118
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/approvals$/ },
|
|
119
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/routines$/ },
|
|
120
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/skills$/ },
|
|
121
|
+
{ method: "GET", path: /^\/api\/projects\/[^/]+$/ },
|
|
122
|
+
{ method: "GET", path: /^\/api\/goals\/[^/]+$/ },
|
|
123
|
+
// Task-bound email actions. Company, inbox ownership, task/run authority,
|
|
124
|
+
// and action policies are enforced by the controller; mailbox setup stays denied.
|
|
125
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/email\/inboxes$/ },
|
|
126
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/email\/tasks\/[^/]+$/ },
|
|
127
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/email\/deliveries\/[^/]+$/ },
|
|
128
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/email\/send$/ },
|
|
129
|
+
// Issue lifecycle: read context, checkout, update, comment, document, release
|
|
130
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+$/ },
|
|
131
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/heartbeat-context$/ },
|
|
132
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/comments(?:\/[^/]+)?$/ },
|
|
133
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/comments$/ },
|
|
134
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/documents(?:\/[^/]+)?$/ },
|
|
135
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/documents\/[^/]+\/revisions$/ },
|
|
136
|
+
{ method: "PUT", path: /^\/api\/issues\/[^/]+\/documents\/[^/]+$/ },
|
|
137
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/checkout$/ },
|
|
138
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/release$/ },
|
|
139
|
+
{ method: "PATCH", path: /^\/api\/issues\/[^/]+$/ },
|
|
140
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/approvals$/ },
|
|
141
|
+
// Files: the queue encodes binary bodies; HTTP/2 carries the same bytes directly.
|
|
142
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/attachments$/ },
|
|
143
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/issues\/[^/]+\/attachments$/ },
|
|
144
|
+
{ method: "GET", path: /^\/api\/attachments\/[^/]+\/content$/ },
|
|
145
|
+
// Work products: publish branch/commit/artifact metadata for completed work.
|
|
146
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/work-products$/ },
|
|
147
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/work-products$/ },
|
|
148
|
+
{ method: "PATCH", path: /^\/api\/work-products\/[^/]+$/ },
|
|
149
|
+
// Issue-thread interactions (create, resolve, verdict, and withdraw)
|
|
150
|
+
{ method: "GET", path: /^\/api\/issues\/[^/]+\/interactions(?:\/[^/]+)?$/ },
|
|
151
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/interactions$/ },
|
|
152
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/interactions\/[^/]+\/(?:accept|reject|respond|verdicts|withdraw)$/ },
|
|
153
|
+
// Subtasks / delegation
|
|
154
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/issues$/ },
|
|
155
|
+
// Hiring (paperclip-create-agent skill): adapter/icon discovery, comparing
|
|
156
|
+
// existing agent configs, submitting the hire request, and linking the
|
|
157
|
+
// resulting approval to its source issue. Direct agent creation
|
|
158
|
+
// (POST /api/companies/:id/agents) stays denied — hires must go through the
|
|
159
|
+
// approval-gated agent-hires endpoint, which the server still permission-checks.
|
|
160
|
+
{ method: "GET", path: /^\/llms\/agent-configuration\.txt$/ },
|
|
161
|
+
{ method: "GET", path: /^\/llms\/agent-configuration\/[^/]+\.txt$/ },
|
|
162
|
+
{ method: "GET", path: /^\/llms\/agent-icons\.txt$/ },
|
|
163
|
+
{ method: "GET", path: /^\/api\/companies\/[^/]+\/agent-configurations$/ },
|
|
164
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/agent-hires$/ },
|
|
165
|
+
{ method: "POST", path: /^\/api\/issues\/[^/]+\/approvals$/ },
|
|
166
|
+
// Approvals (request, read, comment)
|
|
167
|
+
{ method: "GET", path: /^\/api\/approvals\/[^/]+$/ },
|
|
168
|
+
{ method: "GET", path: /^\/api\/approvals\/[^/]+\/issues$/ },
|
|
169
|
+
{ method: "GET", path: /^\/api\/approvals\/[^/]+\/comments$/ },
|
|
170
|
+
{ method: "POST", path: /^\/api\/approvals\/[^/]+\/comments$/ },
|
|
171
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/approvals$/ },
|
|
172
|
+
// Execution workspaces and runtime services (start/stop/restart dev servers)
|
|
173
|
+
{ method: "GET", path: /^\/api\/execution-workspaces\/[^/]+$/ },
|
|
174
|
+
{ method: "POST", path: /^\/api\/execution-workspaces\/[^/]+\/runtime-services\/(?:start|stop|restart)$/ },
|
|
175
|
+
// Routines (agents manage their own routines and triggers)
|
|
176
|
+
{ method: "GET", path: /^\/api\/routines\/[^/]+$/ },
|
|
177
|
+
{ method: "GET", path: /^\/api\/routines\/[^/]+\/runs$/ },
|
|
178
|
+
{ method: "POST", path: /^\/api\/companies\/[^/]+\/routines$/ },
|
|
179
|
+
{ method: "PATCH", path: /^\/api\/routines\/[^/]+$/ },
|
|
180
|
+
{ method: "POST", path: /^\/api\/routines\/[^/]+\/run$/ },
|
|
181
|
+
{ method: "POST", path: /^\/api\/routines\/[^/]+\/triggers$/ },
|
|
182
|
+
{ method: "PATCH", path: /^\/api\/routine-triggers\/[^/]+$/ },
|
|
183
|
+
{ method: "DELETE", path: /^\/api\/routine-triggers\/[^/]+$/ },
|
|
184
|
+
];
|
|
185
|
+
// Keep the public alias for callers selecting the HTTP/2 transport.
|
|
186
|
+
export const HTTP2_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST = DEFAULT_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST;
|
|
187
|
+
export const DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST = [
|
|
188
|
+
"accept",
|
|
189
|
+
"content-type",
|
|
190
|
+
"if-match",
|
|
191
|
+
"if-none-match",
|
|
192
|
+
"x-paperclip-github-capability",
|
|
193
|
+
];
|
|
194
|
+
function shellQuote(value) {
|
|
195
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
196
|
+
}
|
|
197
|
+
function normalizeMethod(value) {
|
|
198
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim().toUpperCase() : "GET";
|
|
199
|
+
}
|
|
200
|
+
function normalizeTimeoutMs(value, fallback) {
|
|
201
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Race a promise against a timeout. On timeout the returned promise rejects with
|
|
205
|
+
* a clear error. The helper clears the timer on every settle path, so it leaks
|
|
206
|
+
* no `setTimeout`. The wrapped promise is not cancelable; when it never settles,
|
|
207
|
+
* it keeps running in the background, but the caller already moved on through
|
|
208
|
+
* the reject.
|
|
209
|
+
*/
|
|
210
|
+
function withTimeout(promise, timeoutMs, label) {
|
|
211
|
+
let timer;
|
|
212
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
213
|
+
timer = setTimeout(() => {
|
|
214
|
+
reject(new Error(`${label} timed out after ${timeoutMs}ms.`));
|
|
215
|
+
}, timeoutMs);
|
|
216
|
+
});
|
|
217
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
218
|
+
if (timer !== undefined) {
|
|
219
|
+
clearTimeout(timer);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function toBuffer(bytes) {
|
|
224
|
+
if (Buffer.isBuffer(bytes))
|
|
225
|
+
return bytes;
|
|
226
|
+
if (bytes instanceof ArrayBuffer)
|
|
227
|
+
return Buffer.from(bytes);
|
|
228
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
229
|
+
}
|
|
230
|
+
function buildRunnerFailureMessage(action, result) {
|
|
231
|
+
const stderr = result.stderr.trim();
|
|
232
|
+
const stdout = result.stdout.trim();
|
|
233
|
+
const detail = stderr || stdout;
|
|
234
|
+
if (result.timedOut) {
|
|
235
|
+
return `${action} timed out${detail ? `: ${detail}` : ""}`;
|
|
236
|
+
}
|
|
237
|
+
return `${action} failed with exit code ${result.exitCode ?? "null"}${detail ? `: ${detail}` : ""}`;
|
|
238
|
+
}
|
|
239
|
+
async function runShell(runner, cwd, script, timeoutMs, shellCommand = "sh", stdin) {
|
|
240
|
+
return await runner.execute({
|
|
241
|
+
command: shellCommand,
|
|
242
|
+
args: shellCommandArgs(script),
|
|
243
|
+
cwd,
|
|
244
|
+
env: {
|
|
245
|
+
[SANDBOX_EXEC_CHANNEL_ENV]: SANDBOX_EXEC_CHANNEL_BRIDGE,
|
|
246
|
+
},
|
|
247
|
+
timeoutMs,
|
|
248
|
+
stdin,
|
|
249
|
+
// Every command that rides this helper is bridge control-plane plumbing:
|
|
250
|
+
// input delivery, output read, callback relay, and queue/setup bookkeeping.
|
|
251
|
+
// It must run concurrently with the agent, so force it off the persistent
|
|
252
|
+
// session. In streamed mode the agent holds that single serialized session
|
|
253
|
+
// for the whole run; a control write on the same session queues behind the
|
|
254
|
+
// agent command that never returns — a permanent deadlock.
|
|
255
|
+
bypassSession: true,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function requireSuccessfulResult(action, result) {
|
|
259
|
+
if (!result.timedOut && result.exitCode === 0)
|
|
260
|
+
return result;
|
|
261
|
+
throw new Error(buildRunnerFailureMessage(action, result));
|
|
262
|
+
}
|
|
263
|
+
function base64Chunks(body) {
|
|
264
|
+
const out = [];
|
|
265
|
+
for (let offset = 0; offset < body.length; offset += REMOTE_WRITE_BASE64_CHUNK_SIZE) {
|
|
266
|
+
out.push(body.slice(offset, offset + REMOTE_WRITE_BASE64_CHUNK_SIZE));
|
|
267
|
+
}
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
async function pathExists(filePath) {
|
|
271
|
+
return await fs.stat(filePath).then(() => true).catch(() => false);
|
|
272
|
+
}
|
|
273
|
+
function buildRemotePidLockAcquireScript(lockDirExpr, timeoutMessage) {
|
|
274
|
+
return [
|
|
275
|
+
"attempts=0",
|
|
276
|
+
`while ! mkdir ${lockDirExpr} 2>/dev/null; do`,
|
|
277
|
+
" holder_pid=\"\"",
|
|
278
|
+
` if [ -s ${lockDirExpr}/pid ]; then`,
|
|
279
|
+
` holder_pid="$(cat ${lockDirExpr}/pid 2>/dev/null || true)"`,
|
|
280
|
+
" fi",
|
|
281
|
+
" if [ -n \"$holder_pid\" ] && ! kill -0 \"$holder_pid\" 2>/dev/null; then",
|
|
282
|
+
` rm -rf ${lockDirExpr}`,
|
|
283
|
+
" continue",
|
|
284
|
+
" fi",
|
|
285
|
+
" attempts=$((attempts + 1))",
|
|
286
|
+
" if [ \"$attempts\" -ge 600 ]; then",
|
|
287
|
+
` echo ${shellQuote(timeoutMessage)} >&2`,
|
|
288
|
+
" exit 1",
|
|
289
|
+
" fi",
|
|
290
|
+
" sleep 0.05",
|
|
291
|
+
"done",
|
|
292
|
+
`printf '%s\\n' "$$" > ${lockDirExpr}/pid`,
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
function buildRemotePidLockCleanupScript(lockDirExpr, cleanupLines) {
|
|
296
|
+
return [
|
|
297
|
+
"cleanup() {",
|
|
298
|
+
...cleanupLines.map((line) => ` ${line}`),
|
|
299
|
+
` rm -rf ${lockDirExpr}`,
|
|
300
|
+
"}",
|
|
301
|
+
"trap cleanup EXIT INT TERM",
|
|
302
|
+
];
|
|
303
|
+
}
|
|
304
|
+
export function createSandboxCallbackBridgeToken(bytes = DEFAULT_BRIDGE_TOKEN_BYTES) {
|
|
305
|
+
return randomBytes(bytes).toString("base64url");
|
|
306
|
+
}
|
|
307
|
+
export function authorizeSandboxCallbackBridgeRequestWithRoutes(request, routes = DEFAULT_SANDBOX_CALLBACK_BRIDGE_ROUTE_ALLOWLIST) {
|
|
308
|
+
const method = normalizeMethod(request.method);
|
|
309
|
+
return routes.some((route) => route.method === method && route.path.test(request.path))
|
|
310
|
+
? null
|
|
311
|
+
: `Route not allowed: ${method} ${request.path}`;
|
|
312
|
+
}
|
|
313
|
+
export function sanitizeSandboxCallbackBridgeHeaders(headers, allowlist = DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST) {
|
|
314
|
+
const allowed = new Set(allowlist.map((header) => header.toLowerCase()));
|
|
315
|
+
return Object.fromEntries(Object.entries(headers).filter(([key]) => allowed.has(key.toLowerCase())));
|
|
316
|
+
}
|
|
317
|
+
export function sandboxCallbackBridgeDirectories(rootDir) {
|
|
318
|
+
return {
|
|
319
|
+
rootDir,
|
|
320
|
+
requestsDir: path.posix.join(rootDir, "requests"),
|
|
321
|
+
responsesDir: path.posix.join(rootDir, "responses"),
|
|
322
|
+
logsDir: path.posix.join(rootDir, "logs"),
|
|
323
|
+
readyFile: path.posix.join(rootDir, "ready.json"),
|
|
324
|
+
pidFile: path.posix.join(rootDir, "server.pid"),
|
|
325
|
+
logFile: path.posix.join(rootDir, "logs", "bridge.log"),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
export function buildSandboxCallbackBridgeEnv(input) {
|
|
329
|
+
return {
|
|
330
|
+
PAPERCLIP_API_BRIDGE_MODE: SANDBOX_CALLBACK_BRIDGE_FILE_MODE,
|
|
331
|
+
PAPERCLIP_BRIDGE_QUEUE_DIR: input.queueDir,
|
|
332
|
+
PAPERCLIP_BRIDGE_TOKEN: input.bridgeToken,
|
|
333
|
+
PAPERCLIP_BRIDGE_HOST: input.host?.trim() || "127.0.0.1",
|
|
334
|
+
PAPERCLIP_BRIDGE_PORT: String(input.port && input.port > 0 ? Math.trunc(input.port) : 0),
|
|
335
|
+
PAPERCLIP_BRIDGE_POLL_INTERVAL_MS: String(normalizeTimeoutMs(input.pollIntervalMs, DEFAULT_BRIDGE_POLL_INTERVAL_MS)),
|
|
336
|
+
PAPERCLIP_BRIDGE_RESPONSE_TIMEOUT_MS: String(normalizeTimeoutMs(input.responseTimeoutMs, DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS)),
|
|
337
|
+
PAPERCLIP_BRIDGE_MAX_QUEUE_DEPTH: String(normalizeTimeoutMs(input.maxQueueDepth, DEFAULT_BRIDGE_MAX_QUEUE_DEPTH)),
|
|
338
|
+
PAPERCLIP_BRIDGE_MAX_BODY_BYTES: String(normalizeTimeoutMs(input.maxBodyBytes, DEFAULT_BRIDGE_MAX_BODY_BYTES)),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
export async function createSandboxCallbackBridgeAsset() {
|
|
342
|
+
const localDir = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-bridge-asset-"));
|
|
343
|
+
const entrypoint = path.join(localDir, SANDBOX_CALLBACK_BRIDGE_ENTRYPOINT);
|
|
344
|
+
await fs.writeFile(entrypoint, getSandboxCallbackBridgeServerSource(), "utf8");
|
|
345
|
+
return {
|
|
346
|
+
localDir,
|
|
347
|
+
entrypoint,
|
|
348
|
+
cleanup: async () => {
|
|
349
|
+
await fs.rm(localDir, { recursive: true, force: true }).catch(() => undefined);
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
export function createFileSystemSandboxCallbackBridgeQueueClient() {
|
|
354
|
+
return {
|
|
355
|
+
makeDir: async (remotePath) => {
|
|
356
|
+
await fs.mkdir(remotePath, { recursive: true });
|
|
357
|
+
},
|
|
358
|
+
makeDirs: async (remotePaths) => {
|
|
359
|
+
for (const remotePath of remotePaths) {
|
|
360
|
+
await fs.mkdir(remotePath, { recursive: true });
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
listJsonFiles: async (remotePath) => {
|
|
364
|
+
const entries = await fs.readdir(remotePath, { withFileTypes: true }).catch(() => []);
|
|
365
|
+
return entries
|
|
366
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
367
|
+
.map((entry) => entry.name)
|
|
368
|
+
.sort((left, right) => left.localeCompare(right));
|
|
369
|
+
},
|
|
370
|
+
fileSize: async (remotePath) => (await fs.stat(remotePath)).size,
|
|
371
|
+
readTextFile: async (remotePath, maxBytes) => {
|
|
372
|
+
if (maxBytes === undefined)
|
|
373
|
+
return fs.readFile(remotePath, "utf8");
|
|
374
|
+
const file = await fs.open(remotePath, "r");
|
|
375
|
+
try {
|
|
376
|
+
const stat = await file.stat();
|
|
377
|
+
if (stat.size > maxBytes)
|
|
378
|
+
throw new Error("Bridge envelope exceeded the configured size limit.");
|
|
379
|
+
const bytes = Buffer.alloc(Math.min(stat.size, maxBytes) + 1);
|
|
380
|
+
let length = 0;
|
|
381
|
+
while (length < bytes.length) {
|
|
382
|
+
const read = await file.read(bytes, length, bytes.length - length, length);
|
|
383
|
+
if (!read.bytesRead)
|
|
384
|
+
break;
|
|
385
|
+
length += read.bytesRead;
|
|
386
|
+
}
|
|
387
|
+
if (length > stat.size)
|
|
388
|
+
throw new Error("Bridge envelope changed while reading.");
|
|
389
|
+
return bytes.subarray(0, length).toString("utf8");
|
|
390
|
+
}
|
|
391
|
+
finally {
|
|
392
|
+
await file.close();
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
writeTextFile: async (remotePath, body) => {
|
|
396
|
+
await fs.mkdir(path.posix.dirname(remotePath), { recursive: true });
|
|
397
|
+
// Write to a temporary path that does NOT end in `.json`, then rename it
|
|
398
|
+
// onto the final `.json` path. A direct `writeFile` truncates the final
|
|
399
|
+
// path first, so a `.json`-only reader (the stdin poller) can see an
|
|
400
|
+
// empty or partial file. The atomic rename never exposes partial content.
|
|
401
|
+
const tempPath = `${remotePath}.paperclip-upload.decoded`;
|
|
402
|
+
await fs.writeFile(tempPath, body, "utf8");
|
|
403
|
+
await fs.rename(tempPath, remotePath);
|
|
404
|
+
},
|
|
405
|
+
writeResponseFile: async (responsePath, body, options = {}) => {
|
|
406
|
+
const responseDir = path.posix.dirname(responsePath);
|
|
407
|
+
const tempPath = `${responsePath}.tmp`;
|
|
408
|
+
const lockDir = `${responsePath}.paperclip-write.lock`;
|
|
409
|
+
const lockPidFile = `${lockDir}/pid`;
|
|
410
|
+
if (options.requestPath) {
|
|
411
|
+
const requestExists = await pathExists(options.requestPath);
|
|
412
|
+
if (!requestExists) {
|
|
413
|
+
return { wrote: false };
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
await fs.mkdir(responseDir, { recursive: true });
|
|
417
|
+
// PID-liveness mkdir-mutex: mirrors the shell-based bridge mutex so a
|
|
418
|
+
// crashed holder (SIGKILL / OOM) doesn't deadlock subsequent writers
|
|
419
|
+
// for the full timeout window.
|
|
420
|
+
let attempts = 0;
|
|
421
|
+
while (true) {
|
|
422
|
+
try {
|
|
423
|
+
await fs.mkdir(lockDir);
|
|
424
|
+
await fs.writeFile(lockPidFile, `${process.pid}\n`, "utf8");
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
const code = error?.code;
|
|
429
|
+
if (code !== "EEXIST") {
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
let holderPid = null;
|
|
433
|
+
try {
|
|
434
|
+
const raw = await fs.readFile(lockPidFile, "utf8");
|
|
435
|
+
const parsed = Number.parseInt(raw.trim(), 10);
|
|
436
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
437
|
+
holderPid = parsed;
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
// pid file missing or unreadable — treat as stale lock
|
|
441
|
+
}
|
|
442
|
+
let holderAlive = false;
|
|
443
|
+
if (holderPid !== null) {
|
|
444
|
+
try {
|
|
445
|
+
process.kill(holderPid, 0);
|
|
446
|
+
holderAlive = true;
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
holderAlive = false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!holderAlive) {
|
|
453
|
+
await fs.rm(lockDir, { recursive: true, force: true }).catch(() => undefined);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
attempts += 1;
|
|
457
|
+
if (attempts >= 600) {
|
|
458
|
+
throw new Error("Timed out acquiring sandbox callback bridge response lock.");
|
|
459
|
+
}
|
|
460
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
if (options.requestPath) {
|
|
465
|
+
const requestExists = await pathExists(options.requestPath);
|
|
466
|
+
if (!requestExists) {
|
|
467
|
+
return { wrote: false };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const responseExists = await pathExists(responsePath);
|
|
471
|
+
if (responseExists) {
|
|
472
|
+
return { wrote: false };
|
|
473
|
+
}
|
|
474
|
+
await fs.writeFile(tempPath, body, "utf8");
|
|
475
|
+
await fs.rename(tempPath, responsePath);
|
|
476
|
+
return { wrote: true };
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
await fs.rm(tempPath, { force: true }).catch(() => undefined);
|
|
480
|
+
await fs.rm(lockDir, { recursive: true, force: true }).catch(() => undefined);
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
rename: async (fromPath, toPath) => {
|
|
484
|
+
await fs.mkdir(path.posix.dirname(toPath), { recursive: true });
|
|
485
|
+
await fs.rename(fromPath, toPath);
|
|
486
|
+
},
|
|
487
|
+
remove: async (remotePath) => {
|
|
488
|
+
await fs.rm(remotePath, { recursive: true, force: true }).catch(() => undefined);
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
export function createCommandManagedSandboxCallbackBridgeQueueClient(input) {
|
|
493
|
+
const timeoutMs = normalizeTimeoutMs(input.timeoutMs, DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS);
|
|
494
|
+
const shellCommand = preferredShellForSandbox(input.shellCommand);
|
|
495
|
+
const runChecked = async (action, script) => requireSuccessfulResult(action, await runShell(input.runner, input.remoteCwd, script, timeoutMs, shellCommand));
|
|
496
|
+
return {
|
|
497
|
+
makeDir: async (remotePath) => {
|
|
498
|
+
await runChecked(`mkdir ${remotePath}`, `mkdir -p ${shellQuote(remotePath)}`);
|
|
499
|
+
},
|
|
500
|
+
makeDirs: async (remotePaths) => {
|
|
501
|
+
if (remotePaths.length === 0) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const quoted = remotePaths.map((remotePath) => shellQuote(remotePath));
|
|
505
|
+
await runChecked(`mkdir ${remotePaths.join(" ")}`, `mkdir -p ${quoted.join(" ")}`);
|
|
506
|
+
},
|
|
507
|
+
listJsonFiles: async (remotePath) => {
|
|
508
|
+
const result = await runShell(input.runner, input.remoteCwd, [
|
|
509
|
+
`if [ -d ${shellQuote(remotePath)} ]; then`,
|
|
510
|
+
` for file in ${shellQuote(remotePath)}/*.json; do`,
|
|
511
|
+
` [ -f "$file" ] || continue`,
|
|
512
|
+
" basename \"$file\"",
|
|
513
|
+
" done",
|
|
514
|
+
"fi",
|
|
515
|
+
].join("\n"), timeoutMs, shellCommand);
|
|
516
|
+
requireSuccessfulResult(`list ${remotePath}`, result);
|
|
517
|
+
return result.stdout
|
|
518
|
+
.split(/\r?\n/)
|
|
519
|
+
.map((line) => line.trim())
|
|
520
|
+
.filter((line) => line.length > 0)
|
|
521
|
+
.sort((left, right) => left.localeCompare(right));
|
|
522
|
+
},
|
|
523
|
+
fileSize: async (remotePath) => {
|
|
524
|
+
const result = await runChecked(`size ${remotePath}`, `wc -c < ${shellQuote(remotePath)}`);
|
|
525
|
+
return Number(result.stdout.trim());
|
|
526
|
+
},
|
|
527
|
+
readTextFile: async (remotePath, maxBytes) => {
|
|
528
|
+
const command = maxBytes === undefined
|
|
529
|
+
? `base64 < ${shellQuote(remotePath)}`
|
|
530
|
+
: `head -c ${Math.trunc(maxBytes) + 1} ${shellQuote(remotePath)} | base64`;
|
|
531
|
+
const result = await runChecked(`read ${remotePath}`, command);
|
|
532
|
+
const bytes = Buffer.from(result.stdout.replace(/\s+/g, ""), "base64");
|
|
533
|
+
if (maxBytes !== undefined && bytes.length > maxBytes)
|
|
534
|
+
throw new Error("Bridge envelope exceeded the configured size limit.");
|
|
535
|
+
return bytes.toString("utf8");
|
|
536
|
+
},
|
|
537
|
+
writeTextFile: async (remotePath, body) => {
|
|
538
|
+
const remoteDir = path.posix.dirname(remotePath);
|
|
539
|
+
// Two temporary paths that do NOT end in `.json`, so a `.json`-only
|
|
540
|
+
// reader (the stdin poller) never lists them. The base64 upload lands in
|
|
541
|
+
// `tempPath`. The decode result lands in `decodedPath`. An atomic rename
|
|
542
|
+
// then moves the complete decoded content onto the final `.json` path.
|
|
543
|
+
// A direct `> remotePath` redirect truncates the final path before the
|
|
544
|
+
// decode writes it, so a reader can see an empty or partial file.
|
|
545
|
+
const tempPath = `${remotePath}.paperclip-upload.b64`;
|
|
546
|
+
const decodedPath = `${remotePath}.paperclip-upload.decoded`;
|
|
547
|
+
await runChecked(`prepare upload ${remotePath}`, `mkdir -p ${shellQuote(remoteDir)} && rm -f ${shellQuote(tempPath)} ${shellQuote(decodedPath)} && : > ${shellQuote(tempPath)}`);
|
|
548
|
+
const base64Body = toBuffer(Buffer.from(body, "utf8")).toString("base64");
|
|
549
|
+
for (const chunk of base64Chunks(base64Body)) {
|
|
550
|
+
await runChecked(`append upload chunk ${remotePath}`, `printf '%s' ${shellQuote(chunk)} >> ${shellQuote(tempPath)}`);
|
|
551
|
+
}
|
|
552
|
+
await runChecked(`finalize upload ${remotePath}`, `base64 -d < ${shellQuote(tempPath)} > ${shellQuote(decodedPath)} && mv ${shellQuote(decodedPath)} ${shellQuote(remotePath)} && rm -f ${shellQuote(tempPath)}`);
|
|
553
|
+
},
|
|
554
|
+
writeResponseFile: async (responsePath, body, options = {}) => {
|
|
555
|
+
const responseDir = path.posix.dirname(responsePath);
|
|
556
|
+
const tempPath = `${responsePath}.tmp`;
|
|
557
|
+
const lockDir = `${responsePath}.paperclip-write.lock`;
|
|
558
|
+
const requestPath = options.requestPath?.trim() || "";
|
|
559
|
+
const result = await runShell(input.runner, input.remoteCwd, [
|
|
560
|
+
"set -eu",
|
|
561
|
+
`response_dir=${shellQuote(responseDir)}`,
|
|
562
|
+
`response_path=${shellQuote(responsePath)}`,
|
|
563
|
+
`temp_path=${shellQuote(tempPath)}`,
|
|
564
|
+
`lock_dir=${shellQuote(lockDir)}`,
|
|
565
|
+
`request_path=${shellQuote(requestPath)}`,
|
|
566
|
+
"mkdir -p \"$response_dir\"",
|
|
567
|
+
...buildRemotePidLockAcquireScript("\"$lock_dir\"", "Timed out acquiring sandbox callback bridge response lock."),
|
|
568
|
+
...buildRemotePidLockCleanupScript("\"$lock_dir\"", [
|
|
569
|
+
"rm -f \"$temp_path\"",
|
|
570
|
+
]),
|
|
571
|
+
"if [ -n \"$request_path\" ] && [ ! -f \"$request_path\" ]; then",
|
|
572
|
+
" printf '{\"wrote\":false}\\n'",
|
|
573
|
+
" exit 0",
|
|
574
|
+
"fi",
|
|
575
|
+
"if [ -f \"$response_path\" ]; then",
|
|
576
|
+
" printf '{\"wrote\":false}\\n'",
|
|
577
|
+
" exit 0",
|
|
578
|
+
"fi",
|
|
579
|
+
"cat > \"$temp_path\"",
|
|
580
|
+
"mv \"$temp_path\" \"$response_path\"",
|
|
581
|
+
"printf '{\"wrote\":true}\\n'",
|
|
582
|
+
].join("\n"), timeoutMs, shellCommand, body);
|
|
583
|
+
requireSuccessfulResult(`write bridge response ${responsePath}`, result);
|
|
584
|
+
try {
|
|
585
|
+
return {
|
|
586
|
+
wrote: JSON.parse(result.stdout.trim())?.wrote === true,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
throw new Error(`Sandbox callback bridge response write wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
rename: async (fromPath, toPath) => {
|
|
594
|
+
await runChecked(`rename ${fromPath}`, `mkdir -p ${shellQuote(path.posix.dirname(toPath))} && mv ${shellQuote(fromPath)} ${shellQuote(toPath)}`);
|
|
595
|
+
},
|
|
596
|
+
remove: async (remotePath) => {
|
|
597
|
+
await runChecked(`remove ${remotePath}`, `rm -rf ${shellQuote(remotePath)}`);
|
|
598
|
+
},
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
async function writeBridgeResponse(client, requestPath, responsePath, response, options = {}) {
|
|
602
|
+
const body = `${JSON.stringify(response)}\n`;
|
|
603
|
+
if (client.writeResponseFile) {
|
|
604
|
+
await client.writeResponseFile(responsePath, body, options.requireRequestPath === false ? {} : { requestPath });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const tempPath = `${responsePath}.tmp`;
|
|
608
|
+
await client.writeTextFile(tempPath, body);
|
|
609
|
+
await client.rename(tempPath, responsePath);
|
|
610
|
+
}
|
|
611
|
+
export async function startSandboxCallbackBridgeWorker(input) {
|
|
612
|
+
const pollIntervalMs = normalizeTimeoutMs(input.pollIntervalMs, DEFAULT_BRIDGE_POLL_INTERVAL_MS);
|
|
613
|
+
const iterationTimeoutMs = normalizeTimeoutMs(input.iterationTimeoutMs, DEFAULT_BRIDGE_ITERATION_TIMEOUT_MS);
|
|
614
|
+
const watchdogTimeoutMs = normalizeTimeoutMs(input.watchdogTimeoutMs, DEFAULT_BRIDGE_WATCHDOG_TIMEOUT_MS);
|
|
615
|
+
const abortedHandlerGraceMs = normalizeTimeoutMs(input.abortedHandlerGraceMs, DEFAULT_BRIDGE_ABORTED_HANDLER_GRACE_MS);
|
|
616
|
+
const maxBodyBytes = normalizeTimeoutMs(input.maxBodyBytes, DEFAULT_BRIDGE_MAX_BODY_BYTES);
|
|
617
|
+
const maxEnvelopeBytes = sandboxBridgeEnvelopeLimit(maxBodyBytes);
|
|
618
|
+
// Load lazily to avoid the HTTP/2 module's constants depending on this module
|
|
619
|
+
// during initialization. Both transports share the host process memory ceiling.
|
|
620
|
+
const { createBridgeBodyReservation } = await import("./http2-bridge-server.js");
|
|
621
|
+
const directories = sandboxCallbackBridgeDirectories(input.queueDir);
|
|
622
|
+
const queueDirectories = [
|
|
623
|
+
directories.rootDir,
|
|
624
|
+
directories.requestsDir,
|
|
625
|
+
directories.responsesDir,
|
|
626
|
+
directories.logsDir,
|
|
627
|
+
];
|
|
628
|
+
if (input.client.makeDirs) {
|
|
629
|
+
await input.client.makeDirs(queueDirectories);
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
// Backward-compatible fallback for a queue client that omits the batched
|
|
633
|
+
// makeDirs method. Create each queue directory with a single makeDir.
|
|
634
|
+
for (const directory of queueDirectories) {
|
|
635
|
+
await input.client.makeDir(directory);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
let stopping = false;
|
|
639
|
+
let inFlight = 0;
|
|
640
|
+
let settled = false;
|
|
641
|
+
let stopDeadline = Number.POSITIVE_INFINITY;
|
|
642
|
+
let settleResolve = null;
|
|
643
|
+
const settledPromise = new Promise((resolve) => {
|
|
644
|
+
settleResolve = resolve;
|
|
645
|
+
});
|
|
646
|
+
const authorizeRequest = input.authorizeRequest ??
|
|
647
|
+
((request) => authorizeSandboxCallbackBridgeRequestWithRoutes(request));
|
|
648
|
+
const buildWorkerFailureMessage = (error) => `Sandbox callback bridge worker failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
649
|
+
const inFlightRequestGuards = new Map();
|
|
650
|
+
const processRequestFile = async (fileName) => {
|
|
651
|
+
// Skip a request that already has an active attempt. The guard map holds only
|
|
652
|
+
// in-flight attempts; the attempt's finally removes its guard when it ends. A
|
|
653
|
+
// file that still has a guard is in flight, or it waits for its aborted-handler
|
|
654
|
+
// 504 backstop. A second attempt would register a new guard and re-run the host
|
|
655
|
+
// mutation, so the mutation could apply twice.
|
|
656
|
+
if (inFlightRequestGuards.has(fileName)) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const requestPath = path.posix.join(directories.requestsDir, fileName);
|
|
660
|
+
const responsePath = path.posix.join(directories.responsesDir, fileName);
|
|
661
|
+
const guard = {
|
|
662
|
+
claim: "unclaimed",
|
|
663
|
+
controller: new AbortController(),
|
|
664
|
+
finalized: false,
|
|
665
|
+
};
|
|
666
|
+
inFlightRequestGuards.set(fileName, guard);
|
|
667
|
+
const reservation = createBridgeBodyReservation();
|
|
668
|
+
const envelopeReadReservation = createBridgeBodyReservation();
|
|
669
|
+
// Claim the request for the handler. Return `false` when the recovery path
|
|
670
|
+
// already claimed it; the caller must then not run the mutation and must not
|
|
671
|
+
// write a response, because the recovery path writes a 503 and the caller
|
|
672
|
+
// may retry.
|
|
673
|
+
const claimForHandler = () => {
|
|
674
|
+
if (guard.claim === "abandon") {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
guard.claim = "handler";
|
|
678
|
+
return true;
|
|
679
|
+
};
|
|
680
|
+
// Finalize the request exactly once. Claim it for the handler first. When the
|
|
681
|
+
// recovery path already won the claim, skip both the write and the remove.
|
|
682
|
+
// The `finalized` fence stops a double write when the backstop timer already
|
|
683
|
+
// wrote a 504 for a handler the recovery path aborted. The handler wins when
|
|
684
|
+
// it settles inside the grace; the backstop wins when the handler never
|
|
685
|
+
// settles.
|
|
686
|
+
const finalize = async (response) => {
|
|
687
|
+
if (!claimForHandler()) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (guard.finalized) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
guard.finalized = true;
|
|
694
|
+
// This finalize now owns delivery for the request, so drop a pending
|
|
695
|
+
// backstop timer. The handler settled inside the grace, so the backstop no
|
|
696
|
+
// longer needs to wait; `finalize` delivers the terminal response itself,
|
|
697
|
+
// inline, and never leaves the request file for a detached timer that the
|
|
698
|
+
// busy poll loop could starve.
|
|
699
|
+
if (guard.backstopTimer !== undefined) {
|
|
700
|
+
clearTimeout(guard.backstopTimer);
|
|
701
|
+
guard.backstopTimer = undefined;
|
|
702
|
+
}
|
|
703
|
+
// Write the handler response, bounded by the per-iteration timeout so a
|
|
704
|
+
// hung sandbox channel never strands the caller until its own generic
|
|
705
|
+
// deadline. Retry a transient write failure, exactly like the 504 backstop
|
|
706
|
+
// write.
|
|
707
|
+
let lastWriteError = "Sandbox callback bridge could not write the handler response.";
|
|
708
|
+
for (let attempt = 1; attempt <= MAX_BACKSTOP_WRITE_ATTEMPTS; attempt += 1) {
|
|
709
|
+
try {
|
|
710
|
+
await withTimeout(writeBridgeResponse(input.client, requestPath, responsePath, response), iterationTimeoutMs, `Sandbox callback bridge write response for ${response.id}`);
|
|
711
|
+
await input.client.remove(requestPath).catch(() => undefined);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
catch (error) {
|
|
715
|
+
lastWriteError = error instanceof Error ? error.message : String(error);
|
|
716
|
+
console.warn(`[paperclip] sandbox callback bridge failed to write response for ${response.id} (attempt ${attempt}/${MAX_BACKSTOP_WRITE_ATTEMPTS}): ${lastWriteError}`);
|
|
717
|
+
if (attempt < MAX_BACKSTOP_WRITE_ATTEMPTS) {
|
|
718
|
+
await new Promise((resolve) => setTimeout(resolve, BACKSTOP_WRITE_RETRY_MS));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// Every handler-response write failed. Deliver a non-retryable 504 backstop
|
|
723
|
+
// inline, so the caller gets a terminal response instead of a retryable 503
|
|
724
|
+
// that repeats a possibly-committed mutation, or a strand until its own
|
|
725
|
+
// deadline. Roll the `finalized` fence back so `writeAbortedHandlerBackstop`
|
|
726
|
+
// can proceed; it re-fences, retries the 504 write, and removes the request
|
|
727
|
+
// file. Await it, so the request file does not linger for the poll loop
|
|
728
|
+
// while the loop still runs.
|
|
729
|
+
guard.finalized = false;
|
|
730
|
+
await writeAbortedHandlerBackstop(fileName, guard, lastWriteError);
|
|
731
|
+
};
|
|
732
|
+
try {
|
|
733
|
+
// Bound the read and its encoded/parsed copies before allocating. Built-in
|
|
734
|
+
// clients stat first so a high configured file limit does not reserve that
|
|
735
|
+
// whole limit for a tiny request. Older clients use the conservative cap.
|
|
736
|
+
const envelopeBytes = input.client.fileSize
|
|
737
|
+
? await input.client.fileSize(requestPath).catch(() => maxEnvelopeBytes)
|
|
738
|
+
: maxEnvelopeBytes;
|
|
739
|
+
if (envelopeBytes > maxEnvelopeBytes) {
|
|
740
|
+
await finalize({
|
|
741
|
+
id: fileName.replace(/\.json$/i, ""), status: 413,
|
|
742
|
+
headers: { "content-type": "application/json" },
|
|
743
|
+
body: JSON.stringify({ error: "Bridge request envelope exceeded the configured size limit." }),
|
|
744
|
+
completedAt: new Date().toISOString(),
|
|
745
|
+
});
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const readLimit = Number.isSafeInteger(envelopeBytes) && envelopeBytes >= 0
|
|
749
|
+
? Math.min(envelopeBytes, maxEnvelopeBytes) : maxEnvelopeBytes;
|
|
750
|
+
if (!envelopeReadReservation.reserve(6 * readLimit)) {
|
|
751
|
+
await finalize({ id: fileName.replace(/\.json$/i, ""), status: 503,
|
|
752
|
+
headers: { "content-type": "application/json" },
|
|
753
|
+
body: JSON.stringify({ error: "Bridge host body capacity is busy. Retry later." }),
|
|
754
|
+
completedAt: new Date().toISOString() });
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
let raw;
|
|
758
|
+
try {
|
|
759
|
+
raw = await input.client.readTextFile(requestPath, readLimit);
|
|
760
|
+
}
|
|
761
|
+
catch (error) {
|
|
762
|
+
// The gateway deletes a request file when its caller stops waiting
|
|
763
|
+
// (client-side timeout cleanup). A read that fails because the file is
|
|
764
|
+
// gone is that benign race, not a channel fault: confirm the file
|
|
765
|
+
// vanished and skip quietly instead of escalating into a recovery
|
|
766
|
+
// pass. A file that is still listed rethrows, so a real read fault
|
|
767
|
+
// keeps its existing handling.
|
|
768
|
+
const remaining = await input.client.listJsonFiles(directories.requestsDir).catch(() => null);
|
|
769
|
+
if (remaining !== null && !remaining.includes(fileName)) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
throw error;
|
|
773
|
+
}
|
|
774
|
+
let request;
|
|
775
|
+
try {
|
|
776
|
+
if (Buffer.byteLength(raw) > maxEnvelopeBytes)
|
|
777
|
+
throw new Error("Bridge envelope too large");
|
|
778
|
+
request = JSON.parse(raw);
|
|
779
|
+
decodeSandboxBridgeBody(request, maxBodyBytes);
|
|
780
|
+
}
|
|
781
|
+
catch {
|
|
782
|
+
const requestId = fileName.replace(/\.json$/i, "") || randomUUID();
|
|
783
|
+
await finalize({
|
|
784
|
+
id: requestId,
|
|
785
|
+
status: 400,
|
|
786
|
+
headers: { "content-type": "application/json" },
|
|
787
|
+
body: JSON.stringify({ error: "Invalid bridge request payload." }),
|
|
788
|
+
completedAt: new Date().toISOString(),
|
|
789
|
+
});
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
// Keep only the actual request allocation reserved while forwarding;
|
|
793
|
+
// an abandoned small request must not retain a maximum-sized reservation.
|
|
794
|
+
envelopeReadReservation.release();
|
|
795
|
+
if (!reservation.reserve(4 * Buffer.byteLength(raw) + 2 * Buffer.byteLength(request.body))) {
|
|
796
|
+
await finalize({ id: request.id, status: 503,
|
|
797
|
+
headers: { "content-type": "application/json" },
|
|
798
|
+
body: JSON.stringify({ error: "Bridge host body capacity is busy. Retry later." }),
|
|
799
|
+
completedAt: new Date().toISOString() });
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const denialReason = await authorizeRequest(request);
|
|
803
|
+
if (denialReason) {
|
|
804
|
+
await finalize({
|
|
805
|
+
id: request.id,
|
|
806
|
+
status: 403,
|
|
807
|
+
headers: { "content-type": "application/json" },
|
|
808
|
+
body: JSON.stringify({ error: denialReason }),
|
|
809
|
+
completedAt: new Date().toISOString(),
|
|
810
|
+
});
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
// Claim the request for the handler before the host operation starts. When
|
|
814
|
+
// the recovery path already claimed it, it writes a 503 and the caller may
|
|
815
|
+
// retry, so do not run the mutation; the retry then applies it once. When
|
|
816
|
+
// the handler claims first, the recovery path leaves the request alone and
|
|
817
|
+
// the handler writes the real response.
|
|
818
|
+
if (!claimForHandler()) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
// Build the response, then finalize once. The handler already holds the
|
|
822
|
+
// claim, so `finalize` writes the real response.
|
|
823
|
+
let response;
|
|
824
|
+
let handlerReturned = false;
|
|
825
|
+
try {
|
|
826
|
+
const { bodyEncoding, ...forwardRequest } = request;
|
|
827
|
+
const result = await input.handleRequest({ ...forwardRequest,
|
|
828
|
+
body: bodyEncoding === "base64" ? decodeSandboxBridgeBody(request, maxBodyBytes) : request.body,
|
|
829
|
+
}, { signal: guard.controller.signal, reservation });
|
|
830
|
+
handlerReturned = true;
|
|
831
|
+
const responseBytes = Buffer.byteLength(result.body ?? "");
|
|
832
|
+
if (responseBytes > maxBodyBytes)
|
|
833
|
+
throw new Error("Bridge response body exceeded the configured size limit.");
|
|
834
|
+
if (!reservation.reserve(4 * sandboxBridgeEnvelopeLimit(responseBytes))) {
|
|
835
|
+
throw new Error("Bridge host response body capacity is busy.");
|
|
836
|
+
}
|
|
837
|
+
const responseBody = encodeSandboxBridgeBody(result.body ?? "", maxBodyBytes);
|
|
838
|
+
response = {
|
|
839
|
+
id: request.id,
|
|
840
|
+
status: result.status,
|
|
841
|
+
headers: result.headers ?? {},
|
|
842
|
+
...responseBody,
|
|
843
|
+
completedAt: new Date().toISOString(),
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
console.warn(`[paperclip] sandbox callback bridge handler failed for ${request.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
848
|
+
// Tell a worker abort apart from a normal handler failure. The recovery
|
|
849
|
+
// path aborts `guard.controller` when the per-iteration timeout or the
|
|
850
|
+
// watchdog fires. The abort reaches this catch only after the handler
|
|
851
|
+
// claimed the request and started the host operation. The bridge cannot
|
|
852
|
+
// cancel a host operation that is in flight, so the mutation may have
|
|
853
|
+
// committed. A 502 (or 503) is a retryable status: the caller retries it
|
|
854
|
+
// and applies the mutation twice. So return a non-retryable 504 and mark
|
|
855
|
+
// the outcome indeterminate. The caller must not retry a 504 from the
|
|
856
|
+
// bridge, unlike the retry-safe 503 that the recovery path writes only
|
|
857
|
+
// before the host operation starts.
|
|
858
|
+
if (guard.controller.signal.aborted || (handlerReturned && !["GET", "HEAD", "OPTIONS", "TRACE"].includes(request.method))) {
|
|
859
|
+
response = {
|
|
860
|
+
id: request.id,
|
|
861
|
+
status: 504,
|
|
862
|
+
headers: {
|
|
863
|
+
"content-type": "application/json",
|
|
864
|
+
"x-paperclip-bridge-outcome": "indeterminate",
|
|
865
|
+
},
|
|
866
|
+
body: JSON.stringify({
|
|
867
|
+
error: error instanceof Error ? error.message : String(error),
|
|
868
|
+
outcome: "indeterminate",
|
|
869
|
+
retryable: false,
|
|
870
|
+
}),
|
|
871
|
+
completedAt: new Date().toISOString(),
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
response = {
|
|
876
|
+
id: request.id,
|
|
877
|
+
status: 502,
|
|
878
|
+
headers: { "content-type": "application/json" },
|
|
879
|
+
body: JSON.stringify({
|
|
880
|
+
error: error instanceof Error ? error.message : String(error),
|
|
881
|
+
}),
|
|
882
|
+
completedAt: new Date().toISOString(),
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
await finalize(response);
|
|
887
|
+
}
|
|
888
|
+
finally {
|
|
889
|
+
envelopeReadReservation.release();
|
|
890
|
+
reservation.release();
|
|
891
|
+
// Drop the guard only when it still points to this attempt. A retry can
|
|
892
|
+
// register a new attempt under the same file name; that new guard must
|
|
893
|
+
// stay in the map. Keep the guard when a backstop is still pending: a
|
|
894
|
+
// failed terminal write re-arms the backstop and keeps the request file,
|
|
895
|
+
// so the guard must stay in the map. The poll loop then skips the file and
|
|
896
|
+
// does not re-run a possibly-committed mutation before the backstop writes
|
|
897
|
+
// its 504.
|
|
898
|
+
if (guard.backstopTimer === undefined && inFlightRequestGuards.get(fileName) === guard) {
|
|
899
|
+
inFlightRequestGuards.delete(fileName);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
// Write the non-retryable 504 backstop for an aborted handler that did not
|
|
904
|
+
// finalize inside the grace. The `finalized` fence makes this a no-op when the
|
|
905
|
+
// handler already wrote its own response. The request file name is the request
|
|
906
|
+
// id plus `.json`, so derive the id from it without another client read that
|
|
907
|
+
// could hang on the same dead channel.
|
|
908
|
+
const writeAbortedHandlerBackstop = async (fileName, guard, message) => {
|
|
909
|
+
guard.backstopTimer = undefined;
|
|
910
|
+
if (guard.finalized) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
guard.finalized = true;
|
|
914
|
+
const requestPath = path.posix.join(directories.requestsDir, fileName);
|
|
915
|
+
const responsePath = path.posix.join(directories.responsesDir, fileName);
|
|
916
|
+
const requestId = fileName.replace(/\.json$/i, "") || randomUUID();
|
|
917
|
+
for (let attempt = 1; attempt <= MAX_BACKSTOP_WRITE_ATTEMPTS; attempt += 1) {
|
|
918
|
+
try {
|
|
919
|
+
await withTimeout(writeBridgeResponse(input.client, requestPath, responsePath, {
|
|
920
|
+
id: requestId,
|
|
921
|
+
status: 504,
|
|
922
|
+
headers: {
|
|
923
|
+
"content-type": "application/json",
|
|
924
|
+
"x-paperclip-bridge-outcome": "indeterminate",
|
|
925
|
+
},
|
|
926
|
+
body: JSON.stringify({ error: message, outcome: "indeterminate", retryable: false }),
|
|
927
|
+
completedAt: new Date().toISOString(),
|
|
928
|
+
}, {
|
|
929
|
+
requireRequestPath: false,
|
|
930
|
+
}), iterationTimeoutMs, `Sandbox callback bridge write 504 backstop for ${requestId}`);
|
|
931
|
+
// The 504 backstop reached the caller. Remove the request file, so the
|
|
932
|
+
// poll loop does not list it again and re-run the mutation.
|
|
933
|
+
await input.client.remove(requestPath).catch(() => undefined);
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
console.warn(`[paperclip] sandbox callback bridge failed to write 504 backstop for ${requestId} (attempt ${attempt}/${MAX_BACKSTOP_WRITE_ATTEMPTS}): ${error instanceof Error ? error.message : String(error)}`);
|
|
938
|
+
if (attempt < MAX_BACKSTOP_WRITE_ATTEMPTS) {
|
|
939
|
+
await new Promise((resolve) => setTimeout(resolve, BACKSTOP_WRITE_RETRY_MS));
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
// Every backstop write failed. Keep the request file and clear the fence, so a
|
|
944
|
+
// late handler can still finalize its own 504. The guard stays in the map, so
|
|
945
|
+
// the poll loop skips the file and does not re-run the mutation. A removed
|
|
946
|
+
// file plus a set fence would strand the caller until its own deadline and
|
|
947
|
+
// give it a generic 502 instead of the terminal 504.
|
|
948
|
+
//
|
|
949
|
+
// Re-arm the backstop directly. A stuck handler never settles, so its
|
|
950
|
+
// `processRequestFile` never runs the `finally` that drops the guard. The
|
|
951
|
+
// poll loop then skips the file on each iteration, so every iteration
|
|
952
|
+
// succeeds and the watchdog never trips again. No later recovery pass runs,
|
|
953
|
+
// so a re-arm here is the only path that retries the 504 write. Clear the
|
|
954
|
+
// fence before the re-arm, because `scheduleAbortedHandlerBackstop` bails on
|
|
955
|
+
// a set fence. The re-arm only re-writes the 504 response; it never re-runs
|
|
956
|
+
// the mutation, so a retry cannot apply the mutation twice.
|
|
957
|
+
guard.finalized = false;
|
|
958
|
+
scheduleAbortedHandlerBackstop(fileName, guard, message);
|
|
959
|
+
};
|
|
960
|
+
// Arm the backstop timer for a handler the recovery path just aborted. It is
|
|
961
|
+
// idempotent: a second recovery pass (the watchdog and the loop catch both run
|
|
962
|
+
// `failPendingRequests`) does not re-arm a live timer or one that already
|
|
963
|
+
// finalized.
|
|
964
|
+
const scheduleAbortedHandlerBackstop = (fileName, guard, message) => {
|
|
965
|
+
if (guard.finalized || guard.backstopTimer !== undefined) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
guard.backstopTimer = setTimeout(() => {
|
|
969
|
+
void writeAbortedHandlerBackstop(fileName, guard, message);
|
|
970
|
+
}, abortedHandlerGraceMs);
|
|
971
|
+
if (typeof guard.backstopTimer.unref === "function") {
|
|
972
|
+
guard.backstopTimer.unref();
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
// Abort every queued request with a 503. The `abandonInFlight` option controls
|
|
976
|
+
// the completion fence for a request a `processRequestFile` attempt still owns.
|
|
977
|
+
// The timeout and watchdog recovery pass `true`: the loop already gave up on
|
|
978
|
+
// the request. When the handler did not yet start the host operation, claim the
|
|
979
|
+
// request so a later handler claim bails, then write the 503. When the handler
|
|
980
|
+
// already started, skip the 503; the recovery cannot cancel an in-flight host
|
|
981
|
+
// operation, and a 503 there would make the caller retry and apply the mutation
|
|
982
|
+
// twice. The stop drain passes `false` (the default): a request the loop
|
|
983
|
+
// already picked up keeps its normal completion, so a late handler result still
|
|
984
|
+
// wins over the drain 503, exactly like the earlier stop behavior.
|
|
985
|
+
const failPendingRequests = async (message, options = {}) => {
|
|
986
|
+
if (options.abandonInFlight) {
|
|
987
|
+
// Abort every in-flight handler first, then arm its 504 backstop. The loop
|
|
988
|
+
// already gave up on the request. A handler that threads the signal into
|
|
989
|
+
// its work stops, rejects, and finalizes with its own error response inside
|
|
990
|
+
// the grace, so the request does not strand. A handler that ignores the
|
|
991
|
+
// signal and never settles does not finalize inside the grace; the backstop
|
|
992
|
+
// timer then writes a non-retryable 504, so the request still never
|
|
993
|
+
// strands. This reads the guard map directly, so it runs even when the
|
|
994
|
+
// request listing below fails on the same dead channel. It never writes a
|
|
995
|
+
// 503 for a handler-owned request: the recovery cannot cancel a committed
|
|
996
|
+
// host mutation, so a retryable status there could apply the mutation twice.
|
|
997
|
+
for (const [fileName, guard] of inFlightRequestGuards.entries()) {
|
|
998
|
+
if (guard.claim === "handler") {
|
|
999
|
+
guard.controller.abort(new Error(message));
|
|
1000
|
+
scheduleAbortedHandlerBackstop(fileName, guard, message);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
// Wrap each client call in the per-iteration timeout. When the sandbox
|
|
1005
|
+
// channel is unresponsive, a client call hangs with no reject. The timeout
|
|
1006
|
+
// keeps this recovery path fail-fast, so it never re-hangs on the same dead
|
|
1007
|
+
// channel that triggered the recovery.
|
|
1008
|
+
const fileNames = await withTimeout(input.client.listJsonFiles(directories.requestsDir), iterationTimeoutMs, "Sandbox callback bridge list pending requests").catch(() => []);
|
|
1009
|
+
for (const fileName of fileNames) {
|
|
1010
|
+
const guard = inFlightRequestGuards.get(fileName);
|
|
1011
|
+
if (guard && guard.claim === "handler" && (options.abandonInFlight || guard.controller.signal.aborted)) {
|
|
1012
|
+
// The handler already started this request's host operation, or it already
|
|
1013
|
+
// finalized the request. The timeout and watchdog cannot cancel a host
|
|
1014
|
+
// operation that is in flight. A competing 503 here makes the caller retry
|
|
1015
|
+
// while the original mutation still completes, so the mutation applies
|
|
1016
|
+
// twice. Leave the request for the handler to finalize, or for the 504
|
|
1017
|
+
// backstop to finalize when the handler never settles. The recovery path
|
|
1018
|
+
// reaches this skip through `abandonInFlight`. The stop drain reaches it
|
|
1019
|
+
// only when the recovery path already aborted the handler, so a request
|
|
1020
|
+
// whose 504 backstop write failed and kept its file never gets a competing
|
|
1021
|
+
// 503 on stop. A normal in-flight handler at a graceful stop is not
|
|
1022
|
+
// aborted, so it still gets the stop drain 503.
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
if (options.abandonInFlight && guard) {
|
|
1026
|
+
// The handler did not start the host operation yet. Claim the request, so
|
|
1027
|
+
// the handler bails at its host-operation claim instead of running the
|
|
1028
|
+
// mutation. A retry after the 503 then applies the mutation once.
|
|
1029
|
+
guard.claim = "abandon";
|
|
1030
|
+
}
|
|
1031
|
+
const requestPath = path.posix.join(directories.requestsDir, fileName);
|
|
1032
|
+
const responsePath = path.posix.join(directories.responsesDir, fileName);
|
|
1033
|
+
const requestId = fileName.replace(/\.json$/i, "") || randomUUID();
|
|
1034
|
+
let responseId = requestId;
|
|
1035
|
+
try {
|
|
1036
|
+
const raw = await withTimeout(input.client.readTextFile(requestPath), iterationTimeoutMs, `Sandbox callback bridge read pending request ${requestId}`);
|
|
1037
|
+
const parsed = JSON.parse(raw);
|
|
1038
|
+
if (typeof parsed.id === "string" && parsed.id.length > 0) {
|
|
1039
|
+
responseId = parsed.id;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
catch (error) {
|
|
1043
|
+
// The read or the parse failed, most likely on the same dead channel that
|
|
1044
|
+
// triggered this recovery. Keep the request file, so a later recovery pass
|
|
1045
|
+
// can still read it and deliver a terminal 503. A remove here drops the
|
|
1046
|
+
// request and strands the caller until its own deadline.
|
|
1047
|
+
console.warn(`[paperclip] sandbox callback bridge could not read pending request ${requestId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
// Write the 503 first, then remove the request file only after the write
|
|
1051
|
+
// lands. Retry a transient failure, bounded by the per-iteration timeout,
|
|
1052
|
+
// exactly like the finalize and 504 backstop writes. When every attempt
|
|
1053
|
+
// fails, keep the request file. A later recovery pass, or the caller retry,
|
|
1054
|
+
// then still finds the queued request and delivers a terminal 503, instead
|
|
1055
|
+
// of a silent drop that strands the caller until its own deadline. The
|
|
1056
|
+
// request is unclaimed or abandoned, so its host mutation never ran; a later
|
|
1057
|
+
// 503 stays exactly-once.
|
|
1058
|
+
let wrote503 = false;
|
|
1059
|
+
let lastWriteError = "Sandbox callback bridge could not write the recovery 503.";
|
|
1060
|
+
for (let attempt = 1; attempt <= MAX_BACKSTOP_WRITE_ATTEMPTS; attempt += 1) {
|
|
1061
|
+
try {
|
|
1062
|
+
await withTimeout(writeBridgeResponse(input.client, requestPath, responsePath, {
|
|
1063
|
+
id: responseId,
|
|
1064
|
+
status: 503,
|
|
1065
|
+
headers: { "content-type": "application/json" },
|
|
1066
|
+
body: JSON.stringify({ error: message }),
|
|
1067
|
+
completedAt: new Date().toISOString(),
|
|
1068
|
+
}, {
|
|
1069
|
+
requireRequestPath: false,
|
|
1070
|
+
}), iterationTimeoutMs, `Sandbox callback bridge write 503 for ${requestId}`);
|
|
1071
|
+
wrote503 = true;
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
catch (error) {
|
|
1075
|
+
lastWriteError = error instanceof Error ? error.message : String(error);
|
|
1076
|
+
console.warn(`[paperclip] sandbox callback bridge failed to write recovery 503 for ${requestId} (attempt ${attempt}/${MAX_BACKSTOP_WRITE_ATTEMPTS}): ${lastWriteError}`);
|
|
1077
|
+
if (attempt < MAX_BACKSTOP_WRITE_ATTEMPTS) {
|
|
1078
|
+
await new Promise((resolve) => setTimeout(resolve, BACKSTOP_WRITE_RETRY_MS));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
if (wrote503) {
|
|
1083
|
+
// The 503 landed. Remove the request file, so the poll loop does not
|
|
1084
|
+
// re-process it.
|
|
1085
|
+
await input.client.remove(requestPath).catch(() => undefined);
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
// Every 503 write failed. Keep the request file for a later recovery pass.
|
|
1089
|
+
console.warn(`[paperclip] sandbox callback bridge kept queued request ${requestId} after every recovery 503 write failed: ${lastWriteError}`);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
// Surface a bridge-worker failure through the run trace, not only stdout. A
|
|
1094
|
+
// failed span under `input.runtimeSpan` records the error against the live run
|
|
1095
|
+
// span, so the run and the orchestrator see the hang. When no `runtimeSpan`
|
|
1096
|
+
// runner is wired (no injected tracer), the helper still writes a warn line,
|
|
1097
|
+
// so the failure is never silent.
|
|
1098
|
+
const surfaceRunError = async (error) => {
|
|
1099
|
+
if (input.runtimeSpan) {
|
|
1100
|
+
try {
|
|
1101
|
+
await input.runtimeSpan(CALLBACK_BRIDGE_WORKER_FAILED_SPAN, async () => {
|
|
1102
|
+
throw error;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
catch {
|
|
1106
|
+
// `runtimeSpan` re-throws after it records the failed span. The error is
|
|
1107
|
+
// now on the trace; swallow it here so the worker recovery continues.
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
console.warn(`[paperclip] ${error.message}`);
|
|
1111
|
+
};
|
|
1112
|
+
// The timestamp of the last successful loop iteration. The watchdog compares
|
|
1113
|
+
// it to the current time. The idle branch and every processed request update
|
|
1114
|
+
// it, so steady progress keeps the watchdog re-armed.
|
|
1115
|
+
let lastSuccessfulIterationAt = Date.now();
|
|
1116
|
+
let watchdogTrippedAt = null;
|
|
1117
|
+
let watchdogTripInFlight = false;
|
|
1118
|
+
// Check often enough to fire soon after the threshold, but not so often that
|
|
1119
|
+
// the check adds load. One fifth of the threshold, with a 10ms floor.
|
|
1120
|
+
const watchdogCheckIntervalMs = Math.max(10, Math.floor(watchdogTimeoutMs / 5));
|
|
1121
|
+
const handleWatchdogTrip = async (idleMs) => {
|
|
1122
|
+
const message = `Sandbox callback bridge made no successful poll iteration for ${idleMs}ms; the sandbox connection is unresponsive.`;
|
|
1123
|
+
await surfaceRunError(new Error(message));
|
|
1124
|
+
try {
|
|
1125
|
+
await failPendingRequests(message, { abandonInFlight: true });
|
|
1126
|
+
}
|
|
1127
|
+
catch (error) {
|
|
1128
|
+
console.warn(`[paperclip] sandbox callback bridge watchdog failed to abort queued requests: ${error instanceof Error ? error.message : String(error)}`);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
// Start the long-lived poll loop outside the measured startup-step store.
|
|
1132
|
+
// The `makeDir` calls above are startup work and must keep the active
|
|
1133
|
+
// `bridge.paperclip` step. The loop runs run-time execs for the whole run,
|
|
1134
|
+
// so each loop `sandbox.exec` span must not parent to the ended step or copy
|
|
1135
|
+
// its `criticalPath` flag. `runWithoutActiveStep` empties the store for the
|
|
1136
|
+
// loop only; Node keeps the empty store on every later poll continuation.
|
|
1137
|
+
const loop = runWithoutActiveStep(() => (async () => {
|
|
1138
|
+
// The watchdog runs on its own timer, so it fires even while the loop is
|
|
1139
|
+
// stuck on an awaited client call. It is the backstop for a hang the
|
|
1140
|
+
// per-iteration timeout does not catch. `unref` keeps it from holding the
|
|
1141
|
+
// process open. The loop `finally` clears it on every exit.
|
|
1142
|
+
const watchdogTimer = setInterval(() => {
|
|
1143
|
+
if (settled || stopping)
|
|
1144
|
+
return;
|
|
1145
|
+
const idleMs = Date.now() - lastSuccessfulIterationAt;
|
|
1146
|
+
if (idleMs < watchdogTimeoutMs)
|
|
1147
|
+
return;
|
|
1148
|
+
// Fire once per hang period. Re-arm only after the loop advances
|
|
1149
|
+
// `lastSuccessfulIterationAt` past the last trip (a new successful
|
|
1150
|
+
// iteration), so a persistent hang never fires the watchdog repeatedly.
|
|
1151
|
+
if (watchdogTrippedAt !== null && watchdogTrippedAt >= lastSuccessfulIterationAt)
|
|
1152
|
+
return;
|
|
1153
|
+
if (watchdogTripInFlight)
|
|
1154
|
+
return;
|
|
1155
|
+
watchdogTrippedAt = Date.now();
|
|
1156
|
+
watchdogTripInFlight = true;
|
|
1157
|
+
void handleWatchdogTrip(idleMs).finally(() => {
|
|
1158
|
+
watchdogTripInFlight = false;
|
|
1159
|
+
});
|
|
1160
|
+
}, watchdogCheckIntervalMs);
|
|
1161
|
+
if (typeof watchdogTimer.unref === "function") {
|
|
1162
|
+
watchdogTimer.unref();
|
|
1163
|
+
}
|
|
1164
|
+
try {
|
|
1165
|
+
// Consecutive transient poll failures. A single failed list call — one
|
|
1166
|
+
// reset or slow sandbox exec — must not end the relay for the rest of the
|
|
1167
|
+
// run: the in-sandbox gateway keeps queueing requests, so a dead loop
|
|
1168
|
+
// strands every later API call from the agent (its status writes then look
|
|
1169
|
+
// like connection failures and the issue loses its disposition). Back off
|
|
1170
|
+
// and retry the poll instead. The watchdog stays the escalation path for a
|
|
1171
|
+
// sustained outage — it fires after `watchdogTimeoutMs` without a
|
|
1172
|
+
// successful iteration and fails the queued requests fast, while this loop
|
|
1173
|
+
// keeps probing for recovery.
|
|
1174
|
+
let consecutivePollFailures = 0;
|
|
1175
|
+
while (true) {
|
|
1176
|
+
let fileNames;
|
|
1177
|
+
try {
|
|
1178
|
+
fileNames = await withTimeout(input.client.listJsonFiles(directories.requestsDir), iterationTimeoutMs, "Sandbox callback bridge list requests");
|
|
1179
|
+
consecutivePollFailures = 0;
|
|
1180
|
+
}
|
|
1181
|
+
catch (error) {
|
|
1182
|
+
if (stopping) {
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
consecutivePollFailures += 1;
|
|
1186
|
+
const message = `${buildWorkerFailureMessage(error)} (transient poll failure ${consecutivePollFailures}; retrying)`;
|
|
1187
|
+
if (consecutivePollFailures === 1) {
|
|
1188
|
+
// Put the first failure of a streak on the run trace; later repeats
|
|
1189
|
+
// only warn, so a flapping channel does not spam failed spans.
|
|
1190
|
+
await surfaceRunError(new Error(message));
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
console.warn(`[paperclip] ${message}`);
|
|
1194
|
+
}
|
|
1195
|
+
const backoffMs = Math.min(pollIntervalMs * 2 ** consecutivePollFailures, MAX_TRANSIENT_ITERATION_BACKOFF_MS);
|
|
1196
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
// A file whose attempt is still in flight (or waiting on its 504
|
|
1200
|
+
// backstop) is not actionable: `processRequestFile` would skip it via
|
|
1201
|
+
// the guard map. Treat an all-guarded listing like an empty one and
|
|
1202
|
+
// sleep a poll interval. Re-listing immediately would spin the loop —
|
|
1203
|
+
// an exec storm against a real sandbox channel, and with an in-memory
|
|
1204
|
+
// client a pure-microtask loop that starves every timer in the process
|
|
1205
|
+
// (including the guard's own backstop and abort timers).
|
|
1206
|
+
const actionableFileNames = fileNames.filter((fileName) => !inFlightRequestGuards.has(fileName));
|
|
1207
|
+
if (actionableFileNames.length === 0) {
|
|
1208
|
+
lastSuccessfulIterationAt = Date.now();
|
|
1209
|
+
if (stopping) {
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
for (const fileName of actionableFileNames) {
|
|
1216
|
+
if (stopping && Date.now() >= stopDeadline)
|
|
1217
|
+
break;
|
|
1218
|
+
inFlight += 1;
|
|
1219
|
+
try {
|
|
1220
|
+
// A request is run-time work, not startup work. Wrap it in a
|
|
1221
|
+
// `sandbox.callbackBridge.relayRequest` span, so its read, write, and
|
|
1222
|
+
// remove execs group under one named span that parents to the live
|
|
1223
|
+
// run span. The span runner reads the run parent per request: the
|
|
1224
|
+
// live parent switches to `agent.turn` during the turn and back to
|
|
1225
|
+
// `task.run` after it. Without a runner, the request runs under the
|
|
1226
|
+
// run parent with no wrapper span, exactly like the earlier behavior.
|
|
1227
|
+
// The per-iteration timeout wraps the whole request, so a hung
|
|
1228
|
+
// request rejects and the catch below runs the recovery pass.
|
|
1229
|
+
await withTimeout(input.runtimeSpan
|
|
1230
|
+
? input.runtimeSpan(CALLBACK_BRIDGE_RELAY_REQUEST_SPAN, () => processRequestFile(fileName))
|
|
1231
|
+
: runWithRuntimeParent(input.getRuntimeParentContext?.(), () => processRequestFile(fileName)), iterationTimeoutMs, `Sandbox callback bridge process request ${fileName}`);
|
|
1232
|
+
lastSuccessfulIterationAt = Date.now();
|
|
1233
|
+
}
|
|
1234
|
+
catch (error) {
|
|
1235
|
+
// A single request attempt failed or hung. Run the same recovery
|
|
1236
|
+
// pass the loop previously died on — abort the in-flight handler
|
|
1237
|
+
// (its 504 backstop keeps the caller from stranding) and 503 the
|
|
1238
|
+
// unclaimed queued requests — but keep the loop alive afterward. A
|
|
1239
|
+
// caller that sees the retry-safe 503 re-queues, and the recovered
|
|
1240
|
+
// loop serves the retry; the old terminal catch left every later
|
|
1241
|
+
// request to strand instead.
|
|
1242
|
+
const message = buildWorkerFailureMessage(error);
|
|
1243
|
+
await surfaceRunError(new Error(message));
|
|
1244
|
+
try {
|
|
1245
|
+
await failPendingRequests(message, { abandonInFlight: true });
|
|
1246
|
+
}
|
|
1247
|
+
catch (failPendingError) {
|
|
1248
|
+
console.warn(`[paperclip] sandbox callback bridge failed to abort queued requests after a request failure: ${failPendingError instanceof Error ? failPendingError.message : String(failPendingError)}`);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
finally {
|
|
1252
|
+
inFlight -= 1;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
lastSuccessfulIterationAt = Date.now();
|
|
1256
|
+
if (stopping && Date.now() >= stopDeadline) {
|
|
1257
|
+
break;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
catch (error) {
|
|
1262
|
+
const message = buildWorkerFailureMessage(error);
|
|
1263
|
+
await surfaceRunError(new Error(message));
|
|
1264
|
+
try {
|
|
1265
|
+
await failPendingRequests(message, { abandonInFlight: true });
|
|
1266
|
+
}
|
|
1267
|
+
catch (failPendingError) {
|
|
1268
|
+
console.warn(`[paperclip] sandbox callback bridge failed to abort queued requests after worker failure: ${failPendingError instanceof Error ? failPendingError.message : String(failPendingError)}`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
finally {
|
|
1272
|
+
clearInterval(watchdogTimer);
|
|
1273
|
+
settled = true;
|
|
1274
|
+
if (settleResolve) {
|
|
1275
|
+
settleResolve();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
})());
|
|
1279
|
+
void loop;
|
|
1280
|
+
return {
|
|
1281
|
+
stop: async (options = {}) => {
|
|
1282
|
+
stopping = true;
|
|
1283
|
+
const drainMs = normalizeTimeoutMs(options.drainTimeoutMs, DEFAULT_BRIDGE_STOP_TIMEOUT_MS);
|
|
1284
|
+
stopDeadline = Date.now() + drainMs;
|
|
1285
|
+
if (!settled) {
|
|
1286
|
+
await Promise.race([
|
|
1287
|
+
settledPromise,
|
|
1288
|
+
new Promise((resolve) => setTimeout(resolve, drainMs)),
|
|
1289
|
+
]);
|
|
1290
|
+
}
|
|
1291
|
+
await failPendingRequests("Bridge worker stopped before request could be handled.");
|
|
1292
|
+
},
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Content-hash-skip write of a Paperclip-authored text file into the sandbox, in
|
|
1297
|
+
* a SINGLE remote exec. The body's sha256 is computed on the host; the one shell
|
|
1298
|
+
* round-trip skips the write entirely when the remote file already hashes to the
|
|
1299
|
+
* same value (warm start — 0 write execs), otherwise it uploads (base64 over
|
|
1300
|
+
* stdin), verifies the decoded bytes, and atomically renames into place. A
|
|
1301
|
+
* PID-liveness lock serializes concurrent writers to the same path and the
|
|
1302
|
+
* verify step guards against a torn upload.
|
|
1303
|
+
*
|
|
1304
|
+
* Fail loudly: a non-zero remote exit (surfaced by `requireSuccessfulResult`) or
|
|
1305
|
+
* malformed result JSON throws rather than silently re-uploading and masking a
|
|
1306
|
+
* failed check. The only intentional degradation is when the remote has neither
|
|
1307
|
+
* `sha256sum` nor `shasum` — then the skip cannot be proven and we conservatively
|
|
1308
|
+
* re-upload (and the post-upload verify is best-effort, as noted inline).
|
|
1309
|
+
*/
|
|
1310
|
+
export async function syncRemoteTextFileWithHashSkip(input) {
|
|
1311
|
+
const timeoutMs = normalizeTimeoutMs(input.timeoutMs, DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS);
|
|
1312
|
+
const shellCommand = preferredShellForSandbox(input.shellCommand);
|
|
1313
|
+
const remotePartial = `${input.remotePath}.partial`;
|
|
1314
|
+
const remoteUploadPath = `${input.remotePath}.paperclip-upload.b64`;
|
|
1315
|
+
const base64Body = toBuffer(Buffer.from(input.body, "utf8")).toString("base64");
|
|
1316
|
+
const sha256 = createHash("sha256").update(input.body, "utf8").digest("hex");
|
|
1317
|
+
const syncResult = await runShell(input.runner, input.remoteCwd, [
|
|
1318
|
+
"set -eu",
|
|
1319
|
+
`remote_dir=${shellQuote(input.remoteDir)}`,
|
|
1320
|
+
`remote_path=${shellQuote(input.remotePath)}`,
|
|
1321
|
+
`remote_partial=${shellQuote(remotePartial)}`,
|
|
1322
|
+
`remote_upload=${shellQuote(remoteUploadPath)}`,
|
|
1323
|
+
`lock_dir=${shellQuote(input.lockDir)}`,
|
|
1324
|
+
`expected_sha=${shellQuote(sha256)}`,
|
|
1325
|
+
"hash_file() {",
|
|
1326
|
+
" if command -v sha256sum >/dev/null 2>&1; then",
|
|
1327
|
+
" sha256sum \"$1\" | awk '{print $1}'",
|
|
1328
|
+
" return 0",
|
|
1329
|
+
" fi",
|
|
1330
|
+
" if command -v shasum >/dev/null 2>&1; then",
|
|
1331
|
+
" shasum -a 256 \"$1\" | awk '{print $1}'",
|
|
1332
|
+
" return 0",
|
|
1333
|
+
" fi",
|
|
1334
|
+
" return 127",
|
|
1335
|
+
"}",
|
|
1336
|
+
"mkdir -p \"$remote_dir\"",
|
|
1337
|
+
...buildRemotePidLockAcquireScript("\"$lock_dir\"", `Timed out acquiring ${input.label} upload lock.`),
|
|
1338
|
+
...buildRemotePidLockCleanupScript("\"$lock_dir\"", [
|
|
1339
|
+
"rm -f \"$remote_upload\" \"$remote_partial\"",
|
|
1340
|
+
]),
|
|
1341
|
+
"current_sha=\"\"",
|
|
1342
|
+
"if [ -f \"$remote_path\" ]; then",
|
|
1343
|
+
" current_sha=\"$(hash_file \"$remote_path\" 2>/dev/null)\" || current_sha=\"\"",
|
|
1344
|
+
"fi",
|
|
1345
|
+
"if [ -n \"$current_sha\" ] && [ \"$current_sha\" = \"$expected_sha\" ]; then",
|
|
1346
|
+
" printf '{\"uploaded\":false}\\n'",
|
|
1347
|
+
" exit 0",
|
|
1348
|
+
"fi",
|
|
1349
|
+
"rm -f \"$remote_upload\" \"$remote_partial\"",
|
|
1350
|
+
"cat > \"$remote_upload\"",
|
|
1351
|
+
"base64 -d < \"$remote_upload\" > \"$remote_partial\"",
|
|
1352
|
+
// Verify upload integrity. If neither sha256sum nor shasum is on PATH
|
|
1353
|
+
// (minimal Alpine/scratch images), surface the missing-tool error
|
|
1354
|
+
// instead of a misleading "sha mismatch" — the verify step is then
|
|
1355
|
+
// best-effort and we trust base64-decode + atomic rename below.
|
|
1356
|
+
"if partial_sha=\"$(hash_file \"$remote_partial\" 2>/dev/null)\"; then",
|
|
1357
|
+
" if [ \"$partial_sha\" != \"$expected_sha\" ]; then",
|
|
1358
|
+
` echo ${shellQuote(`${input.label} upload sha mismatch.`)} >&2`,
|
|
1359
|
+
" exit 1",
|
|
1360
|
+
" fi",
|
|
1361
|
+
"else",
|
|
1362
|
+
` echo ${shellQuote(`${input.label} sha verify skipped: no sha256sum/shasum on remote.`)} >&2`,
|
|
1363
|
+
"fi",
|
|
1364
|
+
"mv \"$remote_partial\" \"$remote_path\"",
|
|
1365
|
+
"printf '{\"uploaded\":true}\\n'",
|
|
1366
|
+
].join("\n"), timeoutMs, shellCommand, base64Body);
|
|
1367
|
+
requireSuccessfulResult(input.action, syncResult);
|
|
1368
|
+
let uploaded = false;
|
|
1369
|
+
try {
|
|
1370
|
+
uploaded = JSON.parse(syncResult.stdout.trim())?.uploaded === true;
|
|
1371
|
+
}
|
|
1372
|
+
catch (error) {
|
|
1373
|
+
throw new Error(`${input.label} sync wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1374
|
+
}
|
|
1375
|
+
return { uploaded, sha256 };
|
|
1376
|
+
}
|
|
1377
|
+
export async function syncSandboxCallbackBridgeEntrypoint(input) {
|
|
1378
|
+
const remoteEntrypoint = path.posix.join(input.assetRemoteDir, SANDBOX_CALLBACK_BRIDGE_ENTRYPOINT);
|
|
1379
|
+
const entrypointSource = await fs.readFile(input.bridgeAsset.entrypoint, "utf8");
|
|
1380
|
+
const { uploaded, sha256 } = await syncRemoteTextFileWithHashSkip({
|
|
1381
|
+
runner: input.runner,
|
|
1382
|
+
remoteCwd: input.remoteCwd,
|
|
1383
|
+
remoteDir: input.assetRemoteDir,
|
|
1384
|
+
remotePath: remoteEntrypoint,
|
|
1385
|
+
body: entrypointSource,
|
|
1386
|
+
label: "Sandbox callback bridge entrypoint",
|
|
1387
|
+
action: "sync sandbox callback bridge entrypoint",
|
|
1388
|
+
lockDir: path.posix.join(input.assetRemoteDir, ".paperclip-bridge-upload.lock"),
|
|
1389
|
+
timeoutMs: input.timeoutMs,
|
|
1390
|
+
shellCommand: input.shellCommand,
|
|
1391
|
+
});
|
|
1392
|
+
return {
|
|
1393
|
+
remoteEntrypoint,
|
|
1394
|
+
sha256,
|
|
1395
|
+
uploaded,
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
export async function startSandboxCallbackBridgeServer(input) {
|
|
1399
|
+
const timeoutMs = normalizeTimeoutMs(input.timeoutMs, DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS);
|
|
1400
|
+
const shellCommand = preferredShellForSandbox(input.shellCommand);
|
|
1401
|
+
const directories = sandboxCallbackBridgeDirectories(input.queueDir);
|
|
1402
|
+
let remoteEntrypoint = path.posix.join(input.assetRemoteDir, SANDBOX_CALLBACK_BRIDGE_ENTRYPOINT);
|
|
1403
|
+
if (input.bridgeAsset) {
|
|
1404
|
+
const assetSync = await syncSandboxCallbackBridgeEntrypoint({
|
|
1405
|
+
runner: input.runner,
|
|
1406
|
+
remoteCwd: input.remoteCwd,
|
|
1407
|
+
assetRemoteDir: input.assetRemoteDir,
|
|
1408
|
+
bridgeAsset: input.bridgeAsset,
|
|
1409
|
+
timeoutMs,
|
|
1410
|
+
shellCommand,
|
|
1411
|
+
});
|
|
1412
|
+
remoteEntrypoint = assetSync.remoteEntrypoint;
|
|
1413
|
+
}
|
|
1414
|
+
const env = buildSandboxCallbackBridgeEnv({
|
|
1415
|
+
queueDir: input.queueDir,
|
|
1416
|
+
bridgeToken: input.bridgeToken,
|
|
1417
|
+
host: input.host,
|
|
1418
|
+
port: input.port,
|
|
1419
|
+
pollIntervalMs: input.pollIntervalMs,
|
|
1420
|
+
responseTimeoutMs: input.responseTimeoutMs,
|
|
1421
|
+
maxQueueDepth: input.maxQueueDepth,
|
|
1422
|
+
maxBodyBytes: input.maxBodyBytes,
|
|
1423
|
+
});
|
|
1424
|
+
const nodeCommand = input.nodeCommand?.trim() || "node";
|
|
1425
|
+
const startResult = await input.runner.execute({
|
|
1426
|
+
command: shellCommand,
|
|
1427
|
+
args: shellCommandArgs([
|
|
1428
|
+
`mkdir -p ${shellQuote(directories.requestsDir)} ${shellQuote(directories.responsesDir)} ${shellQuote(directories.logsDir)}`,
|
|
1429
|
+
`rm -f ${shellQuote(directories.readyFile)} ${shellQuote(directories.pidFile)}`,
|
|
1430
|
+
`nohup ${shellQuote(nodeCommand)} ${shellQuote(remoteEntrypoint)} ` +
|
|
1431
|
+
`>> ${shellQuote(directories.logFile)} 2>&1 < /dev/null &`,
|
|
1432
|
+
"pid=$!",
|
|
1433
|
+
`printf '%s\\n' \"$pid\" > ${shellQuote(directories.pidFile)}`,
|
|
1434
|
+
"printf '{\"pid\":%s}\\n' \"$pid\"",
|
|
1435
|
+
].join("\n")),
|
|
1436
|
+
cwd: input.remoteCwd,
|
|
1437
|
+
env: {
|
|
1438
|
+
[SANDBOX_EXEC_CHANNEL_ENV]: SANDBOX_EXEC_CHANNEL_BRIDGE,
|
|
1439
|
+
...env,
|
|
1440
|
+
},
|
|
1441
|
+
timeoutMs,
|
|
1442
|
+
});
|
|
1443
|
+
requireSuccessfulResult("start sandbox callback bridge", startResult);
|
|
1444
|
+
const readyResult = await runShell(input.runner, input.remoteCwd, [
|
|
1445
|
+
"i=0",
|
|
1446
|
+
`while [ \"$i\" -lt 200 ]; do`,
|
|
1447
|
+
` if [ -s ${shellQuote(directories.readyFile)} ]; then`,
|
|
1448
|
+
` cat ${shellQuote(directories.readyFile)}`,
|
|
1449
|
+
" exit 0",
|
|
1450
|
+
" fi",
|
|
1451
|
+
` if [ -s ${shellQuote(directories.logFile)} ] && ! kill -0 \"$(cat ${shellQuote(directories.pidFile)} 2>/dev/null)\" 2>/dev/null; then`,
|
|
1452
|
+
` cat ${shellQuote(directories.logFile)} >&2`,
|
|
1453
|
+
" exit 1",
|
|
1454
|
+
" fi",
|
|
1455
|
+
" i=$((i + 1))",
|
|
1456
|
+
" sleep 0.05",
|
|
1457
|
+
"done",
|
|
1458
|
+
`echo "Timed out waiting for bridge readiness." >&2`,
|
|
1459
|
+
`if [ -s ${shellQuote(directories.logFile)} ]; then cat ${shellQuote(directories.logFile)} >&2; fi`,
|
|
1460
|
+
"exit 1",
|
|
1461
|
+
].join("\n"), timeoutMs, shellCommand);
|
|
1462
|
+
requireSuccessfulResult("wait for sandbox callback bridge readiness", readyResult);
|
|
1463
|
+
let readyData;
|
|
1464
|
+
try {
|
|
1465
|
+
readyData = JSON.parse(readyResult.stdout.trim());
|
|
1466
|
+
}
|
|
1467
|
+
catch (error) {
|
|
1468
|
+
throw new Error(`Sandbox callback bridge wrote invalid readiness JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
1469
|
+
}
|
|
1470
|
+
const host = typeof readyData.host === "string" && readyData.host.trim().length > 0
|
|
1471
|
+
? readyData.host.trim()
|
|
1472
|
+
: "127.0.0.1";
|
|
1473
|
+
const port = typeof readyData.port === "number" && Number.isFinite(readyData.port) ? readyData.port : 0;
|
|
1474
|
+
if (!port) {
|
|
1475
|
+
throw new Error("Sandbox callback bridge did not report a listening port.");
|
|
1476
|
+
}
|
|
1477
|
+
const baseUrl = typeof readyData.baseUrl === "string" && readyData.baseUrl.trim().length > 0
|
|
1478
|
+
? readyData.baseUrl.trim()
|
|
1479
|
+
: `http://${host}:${port}`;
|
|
1480
|
+
return {
|
|
1481
|
+
baseUrl,
|
|
1482
|
+
host,
|
|
1483
|
+
port,
|
|
1484
|
+
pid: typeof readyData.pid === "number" && Number.isFinite(readyData.pid) ? readyData.pid : 0,
|
|
1485
|
+
directories,
|
|
1486
|
+
stop: async () => {
|
|
1487
|
+
const stopResult = await input.runner.execute({
|
|
1488
|
+
command: shellCommand,
|
|
1489
|
+
args: shellCommandArgs([
|
|
1490
|
+
`if [ -s ${shellQuote(directories.pidFile)} ]; then`,
|
|
1491
|
+
` pid="$(cat ${shellQuote(directories.pidFile)})"`,
|
|
1492
|
+
" kill \"$pid\" 2>/dev/null || true",
|
|
1493
|
+
" i=0",
|
|
1494
|
+
" while kill -0 \"$pid\" 2>/dev/null && [ \"$i\" -lt 40 ]; do",
|
|
1495
|
+
" i=$((i + 1))",
|
|
1496
|
+
" sleep 0.05",
|
|
1497
|
+
" done",
|
|
1498
|
+
"fi",
|
|
1499
|
+
`rm -f ${shellQuote(directories.pidFile)} ${shellQuote(directories.readyFile)}`,
|
|
1500
|
+
].join("\n")),
|
|
1501
|
+
cwd: input.remoteCwd,
|
|
1502
|
+
env: {
|
|
1503
|
+
[SANDBOX_EXEC_CHANNEL_ENV]: SANDBOX_EXEC_CHANNEL_BRIDGE,
|
|
1504
|
+
},
|
|
1505
|
+
timeoutMs,
|
|
1506
|
+
});
|
|
1507
|
+
if (stopResult.timedOut) {
|
|
1508
|
+
throw new Error(buildRunnerFailureMessage("stop sandbox callback bridge", stopResult));
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
// ---------------------------------------------------------------------------
|
|
1514
|
+
// Sandbox HTTP/2 client gateway
|
|
1515
|
+
//
|
|
1516
|
+
// This gateway turns each local loopback request into one HTTP/2 stream to
|
|
1517
|
+
// the host server in `http2-bridge-server.ts`. It sits beside the file-mode
|
|
1518
|
+
// gateway above; it changes neither of them. The generated in-sandbox
|
|
1519
|
+
// entrypoint and the transport-selection path already select it.
|
|
1520
|
+
// ---------------------------------------------------------------------------
|
|
1521
|
+
/**
|
|
1522
|
+
* Constant-time bridge-token compare. Both this sandbox gateway and the host
|
|
1523
|
+
* server in `http2-bridge-server.ts` import this one helper, so the gateway
|
|
1524
|
+
* check and the independent host check (accepted security fix 4) apply the
|
|
1525
|
+
* exact same comparison rule. A length mismatch returns `false` without a
|
|
1526
|
+
* `timingSafeEqual` call, because `timingSafeEqual` throws on unequal buffer
|
|
1527
|
+
* lengths; both operands are bridge tokens of near-fixed length, so this one
|
|
1528
|
+
* length branch leaks no useful timing signal.
|
|
1529
|
+
*/
|
|
1530
|
+
export function compareBridgeTokensConstantTime(expected, received) {
|
|
1531
|
+
const expectedBytes = Buffer.from(expected, "utf8");
|
|
1532
|
+
const receivedBytes = Buffer.from(typeof received === "string" ? received : "", "utf8");
|
|
1533
|
+
if (expectedBytes.length !== receivedBytes.length)
|
|
1534
|
+
return false;
|
|
1535
|
+
return timingSafeEqual(expectedBytes, receivedBytes);
|
|
1536
|
+
}
|
|
1537
|
+
const SANDBOX_HTTP2_GATEWAY_DEFAULT_AUTHORITY = "bridge.internal";
|
|
1538
|
+
function forwardOneHttp2Request(session, request) {
|
|
1539
|
+
return new Promise((resolve, reject) => {
|
|
1540
|
+
const query = request.query.trim();
|
|
1541
|
+
const pathWithQuery = query.length === 0 ? request.path : `${request.path}${query.startsWith("?") ? query : `?${query}`}`;
|
|
1542
|
+
const requestHeaders = {
|
|
1543
|
+
":method": request.method,
|
|
1544
|
+
":path": pathWithQuery,
|
|
1545
|
+
authorization: `Bearer ${request.bridgeToken}`,
|
|
1546
|
+
...request.headers,
|
|
1547
|
+
};
|
|
1548
|
+
let stream;
|
|
1549
|
+
try {
|
|
1550
|
+
stream = session.request(requestHeaders, { endStream: request.body.length === 0 });
|
|
1551
|
+
}
|
|
1552
|
+
catch (error) {
|
|
1553
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
const chunks = [];
|
|
1557
|
+
let responseHeaders = {};
|
|
1558
|
+
let status = 502;
|
|
1559
|
+
let settled = false;
|
|
1560
|
+
const settle = (run) => {
|
|
1561
|
+
if (settled)
|
|
1562
|
+
return;
|
|
1563
|
+
settled = true;
|
|
1564
|
+
run();
|
|
1565
|
+
};
|
|
1566
|
+
stream.on("response", (headers) => {
|
|
1567
|
+
const rawStatus = headers[":status"];
|
|
1568
|
+
status = typeof rawStatus === "number" ? rawStatus : Number(rawStatus) || 502;
|
|
1569
|
+
responseHeaders = {};
|
|
1570
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1571
|
+
if (key.startsWith(":") || value == null)
|
|
1572
|
+
continue;
|
|
1573
|
+
responseHeaders[key] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
1577
|
+
stream.once("end", () => settle(() => resolve({ status, headers: responseHeaders, body: Buffer.concat(chunks) })));
|
|
1578
|
+
stream.once("error", (error) => settle(() => reject(error instanceof Error ? error : new Error(String(error)))));
|
|
1579
|
+
stream.once("aborted", () => settle(() => reject(new Error("Bridge HTTP/2 stream aborted."))));
|
|
1580
|
+
if (request.body.length > 0) {
|
|
1581
|
+
stream.end(request.body);
|
|
1582
|
+
}
|
|
1583
|
+
else if (!stream.writableEnded) {
|
|
1584
|
+
stream.end();
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Create the sandbox HTTP/2 client gateway. It opens one HTTP/2 client
|
|
1590
|
+
* session on the transport `createConnection` returns, and forwards each
|
|
1591
|
+
* local request the caller hands it (already checked against the bridge
|
|
1592
|
+
* token — see {@link SandboxHttp2BridgeGatewayRequest.receivedToken}) as one
|
|
1593
|
+
* HTTP/2 stream. It keeps the header allowlist on the sandbox side, exactly
|
|
1594
|
+
* as the file-mode gateway does.
|
|
1595
|
+
*/
|
|
1596
|
+
export function createSandboxHttp2BridgeGateway(options) {
|
|
1597
|
+
const authority = options.authority?.trim() || SANDBOX_HTTP2_GATEWAY_DEFAULT_AUTHORITY;
|
|
1598
|
+
const headerAllowlist = options.headerAllowlist ?? DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST;
|
|
1599
|
+
const session = http2.connect(`http://${authority}`, {
|
|
1600
|
+
createConnection: options.createConnection,
|
|
1601
|
+
});
|
|
1602
|
+
// A session-level fault fails every in-flight `forwardRequest` call through
|
|
1603
|
+
// that stream's own `error`/`aborted` handler. This listener only stops
|
|
1604
|
+
// Node from raising an unhandled `error` event for the session itself.
|
|
1605
|
+
session.on("error", () => undefined);
|
|
1606
|
+
session.on("goaway", (errorCode, lastStreamId) => {
|
|
1607
|
+
options.onGoaway?.({ lastStreamId, errorCode });
|
|
1608
|
+
});
|
|
1609
|
+
return {
|
|
1610
|
+
forwardRequest(request) {
|
|
1611
|
+
// The gateway check (accepted security fix 4 keeps this as well as the
|
|
1612
|
+
// independent host-side check): a request whose token does not match
|
|
1613
|
+
// the per-run bridge token never opens a stream.
|
|
1614
|
+
if (!compareBridgeTokensConstantTime(options.bridgeToken, request.receivedToken)) {
|
|
1615
|
+
return Promise.reject(new Error("Invalid bridge token."));
|
|
1616
|
+
}
|
|
1617
|
+
return forwardOneHttp2Request(session, {
|
|
1618
|
+
bridgeToken: options.bridgeToken,
|
|
1619
|
+
method: request.method,
|
|
1620
|
+
path: request.path,
|
|
1621
|
+
query: request.query,
|
|
1622
|
+
headers: sanitizeSandboxCallbackBridgeHeaders(request.headers, headerAllowlist),
|
|
1623
|
+
body: request.body,
|
|
1624
|
+
});
|
|
1625
|
+
},
|
|
1626
|
+
close() {
|
|
1627
|
+
return new Promise((resolve) => {
|
|
1628
|
+
if (session.closed || session.destroyed) {
|
|
1629
|
+
resolve();
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
session.close(() => resolve());
|
|
1633
|
+
});
|
|
1634
|
+
},
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* The zero-dependency codec the generated duplex gateway embeds. It is a plain
|
|
1639
|
+
* JavaScript copy of the encode side of the host codec in `duplex-frame-codec.ts`.
|
|
1640
|
+
* The generated gateway never decodes: it writes exactly one READY line, then
|
|
1641
|
+
* hands stdout to its transport. So this copy carries only the frame version and
|
|
1642
|
+
* the encode function, not a decoder. It uses only the `Buffer` and `JSON`
|
|
1643
|
+
* globals, so the generated `.mjs` needs no workspace import. It carries no
|
|
1644
|
+
* template literal and no `${` sequence, so it embeds inside the gateway
|
|
1645
|
+
* template literal with no escape.
|
|
1646
|
+
*
|
|
1647
|
+
* A shared fixture file (`duplex-frame-vectors.json`) proves the host encode side
|
|
1648
|
+
* stays wire compatible with this copy. {@link getSandboxDuplexGatewayCodecSource}
|
|
1649
|
+
* returns this exact source so a test can run the READY encode vector against it.
|
|
1650
|
+
*/
|
|
1651
|
+
const DUPLEX_GATEWAY_CODEC_SOURCE = `const DUPLEX_FRAME_VERSION = 2;
|
|
1652
|
+
|
|
1653
|
+
function encodeDuplexFrame(frame) {
|
|
1654
|
+
return JSON.stringify(frame) + "\\n";
|
|
1655
|
+
}`;
|
|
1656
|
+
/**
|
|
1657
|
+
* Return the exact zero-dependency codec source the generated duplex gateway
|
|
1658
|
+
* embeds. A test wraps this source and calls `encodeDuplexFrame` to prove the
|
|
1659
|
+
* embedded copy encodes the READY frame the same way the host encode side does.
|
|
1660
|
+
* The source declares `encodeDuplexFrame` and `DUPLEX_FRAME_VERSION`, but exports
|
|
1661
|
+
* neither; a caller wraps it to read those names.
|
|
1662
|
+
*/
|
|
1663
|
+
export function getSandboxDuplexGatewayCodecSource() {
|
|
1664
|
+
return DUPLEX_GATEWAY_CODEC_SOURCE;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Zero-dependency source for the gateway's own aggregate body-byte ledger.
|
|
1668
|
+
* `readBodyBytes` in the generated gateway reserves against one instance of
|
|
1669
|
+
* this ledger before it retains a chunk and before it concatenates the final
|
|
1670
|
+
* buffer, so a burst of concurrent requests cannot grow the gateway
|
|
1671
|
+
* process's own memory without limit. This ledger is separate from, and
|
|
1672
|
+
* independent of, the ceiling `http2-bridge-server.ts` enforces on the host
|
|
1673
|
+
* side of the bridge connection: each side bounds only the memory in its own
|
|
1674
|
+
* process. It uses no global beyond plain JavaScript, so it embeds inside the
|
|
1675
|
+
* gateway template literal with no escape. A test wraps this source directly
|
|
1676
|
+
* to exercise `reserve`/`release`, the same way
|
|
1677
|
+
* {@link getSandboxDuplexGatewayCodecSource} lets a test exercise the codec.
|
|
1678
|
+
*/
|
|
1679
|
+
const BRIDGE_PROCESS_BODY_LEDGER_SOURCE = `function createBridgeProcessBodyLedger(maxBytes) {
|
|
1680
|
+
let reservedBytes = 0;
|
|
1681
|
+
return {
|
|
1682
|
+
reserve(byteCount) {
|
|
1683
|
+
if (reservedBytes + byteCount > maxBytes) {
|
|
1684
|
+
return false;
|
|
1685
|
+
}
|
|
1686
|
+
reservedBytes += byteCount;
|
|
1687
|
+
return true;
|
|
1688
|
+
},
|
|
1689
|
+
release(byteCount) {
|
|
1690
|
+
reservedBytes -= byteCount;
|
|
1691
|
+
},
|
|
1692
|
+
get reservedBytes() {
|
|
1693
|
+
return reservedBytes;
|
|
1694
|
+
},
|
|
1695
|
+
};
|
|
1696
|
+
}`;
|
|
1697
|
+
/**
|
|
1698
|
+
* Return the exact zero-dependency ledger source the generated gateway
|
|
1699
|
+
* embeds. A test wraps this source and calls `createBridgeProcessBodyLedger`
|
|
1700
|
+
* to prove the embedded copy reserves and releases bytes correctly, with no
|
|
1701
|
+
* spawned process involved.
|
|
1702
|
+
*/
|
|
1703
|
+
export function getSandboxBridgeProcessBodyLedgerSource() {
|
|
1704
|
+
return BRIDGE_PROCESS_BODY_LEDGER_SOURCE;
|
|
1705
|
+
}
|
|
1706
|
+
export function getSandboxCallbackBridgeServerSource() {
|
|
1707
|
+
return `import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
1708
|
+
import { createServer } from "node:http";
|
|
1709
|
+
import { promises as fs } from "node:fs";
|
|
1710
|
+
import path from "node:path";
|
|
1711
|
+
import http2 from "node:http2";
|
|
1712
|
+
import { Duplex } from "node:stream";
|
|
1713
|
+
|
|
1714
|
+
const bridgeMode = process.env.PAPERCLIP_API_BRIDGE_MODE || "${SANDBOX_CALLBACK_BRIDGE_FILE_MODE}";
|
|
1715
|
+
const queueDir = process.env.PAPERCLIP_BRIDGE_QUEUE_DIR;
|
|
1716
|
+
const bridgeToken = process.env.PAPERCLIP_BRIDGE_TOKEN;
|
|
1717
|
+
const host = process.env.PAPERCLIP_BRIDGE_HOST || "127.0.0.1";
|
|
1718
|
+
const port = Number(process.env.PAPERCLIP_BRIDGE_PORT || "0");
|
|
1719
|
+
// The host assigns the loopback port and passes it through the launch
|
|
1720
|
+
// environment. The gateway binds exactly this port; it never selects a
|
|
1721
|
+
// different one. The host also passes one random per-open nonce here. The
|
|
1722
|
+
// gateway echoes it in the READY frame so the host correlates READY with this
|
|
1723
|
+
// channel open. The nonce is a liveness signal, not authentication.
|
|
1724
|
+
const bridgeNonce = process.env.PAPERCLIP_BRIDGE_NONCE || "";
|
|
1725
|
+
const pollIntervalMs = Number(process.env.PAPERCLIP_BRIDGE_POLL_INTERVAL_MS || "100");
|
|
1726
|
+
const responseTimeoutMs = Number(
|
|
1727
|
+
process.env.PAPERCLIP_BRIDGE_RESPONSE_TIMEOUT_MS || "${DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS}",
|
|
1728
|
+
);
|
|
1729
|
+
const maxQueueDepth = Number(process.env.PAPERCLIP_BRIDGE_MAX_QUEUE_DEPTH || "${DEFAULT_BRIDGE_MAX_QUEUE_DEPTH}");
|
|
1730
|
+
const maxBodyBytes = Number(process.env.PAPERCLIP_BRIDGE_MAX_BODY_BYTES || "${DEFAULT_BRIDGE_MAX_BODY_BYTES}");
|
|
1731
|
+
// The header allowlist. Both the file gateway and the http2 gateway strip an
|
|
1732
|
+
// inbound request to these headers before they forward it. One copy serves both
|
|
1733
|
+
// modes. The route allowlist stays on the host: both modes forward a request to
|
|
1734
|
+
// the host, and the host enforces the same route allowlist for each.
|
|
1735
|
+
const allowedHeaders = new Set(${JSON.stringify([...DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST])});
|
|
1736
|
+
|
|
1737
|
+
if (!bridgeToken) {
|
|
1738
|
+
throw new Error("PAPERCLIP_BRIDGE_TOKEN is required.");
|
|
1739
|
+
}
|
|
1740
|
+
// Closed allowlist for the bridge mode. The generated gateway supports exactly
|
|
1741
|
+
// two transports: http2 and the file-mode queue. Every other value, including
|
|
1742
|
+
// the retired duplex transport, fails startup at once instead of falling
|
|
1743
|
+
// through to a mode that never ran. This check runs before the queue-directory
|
|
1744
|
+
// check below, so an unsupported mode never reaches a state where a missing
|
|
1745
|
+
// queue directory masks the real problem.
|
|
1746
|
+
if (
|
|
1747
|
+
bridgeMode !== "${SANDBOX_CALLBACK_BRIDGE_HTTP2_MODE}" &&
|
|
1748
|
+
bridgeMode !== "${SANDBOX_CALLBACK_BRIDGE_FILE_MODE}"
|
|
1749
|
+
) {
|
|
1750
|
+
throw new Error("Unsupported PAPERCLIP_API_BRIDGE_MODE: " + bridgeMode);
|
|
1751
|
+
}
|
|
1752
|
+
if (bridgeMode !== "${SANDBOX_CALLBACK_BRIDGE_HTTP2_MODE}" && !queueDir) {
|
|
1753
|
+
throw new Error("PAPERCLIP_BRIDGE_QUEUE_DIR and PAPERCLIP_BRIDGE_TOKEN are required.");
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// A crashed gateway is a dead loopback port for the rest of the run: nothing
|
|
1757
|
+
// inside the sandbox respawns this process, and every later agent API call
|
|
1758
|
+
// then fails at the connection level. Once the gateway is ready, log an
|
|
1759
|
+
// uncaught fault to stderr (the host redirects it into logs/bridge.log) and
|
|
1760
|
+
// keep serving — the relay holds no state a fault can corrupt beyond the one
|
|
1761
|
+
// request it interrupted. Before readiness the same fault means the gateway
|
|
1762
|
+
// can never become usable (a failed bind, a failed readiness write), so exit
|
|
1763
|
+
// instead: surviving there only leaves an un-ready zombie behind while the
|
|
1764
|
+
// host waits out its readiness poll.
|
|
1765
|
+
let gatewayReady = false;
|
|
1766
|
+
process.on("uncaughtException", (error) => {
|
|
1767
|
+
process.stderr.write(
|
|
1768
|
+
"[paperclip-bridge] uncaught exception: " + (error && error.stack ? error.stack : String(error)) + "\\n",
|
|
1769
|
+
);
|
|
1770
|
+
if (!gatewayReady) {
|
|
1771
|
+
process.exit(1);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
process.on("unhandledRejection", (reason) => {
|
|
1775
|
+
const detail = reason && typeof reason === "object" && "stack" in reason ? reason.stack : String(reason);
|
|
1776
|
+
process.stderr.write("[paperclip-bridge] unhandled rejection: " + detail + "\\n");
|
|
1777
|
+
if (!gatewayReady) {
|
|
1778
|
+
process.exit(1);
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
// The embedded zero-dependency frame codec. The duplex gateway uses it; the file
|
|
1783
|
+
// gateway ignores it.
|
|
1784
|
+
${DUPLEX_GATEWAY_CODEC_SOURCE}
|
|
1785
|
+
|
|
1786
|
+
// The embedded zero-dependency process body-byte ledger. Both gateway modes
|
|
1787
|
+
// use it: readBodyBytes reserves against it, and each mode's request
|
|
1788
|
+
// handler releases what it reserved once the body is no longer needed.
|
|
1789
|
+
${BRIDGE_PROCESS_BODY_LEDGER_SOURCE}
|
|
1790
|
+
${sandboxBridgeBodyCodecSource()}
|
|
1791
|
+
|
|
1792
|
+
// HTTP/2's multiplier matches HTTP2_BRIDGE_MAX_CONCURRENT_STREAMS (4) in
|
|
1793
|
+
// http2-bridge-server.ts, doubled because readBodyBytes reserves a body's
|
|
1794
|
+
// bytes twice: once for the retained chunk array, once for the concatenated
|
|
1795
|
+
// copy, since both are live buffers at once. This gives the gateway process
|
|
1796
|
+
// its own aggregate ceiling on live request-body bytes, so a burst of
|
|
1797
|
+
// concurrent requests cannot grow this process's memory without limit, even
|
|
1798
|
+
// though every individual body already passes the maxBodyBytes check below.
|
|
1799
|
+
// This ceiling is independent of, and separate from, the ceiling the host
|
|
1800
|
+
// enforces on its own side of the bridge connection. Queue mode also counts
|
|
1801
|
+
// JSON/base64 envelopes; its aggregate ceiling never exceeds 1 GiB.
|
|
1802
|
+
const maxProcessBodyBytes = bridgeMode === "${SANDBOX_CALLBACK_BRIDGE_FILE_MODE}"
|
|
1803
|
+
? Math.min(1024 * 1024 * 1024, 4 * (4 * sandboxBridgeEnvelopeLimit(maxBodyBytes) + 4 * maxBodyBytes))
|
|
1804
|
+
: maxBodyBytes * 8;
|
|
1805
|
+
const processBodyLedger = createBridgeProcessBodyLedger(maxProcessBodyBytes);
|
|
1806
|
+
|
|
1807
|
+
// A denied process-ledger reservation answers 503: the sandbox client should
|
|
1808
|
+
// retry once other in-flight bodies finish and release their bytes, the same
|
|
1809
|
+
// retry contract the host side gives for its own capacity denial.
|
|
1810
|
+
class BridgeProcessCapacityError extends Error {
|
|
1811
|
+
constructor() {
|
|
1812
|
+
super("The bridge gateway process reached its reserved body byte ceiling. Retry later.");
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
function sleep(ms) {
|
|
1817
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
function normalizeHeaders(headers) {
|
|
1821
|
+
const out = {};
|
|
1822
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1823
|
+
if (value == null) continue;
|
|
1824
|
+
const normalizedKey = key.toLowerCase();
|
|
1825
|
+
if (!allowedHeaders.has(normalizedKey)) {
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
out[normalizedKey] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1829
|
+
}
|
|
1830
|
+
return out;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// Reserves each chunk's bytes against the process ledger before the chunk
|
|
1834
|
+
// joins the retained array, and reserves the concatenated buffer's own byte
|
|
1835
|
+
// count before Buffer.concat allocates it, mirroring the order
|
|
1836
|
+
// readHttp2StreamBody enforces on the host side. Returns the body buffer
|
|
1837
|
+
// together with a release function: the caller must call release exactly
|
|
1838
|
+
// once, after the body is no longer needed, so its reserved bytes return to
|
|
1839
|
+
// the ledger on completion, on an error the caller raises later, on a client
|
|
1840
|
+
// abort, and on a timeout — every path funnels through the caller's own
|
|
1841
|
+
// finally block. A read that fails here (the size limit, or a denied
|
|
1842
|
+
// process reservation) releases its own partial reservation immediately, so
|
|
1843
|
+
// no caller-side release call is needed for that path.
|
|
1844
|
+
async function readBodyBytes(req) {
|
|
1845
|
+
const chunks = [];
|
|
1846
|
+
let totalBytes = 0;
|
|
1847
|
+
let reservedBytes = 0;
|
|
1848
|
+
try {
|
|
1849
|
+
for await (const chunk of req) {
|
|
1850
|
+
const nextChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1851
|
+
totalBytes += nextChunk.byteLength;
|
|
1852
|
+
if (totalBytes > maxBodyBytes) {
|
|
1853
|
+
throw new Error("Bridge request body exceeded the configured size limit.");
|
|
1854
|
+
}
|
|
1855
|
+
if (!processBodyLedger.reserve(nextChunk.byteLength)) {
|
|
1856
|
+
throw new BridgeProcessCapacityError();
|
|
1857
|
+
}
|
|
1858
|
+
reservedBytes += nextChunk.byteLength;
|
|
1859
|
+
chunks.push(nextChunk);
|
|
1860
|
+
}
|
|
1861
|
+
if (!processBodyLedger.reserve(totalBytes)) {
|
|
1862
|
+
throw new BridgeProcessCapacityError();
|
|
1863
|
+
}
|
|
1864
|
+
reservedBytes += totalBytes;
|
|
1865
|
+
const body = Buffer.concat(chunks);
|
|
1866
|
+
return { body, release: () => processBodyLedger.release(reservedBytes) };
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
processBodyLedger.release(reservedBytes);
|
|
1869
|
+
throw error;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
function tokensMatch(received) {
|
|
1874
|
+
const expected = Buffer.from(bridgeToken, "utf8");
|
|
1875
|
+
const actual = Buffer.from(typeof received === "string" ? received : "", "utf8");
|
|
1876
|
+
if (expected.length !== actual.length) return false;
|
|
1877
|
+
return timingSafeEqual(expected, actual);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
function writeJsonResponse(res, status, body) {
|
|
1881
|
+
res.statusCode = status;
|
|
1882
|
+
res.setHeader("content-type", "application/json");
|
|
1883
|
+
res.end(JSON.stringify(body));
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
async function runFileGateway() {
|
|
1887
|
+
const requestsDir = path.posix.join(queueDir, "requests");
|
|
1888
|
+
const responsesDir = path.posix.join(queueDir, "responses");
|
|
1889
|
+
const logsDir = path.posix.join(queueDir, "logs");
|
|
1890
|
+
const readyFile = path.posix.join(queueDir, "ready.json");
|
|
1891
|
+
|
|
1892
|
+
async function queueDepth() {
|
|
1893
|
+
const entries = await fs.readdir(requestsDir, { withFileTypes: true }).catch(() => []);
|
|
1894
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).length;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// Delete request files older than the response deadline. Every live caller
|
|
1898
|
+
// cleans its own request file when it times out, so a file this old is an
|
|
1899
|
+
// orphan: its writer was killed mid-wait, or a previous gateway process died
|
|
1900
|
+
// and left its queue behind. Orphans otherwise count toward the queue-depth
|
|
1901
|
+
// cap forever and wedge the gateway at a permanent 503.
|
|
1902
|
+
async function sweepStaleRequests() {
|
|
1903
|
+
const staleBefore = Date.now() - responseTimeoutMs - 2000;
|
|
1904
|
+
const entries = await fs.readdir(requestsDir, { withFileTypes: true }).catch(() => []);
|
|
1905
|
+
for (const entry of entries) {
|
|
1906
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
1907
|
+
const filePath = path.posix.join(requestsDir, entry.name);
|
|
1908
|
+
const stats = await fs.stat(filePath).catch(() => null);
|
|
1909
|
+
if (stats && stats.mtimeMs < staleBefore) {
|
|
1910
|
+
await fs.rm(filePath, { force: true }).catch(() => undefined);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
async function waitForResponse(requestId, reserveResponse) {
|
|
1916
|
+
const responsePath = path.posix.join(responsesDir, \`\${requestId}.json\`);
|
|
1917
|
+
const deadline = Date.now() + responseTimeoutMs;
|
|
1918
|
+
while (Date.now() < deadline) {
|
|
1919
|
+
const handle = await fs.open(responsePath, "r").catch(error => {
|
|
1920
|
+
if (error.code === "ENOENT") return null;
|
|
1921
|
+
throw error;
|
|
1922
|
+
});
|
|
1923
|
+
if (handle) {
|
|
1924
|
+
try {
|
|
1925
|
+
const stat = await handle.stat();
|
|
1926
|
+
if (stat.size > sandboxBridgeEnvelopeLimit(maxBodyBytes)) throw new Error("Bridge response envelope exceeded the configured size limit.");
|
|
1927
|
+
reserveResponse(6 * stat.size + 1);
|
|
1928
|
+
const bytes = Buffer.alloc(stat.size + 1);
|
|
1929
|
+
let length = 0;
|
|
1930
|
+
while (length < bytes.length) {
|
|
1931
|
+
const read = await handle.read(bytes, length, bytes.length - length, length);
|
|
1932
|
+
if (!read.bytesRead) break;
|
|
1933
|
+
length += read.bytesRead;
|
|
1934
|
+
}
|
|
1935
|
+
if (length !== stat.size) throw new Error("Bridge response envelope changed while reading.");
|
|
1936
|
+
return JSON.parse(bytes.subarray(0, length).toString("utf8"));
|
|
1937
|
+
} finally {
|
|
1938
|
+
await handle.close();
|
|
1939
|
+
await fs.rm(responsePath, { force: true }).catch(() => undefined);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
await sleep(pollIntervalMs);
|
|
1943
|
+
}
|
|
1944
|
+
throw new Error("Timed out waiting for host bridge response.");
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
const server = createServer(async (req, res) => {
|
|
1948
|
+
// readBodyBytes reserves the body's bytes against the process ledger and
|
|
1949
|
+
// hands back a release function; this holds it so the finally below
|
|
1950
|
+
// releases those bytes exactly once no matter how this handler ends —
|
|
1951
|
+
// its normal completion, a thrown error, a client abort, or a deadline
|
|
1952
|
+
// timeout all reach the same finally.
|
|
1953
|
+
let releaseBodyReservation = null;
|
|
1954
|
+
let envelopeReservation = 0;
|
|
1955
|
+
let dispatched = false;
|
|
1956
|
+
try {
|
|
1957
|
+
const auth = req.headers.authorization || "";
|
|
1958
|
+
const receivedToken = auth.startsWith("Bearer ") ? auth.slice("Bearer ".length) : "";
|
|
1959
|
+
if (!tokensMatch(receivedToken)) {
|
|
1960
|
+
writeJsonResponse(res, 401, { error: "Invalid bridge token." });
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
if (await queueDepth() >= maxQueueDepth) {
|
|
1965
|
+
// Reclaim orphaned request files before rejecting; only a queue that
|
|
1966
|
+
// is genuinely full of live requests gets the 503.
|
|
1967
|
+
await sweepStaleRequests();
|
|
1968
|
+
if (await queueDepth() >= maxQueueDepth) {
|
|
1969
|
+
writeJsonResponse(res, 503, { error: "Bridge request queue is full." });
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
1975
|
+
const contentType = typeof req.headers["content-type"] === "string" ? req.headers["content-type"] : "";
|
|
1976
|
+
const multipartAttachment = req.method === "POST"
|
|
1977
|
+
&& /^\\/api\\/companies\\/[^/]+\\/issues\\/[^/]+\\/attachments$/.test(url.pathname)
|
|
1978
|
+
&& /^multipart\\/form-data(?:;|$)/i.test(contentType);
|
|
1979
|
+
if (req.method && req.method !== "GET" && req.method !== "HEAD" && !/json/i.test(contentType) && !multipartAttachment) {
|
|
1980
|
+
writeJsonResponse(res, 415, { error: "Bridge only accepts JSON request bodies." });
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
const requestId = randomUUID();
|
|
1984
|
+
// Reserve encoded envelopes and response decoding separately from the
|
|
1985
|
+
// incoming byte buffers. Capacity rejection precedes dispatch, hence 503.
|
|
1986
|
+
const { body: requestBody, release } = await readBodyBytes(req);
|
|
1987
|
+
releaseBodyReservation = release;
|
|
1988
|
+
const reserveBytes = 4 * sandboxBridgeEnvelopeLimit(requestBody.length);
|
|
1989
|
+
if (!processBodyLedger.reserve(reserveBytes)) throw new BridgeProcessCapacityError();
|
|
1990
|
+
envelopeReservation = reserveBytes;
|
|
1991
|
+
const payload = {
|
|
1992
|
+
id: requestId,
|
|
1993
|
+
method: req.method || "GET",
|
|
1994
|
+
path: url.pathname,
|
|
1995
|
+
query: url.search,
|
|
1996
|
+
headers: normalizeHeaders(req.headers),
|
|
1997
|
+
...encodeSandboxBridgeBody(multipartAttachment ? requestBody : requestBody.toString("utf8"), maxBodyBytes),
|
|
1998
|
+
createdAt: new Date().toISOString(),
|
|
1999
|
+
};
|
|
2000
|
+
const requestPath = path.posix.join(requestsDir, \`\${requestId}.json\`);
|
|
2001
|
+
const tempPath = \`\${requestPath}.tmp\`;
|
|
2002
|
+
await fs.writeFile(tempPath, \`\${JSON.stringify(payload)}\\n\`, "utf8");
|
|
2003
|
+
dispatched = true;
|
|
2004
|
+
await fs.rename(tempPath, requestPath);
|
|
2005
|
+
|
|
2006
|
+
let response;
|
|
2007
|
+
try {
|
|
2008
|
+
response = await waitForResponse(requestId, bytes => {
|
|
2009
|
+
if (!processBodyLedger.reserve(bytes)) throw new BridgeProcessCapacityError();
|
|
2010
|
+
envelopeReservation += bytes;
|
|
2011
|
+
});
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
// The host never delivered a response inside the deadline. Remove this
|
|
2014
|
+
// request's file so it cannot pile up toward the queue-depth cap. The
|
|
2015
|
+
// host's normal response write is guarded on the request file, so the
|
|
2016
|
+
// removal also tells the host that no caller waits anymore. Without
|
|
2017
|
+
// this cleanup a stalled host wedges the gateway at the cap and every
|
|
2018
|
+
// later request gets an immediate 503 until run end.
|
|
2019
|
+
await fs.rm(requestPath, { force: true }).catch(() => undefined);
|
|
2020
|
+
throw error;
|
|
2021
|
+
}
|
|
2022
|
+
const responseHeaders = response.headers || {};
|
|
2023
|
+
// The host marks a possibly-committed mutation with an indeterminate outcome.
|
|
2024
|
+
// The host cannot cancel a host operation that is in flight, so the mutation
|
|
2025
|
+
// may have committed before the worker aborted the handler. A 5xx status is
|
|
2026
|
+
// retryable by convention, so a caller that retries 5xx would apply the
|
|
2027
|
+
// mutation twice. Map the indeterminate outcome to a non-retryable 409, so a
|
|
2028
|
+
// standard retry policy does not repeat the request. The outcome header and
|
|
2029
|
+
// body stay, so a caller that reads them still sees the indeterminate result.
|
|
2030
|
+
const bridgeOutcome = responseHeaders["x-paperclip-bridge-outcome"];
|
|
2031
|
+
if (bridgeOutcome === "indeterminate") {
|
|
2032
|
+
res.statusCode = 409;
|
|
2033
|
+
} else {
|
|
2034
|
+
res.statusCode = typeof response.status === "number" ? response.status : 200;
|
|
2035
|
+
}
|
|
2036
|
+
for (const [key, value] of Object.entries(responseHeaders)) {
|
|
2037
|
+
if (typeof value !== "string" || key.toLowerCase() === "content-length") continue;
|
|
2038
|
+
res.setHeader(key, value);
|
|
2039
|
+
}
|
|
2040
|
+
res.end(decodeSandboxBridgeBody(response, maxBodyBytes));
|
|
2041
|
+
} catch (error) {
|
|
2042
|
+
// Capacity rejection before dispatch is retryable. After a mutation was
|
|
2043
|
+
// dispatched, a missing or corrupt receipt cannot prove that it failed.
|
|
2044
|
+
const uncertainWrite = dispatched && !["GET", "HEAD", "OPTIONS", "TRACE"].includes(req.method || "GET");
|
|
2045
|
+
const status = uncertainWrite ? 409 : error instanceof BridgeProcessCapacityError ? 503 : 502;
|
|
2046
|
+
if (uncertainWrite) res.setHeader("x-paperclip-bridge-outcome", "indeterminate");
|
|
2047
|
+
writeJsonResponse(res, status, {
|
|
2048
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2049
|
+
...(uncertainWrite ? { outcome: "indeterminate", retryable: false } : {}),
|
|
2050
|
+
});
|
|
2051
|
+
} finally {
|
|
2052
|
+
releaseBodyReservation?.();
|
|
2053
|
+
processBodyLedger.release(envelopeReservation);
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
async function shutdown() {
|
|
2058
|
+
server.close(() => {
|
|
2059
|
+
process.exit(0);
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
process.on("SIGINT", () => void shutdown());
|
|
2064
|
+
process.on("SIGTERM", () => void shutdown());
|
|
2065
|
+
|
|
2066
|
+
await fs.mkdir(requestsDir, { recursive: true });
|
|
2067
|
+
await fs.mkdir(responsesDir, { recursive: true });
|
|
2068
|
+
await fs.mkdir(logsDir, { recursive: true });
|
|
2069
|
+
|
|
2070
|
+
// Newer Node runtimes do not reliably surface a failed bind through
|
|
2071
|
+
// uncaughtException here: with nothing else keeping the event loop alive,
|
|
2072
|
+
// the process can drain and exit 0 before the error event is delivered
|
|
2073
|
+
// (observed on Node 24/25; Node 22 delivered it). Attach an explicit error
|
|
2074
|
+
// listener and pin the loop with a keepalive until the bind settles, so a
|
|
2075
|
+
// startup failure exits 1 with the fault on stderr on every runtime.
|
|
2076
|
+
const bindKeepalive = setInterval(() => {}, 1000);
|
|
2077
|
+
server.once("error", (error) => {
|
|
2078
|
+
clearInterval(bindKeepalive);
|
|
2079
|
+
process.stderr.write(
|
|
2080
|
+
"[paperclip-bridge] server error: " + (error && error.stack ? error.stack : String(error)) + "\\n",
|
|
2081
|
+
);
|
|
2082
|
+
if (!gatewayReady) {
|
|
2083
|
+
process.exit(1);
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
server.listen(port, host, async () => {
|
|
2087
|
+
clearInterval(bindKeepalive);
|
|
2088
|
+
const address = server.address();
|
|
2089
|
+
if (!address || typeof address === "string") {
|
|
2090
|
+
throw new Error("Bridge server did not expose a TCP address.");
|
|
2091
|
+
}
|
|
2092
|
+
const ready = {
|
|
2093
|
+
pid: process.pid,
|
|
2094
|
+
host,
|
|
2095
|
+
port: address.port,
|
|
2096
|
+
baseUrl: \`http://\${host}:\${address.port}\`,
|
|
2097
|
+
startedAt: new Date().toISOString(),
|
|
2098
|
+
};
|
|
2099
|
+
const tempReadyFile = \`\${readyFile}.tmp\`;
|
|
2100
|
+
await fs.writeFile(tempReadyFile, JSON.stringify(ready), "utf8");
|
|
2101
|
+
await fs.rename(tempReadyFile, readyFile);
|
|
2102
|
+
// The readiness file is on disk, so the host will adopt this process.
|
|
2103
|
+
// From here on an uncaught fault must not kill the listener.
|
|
2104
|
+
gatewayReady = true;
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// ---------------------------------------------------------------------------
|
|
2109
|
+
// http2_v1: run one Node HTTP/2 client session directly on stdin/stdout.
|
|
2110
|
+
//
|
|
2111
|
+
// This gateway writes exactly one frame-codec line before it hands stdin and
|
|
2112
|
+
// stdout to the HTTP/2 client: the READY line, so the host readiness gate
|
|
2113
|
+
// accepts the same handshake it already accepts for every mode. After that
|
|
2114
|
+
// one write, only the HTTP/2 client touches stdout: this function calls
|
|
2115
|
+
// writeFrame no more, and it starts no heartbeat timer, so no non-HTTP/2
|
|
2116
|
+
// writer can put a byte on stdout between the READY line and the client
|
|
2117
|
+
// connection preface.
|
|
2118
|
+
// ---------------------------------------------------------------------------
|
|
2119
|
+
|
|
2120
|
+
function createStdioDuplex() {
|
|
2121
|
+
const duplex = new Duplex({
|
|
2122
|
+
read() {
|
|
2123
|
+
// process.stdin pushes bytes through the "data" listener below; there
|
|
2124
|
+
// is nothing to pull on demand here.
|
|
2125
|
+
},
|
|
2126
|
+
write(chunk, _encoding, callback) {
|
|
2127
|
+
const flushed = process.stdout.write(chunk);
|
|
2128
|
+
if (flushed) {
|
|
2129
|
+
callback();
|
|
2130
|
+
} else {
|
|
2131
|
+
process.stdout.once("drain", () => callback());
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
});
|
|
2135
|
+
process.stdin.on("data", (chunk) => {
|
|
2136
|
+
duplex.push(chunk);
|
|
2137
|
+
});
|
|
2138
|
+
process.stdin.on("end", () => {
|
|
2139
|
+
duplex.push(null);
|
|
2140
|
+
});
|
|
2141
|
+
process.stdin.on("error", (error) => {
|
|
2142
|
+
duplex.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
2143
|
+
});
|
|
2144
|
+
return duplex;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
function runHttp2Gateway() {
|
|
2148
|
+
function diag(message) {
|
|
2149
|
+
// Diagnostics go to stderr only, the same as every other mode.
|
|
2150
|
+
process.stderr.write("[paperclip-bridge] " + message + "\\n");
|
|
2151
|
+
}
|
|
2152
|
+
function writeFrame(frame) {
|
|
2153
|
+
process.stdout.write(encodeDuplexFrame(frame));
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
const authority = "bridge.internal";
|
|
2157
|
+
let session = null;
|
|
2158
|
+
let unavailable = false;
|
|
2159
|
+
|
|
2160
|
+
function openSession() {
|
|
2161
|
+
if (session) return session;
|
|
2162
|
+
const stdio = createStdioDuplex();
|
|
2163
|
+
session = http2.connect("http://" + authority, {
|
|
2164
|
+
createConnection: () => stdio,
|
|
2165
|
+
});
|
|
2166
|
+
session.on("error", () => {
|
|
2167
|
+
unavailable = true;
|
|
2168
|
+
});
|
|
2169
|
+
session.on("close", () => {
|
|
2170
|
+
unavailable = true;
|
|
2171
|
+
});
|
|
2172
|
+
session.on("goaway", (errorCode, lastStreamId) => {
|
|
2173
|
+
diag("host sent GOAWAY (errorCode=" + errorCode + ", lastStreamId=" + lastStreamId + ")");
|
|
2174
|
+
});
|
|
2175
|
+
return session;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
function forwardOverHttp2(request) {
|
|
2179
|
+
return new Promise((resolve, reject) => {
|
|
2180
|
+
const activeSession = openSession();
|
|
2181
|
+
const query = request.query || "";
|
|
2182
|
+
const pathWithQuery =
|
|
2183
|
+
query.length === 0 ? request.path : request.path + (query.charAt(0) === "?" ? query : "?" + query);
|
|
2184
|
+
const outboundHeaders = Object.assign(
|
|
2185
|
+
{
|
|
2186
|
+
":method": request.method,
|
|
2187
|
+
":path": pathWithQuery,
|
|
2188
|
+
authorization: "Bearer " + bridgeToken,
|
|
2189
|
+
},
|
|
2190
|
+
request.headers,
|
|
2191
|
+
);
|
|
2192
|
+
let stream;
|
|
2193
|
+
try {
|
|
2194
|
+
stream = activeSession.request(outboundHeaders, { endStream: request.body.length === 0 });
|
|
2195
|
+
} catch (error) {
|
|
2196
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
const chunks = [];
|
|
2200
|
+
let status = 502;
|
|
2201
|
+
let responseHeaders = {};
|
|
2202
|
+
let settled = false;
|
|
2203
|
+
const settle = (run) => {
|
|
2204
|
+
if (settled) return;
|
|
2205
|
+
settled = true;
|
|
2206
|
+
run();
|
|
2207
|
+
};
|
|
2208
|
+
stream.on("response", (headers) => {
|
|
2209
|
+
const raw = headers[":status"];
|
|
2210
|
+
status = typeof raw === "number" ? raw : Number(raw) || 502;
|
|
2211
|
+
responseHeaders = {};
|
|
2212
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
2213
|
+
if (key.charAt(0) === ":" || value == null) continue;
|
|
2214
|
+
responseHeaders[key] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
2218
|
+
stream.once("end", () =>
|
|
2219
|
+
settle(() => resolve({ status: status, headers: responseHeaders, body: Buffer.concat(chunks) })),
|
|
2220
|
+
);
|
|
2221
|
+
stream.once("error", (error) =>
|
|
2222
|
+
settle(() => reject(error instanceof Error ? error : new Error(String(error)))),
|
|
2223
|
+
);
|
|
2224
|
+
stream.once("aborted", () => settle(() => reject(new Error("Bridge HTTP/2 stream aborted."))));
|
|
2225
|
+
if (request.body.length > 0) {
|
|
2226
|
+
stream.end(request.body);
|
|
2227
|
+
} else if (!stream.writableEnded) {
|
|
2228
|
+
stream.end();
|
|
2229
|
+
}
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
const server = createServer(async (req, res) => {
|
|
2234
|
+
// readBodyBytes reserves the body's bytes against the process ledger
|
|
2235
|
+
// and hands back a release function; this holds it so the finally
|
|
2236
|
+
// below releases those bytes exactly once no matter how this handler
|
|
2237
|
+
// ends — its normal completion, a thrown error, a client abort, or a
|
|
2238
|
+
// deadline timeout all reach the same finally.
|
|
2239
|
+
let releaseBodyReservation = null;
|
|
2240
|
+
try {
|
|
2241
|
+
const auth = req.headers.authorization || "";
|
|
2242
|
+
const receivedToken = auth.startsWith("Bearer ") ? auth.slice("Bearer ".length) : "";
|
|
2243
|
+
if (!tokensMatch(receivedToken)) {
|
|
2244
|
+
writeJsonResponse(res, 401, { error: "Invalid bridge token." });
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
if (unavailable) {
|
|
2248
|
+
writeJsonResponse(res, 503, { error: "bridge_unavailable" });
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
2252
|
+
const { body: requestBodyBuffer, release } = await readBodyBytes(req);
|
|
2253
|
+
releaseBodyReservation = release;
|
|
2254
|
+
let response;
|
|
2255
|
+
try {
|
|
2256
|
+
response = await forwardOverHttp2({
|
|
2257
|
+
method: req.method || "GET",
|
|
2258
|
+
path: url.pathname,
|
|
2259
|
+
query: url.search,
|
|
2260
|
+
headers: normalizeHeaders(req.headers),
|
|
2261
|
+
body: requestBodyBuffer,
|
|
2262
|
+
});
|
|
2263
|
+
} catch (error) {
|
|
2264
|
+
writeJsonResponse(res, 502, { error: error instanceof Error ? error.message : String(error) });
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
// The host marks a possibly-committed mutation with an indeterminate outcome.
|
|
2268
|
+
// The host cannot cancel a host operation that is in flight, so the mutation
|
|
2269
|
+
// may have committed before the worker aborted the handler. A 5xx status is
|
|
2270
|
+
// retryable by convention, so a caller that retries 5xx would apply the
|
|
2271
|
+
// mutation twice. Map the indeterminate outcome to a non-retryable 409, so a
|
|
2272
|
+
// standard retry policy does not repeat the request. The outcome header and
|
|
2273
|
+
// body stay, so a caller that reads them still sees the indeterminate result.
|
|
2274
|
+
const bridgeOutcome = (response.headers || {})["x-paperclip-bridge-outcome"];
|
|
2275
|
+
if (bridgeOutcome === "indeterminate") {
|
|
2276
|
+
res.statusCode = 409;
|
|
2277
|
+
} else {
|
|
2278
|
+
res.statusCode = typeof response.status === "number" ? response.status : 200;
|
|
2279
|
+
}
|
|
2280
|
+
for (const [key, value] of Object.entries(response.headers || {})) {
|
|
2281
|
+
if (typeof value !== "string" || key.toLowerCase() === "content-length") continue;
|
|
2282
|
+
res.setHeader(key, value);
|
|
2283
|
+
}
|
|
2284
|
+
res.end(response.body);
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
// A denied process-ledger reservation is retryable: the caller should
|
|
2287
|
+
// try again once other in-flight bodies release their bytes. Every
|
|
2288
|
+
// other body-read or handling fault stays a generic 502.
|
|
2289
|
+
const status = error instanceof BridgeProcessCapacityError ? 503 : 502;
|
|
2290
|
+
writeJsonResponse(res, status, { error: error instanceof Error ? error.message : String(error) });
|
|
2291
|
+
} finally {
|
|
2292
|
+
releaseBodyReservation?.();
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
|
|
2296
|
+
process.on("SIGINT", () => {
|
|
2297
|
+
try {
|
|
2298
|
+
server.close();
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
diag("server close error: " + (error && error.message ? error.message : String(error)));
|
|
2301
|
+
}
|
|
2302
|
+
process.exit(0);
|
|
2303
|
+
});
|
|
2304
|
+
process.on("SIGTERM", () => {
|
|
2305
|
+
try {
|
|
2306
|
+
server.close();
|
|
2307
|
+
} catch (error) {
|
|
2308
|
+
diag("server close error: " + (error && error.message ? error.message : String(error)));
|
|
2309
|
+
}
|
|
2310
|
+
process.exit(0);
|
|
2311
|
+
});
|
|
2312
|
+
|
|
2313
|
+
// Bind-or-exit, the same rule every mode applies: the host assigns a
|
|
2314
|
+
// positive loopback port, and the gateway binds exactly that port or exits
|
|
2315
|
+
// nonzero. It never selects a different port.
|
|
2316
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
2317
|
+
diag("http2 gateway requires a positive assigned PAPERCLIP_BRIDGE_PORT; got " + String(port));
|
|
2318
|
+
process.exit(1);
|
|
2319
|
+
}
|
|
2320
|
+
server.on("error", (error) => {
|
|
2321
|
+
diag(
|
|
2322
|
+
"http2 gateway could not bind port " +
|
|
2323
|
+
String(port) +
|
|
2324
|
+
": " +
|
|
2325
|
+
(error && error.message ? error.message : String(error)),
|
|
2326
|
+
);
|
|
2327
|
+
process.exit(1);
|
|
2328
|
+
});
|
|
2329
|
+
server.listen(port, host, () => {
|
|
2330
|
+
const address = server.address();
|
|
2331
|
+
if (!address || typeof address === "string") {
|
|
2332
|
+
diag("http2 gateway did not expose a TCP address");
|
|
2333
|
+
process.exit(1);
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
// Send the one frame-codec line on this path: the READY line the host
|
|
2337
|
+
// readiness gate expects. Open the HTTP/2 client session on the very next
|
|
2338
|
+
// statement, so the client connection preface is the next byte the host
|
|
2339
|
+
// sees after READY, with no other writer in between.
|
|
2340
|
+
writeFrame({ version: DUPLEX_FRAME_VERSION, type: "ready", nonce: bridgeNonce });
|
|
2341
|
+
gatewayReady = true;
|
|
2342
|
+
openSession();
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// The startup check above already rejected every value except http2 and
|
|
2347
|
+
// queue, so this dispatch names both modes explicitly and never falls
|
|
2348
|
+
// through to the queue gateway for an unsupported mode.
|
|
2349
|
+
if (bridgeMode === "${SANDBOX_CALLBACK_BRIDGE_HTTP2_MODE}") {
|
|
2350
|
+
runHttp2Gateway();
|
|
2351
|
+
} else if (bridgeMode === "${SANDBOX_CALLBACK_BRIDGE_FILE_MODE}") {
|
|
2352
|
+
await runFileGateway();
|
|
2353
|
+
} else {
|
|
2354
|
+
throw new Error("Unsupported PAPERCLIP_API_BRIDGE_MODE: " + bridgeMode);
|
|
2355
|
+
}`;
|
|
2356
|
+
}
|
|
2357
|
+
//# sourceMappingURL=sandbox-callback-bridge.js.map
|