@elie-laloum/outpost 3.0.0 → 4.1.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/CHANGELOG.md +32 -0
- package/README.md +5 -3
- package/SECURITY.md +3 -1
- package/dist/adapters/agents/codex-adapter.js +2 -0
- package/dist/adapters/agents/codex-adapter.js.map +1 -1
- package/dist/adapters/agents/codex-provider.d.ts +2 -0
- package/dist/adapters/agents/codex-provider.js +32 -0
- package/dist/adapters/agents/codex-provider.js.map +1 -0
- package/dist/adapters/agents/codex-request.js +2 -1
- package/dist/adapters/agents/codex-request.js.map +1 -1
- package/dist/adapters/agents/gemini-adapter.d.ts +3 -0
- package/dist/adapters/agents/gemini-adapter.js +15 -0
- package/dist/adapters/agents/gemini-adapter.js.map +1 -0
- package/dist/adapters/agents/gemini-diagnostics.d.ts +2 -0
- package/dist/adapters/agents/gemini-diagnostics.js +15 -0
- package/dist/adapters/agents/gemini-diagnostics.js.map +1 -0
- package/dist/adapters/agents/gemini-events.d.ts +2 -0
- package/dist/adapters/agents/gemini-events.js +50 -0
- package/dist/adapters/agents/gemini-events.js.map +1 -0
- package/dist/adapters/agents/gemini-request.d.ts +4 -0
- package/dist/adapters/agents/gemini-request.js +23 -0
- package/dist/adapters/agents/gemini-request.js.map +1 -0
- package/dist/adapters/agents/gemini.types.d.ts +6 -0
- package/dist/adapters/agents/gemini.types.js +2 -0
- package/dist/adapters/agents/gemini.types.js.map +1 -0
- package/dist/adapters/agents/protocol-fixtures.constants.d.ts +1 -1
- package/dist/adapters/agents/protocol-fixtures.constants.js +37 -0
- package/dist/adapters/agents/protocol-fixtures.constants.js.map +1 -1
- package/dist/adapters/agents/settings.types.d.ts +5 -0
- package/dist/adapters/agents/settings.types.js.map +1 -1
- package/dist/application/agent-bootstrap.constants.d.ts +1 -0
- package/dist/application/agent-bootstrap.constants.js +1 -0
- package/dist/application/agent-bootstrap.constants.js.map +1 -1
- package/dist/application/agent-bootstrap.js +6 -2
- package/dist/application/agent-bootstrap.js.map +1 -1
- package/dist/application/agent-output.js +9 -2
- package/dist/application/agent-output.js.map +1 -1
- package/dist/application/artifact-tasks.d.ts +5 -0
- package/dist/application/artifact-tasks.js +31 -0
- package/dist/application/artifact-tasks.js.map +1 -0
- package/dist/application/artifact-tasks.types.d.ts +8 -0
- package/dist/application/artifact-tasks.types.js +2 -0
- package/dist/application/artifact-tasks.types.js.map +1 -0
- package/dist/application/dispatch-validation.js +2 -0
- package/dist/application/dispatch-validation.js.map +1 -1
- package/dist/application/doctor-agent.constants.d.ts +2 -0
- package/dist/application/doctor-agent.constants.js +2 -0
- package/dist/application/doctor-agent.constants.js.map +1 -1
- package/dist/application/doctor-agent.js +1 -1
- package/dist/application/doctor-agent.js.map +1 -1
- package/dist/application/doctor-protocol.js +2 -1
- package/dist/application/doctor-protocol.js.map +1 -1
- package/dist/application/doctor.constants.js +1 -1
- package/dist/application/doctor.constants.js.map +1 -1
- package/dist/application/doctor.types.d.ts +1 -1
- package/dist/application/doctor.types.js.map +1 -1
- package/dist/application/outpost.types.d.ts +2 -0
- package/dist/application/outpost.types.js.map +1 -1
- package/dist/application/queue-worker.d.ts +2 -0
- package/dist/application/queue-worker.js +75 -0
- package/dist/application/queue-worker.js.map +1 -0
- package/dist/application/queue-worker.types.d.ts +15 -0
- package/dist/application/queue-worker.types.js +2 -0
- package/dist/application/queue-worker.types.js.map +1 -0
- package/dist/application/queued-task.d.ts +3 -0
- package/dist/application/queued-task.js +56 -0
- package/dist/application/queued-task.js.map +1 -0
- package/dist/application/queued-task.types.d.ts +11 -0
- package/dist/application/queued-task.types.js +2 -0
- package/dist/application/queued-task.types.js.map +1 -0
- package/dist/application/recovery-inspection.js +5 -0
- package/dist/application/recovery-inspection.js.map +1 -1
- package/dist/application/recovery-inspection.types.d.ts +3 -0
- package/dist/application/recovery-inspection.types.js.map +1 -1
- package/dist/application/recovery-restore-snapshot.d.ts +2 -0
- package/dist/application/recovery-restore-snapshot.js +57 -0
- package/dist/application/recovery-restore-snapshot.js.map +1 -0
- package/dist/application/recovery-restore.d.ts +3 -0
- package/dist/application/recovery-restore.js +161 -0
- package/dist/application/recovery-restore.js.map +1 -0
- package/dist/application/recovery-restore.types.d.ts +28 -0
- package/dist/application/recovery-restore.types.js +2 -0
- package/dist/application/recovery-restore.types.js.map +1 -0
- package/dist/application/recovery-retention.js +5 -0
- package/dist/application/recovery-retention.js.map +1 -1
- package/dist/application/recovery-verification.d.ts +2 -1
- package/dist/application/recovery-verification.js +1 -1
- package/dist/application/recovery-verification.js.map +1 -1
- package/dist/application/remote-apply.js +6 -0
- package/dist/application/remote-apply.js.map +1 -1
- package/dist/application/remote-download.js +2 -2
- package/dist/application/remote-download.js.map +1 -1
- package/dist/application/remote-history.d.ts +6 -0
- package/dist/application/remote-history.js +114 -0
- package/dist/application/remote-history.js.map +1 -0
- package/dist/application/remote-upload.d.ts +2 -0
- package/dist/application/remote-upload.js +17 -0
- package/dist/application/remote-upload.js.map +1 -0
- package/dist/application/remote-validation.js +20 -28
- package/dist/application/remote-validation.js.map +1 -1
- package/dist/application/remote-workspace.js +4 -10
- package/dist/application/remote-workspace.js.map +1 -1
- package/dist/application/sandbox-activity.d.ts +3 -0
- package/dist/application/sandbox-activity.js +25 -0
- package/dist/application/sandbox-activity.js.map +1 -0
- package/dist/application/sandbox-provision.js +55 -10
- package/dist/application/sandbox-provision.js.map +1 -1
- package/dist/application/sandbox-session.types.d.ts +2 -0
- package/dist/application/sandbox-session.types.js.map +1 -1
- package/dist/application/sandbox.js +23 -11
- package/dist/application/sandbox.js.map +1 -1
- package/dist/application/speculation-host.d.ts +2 -0
- package/dist/application/speculation-host.js +30 -0
- package/dist/application/speculation-host.js.map +1 -0
- package/dist/application/speculation.constants.d.ts +4 -0
- package/dist/application/speculation.constants.js +2 -0
- package/dist/application/speculation.constants.js.map +1 -0
- package/dist/application/speculation.d.ts +2 -0
- package/dist/application/speculation.js +166 -0
- package/dist/application/speculation.js.map +1 -0
- package/dist/application/speculation.types.d.ts +56 -0
- package/dist/application/speculation.types.js +2 -0
- package/dist/application/speculation.types.js.map +1 -0
- package/dist/application/storage-reservation.d.ts +3 -0
- package/dist/application/storage-reservation.js +10 -0
- package/dist/application/storage-reservation.js.map +1 -0
- package/dist/application/storage-reservation.types.d.ts +4 -0
- package/dist/application/storage-reservation.types.js +2 -0
- package/dist/application/storage-reservation.types.js.map +1 -0
- package/dist/application/task-usage.d.ts +1 -1
- package/dist/application/task-usage.js.map +1 -1
- package/dist/application/tasks.js +1 -1
- package/dist/application/tasks.js.map +1 -1
- package/dist/application/tasks.types.d.ts +4 -3
- package/dist/application/tasks.types.js.map +1 -1
- package/dist/application/workspace-allocation.d.ts +3 -0
- package/dist/application/workspace-allocation.js +34 -0
- package/dist/application/workspace-allocation.js.map +1 -0
- package/dist/application/workspace.js +2 -2
- package/dist/application/workspace.js.map +1 -1
- package/dist/cli/commands.js +55 -18
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/doctor-command.js +1 -1
- package/dist/cli/doctor-command.js.map +1 -1
- package/dist/cli/init-authentication.constants.d.ts +13 -0
- package/dist/cli/init-authentication.constants.js +10 -0
- package/dist/cli/init-authentication.constants.js.map +1 -0
- package/dist/cli/init-authentication.d.ts +4 -0
- package/dist/cli/init-authentication.js +43 -0
- package/dist/cli/init-authentication.js.map +1 -0
- package/dist/cli/init-command.js +74 -15
- package/dist/cli/init-command.js.map +1 -1
- package/dist/cli/main.constants.d.ts +111 -2
- package/dist/cli/main.constants.js +148 -42
- package/dist/cli/main.constants.js.map +1 -1
- package/dist/cli/recovery-command.js +28 -4
- package/dist/cli/recovery-command.js.map +1 -1
- package/dist/cli/recovery-restore-command.d.ts +2 -0
- package/dist/cli/recovery-restore-command.js +34 -0
- package/dist/cli/recovery-restore-command.js.map +1 -0
- package/dist/cli/scaffold-files.js +3 -2
- package/dist/cli/scaffold-files.js.map +1 -1
- package/dist/cli/scaffold-validation.js +20 -1
- package/dist/cli/scaffold-validation.js.map +1 -1
- package/dist/cli/scaffold.constants.js +2 -2
- package/dist/cli/scaffold.constants.js.map +1 -1
- package/dist/cli/scaffold.js +13 -0
- package/dist/cli/scaffold.js.map +1 -1
- package/dist/cli/scaffold.types.d.ts +4 -1
- package/dist/cli/scaffold.types.js.map +1 -1
- package/dist/cli/starters.js +9 -2
- package/dist/cli/starters.js.map +1 -1
- package/dist/domain/agent.types.d.ts +2 -0
- package/dist/domain/agent.types.js.map +1 -1
- package/dist/domain/artifact-reference.d.ts +4 -0
- package/dist/domain/artifact-reference.js +90 -0
- package/dist/domain/artifact-reference.js.map +1 -0
- package/dist/domain/artifact.constants.d.ts +2 -0
- package/dist/domain/artifact.constants.js +3 -0
- package/dist/domain/artifact.constants.js.map +1 -0
- package/dist/domain/artifact.d.ts +7 -0
- package/dist/domain/artifact.js +104 -0
- package/dist/domain/artifact.js.map +1 -0
- package/dist/domain/artifact.types.d.ts +44 -0
- package/dist/domain/artifact.types.js +2 -0
- package/dist/domain/artifact.types.js.map +1 -0
- package/dist/domain/egress.constants.d.ts +1 -0
- package/dist/domain/egress.constants.js +7 -0
- package/dist/domain/egress.constants.js.map +1 -0
- package/dist/domain/egress.d.ts +2 -0
- package/dist/domain/egress.js +48 -0
- package/dist/domain/egress.js.map +1 -0
- package/dist/domain/egress.types.d.ts +8 -0
- package/dist/domain/egress.types.js +2 -0
- package/dist/domain/egress.types.js.map +1 -0
- package/dist/domain/sandbox.types.d.ts +1 -0
- package/dist/domain/sandbox.types.js.map +1 -1
- package/dist/domain/task-queue.constants.d.ts +8 -0
- package/dist/domain/task-queue.constants.js +9 -0
- package/dist/domain/task-queue.constants.js.map +1 -0
- package/dist/domain/task-queue.d.ts +11 -0
- package/dist/domain/task-queue.js +105 -0
- package/dist/domain/task-queue.js.map +1 -0
- package/dist/domain/task-queue.types.d.ts +41 -0
- package/dist/domain/task-queue.types.js +2 -0
- package/dist/domain/task-queue.types.js.map +1 -0
- package/dist/domain/workflow/budget.d.ts +2 -2
- package/dist/domain/workflow/budget.js +5 -3
- package/dist/domain/workflow/budget.js.map +1 -1
- package/dist/domain/workflow/checkpoint-validation.d.ts +3 -0
- package/dist/domain/workflow/checkpoint-validation.js +81 -0
- package/dist/domain/workflow/checkpoint-validation.js.map +1 -0
- package/dist/domain/workflow/checkpoint-value.d.ts +2 -0
- package/dist/domain/workflow/checkpoint-value.js +49 -0
- package/dist/domain/workflow/checkpoint-value.js.map +1 -0
- package/dist/domain/workflow/checkpoint.d.ts +3 -0
- package/dist/domain/workflow/checkpoint.js +70 -0
- package/dist/domain/workflow/checkpoint.js.map +1 -0
- package/dist/domain/workflow/checkpoint.types.d.ts +40 -0
- package/dist/domain/workflow/checkpoint.types.js +2 -0
- package/dist/domain/workflow/checkpoint.types.js.map +1 -0
- package/dist/domain/workflow/gate-validation.d.ts +4 -0
- package/dist/domain/workflow/gate-validation.js +71 -0
- package/dist/domain/workflow/gate-validation.js.map +1 -0
- package/dist/domain/workflow/gates.constants.d.ts +4 -0
- package/dist/domain/workflow/gates.constants.js +5 -0
- package/dist/domain/workflow/gates.constants.js.map +1 -0
- package/dist/domain/workflow/gates.d.ts +7 -0
- package/dist/domain/workflow/gates.js +85 -0
- package/dist/domain/workflow/gates.js.map +1 -0
- package/dist/domain/workflow/gates.types.d.ts +27 -0
- package/dist/domain/workflow/gates.types.js +2 -0
- package/dist/domain/workflow/gates.types.js.map +1 -0
- package/dist/domain/workflow/task-runner.js +14 -2
- package/dist/domain/workflow/task-runner.js.map +1 -1
- package/dist/domain/workflow/usage-receipt.constants.d.ts +2 -0
- package/dist/domain/workflow/usage-receipt.constants.js +3 -0
- package/dist/domain/workflow/usage-receipt.constants.js.map +1 -0
- package/dist/domain/workflow/usage-receipt.d.ts +2 -0
- package/dist/domain/workflow/usage-receipt.js +18 -0
- package/dist/domain/workflow/usage-receipt.js.map +1 -0
- package/dist/domain/workflow/validation.js +6 -0
- package/dist/domain/workflow/validation.js.map +1 -1
- package/dist/domain/workflow/workflow-scheduler.js +65 -32
- package/dist/domain/workflow/workflow-scheduler.js.map +1 -1
- package/dist/domain/workflow/workflow-state.d.ts +2 -1
- package/dist/domain/workflow/workflow-state.js +91 -16
- package/dist/domain/workflow/workflow-state.js.map +1 -1
- package/dist/domain/workflow.types.d.ts +13 -2
- package/dist/domain/workflow.types.js.map +1 -1
- package/dist/index.d.ts +32 -2
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/artifact-store.constants.d.ts +1 -0
- package/dist/infrastructure/artifact-store.constants.js +2 -0
- package/dist/infrastructure/artifact-store.constants.js.map +1 -0
- package/dist/infrastructure/artifact-store.d.ts +3 -0
- package/dist/infrastructure/artifact-store.js +100 -0
- package/dist/infrastructure/artifact-store.js.map +1 -0
- package/dist/infrastructure/artifact-store.types.d.ts +4 -0
- package/dist/infrastructure/artifact-store.types.js +2 -0
- package/dist/infrastructure/artifact-store.types.js.map +1 -0
- package/dist/infrastructure/file-manifest.d.ts +1 -1
- package/dist/infrastructure/file-manifest.js +3 -2
- package/dist/infrastructure/file-manifest.js.map +1 -1
- package/dist/infrastructure/git/verification-command.d.ts +1 -1
- package/dist/infrastructure/git/verification-command.js +3 -2
- package/dist/infrastructure/git/verification-command.js.map +1 -1
- package/dist/infrastructure/opentelemetry.js +1 -1
- package/dist/infrastructure/opentelemetry.js.map +1 -1
- package/dist/infrastructure/resource-activity-inspection.d.ts +2 -0
- package/dist/infrastructure/resource-activity-inspection.js +152 -0
- package/dist/infrastructure/resource-activity-inspection.js.map +1 -0
- package/dist/infrastructure/resource-activity.constants.d.ts +8 -0
- package/dist/infrastructure/resource-activity.constants.js +26 -0
- package/dist/infrastructure/resource-activity.constants.js.map +1 -0
- package/dist/infrastructure/resource-activity.d.ts +2 -0
- package/dist/infrastructure/resource-activity.js +166 -0
- package/dist/infrastructure/resource-activity.js.map +1 -0
- package/dist/infrastructure/resource-activity.types.d.ts +53 -0
- package/dist/infrastructure/resource-activity.types.js +2 -0
- package/dist/infrastructure/resource-activity.types.js.map +1 -0
- package/dist/infrastructure/storage-mutation-lock.constants.d.ts +5 -0
- package/dist/infrastructure/storage-mutation-lock.constants.js +6 -0
- package/dist/infrastructure/storage-mutation-lock.constants.js.map +1 -0
- package/dist/infrastructure/storage-mutation-lock.d.ts +1 -0
- package/dist/infrastructure/storage-mutation-lock.js +21 -0
- package/dist/infrastructure/storage-mutation-lock.js.map +1 -0
- package/dist/infrastructure/storage-reservations.constants.d.ts +4 -0
- package/dist/infrastructure/storage-reservations.constants.js +5 -0
- package/dist/infrastructure/storage-reservations.constants.js.map +1 -0
- package/dist/infrastructure/storage-reservations.d.ts +2 -0
- package/dist/infrastructure/storage-reservations.js +150 -0
- package/dist/infrastructure/storage-reservations.js.map +1 -0
- package/dist/infrastructure/storage-reservations.types.d.ts +21 -0
- package/dist/infrastructure/storage-reservations.types.js +2 -0
- package/dist/infrastructure/storage-reservations.types.js.map +1 -0
- package/dist/infrastructure/task-queue-http.constants.d.ts +2 -0
- package/dist/infrastructure/task-queue-http.constants.js +3 -0
- package/dist/infrastructure/task-queue-http.constants.js.map +1 -0
- package/dist/infrastructure/task-queue-http.d.ts +4 -0
- package/dist/infrastructure/task-queue-http.js +158 -0
- package/dist/infrastructure/task-queue-http.js.map +1 -0
- package/dist/infrastructure/task-queue-http.types.d.ts +16 -0
- package/dist/infrastructure/task-queue-http.types.js +2 -0
- package/dist/infrastructure/task-queue-http.types.js.map +1 -0
- package/dist/infrastructure/task-queue.d.ts +2 -0
- package/dist/infrastructure/task-queue.js +134 -0
- package/dist/infrastructure/task-queue.js.map +1 -0
- package/dist/infrastructure/transfer.js +5 -0
- package/dist/infrastructure/transfer.js.map +1 -1
- package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js +2 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -0
- package/dist/infrastructure/workflow-checkpoint.d.ts +3 -0
- package/dist/infrastructure/workflow-checkpoint.js +77 -0
- package/dist/infrastructure/workflow-checkpoint.js.map +1 -0
- package/dist/infrastructure/workflow-checkpoint.types.d.ts +4 -0
- package/dist/infrastructure/workflow-checkpoint.types.js +2 -0
- package/dist/infrastructure/workflow-checkpoint.types.js.map +1 -0
- package/dist/providers/agents.d.ts +3 -1
- package/dist/providers/agents.js +1 -0
- package/dist/providers/agents.js.map +1 -1
- package/dist/providers/container-isolation.d.ts +2 -0
- package/dist/providers/container-isolation.js +29 -0
- package/dist/providers/container-isolation.js.map +1 -0
- package/dist/providers/container-mounts.js +7 -2
- package/dist/providers/container-mounts.js.map +1 -1
- package/dist/providers/container-network.d.ts +2 -0
- package/dist/providers/container-network.js +14 -0
- package/dist/providers/container-network.js.map +1 -0
- package/dist/providers/container-plan.js +4 -2
- package/dist/providers/container-plan.js.map +1 -1
- package/dist/providers/container.constants.d.ts +1 -0
- package/dist/providers/container.constants.js +1 -0
- package/dist/providers/container.constants.js.map +1 -1
- package/dist/providers/container.js +19 -2
- package/dist/providers/container.js.map +1 -1
- package/dist/providers/container.types.d.ts +3 -0
- package/dist/providers/container.types.js.map +1 -1
- package/dist/providers/daytona-command.constants.d.ts +2 -0
- package/dist/providers/daytona-command.constants.js +13 -0
- package/dist/providers/daytona-command.constants.js.map +1 -0
- package/dist/providers/daytona-command.js +12 -5
- package/dist/providers/daytona-command.js.map +1 -1
- package/dist/providers/daytona-output.d.ts +4 -0
- package/dist/providers/daytona-output.js +37 -0
- package/dist/providers/daytona-output.js.map +1 -0
- package/dist/providers/daytona-terminal.d.ts +3 -0
- package/dist/providers/daytona-terminal.js +130 -0
- package/dist/providers/daytona-terminal.js.map +1 -0
- package/dist/providers/daytona.js +2 -0
- package/dist/providers/daytona.js.map +1 -1
- package/dist/providers/docker.d.ts +1 -0
- package/dist/providers/docker.js.map +1 -1
- package/dist/providers/file-batches.d.ts +1 -1
- package/dist/providers/file-batches.js +9 -0
- package/dist/providers/file-batches.js.map +1 -1
- package/dist/providers/file-upload.constants.d.ts +1 -0
- package/dist/providers/file-upload.constants.js +68 -0
- package/dist/providers/file-upload.constants.js.map +1 -0
- package/dist/providers/file-upload.d.ts +2 -0
- package/dist/providers/file-upload.js +146 -0
- package/dist/providers/file-upload.js.map +1 -0
- package/dist/providers/firecracker-command.d.ts +5 -0
- package/dist/providers/firecracker-command.js +114 -0
- package/dist/providers/firecracker-command.js.map +1 -0
- package/dist/providers/firecracker-files.constants.d.ts +1 -0
- package/dist/providers/firecracker-files.constants.js +21 -0
- package/dist/providers/firecracker-files.constants.js.map +1 -0
- package/dist/providers/firecracker-files.d.ts +3 -0
- package/dist/providers/firecracker-files.js +110 -0
- package/dist/providers/firecracker-files.js.map +1 -0
- package/dist/providers/firecracker-machine.d.ts +3 -0
- package/dist/providers/firecracker-machine.js +155 -0
- package/dist/providers/firecracker-machine.js.map +1 -0
- package/dist/providers/firecracker.constants.d.ts +10 -0
- package/dist/providers/firecracker.constants.js +11 -0
- package/dist/providers/firecracker.constants.js.map +1 -0
- package/dist/providers/firecracker.d.ts +4 -0
- package/dist/providers/firecracker.js +103 -0
- package/dist/providers/firecracker.js.map +1 -0
- package/dist/providers/firecracker.types.d.ts +36 -0
- package/dist/providers/firecracker.types.js +2 -0
- package/dist/providers/firecracker.types.js.map +1 -0
- package/dist/providers/local.js +2 -1
- package/dist/providers/local.js.map +1 -1
- package/dist/providers/podman.d.ts +1 -0
- package/dist/providers/podman.js.map +1 -1
- package/dist/providers/terminal-bridge.d.ts +14 -0
- package/dist/providers/terminal-bridge.js +100 -0
- package/dist/providers/terminal-bridge.js.map +1 -0
- package/dist/providers/vercel-command.js +2 -2
- package/dist/providers/vercel-command.js.map +1 -1
- package/dist/providers/vercel-directory.d.ts +2 -0
- package/dist/providers/vercel-directory.js +7 -0
- package/dist/providers/vercel-directory.js.map +1 -0
- package/dist/providers/vercel-files.js +3 -2
- package/dist/providers/vercel-files.js.map +1 -1
- package/dist/providers/vercel-network.d.ts +2 -0
- package/dist/providers/vercel-network.js +18 -0
- package/dist/providers/vercel-network.js.map +1 -0
- package/dist/providers/vercel.d.ts +1 -0
- package/dist/providers/vercel.js +5 -1
- package/dist/providers/vercel.js.map +1 -1
- package/dist/providers/vercel.types.d.ts +2 -0
- package/dist/providers/vercel.types.js.map +1 -1
- package/dist/providers/versions.constants.d.ts +1 -0
- package/dist/providers/versions.constants.js +1 -0
- package/dist/providers/versions.constants.js.map +1 -1
- package/package.json +10 -2
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, open, realpath, rename, rm, opendir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { invariant } from "../domain/errors.js";
|
|
5
|
+
import { localProcessIdentity } from "./git/process-identity.js";
|
|
6
|
+
import { readInspectionFile } from "./inspection-file.js";
|
|
7
|
+
import { lockStorageMutation } from "./storage-mutation-lock.js";
|
|
8
|
+
import { resourceActivityDefaults as defaults } from "./resource-activity.constants.js";
|
|
9
|
+
async function activityDirectory(repository) {
|
|
10
|
+
let path = repository;
|
|
11
|
+
for (const part of [".outpost", "locks", defaults.directory]) {
|
|
12
|
+
path = join(path, part);
|
|
13
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
14
|
+
invariant((await realpath(path)) === path, "Resource activity directory must not use symlinks");
|
|
15
|
+
}
|
|
16
|
+
return path;
|
|
17
|
+
}
|
|
18
|
+
export async function registerResourceActivity(options) {
|
|
19
|
+
for (const text of [options.workspace, options.provider])
|
|
20
|
+
invariant(text.length > 0 && text.length <= defaults.maxText, "Resource activity metadata exceeds its size limit");
|
|
21
|
+
const directory = await activityDirectory(options.repository);
|
|
22
|
+
const id = randomUUID();
|
|
23
|
+
const path = join(directory, `${id}.json`);
|
|
24
|
+
const createdAt = new Date().toISOString();
|
|
25
|
+
const identity = await localProcessIdentity();
|
|
26
|
+
let record = {
|
|
27
|
+
version: 1,
|
|
28
|
+
id,
|
|
29
|
+
pid: process.pid,
|
|
30
|
+
...(identity ? { identity } : {}),
|
|
31
|
+
provider: options.provider,
|
|
32
|
+
placement: options.placement,
|
|
33
|
+
workspace: options.workspace,
|
|
34
|
+
createdAt,
|
|
35
|
+
updatedAt: createdAt,
|
|
36
|
+
phase: "allocating",
|
|
37
|
+
operations: [],
|
|
38
|
+
};
|
|
39
|
+
const unlock = await lockStorageMutation(options.repository);
|
|
40
|
+
try {
|
|
41
|
+
let count = 0;
|
|
42
|
+
for await (const _entry of await opendir(directory))
|
|
43
|
+
invariant(++count < defaults.maxRecords, "Too many resource activity records; inspect retained ownership before continuing");
|
|
44
|
+
const file = await open(path, "wx", 0o600);
|
|
45
|
+
try {
|
|
46
|
+
await file.writeFile(JSON.stringify(record));
|
|
47
|
+
await file.sync();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await file.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await unlock();
|
|
55
|
+
}
|
|
56
|
+
let pending = Promise.resolve();
|
|
57
|
+
let removed = false;
|
|
58
|
+
function serialize(action) {
|
|
59
|
+
const next = pending.then(action);
|
|
60
|
+
pending = next.catch(() => undefined);
|
|
61
|
+
return next;
|
|
62
|
+
}
|
|
63
|
+
async function verify() {
|
|
64
|
+
invariant((await realpath(directory)) === directory, "Resource activity directory changed");
|
|
65
|
+
invariant(!removed, "Resource activity is closed");
|
|
66
|
+
const current = (await readInspectionFile(path, defaults.maxRecordBytes)).toString("utf8");
|
|
67
|
+
invariant(current === JSON.stringify(record), "Resource activity identity changed; refusing mutation");
|
|
68
|
+
}
|
|
69
|
+
function update(next) {
|
|
70
|
+
return serialize(async () => {
|
|
71
|
+
await verify();
|
|
72
|
+
const value = { ...next(), updatedAt: new Date().toISOString() };
|
|
73
|
+
const data = JSON.stringify(value);
|
|
74
|
+
invariant(Buffer.byteLength(data) <= defaults.maxRecordBytes, "Resource activity record exceeds its size limit");
|
|
75
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
76
|
+
const file = await open(temporary, "wx", 0o600);
|
|
77
|
+
try {
|
|
78
|
+
await file.writeFile(data);
|
|
79
|
+
await file.sync();
|
|
80
|
+
await file.close();
|
|
81
|
+
await rename(temporary, path);
|
|
82
|
+
record = value;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await file.close();
|
|
86
|
+
await rm(temporary, { force: true });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
async idle() {
|
|
92
|
+
await pending;
|
|
93
|
+
return record.operations.length === 0;
|
|
94
|
+
},
|
|
95
|
+
phase: (phase) => update(() => ({ ...record, phase })),
|
|
96
|
+
async run(kind, action) {
|
|
97
|
+
const operation = {
|
|
98
|
+
id: randomUUID(),
|
|
99
|
+
kind,
|
|
100
|
+
startedAt: new Date().toISOString(),
|
|
101
|
+
};
|
|
102
|
+
await update(() => {
|
|
103
|
+
const active = record.operations.find((value) => value.kind === kind);
|
|
104
|
+
const operations = record.operations.filter((value) => value.kind !== kind);
|
|
105
|
+
operations.push({
|
|
106
|
+
kind,
|
|
107
|
+
startedAt: active?.startedAt ?? operation.startedAt,
|
|
108
|
+
count: (active?.count ?? 0) + 1,
|
|
109
|
+
});
|
|
110
|
+
return { ...record, operations };
|
|
111
|
+
});
|
|
112
|
+
let outcome = "completed";
|
|
113
|
+
try {
|
|
114
|
+
return await action();
|
|
115
|
+
}
|
|
116
|
+
catch (cause) {
|
|
117
|
+
outcome = "failed";
|
|
118
|
+
throw cause;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
const result = {
|
|
122
|
+
...operation,
|
|
123
|
+
outcome,
|
|
124
|
+
count: 1,
|
|
125
|
+
finishedAt: new Date().toISOString(),
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
await update(() => ({
|
|
129
|
+
...record,
|
|
130
|
+
operations: record.operations.flatMap((value) => {
|
|
131
|
+
if (value.kind !== kind)
|
|
132
|
+
return [value];
|
|
133
|
+
if (value.count === 1)
|
|
134
|
+
return [];
|
|
135
|
+
return [{ ...value, count: value.count - 1 }];
|
|
136
|
+
}),
|
|
137
|
+
lastOperation: result,
|
|
138
|
+
...(outcome === "failed" ? { lastFailure: result } : {}),
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
catch (cause) {
|
|
142
|
+
if (outcome !== "failed")
|
|
143
|
+
throw cause;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
remove: () => serialize(async () => {
|
|
148
|
+
if (removed)
|
|
149
|
+
return;
|
|
150
|
+
invariant(record.operations.length === 0, "Resource operations remain unsettled; activity record retained");
|
|
151
|
+
try {
|
|
152
|
+
await verify();
|
|
153
|
+
await rm(path);
|
|
154
|
+
}
|
|
155
|
+
catch (cause) {
|
|
156
|
+
if (!(cause &&
|
|
157
|
+
typeof cause === "object" &&
|
|
158
|
+
"code" in cause &&
|
|
159
|
+
cause.code === "ENOENT"))
|
|
160
|
+
throw cause;
|
|
161
|
+
}
|
|
162
|
+
removed = true;
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=resource-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-activity.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, open, realpath, rename, rm, opendir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { localProcessIdentity } from \"./git/process-identity.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\nimport { lockStorageMutation } from \"./storage-mutation-lock.ts\";\nimport { resourceActivityDefaults as defaults } from \"./resource-activity.constants.ts\";\nimport type {\n ResourceActivity,\n ResourceActivityOptions,\n ResourceActivityRecord,\n ResourceOperationResult,\n} from \"./resource-activity.types.ts\";\n\nasync function activityDirectory(repository: string): Promise<string> {\n let path = repository;\n for (const part of [\".outpost\", \"locks\", defaults.directory]) {\n path = join(path, part);\n await mkdir(path, { recursive: true, mode: 0o700 });\n invariant(\n (await realpath(path)) === path,\n \"Resource activity directory must not use symlinks\",\n );\n }\n return path;\n}\n\nexport async function registerResourceActivity(\n options: ResourceActivityOptions,\n): Promise<ResourceActivity> {\n for (const text of [options.workspace, options.provider])\n invariant(\n text.length > 0 && text.length <= defaults.maxText,\n \"Resource activity metadata exceeds its size limit\",\n );\n const directory = await activityDirectory(options.repository);\n const id = randomUUID();\n const path = join(directory, `${id}.json`);\n const createdAt = new Date().toISOString();\n const identity = await localProcessIdentity();\n let record: ResourceActivityRecord = {\n version: 1,\n id,\n pid: process.pid,\n ...(identity ? { identity } : {}),\n provider: options.provider,\n placement: options.placement,\n workspace: options.workspace,\n createdAt,\n updatedAt: createdAt,\n phase: \"allocating\",\n operations: [],\n };\n const unlock = await lockStorageMutation(options.repository);\n try {\n let count = 0;\n for await (const _entry of await opendir(directory))\n invariant(\n ++count < defaults.maxRecords,\n \"Too many resource activity records; inspect retained ownership before continuing\",\n );\n const file = await open(path, \"wx\", 0o600);\n try {\n await file.writeFile(JSON.stringify(record));\n await file.sync();\n } finally {\n await file.close();\n }\n } finally {\n await unlock();\n }\n let pending = Promise.resolve();\n let removed = false;\n function serialize(action: () => Promise<void>): Promise<void> {\n const next = pending.then(action);\n pending = next.catch(() => undefined);\n return next;\n }\n async function verify(): Promise<void> {\n invariant(\n (await realpath(directory)) === directory,\n \"Resource activity directory changed\",\n );\n invariant(!removed, \"Resource activity is closed\");\n const current = (\n await readInspectionFile(path, defaults.maxRecordBytes)\n ).toString(\"utf8\");\n invariant(\n current === JSON.stringify(record),\n \"Resource activity identity changed; refusing mutation\",\n );\n }\n function update(next: () => ResourceActivityRecord): Promise<void> {\n return serialize(async () => {\n await verify();\n const value = { ...next(), updatedAt: new Date().toISOString() };\n const data = JSON.stringify(value);\n invariant(\n Buffer.byteLength(data) <= defaults.maxRecordBytes,\n \"Resource activity record exceeds its size limit\",\n );\n const temporary = `${path}.${randomUUID()}.tmp`;\n const file = await open(temporary, \"wx\", 0o600);\n try {\n await file.writeFile(data);\n await file.sync();\n await file.close();\n await rename(temporary, path);\n record = value;\n } finally {\n await file.close();\n await rm(temporary, { force: true });\n }\n });\n }\n return {\n async idle() {\n await pending;\n return record.operations.length === 0;\n },\n phase: (phase) => update(() => ({ ...record, phase })),\n async run(kind, action) {\n const operation = {\n id: randomUUID(),\n kind,\n startedAt: new Date().toISOString(),\n };\n await update(() => {\n const active = record.operations.find((value) => value.kind === kind);\n const operations = record.operations.filter(\n (value) => value.kind !== kind,\n );\n operations.push({\n kind,\n startedAt: active?.startedAt ?? operation.startedAt,\n count: (active?.count ?? 0) + 1,\n });\n return { ...record, operations };\n });\n let outcome: ResourceOperationResult[\"outcome\"] = \"completed\";\n try {\n return await action();\n } catch (cause) {\n outcome = \"failed\";\n throw cause;\n } finally {\n const result = {\n ...operation,\n outcome,\n count: 1,\n finishedAt: new Date().toISOString(),\n };\n try {\n await update(() => ({\n ...record,\n operations: record.operations.flatMap((value) => {\n if (value.kind !== kind) return [value];\n if (value.count === 1) return [];\n return [{ ...value, count: value.count - 1 }];\n }),\n lastOperation: result,\n ...(outcome === \"failed\" ? { lastFailure: result } : {}),\n }));\n } catch (cause) {\n if (outcome !== \"failed\") throw cause;\n }\n }\n },\n remove: () =>\n serialize(async () => {\n if (removed) return;\n invariant(\n record.operations.length === 0,\n \"Resource operations remain unsettled; activity record retained\",\n );\n try {\n await verify();\n await rm(path);\n } catch (cause) {\n if (!(\n cause &&\n typeof cause === \"object\" &&\n \"code\" in cause &&\n cause.code === \"ENOENT\"\n ))\n throw cause;\n }\n removed = true;\n }),\n };\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { LocalProcessIdentity, LockOwnership } from "./git/process-identity.types.ts";
|
|
2
|
+
import type { StorageIssue } from "./storage-inventory.types.ts";
|
|
3
|
+
import type { resourceOperationKinds, resourcePhases } from "./resource-activity.constants.ts";
|
|
4
|
+
export type ResourcePhase = (typeof resourcePhases)[number];
|
|
5
|
+
export type ResourceOperationKind = (typeof resourceOperationKinds)[number];
|
|
6
|
+
export interface ResourceOperation {
|
|
7
|
+
readonly count: number;
|
|
8
|
+
readonly kind: ResourceOperationKind;
|
|
9
|
+
readonly startedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ResourceOperationResult extends ResourceOperation {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly finishedAt: string;
|
|
14
|
+
readonly outcome: "completed" | "failed";
|
|
15
|
+
}
|
|
16
|
+
export interface ResourceActivityRecord {
|
|
17
|
+
readonly version: 1;
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly pid: number;
|
|
20
|
+
readonly identity?: LocalProcessIdentity;
|
|
21
|
+
readonly provider: string;
|
|
22
|
+
readonly placement: "mounted" | "remote" | "host";
|
|
23
|
+
readonly workspace: string;
|
|
24
|
+
readonly createdAt: string;
|
|
25
|
+
readonly updatedAt: string;
|
|
26
|
+
readonly phase: ResourcePhase;
|
|
27
|
+
readonly operations: readonly ResourceOperation[];
|
|
28
|
+
readonly lastOperation?: ResourceOperationResult;
|
|
29
|
+
readonly lastFailure?: ResourceOperationResult;
|
|
30
|
+
}
|
|
31
|
+
export interface ResourceActivity {
|
|
32
|
+
idle(): Promise<boolean>;
|
|
33
|
+
phase(phase: ResourcePhase): Promise<void>;
|
|
34
|
+
run<T>(kind: ResourceOperationKind, action: () => Promise<T>): Promise<T>;
|
|
35
|
+
remove(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export interface ResourceActivityOptions {
|
|
38
|
+
readonly repository: string;
|
|
39
|
+
readonly workspace: string;
|
|
40
|
+
readonly provider: string;
|
|
41
|
+
readonly placement: ResourceActivityRecord["placement"];
|
|
42
|
+
}
|
|
43
|
+
export interface ResourceInspectionEntry {
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly record?: ResourceActivityRecord;
|
|
46
|
+
readonly ownership: LockOwnership;
|
|
47
|
+
}
|
|
48
|
+
export interface ResourceInspection {
|
|
49
|
+
readonly scope: "recorded-sandboxes";
|
|
50
|
+
readonly complete: boolean;
|
|
51
|
+
readonly entries: readonly ResourceInspectionEntry[];
|
|
52
|
+
readonly issues: readonly StorageIssue[];
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-activity.types.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n LocalProcessIdentity,\n LockOwnership,\n} from \"./git/process-identity.types.ts\";\nimport type { StorageIssue } from \"./storage-inventory.types.ts\";\nimport type {\n resourceOperationKinds,\n resourcePhases,\n} from \"./resource-activity.constants.ts\";\n\nexport type ResourcePhase = (typeof resourcePhases)[number];\nexport type ResourceOperationKind = (typeof resourceOperationKinds)[number];\nexport interface ResourceOperation {\n readonly count: number;\n readonly kind: ResourceOperationKind;\n readonly startedAt: string;\n}\nexport interface ResourceOperationResult extends ResourceOperation {\n readonly id: string;\n readonly finishedAt: string;\n readonly outcome: \"completed\" | \"failed\";\n}\nexport interface ResourceActivityRecord {\n readonly version: 1;\n readonly id: string;\n readonly pid: number;\n readonly identity?: LocalProcessIdentity;\n readonly provider: string;\n readonly placement: \"mounted\" | \"remote\" | \"host\";\n readonly workspace: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly phase: ResourcePhase;\n readonly operations: readonly ResourceOperation[];\n readonly lastOperation?: ResourceOperationResult;\n readonly lastFailure?: ResourceOperationResult;\n}\nexport interface ResourceActivity {\n idle(): Promise<boolean>;\n phase(phase: ResourcePhase): Promise<void>;\n run<T>(kind: ResourceOperationKind, action: () => Promise<T>): Promise<T>;\n remove(): Promise<void>;\n}\nexport interface ResourceActivityOptions {\n readonly repository: string;\n readonly workspace: string;\n readonly provider: string;\n readonly placement: ResourceActivityRecord[\"placement\"];\n}\nexport interface ResourceInspectionEntry {\n readonly path: string;\n readonly record?: ResourceActivityRecord;\n readonly ownership: LockOwnership;\n}\nexport interface ResourceInspection {\n readonly scope: \"recorded-sandboxes\";\n readonly complete: boolean;\n readonly entries: readonly ResourceInspectionEntry[];\n readonly issues: readonly StorageIssue[];\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-mutation-lock.constants.js","sourceRoot":"","sources":["../../src/infrastructure/storage-mutation-lock.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,OAAO,EAAE,yBAAyB;IAClC,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,EAAE;CACN,CAAC","sourcesContent":["export const storageMutationLockDefaults = {\n lockKey: \"storage-reservations:v1\",\n lockWaitMs: 5_000,\n lockPollMs: 20,\n} as const;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function lockStorageMutation(root: string, signal?: AbortSignal): Promise<() => Promise<void>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { setTimeout } from "node:timers/promises";
|
|
2
|
+
import { OutpostError } from "../domain/errors.js";
|
|
3
|
+
import { lock } from "./git/lock.js";
|
|
4
|
+
import { storageMutationLockDefaults as defaults } from "./storage-mutation-lock.constants.js";
|
|
5
|
+
export async function lockStorageMutation(root, signal) {
|
|
6
|
+
const deadline = Date.now() + defaults.lockWaitMs;
|
|
7
|
+
while (true) {
|
|
8
|
+
signal?.throwIfAborted();
|
|
9
|
+
try {
|
|
10
|
+
return await lock(root, defaults.lockKey);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (!(error instanceof OutpostError) ||
|
|
14
|
+
error.code !== "conflict" ||
|
|
15
|
+
Date.now() >= deadline)
|
|
16
|
+
throw error;
|
|
17
|
+
await setTimeout(defaults.lockPollMs, undefined, { signal });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=storage-mutation-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-mutation-lock.js","sourceRoot":"","sources":["../../src/infrastructure/storage-mutation-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,2BAA2B,IAAI,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAE/F,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAoB;IAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC;IAClD,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,CAAC,CAAC,KAAK,YAAY,YAAY,CAAC;gBAChC,KAAK,CAAC,IAAI,KAAK,UAAU;gBACzB,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAEtB,MAAM,KAAK,CAAC;YACd,MAAM,UAAU,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { setTimeout } from \"node:timers/promises\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport { lock } from \"./git/lock.ts\";\nimport { storageMutationLockDefaults as defaults } from \"./storage-mutation-lock.constants.ts\";\n\nexport async function lockStorageMutation(root: string, signal?: AbortSignal) {\n const deadline = Date.now() + defaults.lockWaitMs;\n while (true) {\n signal?.throwIfAborted();\n try {\n return await lock(root, defaults.lockKey);\n } catch (error) {\n if (\n !(error instanceof OutpostError) ||\n error.code !== \"conflict\" ||\n Date.now() >= deadline\n )\n throw error;\n await setTimeout(defaults.lockPollMs, undefined, { signal });\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-reservations.constants.js","sourceRoot":"","sources":["../../src/infrastructure/storage-reservations.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,MAAM;CACV,CAAC","sourcesContent":["export const storageReservationDefaults = {\n maxRecordBytes: 4_096,\n maxRecords: 10_000,\n} as const;\n"]}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, open, readdir, realpath, rm } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { invariant, OutpostError } from "../domain/errors.js";
|
|
5
|
+
import { lockStorageMutation } from "./storage-mutation-lock.js";
|
|
6
|
+
import { localProcessIdentity, observeOwnership, } from "./git/process-identity.js";
|
|
7
|
+
import { readInspectionFile } from "./inspection-file.js";
|
|
8
|
+
import { storageInventory } from "./storage-inventory.js";
|
|
9
|
+
import { storageReservationDefaults as defaults } from "./storage-reservations.constants.js";
|
|
10
|
+
async function reservationDirectory(root) {
|
|
11
|
+
const paths = [
|
|
12
|
+
join(root, ".outpost"),
|
|
13
|
+
join(root, ".outpost", "locks"),
|
|
14
|
+
join(root, ".outpost", "locks", "storage-reservations"),
|
|
15
|
+
];
|
|
16
|
+
for (const path of paths) {
|
|
17
|
+
await mkdir(path, { recursive: true, mode: 0o700 });
|
|
18
|
+
invariant((await realpath(path)) === path, "Storage reservation directory must not use symlinks");
|
|
19
|
+
}
|
|
20
|
+
return paths[2];
|
|
21
|
+
}
|
|
22
|
+
function validRecord(value) {
|
|
23
|
+
if (!value || typeof value !== "object")
|
|
24
|
+
return false;
|
|
25
|
+
return ("version" in value &&
|
|
26
|
+
value.version === 1 &&
|
|
27
|
+
"id" in value &&
|
|
28
|
+
typeof value.id === "string" &&
|
|
29
|
+
"pid" in value &&
|
|
30
|
+
typeof value.pid === "number" &&
|
|
31
|
+
Number.isSafeInteger(value.pid) &&
|
|
32
|
+
value.pid > 0 &&
|
|
33
|
+
value.pid <= 2_147_483_647 &&
|
|
34
|
+
"reserveBytes" in value &&
|
|
35
|
+
typeof value.reserveBytes === "number" &&
|
|
36
|
+
Number.isSafeInteger(value.reserveBytes) &&
|
|
37
|
+
value.reserveBytes >= 0 &&
|
|
38
|
+
"createdAt" in value &&
|
|
39
|
+
typeof value.createdAt === "string" &&
|
|
40
|
+
Number.isFinite(Date.parse(value.createdAt)));
|
|
41
|
+
}
|
|
42
|
+
async function readRecord(path) {
|
|
43
|
+
try {
|
|
44
|
+
const value = JSON.parse((await readInspectionFile(path, defaults.maxRecordBytes)).toString("utf8"));
|
|
45
|
+
if (validRecord(value))
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
throw new OutpostError("workspace", "Storage reservation record is unreadable; admission refused", { path }, cause);
|
|
50
|
+
}
|
|
51
|
+
throw new OutpostError("workspace", "Storage reservation record is invalid; admission refused", { path });
|
|
52
|
+
}
|
|
53
|
+
async function reservedBytes(directory) {
|
|
54
|
+
const names = await readdir(directory);
|
|
55
|
+
invariant(names.length <= defaults.maxRecords, "Too many storage reservation records");
|
|
56
|
+
let reserved = 0;
|
|
57
|
+
for (const name of names) {
|
|
58
|
+
const path = join(directory, name);
|
|
59
|
+
const record = await readRecord(path);
|
|
60
|
+
invariant(name === `${record.id}.json`, "Storage reservation identity mismatch");
|
|
61
|
+
const owner = await observeOwnership(record.pid, record.identity);
|
|
62
|
+
if (owner.status === "inactive") {
|
|
63
|
+
await rm(path);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
reserved += record.reserveBytes;
|
|
67
|
+
invariant(Number.isSafeInteger(reserved), "Storage reservation total exceeds safe integer range");
|
|
68
|
+
}
|
|
69
|
+
return reserved;
|
|
70
|
+
}
|
|
71
|
+
async function releaseRecord(root, path, id) {
|
|
72
|
+
await reservationDirectory(root);
|
|
73
|
+
const unlock = await lockStorageMutation(root);
|
|
74
|
+
try {
|
|
75
|
+
const names = await readdir(join(root, ".outpost", "locks", "storage-reservations"));
|
|
76
|
+
if (!names.includes(`${id}.json`))
|
|
77
|
+
return;
|
|
78
|
+
const record = await readRecord(path);
|
|
79
|
+
invariant(record.id === id, "Storage reservation identity changed; refusing release");
|
|
80
|
+
await rm(path);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await unlock();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function reserveStorage(repository, options) {
|
|
87
|
+
invariant(Number.isSafeInteger(options.maxBytes) && options.maxBytes >= 0, "maxBytes must be a nonnegative integer");
|
|
88
|
+
invariant(Number.isSafeInteger(options.reserveBytes) && options.reserveBytes >= 0, "reserveBytes must be a nonnegative integer");
|
|
89
|
+
options.signal?.throwIfAborted();
|
|
90
|
+
const directory = await reservationDirectory(repository);
|
|
91
|
+
const unlock = await lockStorageMutation(repository, options.signal);
|
|
92
|
+
const id = randomUUID();
|
|
93
|
+
const path = join(directory, `${id}.json`);
|
|
94
|
+
try {
|
|
95
|
+
const reserved = await reservedBytes(directory);
|
|
96
|
+
const inventory = await storageInventory(join(repository, ".outpost"), options.maxEntries);
|
|
97
|
+
const record = {
|
|
98
|
+
version: 1,
|
|
99
|
+
id,
|
|
100
|
+
pid: process.pid,
|
|
101
|
+
identity: await localProcessIdentity(),
|
|
102
|
+
reserveBytes: options.reserveBytes,
|
|
103
|
+
createdAt: new Date().toISOString(),
|
|
104
|
+
};
|
|
105
|
+
const data = JSON.stringify(record);
|
|
106
|
+
if (!inventory.complete ||
|
|
107
|
+
inventory.usage.bytes +
|
|
108
|
+
reserved +
|
|
109
|
+
options.reserveBytes +
|
|
110
|
+
Buffer.byteLength(data) >
|
|
111
|
+
options.maxBytes)
|
|
112
|
+
throw new OutpostError("workspace", "Outpost storage reservation admission refused", {
|
|
113
|
+
usageBytes: inventory.usage.bytes,
|
|
114
|
+
reservedBytes: reserved,
|
|
115
|
+
reserveBytes: options.reserveBytes,
|
|
116
|
+
maxBytes: options.maxBytes,
|
|
117
|
+
complete: inventory.complete,
|
|
118
|
+
});
|
|
119
|
+
options.signal?.throwIfAborted();
|
|
120
|
+
const file = await open(path, "wx", 0o600);
|
|
121
|
+
try {
|
|
122
|
+
await file.writeFile(data);
|
|
123
|
+
await file.sync();
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
await file.close();
|
|
127
|
+
await rm(path, { force: true });
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
await file.close();
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await unlock();
|
|
134
|
+
}
|
|
135
|
+
let released = false;
|
|
136
|
+
const release = async () => {
|
|
137
|
+
if (released)
|
|
138
|
+
return;
|
|
139
|
+
await releaseRecord(repository, path, id);
|
|
140
|
+
released = true;
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
id,
|
|
144
|
+
repository,
|
|
145
|
+
reserveBytes: options.reserveBytes,
|
|
146
|
+
release,
|
|
147
|
+
[Symbol.asyncDispose]: release,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=storage-reservations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-reservations.js","sourceRoot":"","sources":["../../src/infrastructure/storage-reservations.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, realpath, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { lockStorageMutation } from \"./storage-mutation-lock.ts\";\nimport {\n localProcessIdentity,\n observeOwnership,\n} from \"./git/process-identity.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\nimport { storageInventory } from \"./storage-inventory.ts\";\nimport { storageReservationDefaults as defaults } from \"./storage-reservations.constants.ts\";\nimport type {\n StorageReservation,\n StorageReservationOptions,\n StorageReservationRecord,\n} from \"./storage-reservations.types.ts\";\n\nasync function reservationDirectory(root: string): Promise<string> {\n const paths = [\n join(root, \".outpost\"),\n join(root, \".outpost\", \"locks\"),\n join(root, \".outpost\", \"locks\", \"storage-reservations\"),\n ];\n for (const path of paths) {\n await mkdir(path, { recursive: true, mode: 0o700 });\n invariant(\n (await realpath(path)) === path,\n \"Storage reservation directory must not use symlinks\",\n );\n }\n return paths[2]!;\n}\n\nfunction validRecord(value: unknown): value is StorageReservationRecord {\n if (!value || typeof value !== \"object\") return false;\n return (\n \"version\" in value &&\n value.version === 1 &&\n \"id\" in value &&\n typeof value.id === \"string\" &&\n \"pid\" in value &&\n typeof value.pid === \"number\" &&\n Number.isSafeInteger(value.pid) &&\n value.pid > 0 &&\n value.pid <= 2_147_483_647 &&\n \"reserveBytes\" in value &&\n typeof value.reserveBytes === \"number\" &&\n Number.isSafeInteger(value.reserveBytes) &&\n value.reserveBytes >= 0 &&\n \"createdAt\" in value &&\n typeof value.createdAt === \"string\" &&\n Number.isFinite(Date.parse(value.createdAt))\n );\n}\n\nasync function readRecord(path: string): Promise<StorageReservationRecord> {\n try {\n const value: unknown = JSON.parse(\n (await readInspectionFile(path, defaults.maxRecordBytes)).toString(\n \"utf8\",\n ),\n );\n if (validRecord(value)) return value;\n } catch (cause) {\n throw new OutpostError(\n \"workspace\",\n \"Storage reservation record is unreadable; admission refused\",\n { path },\n cause,\n );\n }\n throw new OutpostError(\n \"workspace\",\n \"Storage reservation record is invalid; admission refused\",\n { path },\n );\n}\n\nasync function reservedBytes(directory: string): Promise<number> {\n const names = await readdir(directory);\n invariant(\n names.length <= defaults.maxRecords,\n \"Too many storage reservation records\",\n );\n let reserved = 0;\n for (const name of names) {\n const path = join(directory, name);\n const record = await readRecord(path);\n invariant(\n name === `${record.id}.json`,\n \"Storage reservation identity mismatch\",\n );\n const owner = await observeOwnership(record.pid, record.identity);\n if (owner.status === \"inactive\") {\n await rm(path);\n continue;\n }\n reserved += record.reserveBytes;\n invariant(\n Number.isSafeInteger(reserved),\n \"Storage reservation total exceeds safe integer range\",\n );\n }\n return reserved;\n}\n\nasync function releaseRecord(\n root: string,\n path: string,\n id: string,\n): Promise<void> {\n await reservationDirectory(root);\n const unlock = await lockStorageMutation(root);\n try {\n const names = await readdir(\n join(root, \".outpost\", \"locks\", \"storage-reservations\"),\n );\n if (!names.includes(`${id}.json`)) return;\n const record = await readRecord(path);\n invariant(\n record.id === id,\n \"Storage reservation identity changed; refusing release\",\n );\n await rm(path);\n } finally {\n await unlock();\n }\n}\n\nexport async function reserveStorage(\n repository: string,\n options: StorageReservationOptions,\n): Promise<StorageReservation> {\n invariant(\n Number.isSafeInteger(options.maxBytes) && options.maxBytes >= 0,\n \"maxBytes must be a nonnegative integer\",\n );\n invariant(\n Number.isSafeInteger(options.reserveBytes) && options.reserveBytes >= 0,\n \"reserveBytes must be a nonnegative integer\",\n );\n options.signal?.throwIfAborted();\n const directory = await reservationDirectory(repository);\n const unlock = await lockStorageMutation(repository, options.signal);\n const id = randomUUID();\n const path = join(directory, `${id}.json`);\n try {\n const reserved = await reservedBytes(directory);\n const inventory = await storageInventory(\n join(repository, \".outpost\"),\n options.maxEntries,\n );\n const record: StorageReservationRecord = {\n version: 1,\n id,\n pid: process.pid,\n identity: await localProcessIdentity(),\n reserveBytes: options.reserveBytes,\n createdAt: new Date().toISOString(),\n };\n const data = JSON.stringify(record);\n if (\n !inventory.complete ||\n inventory.usage.bytes +\n reserved +\n options.reserveBytes +\n Buffer.byteLength(data) >\n options.maxBytes\n )\n throw new OutpostError(\n \"workspace\",\n \"Outpost storage reservation admission refused\",\n {\n usageBytes: inventory.usage.bytes,\n reservedBytes: reserved,\n reserveBytes: options.reserveBytes,\n maxBytes: options.maxBytes,\n complete: inventory.complete,\n },\n );\n options.signal?.throwIfAborted();\n const file = await open(path, \"wx\", 0o600);\n try {\n await file.writeFile(data);\n await file.sync();\n } catch (error) {\n await file.close();\n await rm(path, { force: true });\n throw error;\n }\n await file.close();\n } finally {\n await unlock();\n }\n let released = false;\n const release = async () => {\n if (released) return;\n await releaseRecord(repository, path, id);\n released = true;\n };\n return {\n id,\n repository,\n reserveBytes: options.reserveBytes,\n release,\n [Symbol.asyncDispose]: release,\n };\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface StorageReservationOptions {
|
|
2
|
+
readonly maxBytes: number;
|
|
3
|
+
readonly reserveBytes: number;
|
|
4
|
+
readonly maxEntries?: number;
|
|
5
|
+
readonly signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
export interface StorageReservation {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly repository: string;
|
|
10
|
+
readonly reserveBytes: number;
|
|
11
|
+
release(): Promise<void>;
|
|
12
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface StorageReservationRecord {
|
|
15
|
+
readonly version: 1;
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly pid: number;
|
|
18
|
+
readonly identity?: unknown;
|
|
19
|
+
readonly reserveBytes: number;
|
|
20
|
+
readonly createdAt: string;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-reservations.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-reservations.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface StorageReservationOptions {\n readonly maxBytes: number;\n readonly reserveBytes: number;\n readonly maxEntries?: number;\n readonly signal?: AbortSignal;\n}\n\nexport interface StorageReservation {\n readonly id: string;\n readonly repository: string;\n readonly reserveBytes: number;\n release(): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\nexport interface StorageReservationRecord {\n readonly version: 1;\n readonly id: string;\n readonly pid: number;\n readonly identity?: unknown;\n readonly reserveBytes: number;\n readonly createdAt: string;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-queue-http.constants.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-http.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC","sourcesContent":["export const queueHttpTimeoutMs = 10_000;\nexport const queueHttpMaxHeaderBytes = 8192;\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TaskQueue } from "../domain/task-queue.types.ts";
|
|
2
|
+
import type { QueueClientOptions, QueueServer, QueueServerOptions } from "./task-queue-http.types.ts";
|
|
3
|
+
export declare function serveTaskQueue(options: QueueServerOptions): Promise<QueueServer>;
|
|
4
|
+
export declare function httpTaskQueue(options: QueueClientOptions): TaskQueue;
|