@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,1842 @@
|
|
|
1
|
+
import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
2
|
+
import { symlinkSync } from "node:fs";
|
|
3
|
+
import { chmod, lstat, mkdir, mkdtemp, readFile, readdir, readlink, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { promisify } from "node:util";
|
|
8
|
+
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
|
9
|
+
import { getProcessSessionRemoteSource, startAdapterExecutionTargetProcessSessionBridge, } from "./execution-target.js";
|
|
10
|
+
import { createCommandManagedSandboxCallbackBridgeQueueClient } from "./sandbox-callback-bridge.js";
|
|
11
|
+
import { runChildProcess } from "./server-utils.js";
|
|
12
|
+
const execFile = promisify(execFileCallback);
|
|
13
|
+
// Regression coverage for the stdin file race (parent PAP-4037): the host sends
|
|
14
|
+
// each ACP message as a file in the sandbox stdin directory, and a poller in
|
|
15
|
+
// the sandbox reads the file and writes the data to the child. Two defects lost
|
|
16
|
+
// a message. The host write was not atomic, so the poller could read an empty
|
|
17
|
+
// or partial `.json` file. The poller deleted the file before it validated the
|
|
18
|
+
// content, so an empty read was lost and a partial read stopped the loop.
|
|
19
|
+
describe("stdin file race (parent PAP-4037)", () => {
|
|
20
|
+
const cleanupDirs = [];
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
while (cleanupDirs.length > 0) {
|
|
23
|
+
const dir = cleanupDirs.pop();
|
|
24
|
+
if (dir)
|
|
25
|
+
await rm(dir, { recursive: true, force: true }).catch(() => undefined);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// Run the real emitted poller wrapper as a node process. The streamed variant
|
|
29
|
+
// writes one JSON frame per line to its stdout, so the test reads the frames
|
|
30
|
+
// directly. The child command is `cat`, so every byte the poller writes to
|
|
31
|
+
// the child stdin comes back as a `data` frame.
|
|
32
|
+
async function startPollerWrapper(options) {
|
|
33
|
+
const sessionDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-stdin-poll-"));
|
|
34
|
+
cleanupDirs.push(sessionDir);
|
|
35
|
+
const stdinDir = path.join(sessionDir, "stdin");
|
|
36
|
+
await mkdir(stdinDir, { recursive: true });
|
|
37
|
+
const wrapperPath = path.join(sessionDir, "wrapper.mjs");
|
|
38
|
+
await writeFile(wrapperPath, getProcessSessionRemoteSource({ outputToStdout: true }), "utf8");
|
|
39
|
+
const config = { command: "cat", args: [], cwd: sessionDir, env: {} };
|
|
40
|
+
const commandPayload = Buffer.from(JSON.stringify(config), "utf8").toString("base64");
|
|
41
|
+
const env = {
|
|
42
|
+
...process.env,
|
|
43
|
+
PAPERCLIP_PROCESS_SESSION_DIR: sessionDir,
|
|
44
|
+
PAPERCLIP_PROCESS_SESSION_COMMAND_B64: commandPayload,
|
|
45
|
+
};
|
|
46
|
+
if (options?.maxRetries != null) {
|
|
47
|
+
env.PAPERCLIP_PROCESS_SESSION_STDIN_MAX_RETRIES = String(options.maxRetries);
|
|
48
|
+
}
|
|
49
|
+
const child = spawn(process.execPath, [wrapperPath], {
|
|
50
|
+
cwd: sessionDir,
|
|
51
|
+
env,
|
|
52
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
53
|
+
});
|
|
54
|
+
const frames = [];
|
|
55
|
+
let stdoutBuffer = "";
|
|
56
|
+
child.stdout.on("data", (chunk) => {
|
|
57
|
+
stdoutBuffer += chunk.toString("utf8");
|
|
58
|
+
const lines = stdoutBuffer.split("\n");
|
|
59
|
+
stdoutBuffer = lines.pop() || "";
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
if (!line.trim())
|
|
62
|
+
continue;
|
|
63
|
+
frames.push(JSON.parse(line));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const exited = new Promise((resolve) => child.on("close", () => resolve()));
|
|
67
|
+
return {
|
|
68
|
+
sessionDir,
|
|
69
|
+
stdinDir,
|
|
70
|
+
frames,
|
|
71
|
+
// Write a complete stdin file with an atomic rename, so the test never
|
|
72
|
+
// creates its own partial-write race.
|
|
73
|
+
writeFileAtomic: async (name, content) => {
|
|
74
|
+
const finalPath = path.join(stdinDir, name);
|
|
75
|
+
const tempPath = `${finalPath}.writing`;
|
|
76
|
+
await writeFile(tempPath, content, "utf8");
|
|
77
|
+
await rename(tempPath, finalPath);
|
|
78
|
+
},
|
|
79
|
+
// Write a `.json` file directly, so a reader can observe it before the
|
|
80
|
+
// content lands. This simulates the non-atomic-write window.
|
|
81
|
+
writeFileRaw: async (name, content) => {
|
|
82
|
+
await writeFile(path.join(stdinDir, name), content, "utf8");
|
|
83
|
+
},
|
|
84
|
+
exited,
|
|
85
|
+
kill: () => child.kill("SIGKILL"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function stdinMessage(text) {
|
|
89
|
+
return `${JSON.stringify({ type: "stdin", data: Buffer.from(text, "utf8").toString("base64") })}\n`;
|
|
90
|
+
}
|
|
91
|
+
const stdinEndMessage = `${JSON.stringify({ type: "stdinEnd" })}\n`;
|
|
92
|
+
// Concatenate every stdout `data` frame and decode it back to text.
|
|
93
|
+
function collectDelivered(frames) {
|
|
94
|
+
return frames
|
|
95
|
+
.filter((frame) => frame.type === "data" && frame.stream === "stdout" && typeof frame.data === "string")
|
|
96
|
+
.map((frame) => Buffer.from(frame.data, "base64").toString("utf8"))
|
|
97
|
+
.join("");
|
|
98
|
+
}
|
|
99
|
+
function delay(ms) {
|
|
100
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
101
|
+
}
|
|
102
|
+
async function waitFor(check, timeoutMs = 4_000) {
|
|
103
|
+
const deadline = Date.now() + timeoutMs;
|
|
104
|
+
while (Date.now() < deadline) {
|
|
105
|
+
if (check())
|
|
106
|
+
return;
|
|
107
|
+
await delay(20);
|
|
108
|
+
}
|
|
109
|
+
throw new Error("Timed out waiting for condition.");
|
|
110
|
+
}
|
|
111
|
+
// ---- Poller tests -----------------------------------------------------
|
|
112
|
+
it("delivers a stdin file that appears empty first and then gets content", async () => {
|
|
113
|
+
const poller = await startPollerWrapper();
|
|
114
|
+
// The file appears empty first (the non-atomic-write window). The poller
|
|
115
|
+
// must keep it and retry, not delete it and lose the message.
|
|
116
|
+
await poller.writeFileRaw("000000000001.json", "");
|
|
117
|
+
await delay(200);
|
|
118
|
+
// The poller keeps the empty file for a later retry. A poller that deletes
|
|
119
|
+
// before it validates would drop the file here and lose the message.
|
|
120
|
+
const afterEmpty = await readdir(poller.stdinDir);
|
|
121
|
+
expect(afterEmpty).toContain("000000000001.json");
|
|
122
|
+
// The content lands in the same file. The poller must deliver it on a later
|
|
123
|
+
// cycle, because it kept the file across the empty read.
|
|
124
|
+
await poller.writeFileAtomic("000000000001.json", stdinMessage("late-payload"));
|
|
125
|
+
await waitFor(() => collectDelivered(poller.frames).includes("late-payload"));
|
|
126
|
+
await poller.writeFileAtomic("000000000002.json", stdinEndMessage);
|
|
127
|
+
await poller.exited;
|
|
128
|
+
expect(collectDelivered(poller.frames)).toBe("late-payload");
|
|
129
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
it("keeps polling after a malformed file and still delivers a later valid file", async () => {
|
|
132
|
+
const poller = await startPollerWrapper({ maxRetries: 3 });
|
|
133
|
+
// A malformed file sorts before the valid file. The poller keeps the send
|
|
134
|
+
// order: it holds the later file until it drops the malformed file after the
|
|
135
|
+
// retry limit, then it delivers the later valid file. So one bad file blocks
|
|
136
|
+
// the loop only until the retry limit, not forever.
|
|
137
|
+
await poller.writeFileRaw("000000000001.json", "{ this is not valid json");
|
|
138
|
+
await poller.writeFileAtomic("000000000002.json", stdinMessage("valid-after-bad"));
|
|
139
|
+
await waitFor(() => collectDelivered(poller.frames).includes("valid-after-bad"));
|
|
140
|
+
await poller.writeFileAtomic("000000000003.json", stdinEndMessage);
|
|
141
|
+
await poller.exited;
|
|
142
|
+
expect(collectDelivered(poller.frames)).toBe("valid-after-bad");
|
|
143
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
it("does not close the stream on a later stdinEnd while an earlier file awaits retry", async () => {
|
|
146
|
+
const poller = await startPollerWrapper();
|
|
147
|
+
// An earlier stdin file is momentarily unreadable (the non-atomic-write
|
|
148
|
+
// window). A later stdinEnd file is already complete. The poller must keep
|
|
149
|
+
// the send order: it must not read the stdinEnd ahead of the earlier file
|
|
150
|
+
// and close the stream. It must hold the stream open until the earlier file
|
|
151
|
+
// is readable.
|
|
152
|
+
await poller.writeFileRaw("000000000001.json", "");
|
|
153
|
+
await poller.writeFileAtomic("000000000002.json", stdinEndMessage);
|
|
154
|
+
// Give the poller time to scan. The stream stays open, so the child does not
|
|
155
|
+
// exit and no exit frame appears yet.
|
|
156
|
+
await delay(300);
|
|
157
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(false);
|
|
158
|
+
// The earlier file's content lands. The poller delivers it, then reads the
|
|
159
|
+
// stdinEnd and closes the stream.
|
|
160
|
+
await poller.writeFileAtomic("000000000001.json", stdinMessage("early-payload"));
|
|
161
|
+
await waitFor(() => collectDelivered(poller.frames).includes("early-payload"));
|
|
162
|
+
await poller.exited;
|
|
163
|
+
expect(collectDelivered(poller.frames)).toBe("early-payload");
|
|
164
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
it("drops a permanently malformed file after the retry limit and writes an error event", async () => {
|
|
167
|
+
const poller = await startPollerWrapper({ maxRetries: 3 });
|
|
168
|
+
// This file never becomes valid. After the retry limit the poller drops it
|
|
169
|
+
// and writes an error event, so the lost message fails loudly.
|
|
170
|
+
await poller.writeFileRaw("000000000001.json", "{ permanently broken");
|
|
171
|
+
await waitFor(() => poller.frames.some((frame) => frame.type === "error" && typeof frame.message === "string" && frame.message.includes("Dropped unreadable stdin file")));
|
|
172
|
+
// The loop still works after the drop: a later valid file is delivered.
|
|
173
|
+
await poller.writeFileAtomic("000000000002.json", stdinMessage("still-alive"));
|
|
174
|
+
await waitFor(() => collectDelivered(poller.frames).includes("still-alive"));
|
|
175
|
+
await poller.writeFileAtomic("000000000003.json", stdinEndMessage);
|
|
176
|
+
await poller.exited;
|
|
177
|
+
expect(collectDelivered(poller.frames)).toBe("still-alive");
|
|
178
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
it("holds a later stdin file until the missing earlier file appears", async () => {
|
|
181
|
+
const poller = await startPollerWrapper();
|
|
182
|
+
// File 2 is complete, but file 1 has not appeared yet (a host reordering).
|
|
183
|
+
// The poller must not deliver file 2 ahead of the missing file 1. It holds
|
|
184
|
+
// the send order and waits for the earlier file.
|
|
185
|
+
await poller.writeFileAtomic("000000000002.json", stdinMessage("second-payload"));
|
|
186
|
+
await delay(300);
|
|
187
|
+
// File 2 is still on disk and nothing was delivered: the poller holds it.
|
|
188
|
+
const afterHold = await readdir(poller.stdinDir);
|
|
189
|
+
expect(afterHold).toContain("000000000002.json");
|
|
190
|
+
expect(collectDelivered(poller.frames)).toBe("");
|
|
191
|
+
// File 1 arrives. The poller now delivers file 1 then file 2, in send order.
|
|
192
|
+
await poller.writeFileAtomic("000000000001.json", stdinMessage("first-payload"));
|
|
193
|
+
await waitFor(() => collectDelivered(poller.frames).includes("second-payload"));
|
|
194
|
+
expect(collectDelivered(poller.frames)).toBe("first-payloadsecond-payload");
|
|
195
|
+
await poller.writeFileAtomic("000000000003.json", stdinEndMessage);
|
|
196
|
+
await poller.exited;
|
|
197
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it("fails loud and advances past a missing stdin file after the retry limit", async () => {
|
|
200
|
+
const poller = await startPollerWrapper({ maxRetries: 3 });
|
|
201
|
+
// File 1 never appears. File 2 is complete. After the retry limit the poller
|
|
202
|
+
// writes a loud error event and advances past the gap, then delivers file 2.
|
|
203
|
+
// So a permanent reordering fails loud, never silently.
|
|
204
|
+
await poller.writeFileAtomic("000000000002.json", stdinMessage("after-gap"));
|
|
205
|
+
await waitFor(() => poller.frames.some((frame) => frame.type === "error" &&
|
|
206
|
+
typeof frame.message === "string" &&
|
|
207
|
+
frame.message.includes("Advanced past missing stdin files")));
|
|
208
|
+
await waitFor(() => collectDelivered(poller.frames).includes("after-gap"));
|
|
209
|
+
await poller.writeFileAtomic("000000000003.json", stdinEndMessage);
|
|
210
|
+
await poller.exited;
|
|
211
|
+
expect(collectDelivered(poller.frames)).toBe("after-gap");
|
|
212
|
+
expect(poller.frames.some((frame) => frame.type === "exit")).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
// ---- Host serialization test (drives the real bridge) -----------------
|
|
215
|
+
// A runner that runs each bridge shell script as a real child process, so the
|
|
216
|
+
// test drives the whole legacy-poll bridge: the socket handler, the command-
|
|
217
|
+
// managed `writeTextFile` script, the nohup wrapper, and the output poll.
|
|
218
|
+
function createLocalSandboxRunner(onExecute) {
|
|
219
|
+
let counter = 0;
|
|
220
|
+
return {
|
|
221
|
+
execute: async (input) => {
|
|
222
|
+
counter += 1;
|
|
223
|
+
const script = input.args?.[1] ?? "";
|
|
224
|
+
if (onExecute)
|
|
225
|
+
await onExecute(script);
|
|
226
|
+
const command = input.command === "bash" ? "/bin/bash" : input.command === "sh" ? "/bin/sh" : input.command;
|
|
227
|
+
return runChildProcess(`stdin-order-run-${counter}`, command, input.args ?? [], {
|
|
228
|
+
cwd: input.cwd ?? process.cwd(),
|
|
229
|
+
env: input.env ?? {},
|
|
230
|
+
stdin: input.stdin,
|
|
231
|
+
timeoutSec: Math.max(1, Math.ceil((input.timeoutMs ?? 30_000) / 1000)),
|
|
232
|
+
graceSec: 5,
|
|
233
|
+
onLog: input.onLog ?? (async () => { }),
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
it("serializes host stdin writes so a slow earlier write still lands first", async () => {
|
|
239
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-stdin-host-order-"));
|
|
240
|
+
cleanupDirs.push(rootDir);
|
|
241
|
+
// The child echoes every stdin byte to stdout, so the wrapper reports the
|
|
242
|
+
// exact bytes and order the child received on its stdin.
|
|
243
|
+
const childPath = path.join(rootDir, "echo-child.mjs");
|
|
244
|
+
await writeFile(childPath, "process.stdin.on('data', (c) => process.stdout.write(c));\n", "utf8");
|
|
245
|
+
// Record the send-order-relevant event: the completion of each stdin file's
|
|
246
|
+
// finalize (atomic rename). Delay the finalize of the FIRST file, so its
|
|
247
|
+
// write resolves slower than the second. Without serialization the second
|
|
248
|
+
// rename would land first; the per-session chain must keep the send order.
|
|
249
|
+
const finalizeOrder = [];
|
|
250
|
+
const runner = createLocalSandboxRunner(async (script) => {
|
|
251
|
+
const finalizeMatch = /base64 -d[\s\S]*mv '[^']*\.decoded' '([^']+\.json)'/.exec(script);
|
|
252
|
+
if (finalizeMatch) {
|
|
253
|
+
const remotePath = finalizeMatch[1];
|
|
254
|
+
if (remotePath.endsWith("000000000001.json"))
|
|
255
|
+
await delay(300);
|
|
256
|
+
finalizeOrder.push(path.posix.basename(remotePath));
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
const target = {
|
|
260
|
+
kind: "remote",
|
|
261
|
+
transport: "sandbox",
|
|
262
|
+
providerKey: "local-test",
|
|
263
|
+
remoteCwd: rootDir,
|
|
264
|
+
timeoutMs: 30_000,
|
|
265
|
+
runner,
|
|
266
|
+
};
|
|
267
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
268
|
+
runId: "run-stdin-host-order",
|
|
269
|
+
target,
|
|
270
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
271
|
+
adapterKey: "acpx",
|
|
272
|
+
command: process.execPath,
|
|
273
|
+
args: [childPath],
|
|
274
|
+
cwd: rootDir,
|
|
275
|
+
env: {},
|
|
276
|
+
timeoutSec: 5,
|
|
277
|
+
onLog: async () => { },
|
|
278
|
+
});
|
|
279
|
+
expect(bridge).not.toBeNull();
|
|
280
|
+
let peer = null;
|
|
281
|
+
try {
|
|
282
|
+
const proxySource = await readFile(bridge.agentCommand, "utf8");
|
|
283
|
+
const port = Number(/port: (\d+)/.exec(proxySource)?.[1] ?? Number.NaN);
|
|
284
|
+
const tokenLiteral = /const token = (".*?");/.exec(proxySource)?.[1];
|
|
285
|
+
expect(Number.isFinite(port)).toBe(true);
|
|
286
|
+
const token = JSON.parse(tokenLiteral);
|
|
287
|
+
const peerSocket = net.createConnection({ host: "127.0.0.1", port });
|
|
288
|
+
peer = peerSocket;
|
|
289
|
+
peerSocket.setEncoding("utf8");
|
|
290
|
+
peerSocket.on("error", () => undefined);
|
|
291
|
+
const delivered = [];
|
|
292
|
+
let peerBuffer = "";
|
|
293
|
+
peerSocket.on("data", (chunk) => {
|
|
294
|
+
peerBuffer += chunk;
|
|
295
|
+
const lines = peerBuffer.split("\n");
|
|
296
|
+
peerBuffer = lines.pop() || "";
|
|
297
|
+
for (const line of lines) {
|
|
298
|
+
if (!line.trim())
|
|
299
|
+
continue;
|
|
300
|
+
const frame = JSON.parse(line);
|
|
301
|
+
if (frame.type === "data" && frame.stream === "stdout" && typeof frame.data === "string") {
|
|
302
|
+
delivered.push(Buffer.from(frame.data, "base64").toString("utf8"));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
await new Promise((resolve, reject) => {
|
|
307
|
+
peerSocket.once("connect", () => resolve());
|
|
308
|
+
peerSocket.once("error", reject);
|
|
309
|
+
});
|
|
310
|
+
// Send two stdin messages back to back. The first authenticates and writes
|
|
311
|
+
// file 1; the second writes file 2. Both are scheduled before file 1's
|
|
312
|
+
// delayed finalize resolves, so an un-chained handler would race them.
|
|
313
|
+
const head = `${JSON.stringify({ token, type: "stdin", data: Buffer.from("HEAD_ONE_", "utf8").toString("base64") })}\n`;
|
|
314
|
+
const tail = `${JSON.stringify({ token, type: "stdin", data: Buffer.from("TAIL_TWO", "utf8").toString("base64") })}\n`;
|
|
315
|
+
peerSocket.write(head);
|
|
316
|
+
peerSocket.write(tail);
|
|
317
|
+
// The two finalize renames complete in send order, not in the order the
|
|
318
|
+
// delayed and fast writes would otherwise finish.
|
|
319
|
+
await waitFor(() => finalizeOrder.length >= 2, 8_000);
|
|
320
|
+
expect(finalizeOrder.slice(0, 2)).toEqual(["000000000001.json", "000000000002.json"]);
|
|
321
|
+
// End to end: the child receives the two payloads intact and in send
|
|
322
|
+
// order, so the prompt is byte-identical on the child stdin.
|
|
323
|
+
await waitFor(() => delivered.join("").includes("TAIL_TWO"), 8_000);
|
|
324
|
+
expect(delivered.join("")).toBe("HEAD_ONE_TAIL_TWO");
|
|
325
|
+
}
|
|
326
|
+
finally {
|
|
327
|
+
peer?.destroy();
|
|
328
|
+
await bridge?.stop();
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
it("holds stdinEnd on stop until an earlier pending stdin write lands first", async () => {
|
|
332
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-stdin-stop-order-"));
|
|
333
|
+
cleanupDirs.push(rootDir);
|
|
334
|
+
const childPath = path.join(rootDir, "echo-child.mjs");
|
|
335
|
+
await writeFile(childPath, "process.stdin.on('data', (c) => process.stdout.write(c));\n", "utf8");
|
|
336
|
+
// Record each stdin file finalize (atomic rename). `finalizeStarted` marks
|
|
337
|
+
// the start; `finalizeOrder` marks the completion. Delay the FIRST chunk's
|
|
338
|
+
// finalize, so its write is still pending when `stop()` runs. `stop()` must
|
|
339
|
+
// chain the `stdinEnd` write after the pending chunk, so file 2 (stdinEnd)
|
|
340
|
+
// never finishes its rename before file 1.
|
|
341
|
+
const finalizeStarted = [];
|
|
342
|
+
const finalizeOrder = [];
|
|
343
|
+
const runner = createLocalSandboxRunner(async (script) => {
|
|
344
|
+
const finalizeMatch = /base64 -d[\s\S]*mv '[^']*\.decoded' '([^']+\.json)'/.exec(script);
|
|
345
|
+
if (finalizeMatch) {
|
|
346
|
+
const name = path.posix.basename(finalizeMatch[1]);
|
|
347
|
+
finalizeStarted.push(name);
|
|
348
|
+
if (name === "000000000001.json")
|
|
349
|
+
await delay(300);
|
|
350
|
+
finalizeOrder.push(name);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
const target = {
|
|
354
|
+
kind: "remote",
|
|
355
|
+
transport: "sandbox",
|
|
356
|
+
providerKey: "local-test",
|
|
357
|
+
remoteCwd: rootDir,
|
|
358
|
+
timeoutMs: 30_000,
|
|
359
|
+
runner,
|
|
360
|
+
};
|
|
361
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
362
|
+
runId: "run-stdin-stop-order",
|
|
363
|
+
target,
|
|
364
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
365
|
+
adapterKey: "acpx",
|
|
366
|
+
command: process.execPath,
|
|
367
|
+
args: [childPath],
|
|
368
|
+
cwd: rootDir,
|
|
369
|
+
env: {},
|
|
370
|
+
timeoutSec: 5,
|
|
371
|
+
onLog: async () => { },
|
|
372
|
+
});
|
|
373
|
+
expect(bridge).not.toBeNull();
|
|
374
|
+
let peer = null;
|
|
375
|
+
let stopped = false;
|
|
376
|
+
try {
|
|
377
|
+
const proxySource = await readFile(bridge.agentCommand, "utf8");
|
|
378
|
+
const port = Number(/port: (\d+)/.exec(proxySource)?.[1] ?? Number.NaN);
|
|
379
|
+
const tokenLiteral = /const token = (".*?");/.exec(proxySource)?.[1];
|
|
380
|
+
expect(Number.isFinite(port)).toBe(true);
|
|
381
|
+
const token = JSON.parse(tokenLiteral);
|
|
382
|
+
const peerSocket = net.createConnection({ host: "127.0.0.1", port });
|
|
383
|
+
peer = peerSocket;
|
|
384
|
+
peerSocket.setEncoding("utf8");
|
|
385
|
+
peerSocket.on("error", () => undefined);
|
|
386
|
+
peerSocket.on("data", () => undefined);
|
|
387
|
+
await new Promise((resolve, reject) => {
|
|
388
|
+
peerSocket.once("connect", () => resolve());
|
|
389
|
+
peerSocket.once("error", reject);
|
|
390
|
+
});
|
|
391
|
+
// Send one stdin message. It authenticates and writes file 1, whose
|
|
392
|
+
// finalize the runner delays. Wait until that finalize has started, so the
|
|
393
|
+
// write is in flight when `stop()` runs.
|
|
394
|
+
const head = `${JSON.stringify({ token, type: "stdin", data: Buffer.from("HEAD_ONE_", "utf8").toString("base64") })}\n`;
|
|
395
|
+
peerSocket.write(head);
|
|
396
|
+
await waitFor(() => finalizeStarted.includes("000000000001.json"), 8_000);
|
|
397
|
+
// Stop the bridge while file 1's write is still pending. `stop()` awaits
|
|
398
|
+
// the chained `stdinEnd` write, then chains a `shutdown` write after it,
|
|
399
|
+
// so all three finalizes are complete when it returns, in send order.
|
|
400
|
+
await bridge.stop();
|
|
401
|
+
stopped = true;
|
|
402
|
+
expect(finalizeOrder).toEqual(["000000000001.json", "000000000002.json", "000000000003.json"]);
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
peer?.destroy();
|
|
406
|
+
if (!stopped)
|
|
407
|
+
await bridge?.stop();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
// ---- Host atomic-write tests ------------------------------------------
|
|
411
|
+
// A runner that executes each bridge shell script on the local filesystem,
|
|
412
|
+
// so the test exercises the real command-managed `writeTextFile` script.
|
|
413
|
+
function createLocalShellRunner(scripts) {
|
|
414
|
+
return {
|
|
415
|
+
execute: async (input) => {
|
|
416
|
+
const args = input.args ?? [];
|
|
417
|
+
if ((input.command === "sh" || input.command === "bash") && args[0] === "-c" && typeof args[1] === "string") {
|
|
418
|
+
scripts.push(args[1]);
|
|
419
|
+
}
|
|
420
|
+
const command = input.command === "sh" ? "/bin/sh" : input.command === "bash" ? "/bin/bash" : input.command;
|
|
421
|
+
try {
|
|
422
|
+
const result = await execFile(command, args, {
|
|
423
|
+
cwd: input.cwd,
|
|
424
|
+
env: { ...process.env, ...input.env },
|
|
425
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
exitCode: 0,
|
|
429
|
+
signal: null,
|
|
430
|
+
timedOut: false,
|
|
431
|
+
stdout: result.stdout,
|
|
432
|
+
stderr: result.stderr,
|
|
433
|
+
pid: null,
|
|
434
|
+
startedAt: null,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
catch (error) {
|
|
438
|
+
const err = error;
|
|
439
|
+
return {
|
|
440
|
+
exitCode: typeof err.code === "number" ? err.code : null,
|
|
441
|
+
signal: null,
|
|
442
|
+
timedOut: false,
|
|
443
|
+
stdout: err.stdout ?? "",
|
|
444
|
+
stderr: err.stderr ?? "",
|
|
445
|
+
pid: null,
|
|
446
|
+
startedAt: null,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
it("finalizes the command-managed host write with an atomic rename onto the .json path", async () => {
|
|
453
|
+
const remoteRoot = await mkdtemp(path.join(os.tmpdir(), "paperclip-stdin-host-cmd-"));
|
|
454
|
+
cleanupDirs.push(remoteRoot);
|
|
455
|
+
const stdinDir = path.join(remoteRoot, "stdin");
|
|
456
|
+
await mkdir(stdinDir, { recursive: true });
|
|
457
|
+
const scripts = [];
|
|
458
|
+
const client = createCommandManagedSandboxCallbackBridgeQueueClient({
|
|
459
|
+
runner: createLocalShellRunner(scripts),
|
|
460
|
+
remoteCwd: remoteRoot,
|
|
461
|
+
timeoutMs: 30_000,
|
|
462
|
+
});
|
|
463
|
+
const jsonPath = path.join(stdinDir, "000000000001.json");
|
|
464
|
+
const body = `${JSON.stringify({ type: "stdin", data: Buffer.from("host-payload", "utf8").toString("base64") })}\n`;
|
|
465
|
+
await client.writeTextFile(jsonPath, body);
|
|
466
|
+
// The final file holds the complete body.
|
|
467
|
+
expect(await readFile(jsonPath, "utf8")).toBe(body);
|
|
468
|
+
// No temporary upload file remains next to the final file.
|
|
469
|
+
const entries = await readdir(stdinDir);
|
|
470
|
+
expect(entries).toEqual(["000000000001.json"]);
|
|
471
|
+
// The finalize script renames a non-`.json` temporary file onto the final
|
|
472
|
+
// path. It never redirects the decode output straight into the `.json`
|
|
473
|
+
// file, so a reader never sees an empty or partial `.json` file.
|
|
474
|
+
const finalizeScript = scripts.find((script) => script.includes("base64 -d"));
|
|
475
|
+
expect(finalizeScript).toBeDefined();
|
|
476
|
+
expect(finalizeScript).toContain(`mv `);
|
|
477
|
+
expect(finalizeScript).not.toContain(`> '${jsonPath}'`);
|
|
478
|
+
expect(finalizeScript).toContain(`> '${jsonPath}.paperclip-upload.decoded'`);
|
|
479
|
+
});
|
|
480
|
+
it("never exposes a partial .json file under a concurrent reader (command-managed host write)", async () => {
|
|
481
|
+
const remoteRoot = await mkdtemp(path.join(os.tmpdir(), "paperclip-stdin-host-race-"));
|
|
482
|
+
cleanupDirs.push(remoteRoot);
|
|
483
|
+
const stdinDir = path.join(remoteRoot, "stdin");
|
|
484
|
+
await mkdir(stdinDir, { recursive: true });
|
|
485
|
+
const client = createCommandManagedSandboxCallbackBridgeQueueClient({
|
|
486
|
+
runner: createLocalShellRunner([]),
|
|
487
|
+
remoteCwd: remoteRoot,
|
|
488
|
+
timeoutMs: 30_000,
|
|
489
|
+
});
|
|
490
|
+
const jsonPath = path.join(stdinDir, "000000000001.json");
|
|
491
|
+
// A large body needs many decode bytes, so the write window is wide.
|
|
492
|
+
const bigText = "x".repeat(64 * 1024);
|
|
493
|
+
const body = `${JSON.stringify({ type: "stdin", data: Buffer.from(bigText, "utf8").toString("base64") })}\n`;
|
|
494
|
+
let stop = false;
|
|
495
|
+
const readerErrors = [];
|
|
496
|
+
let observedComplete = 0;
|
|
497
|
+
const reader = (async () => {
|
|
498
|
+
while (!stop) {
|
|
499
|
+
const raw = await readFile(jsonPath, "utf8").catch(() => null);
|
|
500
|
+
if (raw) {
|
|
501
|
+
try {
|
|
502
|
+
JSON.parse(raw);
|
|
503
|
+
observedComplete += 1;
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
readerErrors.push(error instanceof Error ? error.message : String(error));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
})();
|
|
511
|
+
for (let round = 0; round < 6; round += 1) {
|
|
512
|
+
await client.remove(jsonPath);
|
|
513
|
+
await client.writeTextFile(jsonPath, body);
|
|
514
|
+
}
|
|
515
|
+
stop = true;
|
|
516
|
+
await reader;
|
|
517
|
+
// Every read of the final file parsed as complete JSON. The reader never
|
|
518
|
+
// saw an empty or partial `.json` file.
|
|
519
|
+
expect(readerErrors).toEqual([]);
|
|
520
|
+
expect(observedComplete).toBeGreaterThan(0);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
// Coverage for deterministic wrapper shutdown (parent PAP-5307): a bridge
|
|
524
|
+
// stop must leave no remote wrapper process and no direct child process
|
|
525
|
+
// alive, the host must send no operating-system signal, and the host must
|
|
526
|
+
// store no process identifier. These tests drive the real emitted wrapper as
|
|
527
|
+
// a node process and, where noted, the real bridge through a local runner.
|
|
528
|
+
describe("deterministic remote process-session wrapper shutdown (PAP-5316)", () => {
|
|
529
|
+
const cleanupDirs = [];
|
|
530
|
+
afterEach(async () => {
|
|
531
|
+
while (cleanupDirs.length > 0) {
|
|
532
|
+
const dir = cleanupDirs.pop();
|
|
533
|
+
if (dir)
|
|
534
|
+
await rm(dir, { recursive: true, force: true }).catch(() => undefined);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
function delay(ms) {
|
|
538
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
539
|
+
}
|
|
540
|
+
async function waitFor(check, timeoutMs = 8_000) {
|
|
541
|
+
const deadline = Date.now() + timeoutMs;
|
|
542
|
+
while (Date.now() < deadline) {
|
|
543
|
+
if (await check())
|
|
544
|
+
return;
|
|
545
|
+
await delay(20);
|
|
546
|
+
}
|
|
547
|
+
throw new Error("Timed out waiting for condition.");
|
|
548
|
+
}
|
|
549
|
+
function isPidAlive(pid) {
|
|
550
|
+
try {
|
|
551
|
+
process.kill(pid, 0);
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// A read-only, test-only liveness probe over the real OS process table. It
|
|
559
|
+
// never signals anything; it only greps `ps` output to tell the test
|
|
560
|
+
// whether a specific test-authored script (identified by its own temp file
|
|
561
|
+
// path) is still running. Production host code never does this — it never
|
|
562
|
+
// matches or signals a process by name or command line.
|
|
563
|
+
async function findLivePidsByArgvSubstring(substring) {
|
|
564
|
+
try {
|
|
565
|
+
const { stdout } = await execFile("ps", ["-eo", "pid=,args="]);
|
|
566
|
+
const pids = [];
|
|
567
|
+
for (const line of stdout.split("\n")) {
|
|
568
|
+
const trimmed = line.trim();
|
|
569
|
+
if (!trimmed)
|
|
570
|
+
continue;
|
|
571
|
+
const match = /^(\d+)\s+(.*)$/.exec(trimmed);
|
|
572
|
+
if (match && match[2].includes(substring)) {
|
|
573
|
+
const pid = Number.parseInt(match[1], 10);
|
|
574
|
+
if (Number.isFinite(pid))
|
|
575
|
+
pids.push(pid);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return pids;
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return [];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// A test-only preload module for the wrapper's node process (PAP-5338).
|
|
585
|
+
// This sandbox's filesystems all report a real, working birthtime, so a
|
|
586
|
+
// test cannot reach the two known "no usable creation time" fallbacks by
|
|
587
|
+
// using a real filesystem alone. This preload patches fs.promises.lstat
|
|
588
|
+
// inside the wrapper's own process, for one directory the test names
|
|
589
|
+
// through an env var, so the wrapper observes the exact Stats shape each
|
|
590
|
+
// fallback produces. It never runs unless a test opts in, and it never
|
|
591
|
+
// touches this test file's own process.
|
|
592
|
+
// Kept outside cleanupDirs (which afterEach drains after every single test):
|
|
593
|
+
// this preload file is created once and reused by every test in this
|
|
594
|
+
// describe block, so an early test's cleanup must not delete it out from
|
|
595
|
+
// under a later test.
|
|
596
|
+
let fakeBirthtimePreloadDir = null;
|
|
597
|
+
afterAll(async () => {
|
|
598
|
+
if (fakeBirthtimePreloadDir)
|
|
599
|
+
await rm(fakeBirthtimePreloadDir, { recursive: true, force: true }).catch(() => undefined);
|
|
600
|
+
});
|
|
601
|
+
let fakeBirthtimePreloadPath = null;
|
|
602
|
+
async function getFakeBirthtimePreloadPath() {
|
|
603
|
+
if (!fakeBirthtimePreloadPath) {
|
|
604
|
+
fakeBirthtimePreloadPath = (async () => {
|
|
605
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "paperclip-birthtime-preload-"));
|
|
606
|
+
fakeBirthtimePreloadDir = dir;
|
|
607
|
+
const preloadPath = path.join(dir, "fake-birthtime-preload.cjs");
|
|
608
|
+
await writeFile(preloadPath, [
|
|
609
|
+
`const fs = require("fs");`,
|
|
610
|
+
`const path = require("path");`,
|
|
611
|
+
`const target = process.env.PAPERCLIP_TEST_FAKE_BIRTHTIME_TARGET;`,
|
|
612
|
+
`const mode = process.env.PAPERCLIP_TEST_FAKE_BIRTHTIME_MODE;`,
|
|
613
|
+
`const sessionDir = process.env.PAPERCLIP_PROCESS_SESSION_DIR;`,
|
|
614
|
+
`if (target && mode && sessionDir) {`,
|
|
615
|
+
` const resolvedTarget = path.resolve(target === "stdinDir" ? path.join(sessionDir, "stdin") : sessionDir);`,
|
|
616
|
+
` const originalLstat = fs.promises.lstat.bind(fs.promises);`,
|
|
617
|
+
` fs.promises.lstat = async (candidatePath, opts) => {`,
|
|
618
|
+
` const stats = await originalLstat(candidatePath, opts);`,
|
|
619
|
+
` if (path.resolve(String(candidatePath)) === resolvedTarget) {`,
|
|
620
|
+
` const fakeValue = mode === "zero" ? 0 : stats.ctimeMs;`,
|
|
621
|
+
` Object.defineProperty(stats, "birthtimeMs", { value: fakeValue, configurable: true });`,
|
|
622
|
+
` }`,
|
|
623
|
+
` return stats;`,
|
|
624
|
+
` };`,
|
|
625
|
+
`}`,
|
|
626
|
+
].join("\n"), "utf8");
|
|
627
|
+
return preloadPath;
|
|
628
|
+
})();
|
|
629
|
+
}
|
|
630
|
+
return fakeBirthtimePreloadPath;
|
|
631
|
+
}
|
|
632
|
+
// A test-only preload for PAP-5355: it deterministically simulates a
|
|
633
|
+
// same-sandbox peer that wins the gap between the wrapper's final identity
|
|
634
|
+
// check and its removal call. nextProbeFileName() is deterministic (pid +
|
|
635
|
+
// call sequence), so this preload can compute the exact probe path the
|
|
636
|
+
// wrapper itself will check next. It patches fs.promises.lstat inside the
|
|
637
|
+
// wrapper's own process: the first time that call targets the expected
|
|
638
|
+
// probe path, it replaces the path with a peer-owned entry before the real
|
|
639
|
+
// lstat runs, so the wrapper observes the swapped entry's identity, not its
|
|
640
|
+
// own. This is the worst case for the wrapper (the swap always lands
|
|
641
|
+
// before the wrapper's very last look at the path), so a wrapper that
|
|
642
|
+
// still leaves the peer's entry untouched under this preload proves the
|
|
643
|
+
// fix for every less-adversarial timing too. It never runs unless a test
|
|
644
|
+
// opts in, and it never touches this test file's own process.
|
|
645
|
+
let probeSwapPreloadDir = null;
|
|
646
|
+
afterAll(async () => {
|
|
647
|
+
if (probeSwapPreloadDir)
|
|
648
|
+
await rm(probeSwapPreloadDir, { recursive: true, force: true }).catch(() => undefined);
|
|
649
|
+
});
|
|
650
|
+
let probeSwapPreloadPath = null;
|
|
651
|
+
async function getProbeSwapPreloadPath() {
|
|
652
|
+
if (!probeSwapPreloadPath) {
|
|
653
|
+
probeSwapPreloadPath = (async () => {
|
|
654
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-swap-preload-"));
|
|
655
|
+
probeSwapPreloadDir = dir;
|
|
656
|
+
const preloadPath = path.join(dir, "probe-swap-preload.cjs");
|
|
657
|
+
await writeFile(preloadPath, [
|
|
658
|
+
`const fs = require("fs");`,
|
|
659
|
+
`const path = require("path");`,
|
|
660
|
+
`const mode = process.env.PAPERCLIP_TEST_PROBE_SWAP_MODE;`,
|
|
661
|
+
`const seq = process.env.PAPERCLIP_TEST_PROBE_SWAP_SEQ;`,
|
|
662
|
+
`const symlinkTarget = process.env.PAPERCLIP_TEST_PROBE_SWAP_SYMLINK_TARGET;`,
|
|
663
|
+
`if (mode && seq) {`,
|
|
664
|
+
` const expectedName = ".paperclip-birthtime-probe-" + process.pid + "-" + seq;`,
|
|
665
|
+
` let swapped = false;`,
|
|
666
|
+
` const originalLstat = fs.promises.lstat.bind(fs.promises);`,
|
|
667
|
+
` fs.promises.lstat = async (candidatePath, opts) => {`,
|
|
668
|
+
` if (!swapped && path.basename(String(candidatePath)) === expectedName) {`,
|
|
669
|
+
` swapped = true;`,
|
|
670
|
+
` try { fs.unlinkSync(candidatePath); } catch {}`,
|
|
671
|
+
` if (mode === "file") fs.writeFileSync(candidatePath, "peer-owned-content");`,
|
|
672
|
+
` else if (mode === "dir") fs.mkdirSync(candidatePath);`,
|
|
673
|
+
` else if (mode === "symlink") fs.symlinkSync(symlinkTarget, candidatePath);`,
|
|
674
|
+
` }`,
|
|
675
|
+
` return originalLstat(candidatePath, opts);`,
|
|
676
|
+
` };`,
|
|
677
|
+
`}`,
|
|
678
|
+
].join("\n"), "utf8");
|
|
679
|
+
return preloadPath;
|
|
680
|
+
})();
|
|
681
|
+
}
|
|
682
|
+
return probeSwapPreloadPath;
|
|
683
|
+
}
|
|
684
|
+
// A test-only preload for PAP-5374: it simulates fstat() failing on the
|
|
685
|
+
// wrapper's own just-opened probe file descriptor, the one signal the real
|
|
686
|
+
// filesystem in this sandbox never produces on demand. nextProbeFileName()
|
|
687
|
+
// is deterministic (pid + call sequence), so this preload knows which
|
|
688
|
+
// fs.promises.open() call is the wrapper's probe write and patches only the
|
|
689
|
+
// FileHandle that call returns, leaving every other open() untouched. It
|
|
690
|
+
// never runs unless a test opts in, and it never touches this test file's
|
|
691
|
+
// own process.
|
|
692
|
+
let fstatFailurePreloadDir = null;
|
|
693
|
+
afterAll(async () => {
|
|
694
|
+
if (fstatFailurePreloadDir)
|
|
695
|
+
await rm(fstatFailurePreloadDir, { recursive: true, force: true }).catch(() => undefined);
|
|
696
|
+
});
|
|
697
|
+
let fstatFailurePreloadPath = null;
|
|
698
|
+
async function getFstatFailurePreloadPath() {
|
|
699
|
+
if (!fstatFailurePreloadPath) {
|
|
700
|
+
fstatFailurePreloadPath = (async () => {
|
|
701
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "paperclip-fstat-failure-preload-"));
|
|
702
|
+
fstatFailurePreloadDir = dir;
|
|
703
|
+
const preloadPath = path.join(dir, "fstat-failure-preload.cjs");
|
|
704
|
+
await writeFile(preloadPath, [
|
|
705
|
+
`const fs = require("fs");`,
|
|
706
|
+
`const path = require("path");`,
|
|
707
|
+
`const seq = process.env.PAPERCLIP_TEST_FSTAT_FAILURE_SEQ;`,
|
|
708
|
+
`if (seq) {`,
|
|
709
|
+
` const expectedName = ".paperclip-birthtime-probe-" + process.pid + "-" + seq;`,
|
|
710
|
+
` const originalOpen = fs.promises.open.bind(fs.promises);`,
|
|
711
|
+
` fs.promises.open = async (targetPath, flags, mode) => {`,
|
|
712
|
+
` const handle = await originalOpen(targetPath, flags, mode);`,
|
|
713
|
+
` if (path.basename(String(targetPath)) === expectedName) {`,
|
|
714
|
+
` handle.stat = async () => {`,
|
|
715
|
+
` throw new Error("EIO: simulated fstat failure for test");`,
|
|
716
|
+
` };`,
|
|
717
|
+
` }`,
|
|
718
|
+
` return handle;`,
|
|
719
|
+
` };`,
|
|
720
|
+
`}`,
|
|
721
|
+
].join("\n"), "utf8");
|
|
722
|
+
return preloadPath;
|
|
723
|
+
})();
|
|
724
|
+
}
|
|
725
|
+
return fstatFailurePreloadPath;
|
|
726
|
+
}
|
|
727
|
+
// Run the real emitted wrapper (either variant) as a node process, with no
|
|
728
|
+
// sandbox and no bridge in front of it. The test owns the wrapper's node
|
|
729
|
+
// ChildProcess handle directly, so it can observe the wrapper's own exit
|
|
730
|
+
// without storing or signaling any process identifier itself.
|
|
731
|
+
async function startWrapperProcess(options) {
|
|
732
|
+
const sessionDir = await mkdtemp(path.join(options?.parentDir ?? os.tmpdir(), "paperclip-wrapper-lifecycle-"));
|
|
733
|
+
cleanupDirs.push(sessionDir);
|
|
734
|
+
const stdinDir = path.join(sessionDir, "stdin");
|
|
735
|
+
const eventsDir = path.join(sessionDir, "events");
|
|
736
|
+
await mkdir(stdinDir, { recursive: true });
|
|
737
|
+
if (options?.outputToStdout !== true)
|
|
738
|
+
await mkdir(eventsDir, { recursive: true });
|
|
739
|
+
const wrapperPath = path.join(sessionDir, "wrapper.mjs");
|
|
740
|
+
await writeFile(wrapperPath, getProcessSessionRemoteSource({ outputToStdout: options?.outputToStdout === true }), "utf8");
|
|
741
|
+
const config = { command: options?.command ?? "cat", args: options?.args ?? [], cwd: sessionDir, env: {} };
|
|
742
|
+
const commandPayload = Buffer.from(JSON.stringify(config), "utf8").toString("base64");
|
|
743
|
+
const env = {
|
|
744
|
+
...process.env,
|
|
745
|
+
PAPERCLIP_PROCESS_SESSION_DIR: sessionDir,
|
|
746
|
+
PAPERCLIP_PROCESS_SESSION_COMMAND_B64: commandPayload,
|
|
747
|
+
};
|
|
748
|
+
if (options?.maxRetries != null)
|
|
749
|
+
env.PAPERCLIP_PROCESS_SESSION_STDIN_MAX_RETRIES = String(options.maxRetries);
|
|
750
|
+
if (options?.terminateGraceMs != null)
|
|
751
|
+
env.PAPERCLIP_PROCESS_SESSION_TERMINATE_GRACE_MS = String(options.terminateGraceMs);
|
|
752
|
+
const execArgv = [];
|
|
753
|
+
if (options?.fakeBirthtime) {
|
|
754
|
+
env.PAPERCLIP_TEST_FAKE_BIRTHTIME_TARGET = options.fakeBirthtime.target;
|
|
755
|
+
env.PAPERCLIP_TEST_FAKE_BIRTHTIME_MODE = options.fakeBirthtime.mode;
|
|
756
|
+
execArgv.push("--require", await getFakeBirthtimePreloadPath());
|
|
757
|
+
}
|
|
758
|
+
if (options?.probeSwap) {
|
|
759
|
+
env.PAPERCLIP_TEST_PROBE_SWAP_SEQ = String(options.probeSwap.seq);
|
|
760
|
+
env.PAPERCLIP_TEST_PROBE_SWAP_MODE = options.probeSwap.mode;
|
|
761
|
+
if (options.probeSwap.symlinkTarget)
|
|
762
|
+
env.PAPERCLIP_TEST_PROBE_SWAP_SYMLINK_TARGET = options.probeSwap.symlinkTarget;
|
|
763
|
+
execArgv.push("--require", await getProbeSwapPreloadPath());
|
|
764
|
+
}
|
|
765
|
+
if (options?.fstatFailure) {
|
|
766
|
+
env.PAPERCLIP_TEST_FSTAT_FAILURE_SEQ = String(options.fstatFailure.seq);
|
|
767
|
+
execArgv.push("--require", await getFstatFailurePreloadPath());
|
|
768
|
+
}
|
|
769
|
+
const child = spawn(process.execPath, [...execArgv, wrapperPath], {
|
|
770
|
+
cwd: sessionDir,
|
|
771
|
+
env,
|
|
772
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
773
|
+
});
|
|
774
|
+
const frames = [];
|
|
775
|
+
let stdoutBuffer = "";
|
|
776
|
+
child.stdout.on("data", (chunk) => {
|
|
777
|
+
stdoutBuffer += chunk.toString("utf8");
|
|
778
|
+
const lines = stdoutBuffer.split("\n");
|
|
779
|
+
stdoutBuffer = lines.pop() || "";
|
|
780
|
+
for (const line of lines) {
|
|
781
|
+
if (!line.trim())
|
|
782
|
+
continue;
|
|
783
|
+
try {
|
|
784
|
+
frames.push(JSON.parse(line));
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
// A partial line at a chunk boundary; ignore.
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
let stderrText = "";
|
|
792
|
+
child.stderr.on("data", (chunk) => {
|
|
793
|
+
stderrText += chunk.toString("utf8");
|
|
794
|
+
});
|
|
795
|
+
let exitCode = null;
|
|
796
|
+
let exitSignal = null;
|
|
797
|
+
const exited = new Promise((resolve) => {
|
|
798
|
+
child.on("close", (code, signal) => {
|
|
799
|
+
exitCode = code;
|
|
800
|
+
exitSignal = signal;
|
|
801
|
+
resolve();
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
async function readEventFiles() {
|
|
805
|
+
const names = (await readdir(eventsDir).catch(() => [])).filter((name) => name.endsWith(".json")).sort();
|
|
806
|
+
const out = [];
|
|
807
|
+
for (const name of names) {
|
|
808
|
+
const body = await readFile(path.join(eventsDir, name), "utf8").catch(() => "");
|
|
809
|
+
if (!body.trim())
|
|
810
|
+
continue;
|
|
811
|
+
try {
|
|
812
|
+
out.push(JSON.parse(body));
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
// Not fully written yet; the test polls again.
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return out;
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
pid: child.pid,
|
|
822
|
+
sessionDir,
|
|
823
|
+
stdinDir,
|
|
824
|
+
eventsDir,
|
|
825
|
+
wrapperPath,
|
|
826
|
+
frames,
|
|
827
|
+
stderrText: () => stderrText,
|
|
828
|
+
exited,
|
|
829
|
+
exitInfo: () => ({ code: exitCode, signal: exitSignal }),
|
|
830
|
+
readEventFiles,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
// A runner that runs each bridge shell script as a real child process
|
|
834
|
+
// (matches the harness in the stdin-race describe block above), so a test
|
|
835
|
+
// drives the whole legacy-poll bridge for real: the socket handler, the
|
|
836
|
+
// command-managed `writeTextFile`/`remove` scripts, the nohup wrapper
|
|
837
|
+
// launch, and the output poll.
|
|
838
|
+
function createLocalSandboxRunner(onExecute) {
|
|
839
|
+
let counter = 0;
|
|
840
|
+
return {
|
|
841
|
+
execute: async (input) => {
|
|
842
|
+
counter += 1;
|
|
843
|
+
const script = input.args?.[1] ?? "";
|
|
844
|
+
if (onExecute)
|
|
845
|
+
await onExecute(script);
|
|
846
|
+
const command = input.command === "bash" ? "/bin/bash" : input.command === "sh" ? "/bin/sh" : input.command;
|
|
847
|
+
return runChildProcess(`wrapper-lifecycle-run-${counter}`, command, input.args ?? [], {
|
|
848
|
+
cwd: input.cwd ?? process.cwd(),
|
|
849
|
+
env: input.env ?? {},
|
|
850
|
+
stdin: input.stdin,
|
|
851
|
+
timeoutSec: Math.max(1, Math.ceil((input.timeoutMs ?? 30_000) / 1000)),
|
|
852
|
+
graceSec: 5,
|
|
853
|
+
onLog: input.onLog ?? (async () => { }),
|
|
854
|
+
});
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function trackedChildSource(pidFile) {
|
|
859
|
+
return [
|
|
860
|
+
`import { writeFileSync } from "node:fs";`,
|
|
861
|
+
`writeFileSync(${JSON.stringify(pidFile)}, String(process.pid));`,
|
|
862
|
+
`process.stdin.resume();`,
|
|
863
|
+
].join("\n");
|
|
864
|
+
}
|
|
865
|
+
// T2's child ignores SIGTERM, so `terminate()` must escalate to SIGKILL
|
|
866
|
+
// after its grace period. Ignoring end-of-file on stdin alone would not
|
|
867
|
+
// prove that: a plain `cat`-like child already dies from the default
|
|
868
|
+
// SIGTERM disposition.
|
|
869
|
+
function stubbornChildSource(pidFile) {
|
|
870
|
+
return [
|
|
871
|
+
`import { writeFileSync } from "node:fs";`,
|
|
872
|
+
`writeFileSync(${JSON.stringify(pidFile)}, String(process.pid));`,
|
|
873
|
+
`process.stdin.resume();`,
|
|
874
|
+
`process.on("SIGTERM", () => {});`,
|
|
875
|
+
`setInterval(() => {}, 1000);`,
|
|
876
|
+
].join("\n");
|
|
877
|
+
}
|
|
878
|
+
async function startTrackedBridgeSession(input) {
|
|
879
|
+
const pidFile = path.join(input.rootDir, `${input.runId}.pid`);
|
|
880
|
+
const childPath = path.join(input.rootDir, `${input.runId}-child.mjs`);
|
|
881
|
+
await writeFile(childPath, input.childSource, "utf8");
|
|
882
|
+
const target = {
|
|
883
|
+
kind: "remote",
|
|
884
|
+
transport: "sandbox",
|
|
885
|
+
providerKey: "local-test",
|
|
886
|
+
remoteCwd: input.rootDir,
|
|
887
|
+
timeoutMs: 30_000,
|
|
888
|
+
runner: input.runner,
|
|
889
|
+
};
|
|
890
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
891
|
+
runId: input.runId,
|
|
892
|
+
target,
|
|
893
|
+
runtimeRootDir: path.posix.join(input.rootDir, ".paperclip-runtime", "acpx"),
|
|
894
|
+
adapterKey: "acpx",
|
|
895
|
+
command: process.execPath,
|
|
896
|
+
args: [childPath],
|
|
897
|
+
cwd: input.rootDir,
|
|
898
|
+
env: {},
|
|
899
|
+
timeoutSec: 5,
|
|
900
|
+
onLog: async () => { },
|
|
901
|
+
});
|
|
902
|
+
expect(bridge).not.toBeNull();
|
|
903
|
+
await waitFor(async () => (await readFile(pidFile, "utf8").catch(() => "")).trim().length > 0, 8_000);
|
|
904
|
+
const pid = Number.parseInt((await readFile(pidFile, "utf8")).trim(), 10);
|
|
905
|
+
return { bridge: bridge, pid };
|
|
906
|
+
}
|
|
907
|
+
it("T1 exits within a bounded time when its session directory disappears with no message ever sent", async () => {
|
|
908
|
+
const wrapper = await startWrapperProcess({ outputToStdout: false, terminateGraceMs: 200 });
|
|
909
|
+
// Let the poll loop run a few cycles before the directory disappears.
|
|
910
|
+
await delay(150);
|
|
911
|
+
await rm(wrapper.sessionDir, { recursive: true, force: true });
|
|
912
|
+
await Promise.race([
|
|
913
|
+
wrapper.exited,
|
|
914
|
+
delay(4_000).then(() => {
|
|
915
|
+
throw new Error("The wrapper did not exit after its session directory disappeared.");
|
|
916
|
+
}),
|
|
917
|
+
]);
|
|
918
|
+
});
|
|
919
|
+
it("T2 leaves neither the wrapper nor a stubborn child alive after stop()", async () => {
|
|
920
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-stubborn-child-"));
|
|
921
|
+
cleanupDirs.push(rootDir);
|
|
922
|
+
const runner = createLocalSandboxRunner();
|
|
923
|
+
const session = await startTrackedBridgeSession({
|
|
924
|
+
rootDir,
|
|
925
|
+
runId: "stubborn",
|
|
926
|
+
childSource: stubbornChildSource(path.join(rootDir, "stubborn.pid")),
|
|
927
|
+
runner,
|
|
928
|
+
});
|
|
929
|
+
// The emitted wrapper script's own path is unique to this test (it lives
|
|
930
|
+
// under this test's fresh temp root), so a `ps` grep on it identifies
|
|
931
|
+
// only this test's wrapper process, not a sibling test's.
|
|
932
|
+
const wrapperScriptSubstring = path.posix.join(rootDir, ".paperclip-runtime", "acpx", "process-sessions");
|
|
933
|
+
try {
|
|
934
|
+
expect(isPidAlive(session.pid)).toBe(true);
|
|
935
|
+
await waitFor(async () => (await findLivePidsByArgvSubstring(wrapperScriptSubstring)).length > 0, 4_000);
|
|
936
|
+
await session.bridge.stop();
|
|
937
|
+
// The child ignores SIGTERM, so `terminate()` needs its own grace
|
|
938
|
+
// period (default 3s) before it escalates to SIGKILL.
|
|
939
|
+
await waitFor(() => !isPidAlive(session.pid), 8_000);
|
|
940
|
+
expect(isPidAlive(session.pid)).toBe(false);
|
|
941
|
+
await waitFor(async () => (await findLivePidsByArgvSubstring(wrapperScriptSubstring)).length === 0, 4_000);
|
|
942
|
+
}
|
|
943
|
+
finally {
|
|
944
|
+
await session.bridge.stop().catch(() => undefined);
|
|
945
|
+
}
|
|
946
|
+
}, 15_000);
|
|
947
|
+
it("T3 exits on its own when the child exits first and no stdinEnd is ever sent", async () => {
|
|
948
|
+
const wrapper = await startWrapperProcess({
|
|
949
|
+
outputToStdout: false,
|
|
950
|
+
command: process.execPath,
|
|
951
|
+
args: ["-e", "process.exit(0)"],
|
|
952
|
+
});
|
|
953
|
+
await Promise.race([
|
|
954
|
+
wrapper.exited,
|
|
955
|
+
delay(4_000).then(() => {
|
|
956
|
+
throw new Error("The wrapper did not exit on its own after its child exited.");
|
|
957
|
+
}),
|
|
958
|
+
]);
|
|
959
|
+
const events = await wrapper.readEventFiles();
|
|
960
|
+
expect(events.some((event) => event.type === "exit")).toBe(true);
|
|
961
|
+
});
|
|
962
|
+
it("T4 stopping one session leaves a sibling session's wrapper and child alive", async () => {
|
|
963
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-session-isolation-"));
|
|
964
|
+
cleanupDirs.push(rootDir);
|
|
965
|
+
const runner = createLocalSandboxRunner();
|
|
966
|
+
const sessionA = await startTrackedBridgeSession({
|
|
967
|
+
rootDir,
|
|
968
|
+
runId: "session-a",
|
|
969
|
+
childSource: trackedChildSource(path.join(rootDir, "session-a.pid")),
|
|
970
|
+
runner,
|
|
971
|
+
});
|
|
972
|
+
const sessionB = await startTrackedBridgeSession({
|
|
973
|
+
rootDir,
|
|
974
|
+
runId: "session-b",
|
|
975
|
+
childSource: trackedChildSource(path.join(rootDir, "session-b.pid")),
|
|
976
|
+
runner,
|
|
977
|
+
});
|
|
978
|
+
try {
|
|
979
|
+
expect(isPidAlive(sessionA.pid)).toBe(true);
|
|
980
|
+
expect(isPidAlive(sessionB.pid)).toBe(true);
|
|
981
|
+
await sessionA.bridge.stop();
|
|
982
|
+
await waitFor(() => !isPidAlive(sessionA.pid), 8_000);
|
|
983
|
+
expect(isPidAlive(sessionA.pid)).toBe(false);
|
|
984
|
+
// Session B never received a stdinEnd or a shutdown message.
|
|
985
|
+
expect(isPidAlive(sessionB.pid)).toBe(true);
|
|
986
|
+
}
|
|
987
|
+
finally {
|
|
988
|
+
await sessionA.bridge.stop().catch(() => undefined);
|
|
989
|
+
await sessionB.bridge.stop().catch(() => undefined);
|
|
990
|
+
}
|
|
991
|
+
}, 15_000);
|
|
992
|
+
it("T5 still writes both control messages, finishes fast, and warns never after a forged exit event the live poll reads early", async () => {
|
|
993
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-forged-exit-"));
|
|
994
|
+
cleanupDirs.push(rootDir);
|
|
995
|
+
const childPath = path.join(rootDir, "quiet-child.mjs");
|
|
996
|
+
await writeFile(childPath, "process.stdin.resume();\n", "utf8");
|
|
997
|
+
const scripts = [];
|
|
998
|
+
const runner = createLocalSandboxRunner(async (script) => {
|
|
999
|
+
scripts.push(script);
|
|
1000
|
+
});
|
|
1001
|
+
const target = {
|
|
1002
|
+
kind: "remote",
|
|
1003
|
+
transport: "sandbox",
|
|
1004
|
+
providerKey: "local-test",
|
|
1005
|
+
remoteCwd: rootDir,
|
|
1006
|
+
timeoutMs: 30_000,
|
|
1007
|
+
runner,
|
|
1008
|
+
};
|
|
1009
|
+
let warnedCount = 0;
|
|
1010
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1011
|
+
runId: "run-forged-exit",
|
|
1012
|
+
target,
|
|
1013
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1014
|
+
adapterKey: "acpx",
|
|
1015
|
+
command: process.execPath,
|
|
1016
|
+
args: [childPath],
|
|
1017
|
+
cwd: rootDir,
|
|
1018
|
+
env: {},
|
|
1019
|
+
timeoutSec: 5,
|
|
1020
|
+
onLog: async (stream, chunk) => {
|
|
1021
|
+
if (stream === "stderr" && chunk.includes("did not acknowledge shutdown"))
|
|
1022
|
+
warnedCount += 1;
|
|
1023
|
+
},
|
|
1024
|
+
});
|
|
1025
|
+
expect(bridge).not.toBeNull();
|
|
1026
|
+
const mkdirScript = scripts.find((script) => script.startsWith("mkdir -p"));
|
|
1027
|
+
const dirsMatch = /mkdir -p '([^']+)' '([^']+)'/.exec(mkdirScript ?? "");
|
|
1028
|
+
expect(dirsMatch).not.toBeNull();
|
|
1029
|
+
const eventsDir = dirsMatch[2];
|
|
1030
|
+
// Forge an exit event from outside the wrapper, before any real shutdown.
|
|
1031
|
+
await mkdir(eventsDir, { recursive: true });
|
|
1032
|
+
await writeFile(path.join(eventsDir, "999999999999.json"), `${JSON.stringify({ type: "exit", code: 0 })}\n`, "utf8");
|
|
1033
|
+
// Give the live 100 ms host poll time to read the forged file well
|
|
1034
|
+
// before stop() runs. This closes the gap T5 used to leave open: a
|
|
1035
|
+
// forged event `stop()` observes only through its own bounded reader
|
|
1036
|
+
// (not through the live poll, which sets `stopping` on its own and
|
|
1037
|
+
// stops re-arming) must not shorten the wait either.
|
|
1038
|
+
await delay(600);
|
|
1039
|
+
scripts.length = 0;
|
|
1040
|
+
const start = Date.now();
|
|
1041
|
+
await bridge.stop();
|
|
1042
|
+
const elapsedMs = Date.now() - start;
|
|
1043
|
+
const finalizeWrites = scripts.filter((script) => script.includes("base64 -d") && script.includes(".paperclip-upload.decoded"));
|
|
1044
|
+
// stdinEnd, then shutdown: both control messages still land.
|
|
1045
|
+
expect(finalizeWrites.length).toBeGreaterThanOrEqual(2);
|
|
1046
|
+
const removeScript = scripts.find((script) => script.trim().startsWith("rm -rf"));
|
|
1047
|
+
expect(removeScript).toBeDefined();
|
|
1048
|
+
// The child never exits on its own, so the wrapper's own genuine
|
|
1049
|
+
// shutdownAck -- not the forged exit event -- is the only thing that can
|
|
1050
|
+
// finish this fast with no warning. T14 (below) proves the forged event
|
|
1051
|
+
// alone gives no such shortcut when no genuine acknowledgement ever
|
|
1052
|
+
// follows it.
|
|
1053
|
+
expect(elapsedMs).toBeLessThan(1_000);
|
|
1054
|
+
expect(warnedCount).toBe(0);
|
|
1055
|
+
}, 10_000);
|
|
1056
|
+
it("T14 a forged exit event alone does not shorten the wait when the wrapper never truly runs", async () => {
|
|
1057
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-forged-only-"));
|
|
1058
|
+
cleanupDirs.push(rootDir);
|
|
1059
|
+
const childPath = path.join(rootDir, "quiet-child.mjs");
|
|
1060
|
+
await writeFile(childPath, "process.stdin.resume();\n", "utf8");
|
|
1061
|
+
const scripts = [];
|
|
1062
|
+
let counter = 0;
|
|
1063
|
+
// Run every script for real except the wrapper launch itself, so the
|
|
1064
|
+
// wrapper never starts. The forged file below is then the only event
|
|
1065
|
+
// that will ever exist under the session's events directory.
|
|
1066
|
+
const runner = {
|
|
1067
|
+
execute: async (input) => {
|
|
1068
|
+
const script = input.args?.[1] ?? "";
|
|
1069
|
+
scripts.push(script);
|
|
1070
|
+
if (script.includes("nohup node")) {
|
|
1071
|
+
return { exitCode: 0, signal: null, timedOut: false, stdout: "", stderr: "", pid: null, startedAt: null };
|
|
1072
|
+
}
|
|
1073
|
+
counter += 1;
|
|
1074
|
+
const command = input.command === "bash" ? "/bin/bash" : input.command === "sh" ? "/bin/sh" : input.command;
|
|
1075
|
+
return runChildProcess(`forged-only-run-${counter}`, command, input.args ?? [], {
|
|
1076
|
+
cwd: input.cwd ?? process.cwd(),
|
|
1077
|
+
env: input.env ?? {},
|
|
1078
|
+
stdin: input.stdin,
|
|
1079
|
+
timeoutSec: Math.max(1, Math.ceil((input.timeoutMs ?? 30_000) / 1000)),
|
|
1080
|
+
graceSec: 5,
|
|
1081
|
+
onLog: input.onLog ?? (async () => { }),
|
|
1082
|
+
});
|
|
1083
|
+
},
|
|
1084
|
+
};
|
|
1085
|
+
const target = {
|
|
1086
|
+
kind: "remote",
|
|
1087
|
+
transport: "sandbox",
|
|
1088
|
+
providerKey: "local-test",
|
|
1089
|
+
remoteCwd: rootDir,
|
|
1090
|
+
timeoutMs: 30_000,
|
|
1091
|
+
runner,
|
|
1092
|
+
};
|
|
1093
|
+
let warnedCount = 0;
|
|
1094
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1095
|
+
runId: "run-forged-only",
|
|
1096
|
+
target,
|
|
1097
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1098
|
+
adapterKey: "acpx",
|
|
1099
|
+
command: process.execPath,
|
|
1100
|
+
args: [childPath],
|
|
1101
|
+
cwd: rootDir,
|
|
1102
|
+
env: {},
|
|
1103
|
+
timeoutSec: 5,
|
|
1104
|
+
onLog: async (stream, chunk) => {
|
|
1105
|
+
if (stream === "stderr" && chunk.includes("did not acknowledge shutdown"))
|
|
1106
|
+
warnedCount += 1;
|
|
1107
|
+
},
|
|
1108
|
+
});
|
|
1109
|
+
expect(bridge).not.toBeNull();
|
|
1110
|
+
const mkdirScript = scripts.find((script) => script.startsWith("mkdir -p"));
|
|
1111
|
+
const dirsMatch = /mkdir -p '([^']+)' '([^']+)'/.exec(mkdirScript ?? "");
|
|
1112
|
+
expect(dirsMatch).not.toBeNull();
|
|
1113
|
+
const eventsDir = dirsMatch[2];
|
|
1114
|
+
// Forge a terminal event from outside the wrapper. The wrapper never
|
|
1115
|
+
// started, so this is the only event that will ever exist on disk.
|
|
1116
|
+
await mkdir(eventsDir, { recursive: true });
|
|
1117
|
+
await writeFile(path.join(eventsDir, "999999999999.json"), `${JSON.stringify({ type: "exit", code: 0 })}\n`, "utf8");
|
|
1118
|
+
// Give the live 100 ms host poll time to read the forged file well
|
|
1119
|
+
// before stop() runs, so this test cannot pass by accident: `stop()`
|
|
1120
|
+
// never itself observes this event through its own first-line
|
|
1121
|
+
// `stopping` flag.
|
|
1122
|
+
await delay(600);
|
|
1123
|
+
const start = Date.now();
|
|
1124
|
+
await bridge.stop();
|
|
1125
|
+
const elapsedMs = Date.now() - start;
|
|
1126
|
+
// An exit event under `sessionDir` is untrusted telemetry. With no
|
|
1127
|
+
// genuine wrapper ever running to write a real shutdownAck, the wait
|
|
1128
|
+
// still runs its full budget and still warns, exactly as it would with
|
|
1129
|
+
// no forged event at all (compare T13).
|
|
1130
|
+
expect(elapsedMs).toBeGreaterThanOrEqual(2_900);
|
|
1131
|
+
expect(warnedCount).toBe(1);
|
|
1132
|
+
const removeScript = scripts.find((script) => script.trim().startsWith("rm -rf"));
|
|
1133
|
+
expect(removeScript).toBeDefined();
|
|
1134
|
+
}, 10_000);
|
|
1135
|
+
it("T6 issues no operating-system signal from the host during stop()", async () => {
|
|
1136
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-no-signal-"));
|
|
1137
|
+
cleanupDirs.push(rootDir);
|
|
1138
|
+
const childPath = path.join(rootDir, "quiet-child.mjs");
|
|
1139
|
+
await writeFile(childPath, "process.stdin.resume();\n", "utf8");
|
|
1140
|
+
const scripts = [];
|
|
1141
|
+
const runner = createLocalSandboxRunner(async (script) => {
|
|
1142
|
+
scripts.push(script);
|
|
1143
|
+
});
|
|
1144
|
+
const target = {
|
|
1145
|
+
kind: "remote",
|
|
1146
|
+
transport: "sandbox",
|
|
1147
|
+
providerKey: "local-test",
|
|
1148
|
+
remoteCwd: rootDir,
|
|
1149
|
+
timeoutMs: 30_000,
|
|
1150
|
+
runner,
|
|
1151
|
+
};
|
|
1152
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1153
|
+
runId: "run-no-signal",
|
|
1154
|
+
target,
|
|
1155
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1156
|
+
adapterKey: "acpx",
|
|
1157
|
+
command: process.execPath,
|
|
1158
|
+
args: [childPath],
|
|
1159
|
+
cwd: rootDir,
|
|
1160
|
+
env: {},
|
|
1161
|
+
timeoutSec: 5,
|
|
1162
|
+
onLog: async () => { },
|
|
1163
|
+
});
|
|
1164
|
+
expect(bridge).not.toBeNull();
|
|
1165
|
+
scripts.length = 0;
|
|
1166
|
+
await bridge.stop();
|
|
1167
|
+
// stop() only ever writes files and removes a directory. None of the
|
|
1168
|
+
// scripts it runs names a signal or a kill command.
|
|
1169
|
+
const signalLike = scripts.filter((script) => /\bkill\b|SIGTERM|SIGKILL/i.test(script));
|
|
1170
|
+
expect(signalLike).toEqual([]);
|
|
1171
|
+
});
|
|
1172
|
+
it("T8 running terminate() a second time, after the child already exited, is a safe no-op", async () => {
|
|
1173
|
+
const wrapper = await startWrapperProcess({
|
|
1174
|
+
outputToStdout: false,
|
|
1175
|
+
command: process.execPath,
|
|
1176
|
+
args: ["-e", "process.exit(0)"],
|
|
1177
|
+
});
|
|
1178
|
+
await Promise.race([
|
|
1179
|
+
wrapper.exited,
|
|
1180
|
+
delay(4_000).then(() => {
|
|
1181
|
+
throw new Error("The wrapper did not exit after its child exited on its own.");
|
|
1182
|
+
}),
|
|
1183
|
+
]);
|
|
1184
|
+
// The wrapper's own child-close handler already ran terminate() once (the
|
|
1185
|
+
// child was already gone, so its child.kill() call no-opped). The
|
|
1186
|
+
// wrapper did not throw and did not hang.
|
|
1187
|
+
expect(wrapper.stderrText()).toBe("");
|
|
1188
|
+
expect(wrapper.exitInfo().code).toBe(0);
|
|
1189
|
+
const events = await wrapper.readEventFiles();
|
|
1190
|
+
expect(events.filter((event) => event.type === "exit").length).toBe(1);
|
|
1191
|
+
// No stray signal-triggered event (e.g. a second exit from a SIGKILL)
|
|
1192
|
+
// ever landed.
|
|
1193
|
+
expect(events.filter((event) => event.type === "error").length).toBe(0);
|
|
1194
|
+
});
|
|
1195
|
+
it("T9 exits with an error event when sessionDir is a symbolic link", async () => {
|
|
1196
|
+
const targetDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-symlink-target-"));
|
|
1197
|
+
cleanupDirs.push(targetDir);
|
|
1198
|
+
const linkDir = `${targetDir}-link`;
|
|
1199
|
+
const { symlink } = await import("node:fs/promises");
|
|
1200
|
+
await symlink(targetDir, linkDir, "dir");
|
|
1201
|
+
cleanupDirs.push(linkDir);
|
|
1202
|
+
const wrapperPath = path.join(targetDir, "wrapper.mjs");
|
|
1203
|
+
await writeFile(wrapperPath, getProcessSessionRemoteSource({ outputToStdout: true }), "utf8");
|
|
1204
|
+
const config = { command: "cat", args: [], cwd: targetDir, env: {} };
|
|
1205
|
+
const commandPayload = Buffer.from(JSON.stringify(config), "utf8").toString("base64");
|
|
1206
|
+
const child = spawn(process.execPath, [wrapperPath], {
|
|
1207
|
+
cwd: targetDir,
|
|
1208
|
+
env: {
|
|
1209
|
+
...process.env,
|
|
1210
|
+
PAPERCLIP_PROCESS_SESSION_DIR: linkDir,
|
|
1211
|
+
PAPERCLIP_PROCESS_SESSION_COMMAND_B64: commandPayload,
|
|
1212
|
+
},
|
|
1213
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1214
|
+
});
|
|
1215
|
+
const frames = [];
|
|
1216
|
+
let stdoutBuffer = "";
|
|
1217
|
+
child.stdout.on("data", (chunk) => {
|
|
1218
|
+
stdoutBuffer += chunk.toString("utf8");
|
|
1219
|
+
const lines = stdoutBuffer.split("\n");
|
|
1220
|
+
stdoutBuffer = lines.pop() || "";
|
|
1221
|
+
for (const line of lines) {
|
|
1222
|
+
if (!line.trim())
|
|
1223
|
+
continue;
|
|
1224
|
+
frames.push(JSON.parse(line));
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
const exited = new Promise((resolve) => child.on("close", () => resolve()));
|
|
1228
|
+
await Promise.race([
|
|
1229
|
+
exited,
|
|
1230
|
+
delay(4_000).then(() => {
|
|
1231
|
+
throw new Error("The wrapper did not exit after sessionDir was a symbolic link.");
|
|
1232
|
+
}),
|
|
1233
|
+
]);
|
|
1234
|
+
expect(frames.some((frame) => frame.type === "error" && typeof frame.message === "string" && frame.message.includes("symbolic link"))).toBe(true);
|
|
1235
|
+
});
|
|
1236
|
+
it("T10 the emitted wrapper strips its own session env vars from the child", async () => {
|
|
1237
|
+
const wrapper = await startWrapperProcess({
|
|
1238
|
+
outputToStdout: true,
|
|
1239
|
+
command: process.execPath,
|
|
1240
|
+
args: [
|
|
1241
|
+
"-e",
|
|
1242
|
+
"process.stdout.write(JSON.stringify(Object.keys(process.env).filter((k) => k.startsWith('PAPERCLIP_PROCESS_SESSION'))));process.exit(0)",
|
|
1243
|
+
],
|
|
1244
|
+
});
|
|
1245
|
+
await waitFor(() => wrapper.frames.some((frame) => frame.type === "exit"), 4_000);
|
|
1246
|
+
const text = wrapper.frames
|
|
1247
|
+
.filter((frame) => frame.type === "data" && frame.stream === "stdout" && typeof frame.data === "string")
|
|
1248
|
+
.map((frame) => Buffer.from(frame.data, "base64").toString("utf8"))
|
|
1249
|
+
.join("");
|
|
1250
|
+
const leakedKeys = JSON.parse(text || "[]");
|
|
1251
|
+
expect(leakedKeys).toEqual([]);
|
|
1252
|
+
});
|
|
1253
|
+
it("T11 each wrapper source has exactly one spawn call site and every kill call is child.kill()", () => {
|
|
1254
|
+
for (const outputToStdout of [true, false]) {
|
|
1255
|
+
const src = getProcessSessionRemoteSource({ outputToStdout });
|
|
1256
|
+
// Strip `//` line comments first, so prose that happens to mention
|
|
1257
|
+
// "spawn(" or "kill(" (e.g. explaining `ChildProcess#kill()`) is never
|
|
1258
|
+
// mistaken for a call site. This checks the code, not the comments.
|
|
1259
|
+
const code = src
|
|
1260
|
+
.split("\n")
|
|
1261
|
+
.map((line) => line.replace(/\/\/.*$/, ""))
|
|
1262
|
+
.join("\n");
|
|
1263
|
+
const spawnCallSites = code.match(/\bspawn\(/g) ?? [];
|
|
1264
|
+
expect(spawnCallSites.length).toBe(1);
|
|
1265
|
+
const killCallSites = [...code.matchAll(/[A-Za-z0-9_.$]*kill\(/g)].map((match) => match[0]);
|
|
1266
|
+
expect(killCallSites.length).toBeGreaterThan(0);
|
|
1267
|
+
for (const site of killCallSites) {
|
|
1268
|
+
expect(site).toBe("child.kill(");
|
|
1269
|
+
}
|
|
1270
|
+
// Regression coverage for PAP-5336: the shared tail must carry the
|
|
1271
|
+
// session-identity latch, not the old counter it replaced. A counter
|
|
1272
|
+
// that a successful `readdir` reset to zero let an attacker who
|
|
1273
|
+
// recreated a deleted control directory keep the wrapper alive
|
|
1274
|
+
// forever. Both wrapper variants append the same shared tail, so this
|
|
1275
|
+
// check runs once per variant and fails if a future edit lands the
|
|
1276
|
+
// latch in only one of them.
|
|
1277
|
+
expect(src).not.toContain("missingSessionDirStreak");
|
|
1278
|
+
const identityLatchDeclarations = code.match(/\blet identityLost = false;/g) ?? [];
|
|
1279
|
+
expect(identityLatchDeclarations.length).toBe(1);
|
|
1280
|
+
const identityCaptureCallSites = code.match(/\bcaptureSessionIdentity\(\)/g) ?? [];
|
|
1281
|
+
expect(identityCaptureCallSites.length).toBeGreaterThan(0);
|
|
1282
|
+
const identityVerifyCallSites = code.match(/\bverifySessionIdentity\(\)/g) ?? [];
|
|
1283
|
+
expect(identityVerifyCallSites.length).toBeGreaterThan(0);
|
|
1284
|
+
// The capture must run before the first poll cycle: its call site must
|
|
1285
|
+
// precede the `pollStdin()` call site in the emitted source.
|
|
1286
|
+
expect(code.indexOf("await captureSessionIdentity();")).toBeGreaterThan(0);
|
|
1287
|
+
expect(code.indexOf("await captureSessionIdentity();")).toBeLessThan(code.indexOf("void pollStdin()"));
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
// Regression coverage for PAP-5323: the host used to burn the full
|
|
1291
|
+
// shutdown budget and log a false warning on every normal run, because
|
|
1292
|
+
// the file-poll loop stopped re-arming right after it delivered the
|
|
1293
|
+
// `exit` event and so never read the `shutdownAck` file that followed it.
|
|
1294
|
+
it("T12 stop() finishes well inside the shutdown budget and logs no warning after a normal child exit", async () => {
|
|
1295
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-normal-exit-"));
|
|
1296
|
+
cleanupDirs.push(rootDir);
|
|
1297
|
+
const childPath = path.join(rootDir, "quick-exit-child.mjs");
|
|
1298
|
+
await writeFile(childPath, "process.exit(0);\n", "utf8");
|
|
1299
|
+
const runner = createLocalSandboxRunner();
|
|
1300
|
+
const target = {
|
|
1301
|
+
kind: "remote",
|
|
1302
|
+
transport: "sandbox",
|
|
1303
|
+
providerKey: "local-test",
|
|
1304
|
+
remoteCwd: rootDir,
|
|
1305
|
+
timeoutMs: 30_000,
|
|
1306
|
+
runner,
|
|
1307
|
+
};
|
|
1308
|
+
let warnedCount = 0;
|
|
1309
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1310
|
+
runId: "run-normal-exit",
|
|
1311
|
+
target,
|
|
1312
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1313
|
+
adapterKey: "acpx",
|
|
1314
|
+
command: process.execPath,
|
|
1315
|
+
args: [childPath],
|
|
1316
|
+
cwd: rootDir,
|
|
1317
|
+
env: {},
|
|
1318
|
+
timeoutSec: 5,
|
|
1319
|
+
onLog: async (stream, chunk) => {
|
|
1320
|
+
if (stream === "stderr" && chunk.includes("did not acknowledge shutdown"))
|
|
1321
|
+
warnedCount += 1;
|
|
1322
|
+
},
|
|
1323
|
+
});
|
|
1324
|
+
expect(bridge).not.toBeNull();
|
|
1325
|
+
// Let the child exit and the wrapper write its own `exit` event before
|
|
1326
|
+
// stop() runs, so this matches a normal run-completion teardown.
|
|
1327
|
+
await delay(500);
|
|
1328
|
+
const start = Date.now();
|
|
1329
|
+
await bridge.stop();
|
|
1330
|
+
const elapsedMs = Date.now() - start;
|
|
1331
|
+
expect(elapsedMs).toBeLessThan(1_000);
|
|
1332
|
+
expect(warnedCount).toBe(0);
|
|
1333
|
+
}, 10_000);
|
|
1334
|
+
// Regression coverage for PAP-5323: a genuinely stuck wrapper, one that
|
|
1335
|
+
// never writes any event, must still warn after the budget and still
|
|
1336
|
+
// remove `sessionDir`. The fix must not turn the bounded wait into an
|
|
1337
|
+
// unconditional skip.
|
|
1338
|
+
it("T13 warns and still removes sessionDir when the wrapper never acknowledges and never exits", async () => {
|
|
1339
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-never-acks-"));
|
|
1340
|
+
cleanupDirs.push(rootDir);
|
|
1341
|
+
const childPath = path.join(rootDir, "quiet-child.mjs");
|
|
1342
|
+
await writeFile(childPath, "process.stdin.resume();\n", "utf8");
|
|
1343
|
+
const scripts = [];
|
|
1344
|
+
let counter = 0;
|
|
1345
|
+
// Run every script for real except the wrapper launch itself, so the
|
|
1346
|
+
// wrapper never starts and the events directory stays empty on every
|
|
1347
|
+
// poll. `stop()` can then never observe a real `shutdownAck` or a real
|
|
1348
|
+
// terminal `exit`/`error` event.
|
|
1349
|
+
const runner = {
|
|
1350
|
+
execute: async (input) => {
|
|
1351
|
+
const script = input.args?.[1] ?? "";
|
|
1352
|
+
scripts.push(script);
|
|
1353
|
+
if (script.includes("nohup node")) {
|
|
1354
|
+
return { exitCode: 0, signal: null, timedOut: false, stdout: "", stderr: "", pid: null, startedAt: null };
|
|
1355
|
+
}
|
|
1356
|
+
counter += 1;
|
|
1357
|
+
const command = input.command === "bash" ? "/bin/bash" : input.command === "sh" ? "/bin/sh" : input.command;
|
|
1358
|
+
return runChildProcess(`never-acks-run-${counter}`, command, input.args ?? [], {
|
|
1359
|
+
cwd: input.cwd ?? process.cwd(),
|
|
1360
|
+
env: input.env ?? {},
|
|
1361
|
+
stdin: input.stdin,
|
|
1362
|
+
timeoutSec: Math.max(1, Math.ceil((input.timeoutMs ?? 30_000) / 1000)),
|
|
1363
|
+
graceSec: 5,
|
|
1364
|
+
onLog: input.onLog ?? (async () => { }),
|
|
1365
|
+
});
|
|
1366
|
+
},
|
|
1367
|
+
};
|
|
1368
|
+
const target = {
|
|
1369
|
+
kind: "remote",
|
|
1370
|
+
transport: "sandbox",
|
|
1371
|
+
providerKey: "local-test",
|
|
1372
|
+
remoteCwd: rootDir,
|
|
1373
|
+
timeoutMs: 30_000,
|
|
1374
|
+
runner,
|
|
1375
|
+
};
|
|
1376
|
+
let warnedCount = 0;
|
|
1377
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1378
|
+
runId: "run-never-acks",
|
|
1379
|
+
target,
|
|
1380
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1381
|
+
adapterKey: "acpx",
|
|
1382
|
+
command: process.execPath,
|
|
1383
|
+
args: [childPath],
|
|
1384
|
+
cwd: rootDir,
|
|
1385
|
+
env: {},
|
|
1386
|
+
timeoutSec: 5,
|
|
1387
|
+
onLog: async (stream, chunk) => {
|
|
1388
|
+
if (stream === "stderr" && chunk.includes("did not acknowledge shutdown"))
|
|
1389
|
+
warnedCount += 1;
|
|
1390
|
+
},
|
|
1391
|
+
});
|
|
1392
|
+
expect(bridge).not.toBeNull();
|
|
1393
|
+
const start = Date.now();
|
|
1394
|
+
await bridge.stop();
|
|
1395
|
+
const elapsedMs = Date.now() - start;
|
|
1396
|
+
// The full shutdown budget elapsed, because nothing ever proved the
|
|
1397
|
+
// wrapper stopped.
|
|
1398
|
+
expect(elapsedMs).toBeGreaterThanOrEqual(2_900);
|
|
1399
|
+
expect(warnedCount).toBe(1);
|
|
1400
|
+
const removeScript = scripts.find((script) => script.trim().startsWith("rm -rf"));
|
|
1401
|
+
expect(removeScript).toBeDefined();
|
|
1402
|
+
}, 10_000);
|
|
1403
|
+
// Regression coverage for PAP-5336: the finding this test reproduces is a
|
|
1404
|
+
// sandbox control-plane integrity failure, not a timing quirk. During
|
|
1405
|
+
// `stop()`, a sandbox peer with access to the session directory can (1)
|
|
1406
|
+
// delete the real `shutdown` control file before the wrapper ever reads
|
|
1407
|
+
// it, (2) forge a `shutdownAck` event so the host's wait ends early, and
|
|
1408
|
+
// (3) recreate `sessionDir/stdin` right after the host removes
|
|
1409
|
+
// `sessionDir`. On the parent commit, a successful `readdir` on the
|
|
1410
|
+
// recreated directory reset the wrapper's only terminal counter to zero,
|
|
1411
|
+
// so the wrapper (and its child) polled forever.
|
|
1412
|
+
//
|
|
1413
|
+
// The fix replaces the counter with an identity captured at startup: the
|
|
1414
|
+
// device number, the inode number, and the inode's own creation time. All
|
|
1415
|
+
// three matter for this test to be a real regression check, not a check
|
|
1416
|
+
// that passes by luck. Recreating a directory at the same path right after
|
|
1417
|
+
// removal, with nothing else on the filesystem in between, can reissue the
|
|
1418
|
+
// exact same device and inode numbers on common filesystems (this test's
|
|
1419
|
+
// recreate step does exactly that): a device/inode-only identity would
|
|
1420
|
+
// then wrongly read as unchanged. The creation time does not have this
|
|
1421
|
+
// gap, because it is set fresh on every inode allocation even when the
|
|
1422
|
+
// allocator reissues an old inode number.
|
|
1423
|
+
it("T15 latches on a lost session identity: a recreated control directory cannot keep the wrapper or its child alive", async () => {
|
|
1424
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-lost-identity-"));
|
|
1425
|
+
cleanupDirs.push(rootDir);
|
|
1426
|
+
const pidFile = path.join(rootDir, "t15-child.pid");
|
|
1427
|
+
const childPath = path.join(rootDir, "t15-child.mjs");
|
|
1428
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1429
|
+
let sessionDir = "";
|
|
1430
|
+
let stdinDir = "";
|
|
1431
|
+
let eventsDir = "";
|
|
1432
|
+
let shutdownFileDeleted = false;
|
|
1433
|
+
let shutdownAckForged = false;
|
|
1434
|
+
let stdinDirRecreated = false;
|
|
1435
|
+
const scripts = [];
|
|
1436
|
+
let counter = 0;
|
|
1437
|
+
const syntheticSuccess = {
|
|
1438
|
+
exitCode: 0,
|
|
1439
|
+
signal: null,
|
|
1440
|
+
timedOut: false,
|
|
1441
|
+
stdout: "",
|
|
1442
|
+
stderr: "",
|
|
1443
|
+
pid: null,
|
|
1444
|
+
startedAt: null,
|
|
1445
|
+
};
|
|
1446
|
+
// Run every script for real on the local filesystem (matching T2/T4/T5's
|
|
1447
|
+
// harness), so a real wrapper process and a real child process come up,
|
|
1448
|
+
// with two exceptions that make the attack deterministic instead of a
|
|
1449
|
+
// race against the wrapper's own 50 ms poll:
|
|
1450
|
+
//
|
|
1451
|
+
// 1. With no stdin data ever sent, the host's shutdown control message
|
|
1452
|
+
// always targets stdin file 000000000002.json (file 1 is stdinEnd).
|
|
1453
|
+
// Never let that write's script pipeline actually run: this is
|
|
1454
|
+
// equivalent to an attacker who deletes the file before the wrapper
|
|
1455
|
+
// ever reads it, but with no window in which the wrapper could win a
|
|
1456
|
+
// race and read it first.
|
|
1457
|
+
// 2. Perform the sessionDir removal and the sessionDir/stdin,
|
|
1458
|
+
// sessionDir/events recreation as direct filesystem calls in this
|
|
1459
|
+
// same async step, instead of spawning a real `rm -rf` subprocess.
|
|
1460
|
+
// That removes an external process's scheduling latency from the
|
|
1461
|
+
// window the wrapper's next poll cycle has to observe the recreated
|
|
1462
|
+
// directory.
|
|
1463
|
+
const runner = {
|
|
1464
|
+
execute: async (input) => {
|
|
1465
|
+
counter += 1;
|
|
1466
|
+
const script = input.args?.[1] ?? "";
|
|
1467
|
+
scripts.push(script);
|
|
1468
|
+
const shutdownFilePath = stdinDir ? path.posix.join(stdinDir, "000000000002.json") : null;
|
|
1469
|
+
if (shutdownFilePath && script.includes(shutdownFilePath)) {
|
|
1470
|
+
if (!shutdownFileDeleted) {
|
|
1471
|
+
shutdownFileDeleted = true;
|
|
1472
|
+
await writeFile(path.join(eventsDir, "999999999999.json"), `${JSON.stringify({ type: "shutdownAck" })}\n`, "utf8").catch(() => undefined);
|
|
1473
|
+
shutdownAckForged = true;
|
|
1474
|
+
}
|
|
1475
|
+
return syntheticSuccess;
|
|
1476
|
+
}
|
|
1477
|
+
// Match the removal of sessionDir itself, not the host's own
|
|
1478
|
+
// per-file event cleanup (which also runs `rm -rf` on a path that
|
|
1479
|
+
// has sessionDir as a substring).
|
|
1480
|
+
if (!stdinDirRecreated && sessionDir && script.trim() === `rm -rf '${sessionDir}'`) {
|
|
1481
|
+
stdinDirRecreated = true;
|
|
1482
|
+
await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1483
|
+
await mkdir(stdinDir, { recursive: true }).catch(() => undefined);
|
|
1484
|
+
await mkdir(eventsDir, { recursive: true }).catch(() => undefined);
|
|
1485
|
+
return syntheticSuccess;
|
|
1486
|
+
}
|
|
1487
|
+
const command = input.command === "bash" ? "/bin/bash" : input.command === "sh" ? "/bin/sh" : input.command;
|
|
1488
|
+
return runChildProcess(`lost-identity-run-${counter}`, command, input.args ?? [], {
|
|
1489
|
+
cwd: input.cwd ?? process.cwd(),
|
|
1490
|
+
env: input.env ?? {},
|
|
1491
|
+
stdin: input.stdin,
|
|
1492
|
+
timeoutSec: Math.max(1, Math.ceil((input.timeoutMs ?? 30_000) / 1000)),
|
|
1493
|
+
graceSec: 5,
|
|
1494
|
+
onLog: input.onLog ?? (async () => { }),
|
|
1495
|
+
});
|
|
1496
|
+
},
|
|
1497
|
+
};
|
|
1498
|
+
const target = {
|
|
1499
|
+
kind: "remote",
|
|
1500
|
+
transport: "sandbox",
|
|
1501
|
+
providerKey: "local-test",
|
|
1502
|
+
remoteCwd: rootDir,
|
|
1503
|
+
timeoutMs: 30_000,
|
|
1504
|
+
runner,
|
|
1505
|
+
};
|
|
1506
|
+
const bridge = await startAdapterExecutionTargetProcessSessionBridge({
|
|
1507
|
+
runId: "run-lost-identity",
|
|
1508
|
+
target,
|
|
1509
|
+
runtimeRootDir: path.posix.join(rootDir, ".paperclip-runtime", "acpx"),
|
|
1510
|
+
adapterKey: "acpx",
|
|
1511
|
+
command: process.execPath,
|
|
1512
|
+
args: [childPath],
|
|
1513
|
+
cwd: rootDir,
|
|
1514
|
+
env: {},
|
|
1515
|
+
timeoutSec: 5,
|
|
1516
|
+
onLog: async () => { },
|
|
1517
|
+
});
|
|
1518
|
+
expect(bridge).not.toBeNull();
|
|
1519
|
+
const mkdirScript = scripts.find((script) => script.startsWith("mkdir -p"));
|
|
1520
|
+
const dirsMatch = /mkdir -p '([^']+)' '([^']+)'/.exec(mkdirScript ?? "");
|
|
1521
|
+
expect(dirsMatch).not.toBeNull();
|
|
1522
|
+
stdinDir = dirsMatch[1];
|
|
1523
|
+
eventsDir = dirsMatch[2];
|
|
1524
|
+
sessionDir = path.posix.dirname(stdinDir);
|
|
1525
|
+
await waitFor(async () => (await readFile(pidFile, "utf8").catch(() => "")).trim().length > 0, 8_000);
|
|
1526
|
+
const pid = Number.parseInt((await readFile(pidFile, "utf8")).trim(), 10);
|
|
1527
|
+
expect(isPidAlive(pid)).toBe(true);
|
|
1528
|
+
const wrapperScriptSubstring = path.posix.join(rootDir, ".paperclip-runtime", "acpx", "process-sessions");
|
|
1529
|
+
await waitFor(async () => (await findLivePidsByArgvSubstring(wrapperScriptSubstring)).length > 0, 4_000);
|
|
1530
|
+
await bridge.stop();
|
|
1531
|
+
// Both the attacker's forged shutdownAck and its directory recreation
|
|
1532
|
+
// ran; this is the full chain the finding describes, not a partial one.
|
|
1533
|
+
expect(shutdownFileDeleted).toBe(true);
|
|
1534
|
+
expect(shutdownAckForged).toBe(true);
|
|
1535
|
+
expect(stdinDirRecreated).toBe(true);
|
|
1536
|
+
// The wrapper process and its child process both exit within a bounded
|
|
1537
|
+
// time, even though the wrapper never read a real shutdown message and
|
|
1538
|
+
// the host's wait ended early on a forged hint. Only the wrapper's own
|
|
1539
|
+
// identity latch can explain this: it observes the recreated directory
|
|
1540
|
+
// carries a different identity than the one captured at startup.
|
|
1541
|
+
await waitFor(() => !isPidAlive(pid), 8_000);
|
|
1542
|
+
expect(isPidAlive(pid)).toBe(false);
|
|
1543
|
+
await waitFor(async () => (await findLivePidsByArgvSubstring(wrapperScriptSubstring)).length === 0, 8_000);
|
|
1544
|
+
}, 15_000);
|
|
1545
|
+
// ---- PAP-5338: reject a change-time creation-time substitute, and every
|
|
1546
|
+
// lstat error during verification -------------------------------------
|
|
1547
|
+
async function waitForTrackedChildPid(pidFile) {
|
|
1548
|
+
await waitFor(async () => (await readFile(pidFile, "utf8").catch(() => "")).trim().length > 0, 8_000);
|
|
1549
|
+
return Number.parseInt((await readFile(pidFile, "utf8")).trim(), 10);
|
|
1550
|
+
}
|
|
1551
|
+
async function expectWrapperAndTrackedChildToDie(wrapper, pid) {
|
|
1552
|
+
await waitFor(() => !isPidAlive(pid), 8_000);
|
|
1553
|
+
expect(isPidAlive(pid)).toBe(false);
|
|
1554
|
+
await Promise.race([
|
|
1555
|
+
wrapper.exited,
|
|
1556
|
+
delay(8_000).then(() => {
|
|
1557
|
+
throw new Error("The wrapper process did not exit.");
|
|
1558
|
+
}),
|
|
1559
|
+
]);
|
|
1560
|
+
}
|
|
1561
|
+
// A capture failure latches and calls terminate() before the poll loop
|
|
1562
|
+
// ever starts, often within a few milliseconds of the child's own spawn()
|
|
1563
|
+
// call returning. A freshly spawned Node.js child needs real wall-clock
|
|
1564
|
+
// time just to boot before it can run its own code, so it can lose the
|
|
1565
|
+
// race to write a pid file before terminate()'s SIGTERM reaches it. The
|
|
1566
|
+
// change-time fallback test below therefore proves death through the OS
|
|
1567
|
+
// process table by the child's own script path (the same technique T15
|
|
1568
|
+
// above uses for the wrapper itself), which needs no cooperation from code
|
|
1569
|
+
// inside the child.
|
|
1570
|
+
async function expectNoLiveProcessByArgvSubstring(substring) {
|
|
1571
|
+
await waitFor(async () => (await findLivePidsByArgvSubstring(substring)).length === 0, 8_000);
|
|
1572
|
+
expect(await findLivePidsByArgvSubstring(substring)).toEqual([]);
|
|
1573
|
+
}
|
|
1574
|
+
it("T16 accepts a zero creation time when the filesystem does not report birth time", async () => {
|
|
1575
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-birthtime-zero-"));
|
|
1576
|
+
cleanupDirs.push(rootDir);
|
|
1577
|
+
const pidFile = path.join(rootDir, "t16-child.pid");
|
|
1578
|
+
const childPath = path.join(rootDir, "t16-child.mjs");
|
|
1579
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1580
|
+
const wrapper = await startWrapperProcess({
|
|
1581
|
+
outputToStdout: false,
|
|
1582
|
+
command: process.execPath,
|
|
1583
|
+
args: [childPath],
|
|
1584
|
+
fakeBirthtime: { target: "sessionDir", mode: "zero" },
|
|
1585
|
+
});
|
|
1586
|
+
const pid = await waitForTrackedChildPid(pidFile);
|
|
1587
|
+
expect(isPidAlive(pid)).toBe(true);
|
|
1588
|
+
await writeFile(path.join(wrapper.stdinDir, "000000000001.json"), `${JSON.stringify({ type: "stdinEnd" })}\n`, "utf8");
|
|
1589
|
+
await Promise.race([
|
|
1590
|
+
wrapper.exited,
|
|
1591
|
+
delay(8_000).then(() => {
|
|
1592
|
+
throw new Error("The wrapper process did not exit.");
|
|
1593
|
+
}),
|
|
1594
|
+
]);
|
|
1595
|
+
expect(wrapper.exitInfo().code).toBe(0);
|
|
1596
|
+
expect(wrapper.stderrText()).not.toMatch(/not usable/);
|
|
1597
|
+
}, 15_000);
|
|
1598
|
+
it("T17 fails closed at capture when the reported creation time follows the change time, so a change-time copy never passes as a real creation time", async () => {
|
|
1599
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-birthtime-followctime-"));
|
|
1600
|
+
cleanupDirs.push(rootDir);
|
|
1601
|
+
const pidFile = path.join(rootDir, "t17-child.pid");
|
|
1602
|
+
const childPath = path.join(rootDir, "t17-child.mjs");
|
|
1603
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1604
|
+
const wrapper = await startWrapperProcess({
|
|
1605
|
+
outputToStdout: false,
|
|
1606
|
+
command: process.execPath,
|
|
1607
|
+
args: [childPath],
|
|
1608
|
+
fakeBirthtime: { target: "stdinDir", mode: "followCtime" },
|
|
1609
|
+
});
|
|
1610
|
+
await Promise.race([
|
|
1611
|
+
wrapper.exited,
|
|
1612
|
+
delay(8_000).then(() => {
|
|
1613
|
+
throw new Error("The wrapper process did not exit.");
|
|
1614
|
+
}),
|
|
1615
|
+
]);
|
|
1616
|
+
await expectNoLiveProcessByArgvSubstring(childPath);
|
|
1617
|
+
expect(wrapper.stderrText()).toMatch(/changed after a probe write/);
|
|
1618
|
+
}, 15_000);
|
|
1619
|
+
it("T18 latches on an EACCES lstat failure on sessionDir during verification, not only on a removed directory", async () => {
|
|
1620
|
+
// sessionDir lives inside a parent this test owns, never the shared OS
|
|
1621
|
+
// temp directory: the test denies traversal on that parent, and doing
|
|
1622
|
+
// that to the shared OS temp directory would break every other process
|
|
1623
|
+
// on the host that also uses it.
|
|
1624
|
+
const parentDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-eacces-sessiondir-"));
|
|
1625
|
+
cleanupDirs.push(parentDir);
|
|
1626
|
+
const pidFile = path.join(parentDir, "t18-child.pid");
|
|
1627
|
+
const childPath = path.join(parentDir, "t18-child.mjs");
|
|
1628
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1629
|
+
const wrapper = await startWrapperProcess({
|
|
1630
|
+
outputToStdout: false,
|
|
1631
|
+
command: process.execPath,
|
|
1632
|
+
args: [childPath],
|
|
1633
|
+
parentDir,
|
|
1634
|
+
terminateGraceMs: 200,
|
|
1635
|
+
});
|
|
1636
|
+
const pid = await waitForTrackedChildPid(pidFile);
|
|
1637
|
+
// Let capture succeed and the poll loop run a clean cycle first, so the
|
|
1638
|
+
// termination below proves the verify-time latch, not the capture-time
|
|
1639
|
+
// one.
|
|
1640
|
+
await delay(150);
|
|
1641
|
+
await chmod(parentDir, 0o000);
|
|
1642
|
+
try {
|
|
1643
|
+
await expectWrapperAndTrackedChildToDie(wrapper, pid);
|
|
1644
|
+
}
|
|
1645
|
+
finally {
|
|
1646
|
+
// Restore permission so the shared cleanup can remove this directory.
|
|
1647
|
+
await chmod(parentDir, 0o700).catch(() => undefined);
|
|
1648
|
+
}
|
|
1649
|
+
expect(wrapper.stderrText()).toMatch(/Latching on a lost process session identity/);
|
|
1650
|
+
}, 15_000);
|
|
1651
|
+
it("T19 latches on an EACCES lstat failure on stdinDir during verification, even though sessionDir itself still stats cleanly", async () => {
|
|
1652
|
+
const wrapperOptions = { outputToStdout: false, terminateGraceMs: 200 };
|
|
1653
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-eacces-stdindir-"));
|
|
1654
|
+
cleanupDirs.push(rootDir);
|
|
1655
|
+
const pidFile = path.join(rootDir, "t19-child.pid");
|
|
1656
|
+
const childPath = path.join(rootDir, "t19-child.mjs");
|
|
1657
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1658
|
+
const wrapper = await startWrapperProcess({ ...wrapperOptions, command: process.execPath, args: [childPath] });
|
|
1659
|
+
const pid = await waitForTrackedChildPid(pidFile);
|
|
1660
|
+
await delay(150);
|
|
1661
|
+
// Deny traversal into sessionDir itself: lstat(stdinDir) fails EACCES
|
|
1662
|
+
// while lstat(sessionDir) still succeeds, since a directory's own mode
|
|
1663
|
+
// never gates lstat of the directory itself, only lookups inside it.
|
|
1664
|
+
await chmod(wrapper.sessionDir, 0o000);
|
|
1665
|
+
try {
|
|
1666
|
+
await expectWrapperAndTrackedChildToDie(wrapper, pid);
|
|
1667
|
+
}
|
|
1668
|
+
finally {
|
|
1669
|
+
await chmod(wrapper.sessionDir, 0o700).catch(() => undefined);
|
|
1670
|
+
}
|
|
1671
|
+
expect(wrapper.stderrText()).toMatch(/Latching on a lost process session identity/);
|
|
1672
|
+
}, 15_000);
|
|
1673
|
+
it("T20 refuses to write through a probe path a sandbox peer pre-created as a symbolic link, and leaves that link and its target untouched", async () => {
|
|
1674
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-symlink-race-"));
|
|
1675
|
+
cleanupDirs.push(rootDir);
|
|
1676
|
+
const pidFile = path.join(rootDir, "t20-child.pid");
|
|
1677
|
+
const childPath = path.join(rootDir, "t20-child.mjs");
|
|
1678
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1679
|
+
const sessionDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-symlink-session-"));
|
|
1680
|
+
cleanupDirs.push(sessionDir);
|
|
1681
|
+
const stdinDir = path.join(sessionDir, "stdin");
|
|
1682
|
+
await mkdir(stdinDir, { recursive: true });
|
|
1683
|
+
const wrapperPath = path.join(sessionDir, "wrapper.mjs");
|
|
1684
|
+
await writeFile(wrapperPath, getProcessSessionRemoteSource({ outputToStdout: true }), "utf8");
|
|
1685
|
+
const config = { command: process.execPath, args: [childPath], cwd: sessionDir, env: {} };
|
|
1686
|
+
const commandPayload = Buffer.from(JSON.stringify(config), "utf8").toString("base64");
|
|
1687
|
+
// A file this test owns, standing in for a file a sandbox peer already
|
|
1688
|
+
// controls. The wrapper's probe write must never reach it.
|
|
1689
|
+
const probeLinkTarget = path.join(rootDir, "t20-probe-target.txt");
|
|
1690
|
+
const knownContent = "t20-untouched-content";
|
|
1691
|
+
await writeFile(probeLinkTarget, knownContent, "utf8");
|
|
1692
|
+
const child = spawn(process.execPath, [wrapperPath], {
|
|
1693
|
+
cwd: sessionDir,
|
|
1694
|
+
env: {
|
|
1695
|
+
...process.env,
|
|
1696
|
+
PAPERCLIP_PROCESS_SESSION_DIR: sessionDir,
|
|
1697
|
+
PAPERCLIP_PROCESS_SESSION_COMMAND_B64: commandPayload,
|
|
1698
|
+
},
|
|
1699
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1700
|
+
});
|
|
1701
|
+
// Wins the race to the probe path against the wrapper's own probe write.
|
|
1702
|
+
// nextProbeFileName() is deterministic: it names
|
|
1703
|
+
// ".paperclip-birthtime-probe-<pid>-1" on the wrapper's first probe call,
|
|
1704
|
+
// which always targets sessionDir. child.pid is available synchronously
|
|
1705
|
+
// right after spawn() returns, well before the freshly spawned process
|
|
1706
|
+
// has loaded Node or parsed its own script, so this synchronous
|
|
1707
|
+
// symlinkSync call lands first. This is the same advantage a real
|
|
1708
|
+
// sandbox peer racing to pre-create the path would have, so it gives the
|
|
1709
|
+
// strongest proof: the real wrapper process, under the real race, must
|
|
1710
|
+
// still refuse to follow the link.
|
|
1711
|
+
const probePath = path.join(sessionDir, `.paperclip-birthtime-probe-${child.pid}-1`);
|
|
1712
|
+
symlinkSync(probeLinkTarget, probePath);
|
|
1713
|
+
let stderrText = "";
|
|
1714
|
+
child.stderr.on("data", (chunk) => {
|
|
1715
|
+
stderrText += chunk.toString("utf8");
|
|
1716
|
+
});
|
|
1717
|
+
const exited = new Promise((resolve) => child.on("close", () => resolve()));
|
|
1718
|
+
await Promise.race([
|
|
1719
|
+
exited,
|
|
1720
|
+
delay(8_000).then(() => {
|
|
1721
|
+
throw new Error("The wrapper process did not exit.");
|
|
1722
|
+
}),
|
|
1723
|
+
]);
|
|
1724
|
+
await expectNoLiveProcessByArgvSubstring(childPath);
|
|
1725
|
+
expect(stderrText).toMatch(/could not be created exclusively/);
|
|
1726
|
+
expect((await lstat(probePath)).isSymbolicLink()).toBe(true);
|
|
1727
|
+
expect(await readFile(probeLinkTarget, "utf8")).toBe(knownContent);
|
|
1728
|
+
}, 15_000);
|
|
1729
|
+
// ---- PAP-5355: identity-aware cleanup after a same-sandbox peer replaces
|
|
1730
|
+
// the probe file this wrapper just created, in the gap between this
|
|
1731
|
+
// wrapper's last identity check and its removal call. The probeSwap
|
|
1732
|
+
// preload (see getProbeSwapPreloadPath above) simulates the worst-case
|
|
1733
|
+
// timing for that gap deterministically, instead of racing real wall-clock
|
|
1734
|
+
// time: it swaps the path the instant the wrapper itself looks at it for
|
|
1735
|
+
// the last time before deciding whether to remove it.
|
|
1736
|
+
it("T21 still removes its own probe file normally when no peer ever replaces it", async () => {
|
|
1737
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-no-swap-"));
|
|
1738
|
+
cleanupDirs.push(rootDir);
|
|
1739
|
+
const pidFile = path.join(rootDir, "t21-child.pid");
|
|
1740
|
+
const childPath = path.join(rootDir, "t21-child.mjs");
|
|
1741
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1742
|
+
const wrapper = await startWrapperProcess({
|
|
1743
|
+
outputToStdout: false,
|
|
1744
|
+
command: process.execPath,
|
|
1745
|
+
args: [childPath],
|
|
1746
|
+
});
|
|
1747
|
+
await waitForTrackedChildPid(pidFile);
|
|
1748
|
+
const probePath = path.join(wrapper.sessionDir, `.paperclip-birthtime-probe-${wrapper.pid}-1`);
|
|
1749
|
+
await waitFor(async () => !(await lstat(probePath).then(() => true).catch(() => false)), 4_000);
|
|
1750
|
+
await expect(lstat(probePath)).rejects.toThrow();
|
|
1751
|
+
}, 15_000);
|
|
1752
|
+
it("T22 leaves a peer's replacement file untouched instead of deleting it", async () => {
|
|
1753
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-swap-file-"));
|
|
1754
|
+
cleanupDirs.push(rootDir);
|
|
1755
|
+
const pidFile = path.join(rootDir, "t22-child.pid");
|
|
1756
|
+
const childPath = path.join(rootDir, "t22-child.mjs");
|
|
1757
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1758
|
+
const wrapper = await startWrapperProcess({
|
|
1759
|
+
outputToStdout: false,
|
|
1760
|
+
command: process.execPath,
|
|
1761
|
+
args: [childPath],
|
|
1762
|
+
probeSwap: { seq: 1, mode: "file" },
|
|
1763
|
+
});
|
|
1764
|
+
await waitForTrackedChildPid(pidFile);
|
|
1765
|
+
const probePath = path.join(wrapper.sessionDir, `.paperclip-birthtime-probe-${wrapper.pid}-1`);
|
|
1766
|
+
await waitFor(async () => (await readFile(probePath, "utf8").catch(() => null)) === "peer-owned-content", 4_000);
|
|
1767
|
+
// The wrapper's own cleanup call already ran (the preload only swaps the
|
|
1768
|
+
// path the moment the wrapper itself checks it). This delay proves that
|
|
1769
|
+
// run settled and nothing removes the peer's file afterward.
|
|
1770
|
+
await delay(200);
|
|
1771
|
+
expect(await readFile(probePath, "utf8")).toBe("peer-owned-content");
|
|
1772
|
+
}, 15_000);
|
|
1773
|
+
it("T23 leaves a peer's replacement directory untouched instead of deleting it", async () => {
|
|
1774
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-swap-dir-"));
|
|
1775
|
+
cleanupDirs.push(rootDir);
|
|
1776
|
+
const pidFile = path.join(rootDir, "t23-child.pid");
|
|
1777
|
+
const childPath = path.join(rootDir, "t23-child.mjs");
|
|
1778
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1779
|
+
const wrapper = await startWrapperProcess({
|
|
1780
|
+
outputToStdout: false,
|
|
1781
|
+
command: process.execPath,
|
|
1782
|
+
args: [childPath],
|
|
1783
|
+
probeSwap: { seq: 1, mode: "dir" },
|
|
1784
|
+
});
|
|
1785
|
+
await waitForTrackedChildPid(pidFile);
|
|
1786
|
+
const probePath = path.join(wrapper.sessionDir, `.paperclip-birthtime-probe-${wrapper.pid}-1`);
|
|
1787
|
+
await waitFor(async () => await lstat(probePath).then((stats) => stats.isDirectory()).catch(() => false), 4_000);
|
|
1788
|
+
await delay(200);
|
|
1789
|
+
expect((await lstat(probePath)).isDirectory()).toBe(true);
|
|
1790
|
+
}, 15_000);
|
|
1791
|
+
it("T24 leaves a peer's replacement symbolic link and its target untouched instead of deleting or following it", async () => {
|
|
1792
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-swap-symlink-"));
|
|
1793
|
+
cleanupDirs.push(rootDir);
|
|
1794
|
+
const pidFile = path.join(rootDir, "t24-child.pid");
|
|
1795
|
+
const childPath = path.join(rootDir, "t24-child.mjs");
|
|
1796
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1797
|
+
const linkTarget = path.join(rootDir, "t24-probe-target.txt");
|
|
1798
|
+
const knownContent = "t24-untouched-content";
|
|
1799
|
+
await writeFile(linkTarget, knownContent, "utf8");
|
|
1800
|
+
const wrapper = await startWrapperProcess({
|
|
1801
|
+
outputToStdout: false,
|
|
1802
|
+
command: process.execPath,
|
|
1803
|
+
args: [childPath],
|
|
1804
|
+
probeSwap: { seq: 1, mode: "symlink", symlinkTarget: linkTarget },
|
|
1805
|
+
});
|
|
1806
|
+
await waitForTrackedChildPid(pidFile);
|
|
1807
|
+
const probePath = path.join(wrapper.sessionDir, `.paperclip-birthtime-probe-${wrapper.pid}-1`);
|
|
1808
|
+
await waitFor(async () => await lstat(probePath).then((stats) => stats.isSymbolicLink()).catch(() => false), 4_000);
|
|
1809
|
+
await delay(200);
|
|
1810
|
+
expect((await lstat(probePath)).isSymbolicLink()).toBe(true);
|
|
1811
|
+
expect(await readlink(probePath)).toBe(linkTarget);
|
|
1812
|
+
expect(await readFile(linkTarget, "utf8")).toBe(knownContent);
|
|
1813
|
+
}, 15_000);
|
|
1814
|
+
it("T25 fails closed at capture when its own probe file's identity cannot be read, so no orphan wrapper or child ever starts polling", async () => {
|
|
1815
|
+
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-probe-fstat-failure-"));
|
|
1816
|
+
cleanupDirs.push(rootDir);
|
|
1817
|
+
const pidFile = path.join(rootDir, "t25-child.pid");
|
|
1818
|
+
const childPath = path.join(rootDir, "t25-child.mjs");
|
|
1819
|
+
await writeFile(childPath, trackedChildSource(pidFile), "utf8");
|
|
1820
|
+
const wrapper = await startWrapperProcess({
|
|
1821
|
+
outputToStdout: false,
|
|
1822
|
+
command: process.execPath,
|
|
1823
|
+
args: [childPath],
|
|
1824
|
+
fstatFailure: { seq: 1 },
|
|
1825
|
+
});
|
|
1826
|
+
await Promise.race([
|
|
1827
|
+
wrapper.exited,
|
|
1828
|
+
delay(8_000).then(() => {
|
|
1829
|
+
throw new Error("The wrapper process did not exit.");
|
|
1830
|
+
}),
|
|
1831
|
+
]);
|
|
1832
|
+
expect(wrapper.stderrText()).toMatch(/its own probe file's identity could not be read/);
|
|
1833
|
+
await expectNoLiveProcessByArgvSubstring(childPath);
|
|
1834
|
+
// With no verified identity for the probe file, the wrapper must not
|
|
1835
|
+
// remove it by path alone: it leaves the file exactly as it created it,
|
|
1836
|
+
// rather than risking removal of a different entry a peer may have put
|
|
1837
|
+
// at the same path.
|
|
1838
|
+
const probePath = path.join(wrapper.sessionDir, `.paperclip-birthtime-probe-${wrapper.pid}-1`);
|
|
1839
|
+
expect((await lstat(probePath)).isFile()).toBe(true);
|
|
1840
|
+
}, 15_000);
|
|
1841
|
+
});
|
|
1842
|
+
//# sourceMappingURL=execution-target-stdin-race.test.js.map
|