@elie-laloum/outpost 11.1.2 → 11.2.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 +34 -0
- package/README.md +2 -1
- package/SECURITY.md +5 -1
- package/dist/adapters/agents/antigravity/antigravity-adapter.js +9 -0
- package/dist/adapters/agents/antigravity/antigravity-adapter.js.map +1 -1
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.js +39 -0
- package/dist/adapters/agents/antigravity/antigravity-hook-plan.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js +394 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.d.ts +2 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.js +6 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.d.ts +114 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.js +2 -0
- package/dist/adapters/agents/antigravity/antigravity-hooks.types.js.map +1 -0
- package/dist/adapters/agents/antigravity/antigravity.types.d.ts +1 -0
- package/dist/adapters/agents/antigravity/antigravity.types.js.map +1 -1
- package/dist/adapters/agents/authentication.js +1 -0
- package/dist/adapters/agents/authentication.js.map +1 -1
- package/dist/adapters/agents/claude/claude-adapter.js +15 -0
- package/dist/adapters/agents/claude/claude-adapter.js.map +1 -1
- package/dist/adapters/agents/claude/claude-hook-plan.constants.d.ts +1 -0
- package/dist/adapters/agents/claude/claude-hook-plan.constants.js +20 -0
- package/dist/adapters/agents/claude/claude-hook-plan.constants.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hook-plan.d.ts +4 -0
- package/dist/adapters/agents/claude/claude-hook-plan.js +60 -0
- package/dist/adapters/agents/claude/claude-hook-plan.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.js +2968 -0
- package/dist/adapters/agents/claude/claude-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.d.ts +2 -0
- package/dist/adapters/agents/claude/claude-hooks.js +6 -0
- package/dist/adapters/agents/claude/claude-hooks.js.map +1 -0
- package/dist/adapters/agents/claude/claude-hooks.types.d.ts +900 -0
- package/dist/adapters/agents/claude/claude-hooks.types.js +2 -0
- package/dist/adapters/agents/claude/claude-hooks.types.js.map +1 -0
- package/dist/adapters/agents/claude/claude-profile.d.ts +1 -0
- package/dist/adapters/agents/claude/claude-profile.js +23 -11
- package/dist/adapters/agents/claude/claude-profile.js.map +1 -1
- package/dist/adapters/agents/codex/codex-adapter.js +17 -1
- package/dist/adapters/agents/codex/codex-adapter.js.map +1 -1
- package/dist/adapters/agents/codex/codex-authentication.js +1 -0
- package/dist/adapters/agents/codex/codex-authentication.js.map +1 -1
- package/dist/adapters/agents/codex/codex-conversations.constants.d.ts +1 -0
- package/dist/adapters/agents/codex/codex-conversations.constants.js +2 -0
- package/dist/adapters/agents/codex/codex-conversations.constants.js.map +1 -0
- package/dist/adapters/agents/codex/codex-conversations.js +8 -5
- package/dist/adapters/agents/codex/codex-conversations.js.map +1 -1
- package/dist/adapters/agents/codex/codex-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/codex/codex-hook-plan.js +72 -0
- package/dist/adapters/agents/codex/codex-hook-plan.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.js +942 -0
- package/dist/adapters/agents/codex/codex-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.d.ts +2 -0
- package/dist/adapters/agents/codex/codex-hooks.js +6 -0
- package/dist/adapters/agents/codex/codex-hooks.js.map +1 -0
- package/dist/adapters/agents/codex/codex-hooks.types.d.ts +255 -0
- package/dist/adapters/agents/codex/codex-hooks.types.js +2 -0
- package/dist/adapters/agents/codex/codex-hooks.types.js.map +1 -0
- package/dist/adapters/agents/codex/codex-provider.js +2 -2
- package/dist/adapters/agents/codex/codex-provider.js.map +1 -1
- package/dist/adapters/agents/copilot/copilot-adapter.js +14 -0
- package/dist/adapters/agents/copilot/copilot-adapter.js.map +1 -1
- package/dist/adapters/agents/copilot/copilot-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/copilot/copilot-hook-plan.js +26 -0
- package/dist/adapters/agents/copilot/copilot-hook-plan.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.js +528 -0
- package/dist/adapters/agents/copilot/copilot-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.d.ts +2 -0
- package/dist/adapters/agents/copilot/copilot-hooks.js +6 -0
- package/dist/adapters/agents/copilot/copilot-hooks.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.d.ts +166 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.js +2 -0
- package/dist/adapters/agents/copilot/copilot-hooks.types.js.map +1 -0
- package/dist/adapters/agents/copilot/copilot.types.d.ts +1 -0
- package/dist/adapters/agents/copilot/copilot.types.js.map +1 -1
- package/dist/adapters/agents/hook-groups.d.ts +2 -0
- package/dist/adapters/agents/hook-groups.js +17 -0
- package/dist/adapters/agents/hook-groups.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-adapter.js +16 -1
- package/dist/adapters/agents/kimi/kimi-adapter.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi-hook-plan.d.ts +3 -0
- package/dist/adapters/agents/kimi/kimi-hook-plan.js +33 -0
- package/dist/adapters/agents/kimi/kimi-hook-plan.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.d.ts +3 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.js +747 -0
- package/dist/adapters/agents/kimi/kimi-hooks.constants.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.d.ts +2 -0
- package/dist/adapters/agents/kimi/kimi-hooks.js +6 -0
- package/dist/adapters/agents/kimi/kimi-hooks.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.d.ts +226 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.js +2 -0
- package/dist/adapters/agents/kimi/kimi-hooks.types.js.map +1 -0
- package/dist/adapters/agents/kimi/kimi-request.js +2 -0
- package/dist/adapters/agents/kimi/kimi-request.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi.constants.d.ts +1 -0
- package/dist/adapters/agents/kimi/kimi.constants.js +1 -0
- package/dist/adapters/agents/kimi/kimi.constants.js.map +1 -1
- package/dist/adapters/agents/kimi/kimi.types.d.ts +1 -0
- package/dist/adapters/agents/kimi/kimi.types.js.map +1 -1
- package/dist/adapters/agents/native-hooks.d.ts +4 -0
- package/dist/adapters/agents/native-hooks.js +63 -0
- package/dist/adapters/agents/native-hooks.js.map +1 -0
- package/dist/adapters/agents/native-hooks.types.d.ts +22 -0
- package/dist/adapters/agents/native-hooks.types.js +2 -0
- package/dist/adapters/agents/native-hooks.types.js.map +1 -0
- package/dist/adapters/agents/settings.types.d.ts +2 -0
- package/dist/adapters/agents/settings.types.js.map +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.d.ts +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.js +1 -1
- package/dist/adapters/decisions/system-one-provider.constants.js.map +1 -1
- package/dist/adapters/decisions/system-one-provider.js +8 -21
- package/dist/adapters/decisions/system-one-provider.js.map +1 -1
- package/dist/adapters/models/anthropic-model-provider.constants.d.ts +1 -0
- package/dist/adapters/models/anthropic-model-provider.constants.js +4 -0
- package/dist/adapters/models/anthropic-model-provider.constants.js.map +1 -1
- package/dist/adapters/models/anthropic-model-provider.js +7 -1
- package/dist/adapters/models/anthropic-model-provider.js.map +1 -1
- package/dist/adapters/models/http-failure.d.ts +3 -0
- package/dist/adapters/models/http-failure.js +43 -0
- package/dist/adapters/models/http-failure.js.map +1 -0
- package/dist/adapters/models/http-provider.js +14 -20
- package/dist/adapters/models/http-provider.js.map +1 -1
- package/dist/adapters/models/model-protocol.types.d.ts +1 -0
- package/dist/adapters/models/model-protocol.types.js.map +1 -1
- package/dist/adapters/models/model.constants.d.ts +2 -0
- package/dist/adapters/models/model.constants.js +17 -0
- package/dist/adapters/models/model.constants.js.map +1 -1
- package/dist/adapters/models/openai-chat-stream.js +3 -1
- package/dist/adapters/models/openai-chat-stream.js.map +1 -1
- package/dist/adapters/models/openai-responses.js +4 -0
- package/dist/adapters/models/openai-responses.js.map +1 -1
- package/dist/adapters/secrets/aws.js +5 -4
- package/dist/adapters/secrets/aws.js.map +1 -1
- package/dist/adapters/secrets/azure.constants.d.ts +1 -0
- package/dist/adapters/secrets/azure.constants.js +2 -0
- package/dist/adapters/secrets/azure.constants.js.map +1 -0
- package/dist/adapters/secrets/azure.js +2 -1
- package/dist/adapters/secrets/azure.js.map +1 -1
- package/dist/adapters/secrets/gcp.constants.d.ts +1 -0
- package/dist/adapters/secrets/gcp.constants.js +2 -0
- package/dist/adapters/secrets/gcp.constants.js.map +1 -0
- package/dist/adapters/secrets/gcp.js +11 -9
- package/dist/adapters/secrets/gcp.js.map +1 -1
- package/dist/adapters/secrets/infisical.js +3 -2
- package/dist/adapters/secrets/infisical.js.map +1 -1
- package/dist/adapters/secrets/onepassword.constants.d.ts +1 -0
- package/dist/adapters/secrets/onepassword.constants.js +2 -0
- package/dist/adapters/secrets/onepassword.constants.js.map +1 -0
- package/dist/adapters/secrets/onepassword.js +2 -1
- package/dist/adapters/secrets/onepassword.js.map +1 -1
- package/dist/adapters/secrets/secret-source.js +3 -1
- package/dist/adapters/secrets/secret-source.js.map +1 -1
- package/dist/adapters/secrets/vault-response.js +5 -5
- package/dist/adapters/secrets/vault-response.js.map +1 -1
- package/dist/adapters/secrets/vault.js +4 -3
- package/dist/adapters/secrets/vault.js.map +1 -1
- package/dist/adapters/testing/scripted-agent.d.ts +3 -1
- package/dist/adapters/testing/scripted-agent.js +3 -1
- package/dist/adapters/testing/scripted-agent.js.map +1 -1
- package/dist/adapters/tools/edit-tools.js +3 -3
- package/dist/adapters/tools/edit-tools.js.map +1 -1
- package/dist/adapters/tools/git-tools.js +16 -1
- package/dist/adapters/tools/git-tools.js.map +1 -1
- package/dist/adapters/tools/mcp-launcher.constants.d.ts +1 -1
- package/dist/adapters/tools/mcp-launcher.constants.js +29 -7
- package/dist/adapters/tools/mcp-launcher.constants.js.map +1 -1
- package/dist/adapters/tools/mcp-resources.js +5 -1
- package/dist/adapters/tools/mcp-resources.js.map +1 -1
- package/dist/adapters/tools/mcp.constants.d.ts +0 -6
- package/dist/adapters/tools/mcp.constants.js +0 -6
- package/dist/adapters/tools/mcp.constants.js.map +1 -1
- package/dist/adapters/tools/mcp.js +1 -0
- package/dist/adapters/tools/mcp.js.map +1 -1
- package/dist/adapters/tools/sandbox-files.constants.d.ts +1 -0
- package/dist/adapters/tools/sandbox-files.constants.js +17 -0
- package/dist/adapters/tools/sandbox-files.constants.js.map +1 -0
- package/dist/adapters/tools/sandbox-files.d.ts +1 -0
- package/dist/adapters/tools/sandbox-files.js +14 -1
- package/dist/adapters/tools/sandbox-files.js.map +1 -1
- package/dist/adapters/tools/tools.constants.js +1 -0
- package/dist/adapters/tools/tools.constants.js.map +1 -1
- package/dist/adapters/triggers/github-webhook.js +14 -11
- package/dist/adapters/triggers/github-webhook.js.map +1 -1
- package/dist/adapters/triggers/gitlab-webhook.js +16 -13
- package/dist/adapters/triggers/gitlab-webhook.js.map +1 -1
- package/dist/adapters/triggers/slack-request.js +10 -7
- package/dist/adapters/triggers/slack-request.js.map +1 -1
- package/dist/adapters/triggers/standard-webhook.js +10 -7
- package/dist/adapters/triggers/standard-webhook.js.map +1 -1
- package/dist/application/agent-authentication.js +4 -1
- package/dist/application/agent-authentication.js.map +1 -1
- package/dist/application/agent-turn.js +20 -5
- package/dist/application/agent-turn.js.map +1 -1
- package/dist/application/cli-hooks.constants.d.ts +2 -0
- package/dist/application/cli-hooks.constants.js +69 -0
- package/dist/application/cli-hooks.constants.js.map +1 -0
- package/dist/application/cli-hooks.d.ts +3 -0
- package/dist/application/cli-hooks.js +168 -0
- package/dist/application/cli-hooks.js.map +1 -0
- package/dist/application/cli-steering.js +33 -2
- package/dist/application/cli-steering.js.map +1 -1
- package/dist/application/custom-turn.js +10 -1
- package/dist/application/custom-turn.js.map +1 -1
- package/dist/application/diagnostic-probe.d.ts +1 -1
- package/dist/application/diagnostic-probe.js +7 -3
- package/dist/application/diagnostic-probe.js.map +1 -1
- package/dist/application/dispatch-observation.js +4 -0
- package/dist/application/dispatch-observation.js.map +1 -1
- package/dist/application/doctor-agent.d.ts +1 -1
- package/dist/application/doctor-agent.js +2 -2
- package/dist/application/doctor-agent.js.map +1 -1
- package/dist/application/doctor-deadline.d.ts +1 -0
- package/dist/application/doctor-deadline.js +10 -0
- package/dist/application/doctor-deadline.js.map +1 -0
- package/dist/application/doctor-image.js +9 -7
- package/dist/application/doctor-image.js.map +1 -1
- package/dist/application/doctor-sandbox.js +4 -4
- package/dist/application/doctor-sandbox.js.map +1 -1
- package/dist/application/doctor.constants.d.ts +2 -0
- package/dist/application/doctor.constants.js +2 -0
- package/dist/application/doctor.constants.js.map +1 -1
- package/dist/application/doctor.js +12 -8
- package/dist/application/doctor.js.map +1 -1
- package/dist/application/doctor.types.d.ts +2 -0
- package/dist/application/doctor.types.js.map +1 -1
- package/dist/application/file-sandbox.js +49 -11
- package/dist/application/file-sandbox.js.map +1 -1
- package/dist/application/harness-context.js +4 -2
- package/dist/application/harness-context.js.map +1 -1
- package/dist/application/harness-hook-transforms.d.ts +8 -0
- package/dist/application/harness-hook-transforms.js +115 -0
- package/dist/application/harness-hook-transforms.js.map +1 -0
- package/dist/application/harness-hooks.d.ts +5 -3
- package/dist/application/harness-hooks.js +37 -50
- package/dist/application/harness-hooks.js.map +1 -1
- package/dist/application/harness-loop.d.ts +2 -1
- package/dist/application/harness-loop.js +95 -21
- package/dist/application/harness-loop.js.map +1 -1
- package/dist/application/harness-subagent.d.ts +1 -1
- package/dist/application/harness-subagent.js +63 -12
- package/dist/application/harness-subagent.js.map +1 -1
- package/dist/application/harness.types.d.ts +3 -0
- package/dist/application/harness.types.js.map +1 -1
- package/dist/application/hook-invocation.d.ts +4 -0
- package/dist/application/hook-invocation.js +59 -0
- package/dist/application/hook-invocation.js.map +1 -0
- package/dist/application/queue-worker.js +51 -9
- package/dist/application/queue-worker.js.map +1 -1
- package/dist/application/queue-worker.types.d.ts +1 -0
- package/dist/application/queue-worker.types.js.map +1 -1
- package/dist/application/recipes/native-schemas.constants.js +1439 -218
- package/dist/application/recipes/native-schemas.constants.js.map +1 -1
- package/dist/application/recovery-inspection.js +4 -1
- package/dist/application/recovery-inspection.js.map +1 -1
- package/dist/application/remote-download.js +15 -5
- package/dist/application/remote-download.js.map +1 -1
- package/dist/application/remote-paths.d.ts +1 -0
- package/dist/application/remote-paths.js +7 -0
- package/dist/application/remote-paths.js.map +1 -0
- package/dist/application/remote-validation.js +40 -9
- package/dist/application/remote-validation.js.map +1 -1
- package/dist/application/remote-workspace.js +17 -2
- package/dist/application/remote-workspace.js.map +1 -1
- package/dist/application/repetition-watchdog.js +3 -1
- package/dist/application/repetition-watchdog.js.map +1 -1
- package/dist/application/sandbox-attach.js +38 -9
- package/dist/application/sandbox-attach.js.map +1 -1
- package/dist/application/sandbox-dispatch.js +7 -1
- package/dist/application/sandbox-dispatch.js.map +1 -1
- package/dist/application/schedules.js +2 -0
- package/dist/application/schedules.js.map +1 -1
- package/dist/application/secrets.js +4 -0
- package/dist/application/secrets.js.map +1 -1
- package/dist/application/tool-execution.js +28 -8
- package/dist/application/tool-execution.js.map +1 -1
- package/dist/application/transport-retention.js +3 -3
- package/dist/application/transport-retention.js.map +1 -1
- package/dist/application/workspace-fingerprint.d.ts +1 -0
- package/dist/application/workspace-fingerprint.js +19 -4
- package/dist/application/workspace-fingerprint.js.map +1 -1
- package/dist/cli/doctor-command.js +12 -2
- package/dist/cli/doctor-command.js.map +1 -1
- package/dist/cli/image.js +2 -0
- package/dist/cli/image.js.map +1 -1
- package/dist/cli/main.constants.d.ts +14 -2
- package/dist/cli/main.constants.js +27 -1
- package/dist/cli/main.constants.js.map +1 -1
- package/dist/cli/project-settings.js +7 -1
- package/dist/cli/project-settings.js.map +1 -1
- package/dist/cli/recipe-yaml.js +8 -0
- package/dist/cli/recipe-yaml.js.map +1 -1
- package/dist/cli/recovery-command.js +3 -0
- package/dist/cli/recovery-command.js.map +1 -1
- package/dist/cli/recovery-unlock-command.d.ts +2 -0
- package/dist/cli/recovery-unlock-command.js +19 -0
- package/dist/cli/recovery-unlock-command.js.map +1 -0
- package/dist/cli/scaffold-files.js +4 -1
- package/dist/cli/scaffold-files.js.map +1 -1
- package/dist/cli/scaffold-validation.js +3 -0
- package/dist/cli/scaffold-validation.js.map +1 -1
- package/dist/cli/scaffold.js +2 -0
- package/dist/cli/scaffold.js.map +1 -1
- package/dist/domain/agent-hook.types.d.ts +28 -0
- package/dist/domain/agent-hook.types.js +2 -0
- package/dist/domain/agent-hook.types.js.map +1 -0
- package/dist/domain/agent-profile-hooks.d.ts +2 -0
- package/dist/domain/agent-profile-hooks.js +52 -0
- package/dist/domain/agent-profile-hooks.js.map +1 -0
- package/dist/domain/agent-profile-permissions.js +11 -1
- package/dist/domain/agent-profile-permissions.js.map +1 -1
- package/dist/domain/agent-profile.constants.d.ts +1 -0
- package/dist/domain/agent-profile.constants.js +9 -0
- package/dist/domain/agent-profile.constants.js.map +1 -1
- package/dist/domain/agent-profile.js +6 -1
- package/dist/domain/agent-profile.js.map +1 -1
- package/dist/domain/agent-profile.types.d.ts +11 -0
- package/dist/domain/agent-profile.types.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/container-image.d.ts +1 -0
- package/dist/domain/container-image.js +8 -0
- package/dist/domain/container-image.js.map +1 -0
- package/dist/domain/context.constants.d.ts +1 -0
- package/dist/domain/context.constants.js +1 -0
- package/dist/domain/context.constants.js.map +1 -1
- package/dist/domain/context.js +6 -1
- package/dist/domain/context.js.map +1 -1
- package/dist/domain/egress.js +6 -1
- package/dist/domain/egress.js.map +1 -1
- package/dist/domain/glob.js +1 -1
- package/dist/domain/glob.js.map +1 -1
- package/dist/domain/harness.js +4 -1
- package/dist/domain/harness.js.map +1 -1
- package/dist/domain/hook-command.d.ts +2 -0
- package/dist/domain/hook-command.js +26 -0
- package/dist/domain/hook-command.js.map +1 -0
- package/dist/domain/hook-command.types.d.ts +7 -0
- package/dist/domain/hook-command.types.js +2 -0
- package/dist/domain/hook-command.types.js.map +1 -0
- package/dist/domain/hook-decision.d.ts +2 -0
- package/dist/domain/hook-decision.js +38 -0
- package/dist/domain/hook-decision.js.map +1 -0
- package/dist/domain/hook.constants.d.ts +3 -0
- package/dist/domain/hook.constants.js +31 -1
- package/dist/domain/hook.constants.js.map +1 -1
- package/dist/domain/hook.js +49 -5
- package/dist/domain/hook.js.map +1 -1
- package/dist/domain/hook.types.d.ts +82 -5
- package/dist/domain/hook.types.js.map +1 -1
- package/dist/domain/mcp-server.constants.d.ts +6 -0
- package/dist/domain/mcp-server.constants.js +6 -0
- package/dist/domain/mcp-server.constants.js.map +1 -1
- package/dist/domain/mcp-server.js +1 -0
- package/dist/domain/mcp-server.js.map +1 -1
- package/dist/domain/permissions.types.d.ts +1 -0
- package/dist/domain/permissions.types.js.map +1 -1
- package/dist/domain/pricing.js +4 -2
- package/dist/domain/pricing.js.map +1 -1
- package/dist/domain/redaction.js +6 -3
- package/dist/domain/redaction.js.map +1 -1
- package/dist/domain/secret-fault.d.ts +4 -0
- package/dist/domain/secret-fault.js +11 -0
- package/dist/domain/secret-fault.js.map +1 -0
- package/dist/domain/secrets.js +3 -2
- package/dist/domain/secrets.js.map +1 -1
- package/dist/domain/steering.js +2 -0
- package/dist/domain/steering.js.map +1 -1
- package/dist/domain/steering.types.d.ts +1 -1
- package/dist/domain/steering.types.js.map +1 -1
- package/dist/domain/transport.js +5 -1
- package/dist/domain/transport.js.map +1 -1
- package/dist/domain/trigger-payload.d.ts +4 -0
- package/dist/domain/trigger-payload.js +15 -0
- package/dist/domain/trigger-payload.js.map +1 -0
- package/dist/domain/workflow/checkpoint.js +19 -2
- package/dist/domain/workflow/checkpoint.js.map +1 -1
- package/dist/domain/workflow/retry.constants.d.ts +1 -0
- package/dist/domain/workflow/retry.constants.js +1 -0
- package/dist/domain/workflow/retry.constants.js.map +1 -1
- package/dist/domain/workflow/retry.js +6 -2
- package/dist/domain/workflow/retry.js.map +1 -1
- package/dist/domain/workflow/task-runner.js +5 -3
- package/dist/domain/workflow/task-runner.js.map +1 -1
- package/dist/domain/workflow/workflow-state.js +2 -1
- package/dist/domain/workflow/workflow-state.js.map +1 -1
- package/dist/domain/workflow.types.d.ts +1 -0
- package/dist/domain/workflow.types.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/conversations/transcript-store.js +32 -5
- package/dist/infrastructure/conversations/transcript-store.js.map +1 -1
- package/dist/infrastructure/files.d.ts +1 -0
- package/dist/infrastructure/files.js +8 -4
- package/dist/infrastructure/files.js.map +1 -1
- package/dist/infrastructure/git/integration-candidate.js +3 -2
- package/dist/infrastructure/git/integration-candidate.js.map +1 -1
- package/dist/infrastructure/git/lock-recovery.d.ts +3 -0
- package/dist/infrastructure/git/lock-recovery.js +58 -0
- package/dist/infrastructure/git/lock-recovery.js.map +1 -0
- package/dist/infrastructure/git/lock-recovery.types.d.ts +11 -0
- package/dist/infrastructure/git/lock-recovery.types.js +2 -0
- package/dist/infrastructure/git/lock-recovery.types.js.map +1 -0
- package/dist/infrastructure/git/managed-worktree.js +5 -8
- package/dist/infrastructure/git/managed-worktree.js.map +1 -1
- package/dist/infrastructure/git/verification-command.js +35 -30
- package/dist/infrastructure/git/verification-command.js.map +1 -1
- package/dist/infrastructure/git/workspace-lease.js +12 -5
- package/dist/infrastructure/git/workspace-lease.js.map +1 -1
- package/dist/infrastructure/hook-configuration.constants.d.ts +1 -0
- package/dist/infrastructure/hook-configuration.constants.js +59 -0
- package/dist/infrastructure/hook-configuration.constants.js.map +1 -0
- package/dist/infrastructure/hook-configuration.d.ts +3 -0
- package/dist/infrastructure/hook-configuration.js +16 -0
- package/dist/infrastructure/hook-configuration.js.map +1 -0
- package/dist/infrastructure/hook-configuration.types.d.ts +14 -0
- package/dist/infrastructure/hook-configuration.types.js +2 -0
- package/dist/infrastructure/hook-configuration.types.js.map +1 -0
- package/dist/infrastructure/http-transport.constants.d.ts +1 -0
- package/dist/infrastructure/http-transport.constants.js +14 -0
- package/dist/infrastructure/http-transport.constants.js.map +1 -0
- package/dist/infrastructure/http-transport.d.ts +7 -0
- package/dist/infrastructure/http-transport.js +19 -0
- package/dist/infrastructure/http-transport.js.map +1 -0
- package/dist/infrastructure/input-failure.d.ts +4 -0
- package/dist/infrastructure/input-failure.js +14 -0
- package/dist/infrastructure/input-failure.js.map +1 -0
- package/dist/infrastructure/inspection-file.d.ts +1 -0
- package/dist/infrastructure/inspection-file.js +8 -2
- package/dist/infrastructure/inspection-file.js.map +1 -1
- package/dist/infrastructure/local-transport.js +16 -11
- package/dist/infrastructure/local-transport.js.map +1 -1
- package/dist/infrastructure/process.constants.d.ts +1 -0
- package/dist/infrastructure/process.constants.js +1 -0
- package/dist/infrastructure/process.constants.js.map +1 -1
- package/dist/infrastructure/process.d.ts +1 -0
- package/dist/infrastructure/process.js +48 -8
- package/dist/infrastructure/process.js.map +1 -1
- package/dist/infrastructure/reservation-ledger.d.ts +2 -0
- package/dist/infrastructure/reservation-ledger.js +84 -0
- package/dist/infrastructure/reservation-ledger.js.map +1 -0
- package/dist/infrastructure/reservation-recovery.d.ts +4 -0
- package/dist/infrastructure/reservation-recovery.js +28 -0
- package/dist/infrastructure/reservation-recovery.js.map +1 -0
- package/dist/infrastructure/s3-tombstones.d.ts +3 -0
- package/dist/infrastructure/s3-tombstones.js +46 -0
- package/dist/infrastructure/s3-tombstones.js.map +1 -0
- package/dist/infrastructure/s3-tombstones.types.d.ts +6 -0
- package/dist/infrastructure/s3-tombstones.types.js +2 -0
- package/dist/infrastructure/s3-tombstones.types.js.map +1 -0
- package/dist/infrastructure/s3-transport.d.ts +2 -0
- package/dist/infrastructure/s3-transport.js +4 -2
- package/dist/infrastructure/s3-transport.js.map +1 -1
- package/dist/infrastructure/sse.js +6 -3
- package/dist/infrastructure/sse.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq-connection.d.ts +3 -0
- package/dist/infrastructure/task-queue-bullmq-connection.js +32 -0
- package/dist/infrastructure/task-queue-bullmq-connection.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.d.ts +8 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.js +2 -0
- package/dist/infrastructure/task-queue-bullmq-connection.types.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq-resources.js +21 -15
- package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq-state.d.ts +1 -0
- package/dist/infrastructure/task-queue-bullmq-state.js +1 -0
- package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq.constants.d.ts +2 -1
- package/dist/infrastructure/task-queue-bullmq.constants.js +2 -1
- package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq.js +10 -1
- package/dist/infrastructure/task-queue-bullmq.js.map +1 -1
- package/dist/infrastructure/task-queue.js +13 -4
- package/dist/infrastructure/task-queue.js.map +1 -1
- package/dist/infrastructure/transfer-copy.d.ts +1 -0
- package/dist/infrastructure/transfer-copy.js +16 -1
- package/dist/infrastructure/transfer-copy.js.map +1 -1
- package/dist/infrastructure/transport-archive.d.ts +3 -3
- package/dist/infrastructure/transport-archive.js +71 -48
- package/dist/infrastructure/transport-archive.js.map +1 -1
- package/dist/infrastructure/transport-archive.types.d.ts +3 -0
- package/dist/infrastructure/transport-archive.types.js.map +1 -1
- package/dist/infrastructure/transport-checkpoint.js +14 -4
- package/dist/infrastructure/transport-checkpoint.js.map +1 -1
- package/dist/infrastructure/transport-envelope.d.ts +1 -0
- package/dist/infrastructure/transport-envelope.js +22 -5
- package/dist/infrastructure/transport-envelope.js.map +1 -1
- package/dist/infrastructure/transport-reservations.constants.d.ts +2 -0
- package/dist/infrastructure/transport-reservations.constants.js +3 -0
- package/dist/infrastructure/transport-reservations.constants.js.map +1 -0
- package/dist/infrastructure/transport-reservations.d.ts +1 -2
- package/dist/infrastructure/transport-reservations.js +18 -22
- package/dist/infrastructure/transport-reservations.js.map +1 -1
- package/dist/infrastructure/transport-reservations.types.d.ts +23 -2
- package/dist/infrastructure/transport-reservations.types.js.map +1 -1
- package/dist/infrastructure/trigger-server.js +12 -9
- package/dist/infrastructure/trigger-server.js.map +1 -1
- package/dist/infrastructure/trigger-server.types.d.ts +1 -1
- package/dist/infrastructure/trigger-server.types.js.map +1 -1
- package/dist/infrastructure/workflow-checkpoint.constants.d.ts +1 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js +1 -0
- package/dist/infrastructure/workflow-checkpoint.constants.js.map +1 -1
- package/dist/infrastructure/workspace-lock.constants.d.ts +2 -1
- package/dist/infrastructure/workspace-lock.constants.js +2 -1
- package/dist/infrastructure/workspace-lock.constants.js.map +1 -1
- package/dist/infrastructure/workspace-lock.js +48 -10
- package/dist/infrastructure/workspace-lock.js.map +1 -1
- package/dist/providers/cloud-cache.js +14 -9
- package/dist/providers/cloud-cache.js.map +1 -1
- package/dist/providers/cloud-cache.types.d.ts +1 -0
- package/dist/providers/cloud-cache.types.js.map +1 -1
- package/dist/providers/cloud-files.js +3 -4
- package/dist/providers/cloud-files.js.map +1 -1
- package/dist/providers/cloud-input.constants.d.ts +2 -2
- package/dist/providers/cloud-input.constants.js +2 -1
- package/dist/providers/cloud-input.constants.js.map +1 -1
- package/dist/providers/cloud-input.d.ts +2 -2
- package/dist/providers/cloud-input.js +14 -3
- package/dist/providers/cloud-input.js.map +1 -1
- package/dist/providers/container-command.js +30 -5
- package/dist/providers/container-command.js.map +1 -1
- package/dist/providers/container-egress.constants.d.ts +7 -0
- package/dist/providers/container-egress.constants.js +8 -0
- package/dist/providers/container-egress.constants.js.map +1 -0
- package/dist/providers/container-egress.d.ts +7 -0
- package/dist/providers/container-egress.js +101 -0
- package/dist/providers/container-egress.js.map +1 -0
- package/dist/providers/container-files.js +10 -11
- package/dist/providers/container-files.js.map +1 -1
- package/dist/providers/container-mounts.js +3 -0
- package/dist/providers/container-mounts.js.map +1 -1
- package/dist/providers/container-network.js +16 -1
- package/dist/providers/container-network.js.map +1 -1
- package/dist/providers/container-plan.js +7 -10
- package/dist/providers/container-plan.js.map +1 -1
- package/dist/providers/container-recovery.js +8 -3
- package/dist/providers/container-recovery.js.map +1 -1
- package/dist/providers/container.js +36 -5
- package/dist/providers/container.js.map +1 -1
- package/dist/providers/container.types.d.ts +1 -0
- package/dist/providers/container.types.js.map +1 -1
- package/dist/providers/daytona-command.constants.js +4 -1
- package/dist/providers/daytona-command.constants.js.map +1 -1
- package/dist/providers/daytona-command.js +39 -16
- package/dist/providers/daytona-command.js.map +1 -1
- package/dist/providers/daytona-files.js +5 -5
- package/dist/providers/daytona-files.js.map +1 -1
- package/dist/providers/daytona-output.js +11 -3
- package/dist/providers/daytona-output.js.map +1 -1
- package/dist/providers/daytona.js +11 -9
- package/dist/providers/daytona.js.map +1 -1
- package/dist/providers/egress/address.d.ts +3 -0
- package/dist/providers/egress/address.js +26 -0
- package/dist/providers/egress/address.js.map +1 -0
- package/dist/providers/egress/egress.constants.d.ts +22 -0
- package/dist/providers/egress/egress.constants.js +23 -0
- package/dist/providers/egress/egress.constants.js.map +1 -0
- package/dist/providers/egress/egress.types.d.ts +11 -0
- package/dist/providers/egress/egress.types.js +2 -0
- package/dist/providers/egress/egress.types.js.map +1 -0
- package/dist/providers/egress/firewall.d.ts +3 -0
- package/dist/providers/egress/firewall.js +29 -0
- package/dist/providers/egress/firewall.js.map +1 -0
- package/dist/providers/egress/main.d.ts +1 -0
- package/dist/providers/egress/main.js +90 -0
- package/dist/providers/egress/main.js.map +1 -0
- package/dist/providers/egress/policy.d.ts +5 -0
- package/dist/providers/egress/policy.js +54 -0
- package/dist/providers/egress/policy.js.map +1 -0
- package/dist/providers/egress/proxy.d.ts +2 -0
- package/dist/providers/egress/proxy.js +129 -0
- package/dist/providers/egress/proxy.js.map +1 -0
- package/dist/providers/egress/tls-hello.d.ts +1 -0
- package/dist/providers/egress/tls-hello.js +75 -0
- package/dist/providers/egress/tls-hello.js.map +1 -0
- package/dist/providers/firecracker-command.js +12 -6
- package/dist/providers/firecracker-command.js.map +1 -1
- package/dist/providers/local-files.d.ts +2 -0
- package/dist/providers/local-files.js +24 -0
- package/dist/providers/local-files.js.map +1 -0
- package/dist/providers/local.js +2 -20
- package/dist/providers/local.js.map +1 -1
- package/dist/providers/vercel-command.js +33 -9
- package/dist/providers/vercel-command.js.map +1 -1
- package/dist/providers/vercel.js +11 -9
- package/dist/providers/vercel.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/recipe-configuration.schema.json +2164 -35
- package/recipe.schema.json +2512 -404
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-queue-bullmq.constants.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"task-queue-bullmq.constants.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuEhC,CAAC","sourcesContent":["export const bullMQDefaultPrefix = \"outpost\";\nexport const bullMQDefaultStalledIntervalMs = 1000;\nexport const bullMQClaimBatchSize = 100;\nexport const bullMQConnectionTimeoutMs = 10_000;\nexport const bullMQConnectionRetries = 3;\nexport const bullMQRetryDelayMs = 100;\nexport const bullMQMaxRetryDelayMs = 1000;\n\nexport const bullMQStateScript = `\nlocal operation = ARGV[1]\nlocal request = redis.call('HGET', KEYS[1], 'request')\nif operation == 'enqueue' then\n if request and request ~= ARGV[2] and request ~= ARGV[4] then\n return redis.error_reply('Queue identity already has a different request')\n end\n if not request then\n request = ARGV[2]\n redis.call('HSET', KEYS[1], 'request', request, 'deadline', ARGV[3], 'meta', '{\"status\":\"pending\",\"fence\":0}')\n end\nend\nif not request then\n if operation == 'get' then return nil end\n return redis.error_reply('Queue job does not exist')\nend\nlocal meta = cjson.decode(redis.call('HGET', KEYS[1], 'meta'))\nlocal clock = redis.call('TIME')\nlocal now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)\nlocal deadline = tonumber(redis.call('HGET', KEYS[1], 'deadline'))\nlocal function live()\n return meta.status == 'pending' or meta.status == 'active'\nend\nlocal function save()\n redis.call('HSET', KEYS[1], 'meta', cjson.encode(meta))\nend\nif live() and deadline and deadline <= now then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\nend\nlocal function owned()\n return meta.status == 'active' and meta.worker == ARGV[2]\n and meta.fence == tonumber(ARGV[3]) and meta.expires > now\n and redis.call('GET', KEYS[2]) == meta.token\nend\nlocal function extend(duration)\n meta.expires = now + duration\n if deadline then meta.expires = math.min(meta.expires, deadline) end\n redis.call('PEXPIRE', KEYS[2], meta.expires - now)\n redis.call('SREM', KEYS[3], ARGV[5])\nend\nif operation == 'claim' and live() then\n if redis.call('GET', KEYS[2]) ~= ARGV[3] then\n return redis.error_reply('Stale queue lease')\n end\n meta.status = 'active'\n meta.worker = ARGV[2]\n meta.token = ARGV[3]\n meta.fence = meta.fence + 1\n extend(tonumber(ARGV[4]))\n save()\nend\nif operation == 'renew' or operation == 'complete' then\n if not owned() then return redis.error_reply('Stale queue lease') end\n if operation == 'renew' then extend(tonumber(ARGV[4])) end\n if operation == 'complete' then\n meta.status = ARGV[5]\n redis.call('HSET', KEYS[1], 'result', ARGV[4])\n end\n save()\nend\nif operation == 'cancel' then\n if meta.fence ~= tonumber(ARGV[2]) then return redis.error_reply('Stale queue fence') end\n if live() then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\n end\nend\nreturn {request, cjson.encode(meta), redis.call('HGET', KEYS[1], 'result') or ''}\n`;\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalJson } from "../domain/workflow/canonical-json.js";
|
|
2
|
+
import { queueJson } from "../domain/task-queue.js";
|
|
1
3
|
import { randomUUID } from "node:crypto";
|
|
2
4
|
import { openBullMQResources } from "./task-queue-bullmq-resources.js";
|
|
3
5
|
import { queueClaim, queueLease, queueLeaseMs, queueNumber, queueRequest, queueResult, queueString, } from "../domain/task-queue.js";
|
|
@@ -51,9 +53,16 @@ export async function createBullMQTaskQueue(options) {
|
|
|
51
53
|
enqueue(input) {
|
|
52
54
|
return perform(async () => {
|
|
53
55
|
const request = queueRequest(input);
|
|
56
|
+
const encoded = canonicalJson(queueJson(request));
|
|
57
|
+
const existing = await state(request.id, "get");
|
|
58
|
+
const compatible = existing &&
|
|
59
|
+
canonicalJson(queueJson(queueRequest(existing.job))) === encoded
|
|
60
|
+
? existing.request
|
|
61
|
+
: "";
|
|
54
62
|
await state(request.id, "enqueue", [
|
|
55
|
-
|
|
63
|
+
encoded,
|
|
56
64
|
request.deadline ?? "",
|
|
65
|
+
compatible,
|
|
57
66
|
]);
|
|
58
67
|
const { queue } = await handlerQueue(request.handler);
|
|
59
68
|
await queue.add(request.handler, request, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-queue-bullmq.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { openBullMQResources } from \"./task-queue-bullmq-resources.ts\";\nimport {\n queueClaim,\n queueLease,\n queueLeaseMs,\n queueNumber,\n queueRequest,\n queueResult,\n queueString,\n} from \"../domain/task-queue.ts\";\nimport type { QueueJob, QueueLease } from \"../domain/task-queue.types.ts\";\nimport type {\n BullMQTaskQueueOptions,\n BullMQTaskQueue,\n BullMQJob,\n} from \"./task-queue-bullmq.types.ts\";\nimport { bullMQKey, bullMQState } from \"./task-queue-bullmq-state.ts\";\nimport {\n bullMQClaimBatchSize,\n bullMQStateScript,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport type {\n BullMQTaskQueueOptions,\n BullMQTaskQueue,\n} from \"./task-queue-bullmq.types.ts\";\n\nexport async function createBullMQTaskQueue(\n options: BullMQTaskQueueOptions,\n): Promise<BullMQTaskQueue> {\n const resources = await openBullMQResources(options);\n const { root, prefix, nativeName, nativeKey, handlerQueue, perform, report } =\n resources;\n let cursor = 0;\n const client = await root.client;\n client.defineCommand(\"outpostQueueStateV1\", {\n numberOfKeys: 3,\n lua: bullMQStateScript,\n });\n async function state(\n id: string,\n operation: string,\n args: readonly (string | number)[] = [],\n handler = \"\",\n ) {\n return bullMQState(\n await client.runCommand(\"outpostQueueStateV1\", [\n `${root.qualifiedName}:outpost:${bullMQKey(id)}`,\n `${nativeKey(handler, id)}:lock`,\n `${prefix}:${nativeName(handler)}:stalled`,\n operation,\n ...args,\n ]),\n );\n }\n async function required(id: string) {\n const current = await state(id, \"get\");\n if (!current) throw new Error(\"Queue job does not exist\");\n return current;\n }\n async function settle(native: BullMQJob, job: QueueJob, token: string) {\n try {\n if (job.status === \"done\") {\n await native.moveToCompleted(job.result!, token, false);\n return;\n }\n await native.moveToFailed(\n new Error(job.result?.error ?? \"Queue job cancelled\"),\n token,\n false,\n );\n } catch (error) {\n report(error);\n }\n }\n async function ownedOperation(\n input: QueueLease,\n operation: \"renew\" | \"complete\",\n value: string | number,\n extra: string,\n ) {\n const lease = queueLease(input);\n const previous = await required(lease.id);\n const current = await state(\n lease.id,\n operation,\n [lease.worker, lease.fence, value, extra],\n previous.job.handler,\n );\n if (!current) throw new Error(\"Queue job does not exist\");\n return current;\n }\n return {\n enqueue(input) {\n return perform(async () => {\n const request = queueRequest(input);\n await state(request.id, \"enqueue\", [\n JSON.stringify(request),\n request.deadline ?? \"\",\n ]);\n const { queue } = await handlerQueue(request.handler);\n await queue.add(request.handler, request, {\n jobId: bullMQKey(request.id),\n removeOnComplete: false,\n removeOnFail: false,\n });\n return (await required(request.id)).job;\n });\n },\n get(id) {\n return perform(async () => (await state(queueString(id), \"get\"))?.job);\n },\n claim(input) {\n return perform(async () => {\n const claim = queueClaim(input);\n const eligible = [...new Set(claim.handlers)];\n const start = cursor++ % eligible.length;\n for (let index = 0; index < eligible.length; index++) {\n const handler = eligible[(start + index) % eligible.length]!;\n const { worker } = await handlerQueue(handler);\n for (let attempt = 0; attempt < bullMQClaimBatchSize; attempt++) {\n const token = randomUUID();\n const native = await worker.getNextJob(token, { block: false });\n if (!native) break;\n const request = queueRequest(native.data);\n const current = await state(\n request.id,\n \"claim\",\n [claim.worker, token, claim.leaseMs, native.id!],\n handler,\n );\n if (!current) throw new Error(\"Queue job does not exist\");\n if (current.job.status === \"active\") return current.job;\n await settle(native, current.job, token);\n }\n }\n return undefined;\n });\n },\n renew(lease, leaseMs) {\n return perform(async () => {\n return (\n await ownedOperation(\n lease,\n \"renew\",\n queueLeaseMs(leaseMs),\n bullMQKey(lease.id),\n )\n ).job;\n });\n },\n complete(lease, input) {\n return perform(async () => {\n const result = queueResult(input);\n const current = await ownedOperation(\n lease,\n \"complete\",\n JSON.stringify(result),\n result.error === undefined ? \"done\" : \"failed\",\n );\n const { queue } = await handlerQueue(current.job.handler);\n const native = await queue.getJob(bullMQKey(lease.id));\n if (native && current.token)\n await settle(native, current.job, current.token);\n return current.job;\n });\n },\n cancel(id, fence) {\n return perform(async () => {\n const current = await state(queueString(id), \"cancel\", [\n queueNumber(fence),\n ]);\n if (!current) throw new Error(\"Queue job does not exist\");\n return current.job;\n });\n },\n close: resources.close,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"task-queue-bullmq.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq.ts"],"names":[],"mappings":"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","sourcesContent":["import { canonicalJson } from \"../domain/workflow/canonical-json.ts\";\nimport { queueJson } from \"../domain/task-queue.ts\";\nimport { randomUUID } from \"node:crypto\";\nimport { openBullMQResources } from \"./task-queue-bullmq-resources.ts\";\nimport {\n queueClaim,\n queueLease,\n queueLeaseMs,\n queueNumber,\n queueRequest,\n queueResult,\n queueString,\n} from \"../domain/task-queue.ts\";\nimport type { QueueJob, QueueLease } from \"../domain/task-queue.types.ts\";\nimport type {\n BullMQTaskQueueOptions,\n BullMQTaskQueue,\n BullMQJob,\n} from \"./task-queue-bullmq.types.ts\";\nimport { bullMQKey, bullMQState } from \"./task-queue-bullmq-state.ts\";\nimport {\n bullMQClaimBatchSize,\n bullMQStateScript,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport type {\n BullMQTaskQueueOptions,\n BullMQTaskQueue,\n} from \"./task-queue-bullmq.types.ts\";\n\nexport async function createBullMQTaskQueue(\n options: BullMQTaskQueueOptions,\n): Promise<BullMQTaskQueue> {\n const resources = await openBullMQResources(options);\n const { root, prefix, nativeName, nativeKey, handlerQueue, perform, report } =\n resources;\n let cursor = 0;\n const client = await root.client;\n client.defineCommand(\"outpostQueueStateV1\", {\n numberOfKeys: 3,\n lua: bullMQStateScript,\n });\n async function state(\n id: string,\n operation: string,\n args: readonly (string | number)[] = [],\n handler = \"\",\n ) {\n return bullMQState(\n await client.runCommand(\"outpostQueueStateV1\", [\n `${root.qualifiedName}:outpost:${bullMQKey(id)}`,\n `${nativeKey(handler, id)}:lock`,\n `${prefix}:${nativeName(handler)}:stalled`,\n operation,\n ...args,\n ]),\n );\n }\n async function required(id: string) {\n const current = await state(id, \"get\");\n if (!current) throw new Error(\"Queue job does not exist\");\n return current;\n }\n async function settle(native: BullMQJob, job: QueueJob, token: string) {\n try {\n if (job.status === \"done\") {\n await native.moveToCompleted(job.result!, token, false);\n return;\n }\n await native.moveToFailed(\n new Error(job.result?.error ?? \"Queue job cancelled\"),\n token,\n false,\n );\n } catch (error) {\n report(error);\n }\n }\n async function ownedOperation(\n input: QueueLease,\n operation: \"renew\" | \"complete\",\n value: string | number,\n extra: string,\n ) {\n const lease = queueLease(input);\n const previous = await required(lease.id);\n const current = await state(\n lease.id,\n operation,\n [lease.worker, lease.fence, value, extra],\n previous.job.handler,\n );\n if (!current) throw new Error(\"Queue job does not exist\");\n return current;\n }\n return {\n enqueue(input) {\n return perform(async () => {\n const request = queueRequest(input);\n const encoded = canonicalJson(queueJson(request));\n const existing = await state(request.id, \"get\");\n const compatible =\n existing &&\n canonicalJson(queueJson(queueRequest(existing.job))) === encoded\n ? existing.request\n : \"\";\n await state(request.id, \"enqueue\", [\n encoded,\n request.deadline ?? \"\",\n compatible,\n ]);\n const { queue } = await handlerQueue(request.handler);\n await queue.add(request.handler, request, {\n jobId: bullMQKey(request.id),\n removeOnComplete: false,\n removeOnFail: false,\n });\n return (await required(request.id)).job;\n });\n },\n get(id) {\n return perform(async () => (await state(queueString(id), \"get\"))?.job);\n },\n claim(input) {\n return perform(async () => {\n const claim = queueClaim(input);\n const eligible = [...new Set(claim.handlers)];\n const start = cursor++ % eligible.length;\n for (let index = 0; index < eligible.length; index++) {\n const handler = eligible[(start + index) % eligible.length]!;\n const { worker } = await handlerQueue(handler);\n for (let attempt = 0; attempt < bullMQClaimBatchSize; attempt++) {\n const token = randomUUID();\n const native = await worker.getNextJob(token, { block: false });\n if (!native) break;\n const request = queueRequest(native.data);\n const current = await state(\n request.id,\n \"claim\",\n [claim.worker, token, claim.leaseMs, native.id!],\n handler,\n );\n if (!current) throw new Error(\"Queue job does not exist\");\n if (current.job.status === \"active\") return current.job;\n await settle(native, current.job, token);\n }\n }\n return undefined;\n });\n },\n renew(lease, leaseMs) {\n return perform(async () => {\n return (\n await ownedOperation(\n lease,\n \"renew\",\n queueLeaseMs(leaseMs),\n bullMQKey(lease.id),\n )\n ).job;\n });\n },\n complete(lease, input) {\n return perform(async () => {\n const result = queueResult(input);\n const current = await ownedOperation(\n lease,\n \"complete\",\n JSON.stringify(result),\n result.error === undefined ? \"done\" : \"failed\",\n );\n const { queue } = await handlerQueue(current.job.handler);\n const native = await queue.getJob(bullMQKey(lease.id));\n if (native && current.token)\n await settle(native, current.job, current.token);\n return current.job;\n });\n },\n cancel(id, fence) {\n return perform(async () => {\n const current = await state(queueString(id), \"cancel\", [\n queueNumber(fence),\n ]);\n if (!current) throw new Error(\"Queue job does not exist\");\n return current.job;\n });\n },\n close: resources.close,\n };\n}\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalJson } from "../domain/workflow/canonical-json.js";
|
|
2
|
+
import { queueJson } from "../domain/task-queue.js";
|
|
1
3
|
import { mkdirSync, chmodSync } from "node:fs";
|
|
2
4
|
import { dirname, resolve } from "node:path";
|
|
3
5
|
import { queueClaim, queueJob, queueLease, queueLeaseMs, queueNumber, queueRequest, queueResult, queueString, } from "../domain/task-queue.js";
|
|
@@ -7,7 +9,7 @@ export async function createSqliteTaskQueue(path) {
|
|
|
7
9
|
mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
|
|
8
10
|
const db = new DatabaseSync(file);
|
|
9
11
|
chmodSync(file, 0o600);
|
|
10
|
-
db.exec("PRAGMA busy_timeout=5000; PRAGMA synchronous=FULL; CREATE TABLE IF NOT EXISTS queue_jobs (id TEXT PRIMARY KEY, body TEXT NOT NULL, status TEXT NOT NULL, handler TEXT NOT NULL, expires INTEGER, deadline INTEGER); CREATE INDEX IF NOT EXISTS queue_eligible ON queue_jobs(status, handler, expires, deadline, id)");
|
|
12
|
+
db.exec("PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; CREATE TABLE IF NOT EXISTS queue_jobs (id TEXT PRIMARY KEY, body TEXT NOT NULL, status TEXT NOT NULL, handler TEXT NOT NULL, expires INTEGER, deadline INTEGER); CREATE INDEX IF NOT EXISTS queue_eligible ON queue_jobs(status, handler, expires, deadline, id)");
|
|
11
13
|
function read(id) {
|
|
12
14
|
const row = db.prepare("SELECT body FROM queue_jobs WHERE id = ?").get(id);
|
|
13
15
|
return row ? queueJob(JSON.parse(String(row.body))) : undefined;
|
|
@@ -57,7 +59,8 @@ export async function createSqliteTaskQueue(path) {
|
|
|
57
59
|
const request = queueRequest(input);
|
|
58
60
|
const existing = read(request.id);
|
|
59
61
|
if (existing) {
|
|
60
|
-
if (
|
|
62
|
+
if (canonicalJson(queueJson(queueRequest(existing))) !==
|
|
63
|
+
canonicalJson(queueJson(request)))
|
|
61
64
|
throw new Error("Queue identity already has a different request");
|
|
62
65
|
return expire(existing);
|
|
63
66
|
}
|
|
@@ -65,9 +68,15 @@ export async function createSqliteTaskQueue(path) {
|
|
|
65
68
|
});
|
|
66
69
|
},
|
|
67
70
|
async get(id) {
|
|
71
|
+
const job = read(queueString(id));
|
|
72
|
+
if (!job ||
|
|
73
|
+
!["pending", "active"].includes(job.status) ||
|
|
74
|
+
job.deadline === undefined ||
|
|
75
|
+
job.deadline > Date.now())
|
|
76
|
+
return job;
|
|
68
77
|
return transaction(() => {
|
|
69
|
-
const
|
|
70
|
-
return
|
|
78
|
+
const current = read(queueString(id));
|
|
79
|
+
return current ? expire(current) : undefined;
|
|
71
80
|
});
|
|
72
81
|
},
|
|
73
82
|
async claim(input) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-queue.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, chmodSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport {\n queueClaim,\n queueJob,\n queueLease,\n queueLeaseMs,\n queueNumber,\n queueRequest,\n queueResult,\n queueString,\n} from \"../domain/task-queue.ts\";\nimport type {\n DurableTaskQueue,\n QueueJob,\n QueueLease,\n} from \"../domain/task-queue.types.ts\";\n\nexport async function createSqliteTaskQueue(\n path: string,\n): Promise<DurableTaskQueue> {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const file = resolve(path);\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const db = new DatabaseSync(file);\n chmodSync(file, 0o600);\n db.exec(\n \"PRAGMA busy_timeout=5000; PRAGMA synchronous=FULL; CREATE TABLE IF NOT EXISTS queue_jobs (id TEXT PRIMARY KEY, body TEXT NOT NULL, status TEXT NOT NULL, handler TEXT NOT NULL, expires INTEGER, deadline INTEGER); CREATE INDEX IF NOT EXISTS queue_eligible ON queue_jobs(status, handler, expires, deadline, id)\",\n );\n function read(id: string): QueueJob | undefined {\n const row = db.prepare(\"SELECT body FROM queue_jobs WHERE id = ?\").get(id);\n return row ? queueJob(JSON.parse(String(row.body))) : undefined;\n }\n function save(job: QueueJob): QueueJob {\n db.prepare(\n \"INSERT INTO queue_jobs(id, body, status, handler, expires, deadline) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET body=excluded.body, status=excluded.status, handler=excluded.handler, expires=excluded.expires, deadline=excluded.deadline\",\n ).run(\n job.id,\n JSON.stringify(job),\n job.status,\n job.handler,\n job.expires ?? null,\n job.deadline ?? null,\n );\n return job;\n }\n function transaction<T>(operation: () => T): T {\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n const result = operation();\n db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n function expire(job: QueueJob): QueueJob {\n if (\n (job.status === \"pending\" || job.status === \"active\") &&\n job.deadline !== undefined &&\n job.deadline <= Date.now()\n )\n return save({ ...job, status: \"cancelled\", fence: job.fence + 1 });\n return job;\n }\n function required(id: string): QueueJob {\n const job = read(queueString(id));\n if (!job) throw new Error(\"Queue job does not exist\");\n return expire(job);\n }\n function owned(input: QueueLease): QueueJob {\n const lease = queueLease(input);\n const job = required(lease.id);\n if (\n job.status !== \"active\" ||\n job.worker !== lease.worker ||\n job.fence !== lease.fence ||\n (job.expires ?? 0) <= Date.now()\n )\n throw new Error(\"Stale queue lease\");\n return job;\n }\n return {\n async enqueue(input) {\n return transaction(() => {\n const request = queueRequest(input);\n const existing = read(request.id);\n if (existing) {\n if (\n JSON.stringify(queueRequest(existing)) !== JSON.stringify(request)\n )\n throw new Error(\"Queue identity already has a different request\");\n return expire(existing);\n }\n return expire(save({ ...request, status: \"pending\", fence: 0 }));\n });\n },\n async get(id) {\n return transaction(() => {\n const job = read(queueString(id));\n return job ? expire(job) : undefined;\n });\n },\n async claim(input) {\n return transaction(() => {\n const claim = queueClaim(input);\n const now = Date.now();\n const placeholders = claim.handlers.map(() => \"?\").join(\",\");\n const selected = db\n .prepare(\n `SELECT id FROM queue_jobs INDEXED BY queue_eligible\n WHERE status IN ('pending', 'active') AND handler IN (${placeholders})\n AND (status = 'pending' OR expires <= ?)\n AND (deadline IS NULL OR deadline > ?)\n ORDER BY rowid LIMIT 1`,\n )\n .get(...claim.handlers, now, now);\n if (!selected) return undefined;\n const job = required(String(selected.id));\n if (job.status === \"cancelled\") return undefined;\n return save({\n ...job,\n status: \"active\",\n worker: claim.worker,\n fence: job.fence + 1,\n expires: Math.min(now + claim.leaseMs, job.deadline ?? Infinity),\n });\n });\n },\n async renew(lease, ms) {\n return transaction(() => {\n const job = owned(lease);\n return save({\n ...job,\n expires: Math.min(\n Date.now() + queueLeaseMs(ms),\n job.deadline ?? Infinity,\n ),\n });\n });\n },\n async complete(lease, input) {\n return transaction(() => {\n const job = owned(lease);\n const result = queueResult(input);\n return save({\n ...job,\n status: result.error === undefined ? \"done\" : \"failed\",\n result,\n });\n });\n },\n async cancel(id, fence) {\n return transaction(() => {\n const job = required(id);\n if (job.fence !== queueNumber(fence))\n throw new Error(\"Stale queue fence\");\n if (job.status !== \"pending\" && job.status !== \"active\") return job;\n return save({ ...job, status: \"cancelled\", fence: job.fence + 1 });\n });\n },\n close() {\n db.close();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"task-queue.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { canonicalJson } from \"../domain/workflow/canonical-json.ts\";\nimport { queueJson } from \"../domain/task-queue.ts\";\nimport { mkdirSync, chmodSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport {\n queueClaim,\n queueJob,\n queueLease,\n queueLeaseMs,\n queueNumber,\n queueRequest,\n queueResult,\n queueString,\n} from \"../domain/task-queue.ts\";\nimport type {\n DurableTaskQueue,\n QueueJob,\n QueueLease,\n} from \"../domain/task-queue.types.ts\";\n\nexport async function createSqliteTaskQueue(\n path: string,\n): Promise<DurableTaskQueue> {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const file = resolve(path);\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const db = new DatabaseSync(file);\n chmodSync(file, 0o600);\n db.exec(\n \"PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; CREATE TABLE IF NOT EXISTS queue_jobs (id TEXT PRIMARY KEY, body TEXT NOT NULL, status TEXT NOT NULL, handler TEXT NOT NULL, expires INTEGER, deadline INTEGER); CREATE INDEX IF NOT EXISTS queue_eligible ON queue_jobs(status, handler, expires, deadline, id)\",\n );\n function read(id: string): QueueJob | undefined {\n const row = db.prepare(\"SELECT body FROM queue_jobs WHERE id = ?\").get(id);\n return row ? queueJob(JSON.parse(String(row.body))) : undefined;\n }\n function save(job: QueueJob): QueueJob {\n db.prepare(\n \"INSERT INTO queue_jobs(id, body, status, handler, expires, deadline) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET body=excluded.body, status=excluded.status, handler=excluded.handler, expires=excluded.expires, deadline=excluded.deadline\",\n ).run(\n job.id,\n JSON.stringify(job),\n job.status,\n job.handler,\n job.expires ?? null,\n job.deadline ?? null,\n );\n return job;\n }\n function transaction<T>(operation: () => T): T {\n db.exec(\"BEGIN IMMEDIATE\");\n try {\n const result = operation();\n db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n function expire(job: QueueJob): QueueJob {\n if (\n (job.status === \"pending\" || job.status === \"active\") &&\n job.deadline !== undefined &&\n job.deadline <= Date.now()\n )\n return save({ ...job, status: \"cancelled\", fence: job.fence + 1 });\n return job;\n }\n function required(id: string): QueueJob {\n const job = read(queueString(id));\n if (!job) throw new Error(\"Queue job does not exist\");\n return expire(job);\n }\n function owned(input: QueueLease): QueueJob {\n const lease = queueLease(input);\n const job = required(lease.id);\n if (\n job.status !== \"active\" ||\n job.worker !== lease.worker ||\n job.fence !== lease.fence ||\n (job.expires ?? 0) <= Date.now()\n )\n throw new Error(\"Stale queue lease\");\n return job;\n }\n return {\n async enqueue(input) {\n return transaction(() => {\n const request = queueRequest(input);\n const existing = read(request.id);\n if (existing) {\n if (\n canonicalJson(queueJson(queueRequest(existing))) !==\n canonicalJson(queueJson(request))\n )\n throw new Error(\"Queue identity already has a different request\");\n return expire(existing);\n }\n return expire(save({ ...request, status: \"pending\", fence: 0 }));\n });\n },\n async get(id) {\n const job = read(queueString(id));\n if (\n !job ||\n ![\"pending\", \"active\"].includes(job.status) ||\n job.deadline === undefined ||\n job.deadline > Date.now()\n )\n return job;\n return transaction(() => {\n const current = read(queueString(id));\n return current ? expire(current) : undefined;\n });\n },\n async claim(input) {\n return transaction(() => {\n const claim = queueClaim(input);\n const now = Date.now();\n const placeholders = claim.handlers.map(() => \"?\").join(\",\");\n const selected = db\n .prepare(\n `SELECT id FROM queue_jobs INDEXED BY queue_eligible\n WHERE status IN ('pending', 'active') AND handler IN (${placeholders})\n AND (status = 'pending' OR expires <= ?)\n AND (deadline IS NULL OR deadline > ?)\n ORDER BY rowid LIMIT 1`,\n )\n .get(...claim.handlers, now, now);\n if (!selected) return undefined;\n const job = required(String(selected.id));\n if (job.status === \"cancelled\") return undefined;\n return save({\n ...job,\n status: \"active\",\n worker: claim.worker,\n fence: job.fence + 1,\n expires: Math.min(now + claim.leaseMs, job.deadline ?? Infinity),\n });\n });\n },\n async renew(lease, ms) {\n return transaction(() => {\n const job = owned(lease);\n return save({\n ...job,\n expires: Math.min(\n Date.now() + queueLeaseMs(ms),\n job.deadline ?? Infinity,\n ),\n });\n });\n },\n async complete(lease, input) {\n return transaction(() => {\n const job = owned(lease);\n const result = queueResult(input);\n return save({\n ...job,\n status: result.error === undefined ? \"done\" : \"failed\",\n result,\n });\n });\n },\n async cancel(id, fence) {\n return transaction(() => {\n const job = required(id);\n if (job.fence !== queueNumber(fence))\n throw new Error(\"Stale queue fence\");\n if (job.status !== \"pending\" && job.status !== \"active\") return job;\n return save({ ...job, status: \"cancelled\", fence: job.fence + 1 });\n });\n },\n close() {\n db.close();\n },\n };\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare function transferDestination(destination: string): Promise<void>;
|
|
2
|
+
export declare function transferTarget(entry: string, destination: string, contents: boolean): Promise<string>;
|
|
2
3
|
export declare function copyTransfer(source: string, destination: string, signal: AbortSignal): Promise<void>;
|
|
3
4
|
export declare function removeTransferDirectory(root: string): Promise<void>;
|
|
@@ -9,10 +9,25 @@ export async function transferDestination(destination) {
|
|
|
9
9
|
return undefined;
|
|
10
10
|
throw cause;
|
|
11
11
|
});
|
|
12
|
-
if (info?.isSymbolicLink()
|
|
12
|
+
if (info?.isSymbolicLink() &&
|
|
13
|
+
!(process.platform === "darwin" &&
|
|
14
|
+
info.uid === 0 &&
|
|
15
|
+
["/var", "/tmp", "/etc"].includes(path) &&
|
|
16
|
+
path !== absolute))
|
|
13
17
|
throw new OutpostError("provider", "Transfer destination traverses a symlink", { destination });
|
|
14
18
|
}
|
|
15
19
|
}
|
|
20
|
+
export async function transferTarget(entry, destination, contents) {
|
|
21
|
+
await transferDestination(destination);
|
|
22
|
+
const existing = await lstat(destination).catch((cause) => {
|
|
23
|
+
if (cause.code === "ENOENT")
|
|
24
|
+
return undefined;
|
|
25
|
+
throw cause;
|
|
26
|
+
});
|
|
27
|
+
return existing?.isDirectory() && !contents
|
|
28
|
+
? join(destination, entry)
|
|
29
|
+
: destination;
|
|
30
|
+
}
|
|
16
31
|
export async function copyTransfer(source, destination, signal) {
|
|
17
32
|
signal.throwIfAborted();
|
|
18
33
|
await transferDestination(destination);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-copy.js","sourceRoot":"","sources":["../../src/infrastructure/transfer-copy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,QAAQ,EACR,EAAE,EACF,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,GACR,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAAmB;IAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,KAAK,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAA4B,EAAE,EAAE;YACpE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC;YAC9C,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QACH,
|
|
1
|
+
{"version":3,"file":"transfer-copy.js","sourceRoot":"","sources":["../../src/infrastructure/transfer-copy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n chmod,\n copyFile,\n rm,\n lstat,\n mkdir,\n readdir,\n readlink,\n symlink,\n} from \"node:fs/promises\";\nimport { dirname, join, parse, resolve } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\n\nexport async function transferDestination(destination: string): Promise<void> {\n const absolute = resolve(destination);\n for (let path = absolute; path !== parse(path).root; path = dirname(path)) {\n const info = await lstat(path).catch((cause: NodeJS.ErrnoException) => {\n if (cause.code === \"ENOENT\") return undefined;\n throw cause;\n });\n if (\n info?.isSymbolicLink() &&\n !(\n process.platform === \"darwin\" &&\n info.uid === 0 &&\n [\"/var\", \"/tmp\", \"/etc\"].includes(path) &&\n path !== absolute\n )\n )\n throw new OutpostError(\n \"provider\",\n \"Transfer destination traverses a symlink\",\n { destination },\n );\n }\n}\n\nexport async function transferTarget(\n entry: string,\n destination: string,\n contents: boolean,\n): Promise<string> {\n await transferDestination(destination);\n const existing = await lstat(destination).catch(\n (cause: NodeJS.ErrnoException) => {\n if (cause.code === \"ENOENT\") return undefined;\n throw cause;\n },\n );\n return existing?.isDirectory() && !contents\n ? join(destination, entry)\n : destination;\n}\n\nexport async function copyTransfer(\n source: string,\n destination: string,\n signal: AbortSignal,\n): Promise<void> {\n signal.throwIfAborted();\n await transferDestination(destination);\n const info = await lstat(source);\n if (info.isDirectory()) {\n await mkdir(destination, { recursive: true });\n for (const name of await readdir(source))\n await copyTransfer(join(source, name), join(destination, name), signal);\n await chmod(destination, info.mode & 0o777);\n return;\n }\n await mkdir(dirname(destination), { recursive: true });\n if (info.isSymbolicLink()) {\n await symlink(await readlink(source), destination);\n return;\n }\n if (!info.isFile())\n throw new OutpostError(\"provider\", \"Unsupported transfer file type\", {\n source,\n });\n await copyFile(source, destination);\n await chmod(destination, info.mode & 0o777);\n}\n\nexport async function removeTransferDirectory(root: string): Promise<void> {\n async function writable(path: string): Promise<void> {\n const info = await lstat(path);\n if (!info.isDirectory() || info.isSymbolicLink()) return;\n await chmod(path, 0o700);\n for (const name of await readdir(path)) await writable(join(path, name));\n }\n await writable(root);\n await rm(root, { recursive: true, force: true });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Transport, TransportReference } from "../domain/transport.types.ts";
|
|
2
|
-
import type { ArchiveManifest } from "./transport-archive.types.ts";
|
|
3
|
-
export declare function archiveFiles(transporter: Transport, root: string, paths: readonly string[], prefix: string, maxBytes?: number): Promise<TransportReference>;
|
|
2
|
+
import type { ArchiveManifest, ArchivePathOptions } from "./transport-archive.types.ts";
|
|
3
|
+
export declare function archiveFiles(transporter: Transport, root: string, paths: readonly string[], prefix: string, maxBytes?: number, options?: ArchivePathOptions): Promise<TransportReference>;
|
|
4
4
|
export declare function archiveManifest(value: unknown): ArchiveManifest;
|
|
5
|
-
export declare function restoreArchiveFiles(transporter: Transport, reference: TransportReference, destination: string, maxBytes?: number): Promise<void>;
|
|
5
|
+
export declare function restoreArchiveFiles(transporter: Transport, reference: TransportReference, destination: string, maxBytes?: number, options?: ArchivePathOptions): Promise<void>;
|
|
@@ -3,63 +3,85 @@ import { lstat, open, readlink, mkdir, symlink, chmod } from "node:fs/promises";
|
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { inspectionFileFlags } from "./inspection-file.constants.js";
|
|
5
5
|
import { invariant, positive } from "../domain/errors.js";
|
|
6
|
-
import { safeDestination } from "./files.js";
|
|
6
|
+
import { fileDestination, safeDestination } from "./files.js";
|
|
7
7
|
import { jsonBytes, jsonObject, readReference, transportReference, } from "./transport-json.js";
|
|
8
8
|
import { archiveDefaults as defaults } from "./transport-archive.constants.js";
|
|
9
9
|
const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
10
|
-
export async function archiveFiles(transporter, root, paths, prefix, maxBytes = defaults.maxBytes) {
|
|
10
|
+
export async function archiveFiles(transporter, root, paths, prefix, maxBytes = defaults.maxBytes, options = {}) {
|
|
11
|
+
const destination = options.allowGitMetadata
|
|
12
|
+
? fileDestination
|
|
13
|
+
: safeDestination;
|
|
11
14
|
positive(maxBytes, "maxBytes");
|
|
12
15
|
invariant(paths.length <= defaults.maxFiles && new Set(paths).size === paths.length, "Invalid archive path count");
|
|
13
16
|
const files = [];
|
|
17
|
+
const published = [];
|
|
14
18
|
let total = 0;
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
19
|
+
try {
|
|
20
|
+
for (const path of paths) {
|
|
21
|
+
const source = await destination(root, path);
|
|
22
|
+
const info = await lstat(source);
|
|
23
|
+
invariant(info.isFile() || info.isSymbolicLink(), "Unsupported archive entry");
|
|
24
|
+
const chunks = [];
|
|
25
|
+
let size = 0;
|
|
26
|
+
const publish = async (bytes) => {
|
|
27
|
+
total += bytes.length;
|
|
28
|
+
size += bytes.length;
|
|
29
|
+
invariant(total <= maxBytes, "Archive exceeds byte limit");
|
|
30
|
+
const reference = await transporter.write(`${prefix}/files/${files.length}/${chunks.length}`, bytes, { ifRevision: null });
|
|
31
|
+
published.push(reference);
|
|
32
|
+
chunks.push({ reference, sha256: digest(bytes), size: bytes.length });
|
|
33
|
+
};
|
|
34
|
+
if (info.isSymbolicLink())
|
|
35
|
+
await publish(Buffer.from(await readlink(source)));
|
|
36
|
+
if (info.isFile()) {
|
|
37
|
+
const file = await open(source, inspectionFileFlags);
|
|
38
|
+
try {
|
|
39
|
+
const buffer = Buffer.alloc(defaults.chunkBytes);
|
|
40
|
+
for (;;) {
|
|
41
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, null);
|
|
42
|
+
if (!bytesRead)
|
|
43
|
+
break;
|
|
44
|
+
await publish(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
45
|
+
}
|
|
46
|
+
const after = await file.stat();
|
|
47
|
+
invariant(info.ino === after.ino &&
|
|
48
|
+
info.dev === after.dev &&
|
|
49
|
+
info.size === size &&
|
|
50
|
+
info.mtimeMs === after.mtimeMs &&
|
|
51
|
+
info.ctimeMs === after.ctimeMs, "Archive source changed");
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await file.close();
|
|
39
55
|
}
|
|
40
|
-
const after = await file.stat();
|
|
41
|
-
invariant(info.ino === after.ino &&
|
|
42
|
-
info.dev === after.dev &&
|
|
43
|
-
info.size === size &&
|
|
44
|
-
info.mtimeMs === after.mtimeMs &&
|
|
45
|
-
info.ctimeMs === after.ctimeMs, "Archive source changed");
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
await file.close();
|
|
49
56
|
}
|
|
57
|
+
files.push({
|
|
58
|
+
path,
|
|
59
|
+
kind: info.isSymbolicLink() ? "symlink" : "file",
|
|
60
|
+
mode: info.mode & 0o777,
|
|
61
|
+
size,
|
|
62
|
+
chunks,
|
|
63
|
+
});
|
|
50
64
|
}
|
|
51
|
-
files
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
chunks,
|
|
65
|
+
const manifest = { format: 1, files };
|
|
66
|
+
const bytes = jsonBytes(manifest);
|
|
67
|
+
invariant(bytes.length <= defaults.manifestBytes, "Archive manifest exceeds byte limit");
|
|
68
|
+
return await transporter.write(`${prefix}/manifest`, bytes, {
|
|
69
|
+
ifRevision: null,
|
|
57
70
|
});
|
|
58
71
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
catch (cause) {
|
|
73
|
+
// Preserve chunks if publication may have succeeded before its acknowledgement failed.
|
|
74
|
+
const manifest = await transporter
|
|
75
|
+
.read(`${prefix}/manifest`)
|
|
76
|
+
.catch(() => true);
|
|
77
|
+
if (!manifest) {
|
|
78
|
+
const cleanup = await Promise.allSettled(published.map((reference) => transporter.remove(reference.key, { ifRevision: reference.revision })));
|
|
79
|
+
const failures = cleanup.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
80
|
+
if (failures.length)
|
|
81
|
+
throw new AggregateError([cause, ...failures], "Archive publication and cleanup failed");
|
|
82
|
+
}
|
|
83
|
+
throw cause;
|
|
84
|
+
}
|
|
63
85
|
}
|
|
64
86
|
export function archiveManifest(value) {
|
|
65
87
|
invariant(value &&
|
|
@@ -117,16 +139,17 @@ export function archiveManifest(value) {
|
|
|
117
139
|
invariant(new Set(files.map((file) => file.path)).size === files.length, "Duplicate archive paths");
|
|
118
140
|
return { format: 1, files };
|
|
119
141
|
}
|
|
120
|
-
export async function restoreArchiveFiles(transporter, reference, destination, maxBytes = defaults.maxBytes) {
|
|
142
|
+
export async function restoreArchiveFiles(transporter, reference, destination, maxBytes = defaults.maxBytes, options = {}) {
|
|
143
|
+
const path = options.allowGitMetadata ? fileDestination : safeDestination;
|
|
121
144
|
positive(maxBytes, "maxBytes");
|
|
122
145
|
const manifest = archiveManifest(jsonObject(await readReference(transporter, reference, defaults.manifestBytes)));
|
|
123
146
|
const total = manifest.files.reduce((sum, file) => sum + file.size, 0);
|
|
124
147
|
invariant(Number.isSafeInteger(total) && total <= maxBytes, "Archive exceeds byte limit");
|
|
125
148
|
for (const file of manifest.files)
|
|
126
|
-
await
|
|
149
|
+
await path(destination, file.path);
|
|
127
150
|
await mkdir(destination, { mode: 0o700 });
|
|
128
151
|
for (const entry of manifest.files) {
|
|
129
|
-
const target = await
|
|
152
|
+
const target = await path(destination, entry.path);
|
|
130
153
|
await mkdir(dirname(target), { recursive: true, mode: 0o700 });
|
|
131
154
|
const data = async (chunk) => {
|
|
132
155
|
const value = await readReference(transporter, chunk.reference, chunk.size);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport-archive.js","sourceRoot":"","sources":["../../src/infrastructure/transport-archive.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { lstat, open, readlink, mkdir, symlink, chmod } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { inspectionFileFlags } from \"./inspection-file.constants.ts\";\nimport type {\n Transport,\n TransportReference,\n} from \"../domain/transport.types.ts\";\nimport { invariant, positive } from \"../domain/errors.ts\";\nimport { safeDestination } from \"./files.ts\";\nimport {\n jsonBytes,\n jsonObject,\n readReference,\n transportReference,\n} from \"./transport-json.ts\";\nimport type {\n ArchiveManifest,\n ArchiveFile,\n ArchiveChunk,\n} from \"./transport-archive.types.ts\";\nimport { archiveDefaults as defaults } from \"./transport-archive.constants.ts\";\n\nconst digest = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nexport async function archiveFiles(\n transporter: Transport,\n root: string,\n paths: readonly string[],\n prefix: string,\n maxBytes = defaults.maxBytes,\n): Promise<TransportReference> {\n positive(maxBytes, \"maxBytes\");\n invariant(\n paths.length <= defaults.maxFiles && new Set(paths).size === paths.length,\n \"Invalid archive path count\",\n );\n const files: ArchiveFile[] = [];\n let total = 0;\n for (const path of paths) {\n const source = await safeDestination(root, path);\n const info = await lstat(source);\n invariant(\n info.isFile() || info.isSymbolicLink(),\n \"Unsupported archive entry\",\n );\n const chunks: ArchiveChunk[] = [];\n let size = 0;\n const publish = async (bytes: Uint8Array) => {\n total += bytes.length;\n size += bytes.length;\n invariant(total <= maxBytes, \"Archive exceeds byte limit\");\n const reference = await transporter.write(\n `${prefix}/files/${files.length}/${chunks.length}`,\n bytes,\n { ifRevision: null },\n );\n chunks.push({ reference, sha256: digest(bytes), size: bytes.length });\n };\n if (info.isSymbolicLink())\n await publish(Buffer.from(await readlink(source)));\n if (info.isFile()) {\n const file = await open(source, inspectionFileFlags);\n try {\n const buffer = Buffer.alloc(defaults.chunkBytes);\n for (;;) {\n const { bytesRead } = await file.read(buffer, 0, buffer.length, null);\n if (!bytesRead) break;\n await publish(Buffer.from(buffer.subarray(0, bytesRead)));\n }\n const after = await file.stat();\n invariant(\n info.ino === after.ino &&\n info.dev === after.dev &&\n info.size === size &&\n info.mtimeMs === after.mtimeMs &&\n info.ctimeMs === after.ctimeMs,\n \"Archive source changed\",\n );\n } finally {\n await file.close();\n }\n }\n files.push({\n path,\n kind: info.isSymbolicLink() ? \"symlink\" : \"file\",\n mode: info.mode & 0o777,\n size,\n chunks,\n });\n }\n const manifest: ArchiveManifest = { format: 1, files };\n const bytes = jsonBytes(manifest);\n invariant(\n bytes.length <= defaults.manifestBytes,\n \"Archive manifest exceeds byte limit\",\n );\n return transporter.write(`${prefix}/manifest`, bytes, { ifRevision: null });\n}\n\nexport function archiveManifest(value: unknown): ArchiveManifest {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"format\" in value &&\n value.format === 1 &&\n \"files\" in value &&\n Array.isArray(value.files) &&\n value.files.length <= defaults.maxFiles,\n \"Invalid archive manifest\",\n );\n const files: ArchiveFile[] = value.files.map((file: unknown) => {\n invariant(\n file &&\n typeof file === \"object\" &&\n \"path\" in file &&\n typeof file.path === \"string\" &&\n \"kind\" in file &&\n (file.kind === \"file\" || file.kind === \"symlink\") &&\n \"mode\" in file &&\n typeof file.mode === \"number\" &&\n Number.isInteger(file.mode) &&\n file.mode >= 0 &&\n file.mode <= 0o777 &&\n \"size\" in file &&\n typeof file.size === \"number\" &&\n Number.isSafeInteger(file.size) &&\n file.size >= 0 &&\n \"chunks\" in file &&\n Array.isArray(file.chunks),\n \"Invalid archive file\",\n );\n const chunks = file.chunks.map((chunk: unknown): ArchiveChunk => {\n invariant(\n chunk &&\n typeof chunk === \"object\" &&\n \"reference\" in chunk &&\n \"sha256\" in chunk &&\n typeof chunk.sha256 === \"string\" &&\n /^[a-f0-9]{64}$/.test(chunk.sha256) &&\n \"size\" in chunk &&\n typeof chunk.size === \"number\" &&\n Number.isSafeInteger(chunk.size) &&\n chunk.size >= 0 &&\n chunk.size <= defaults.chunkBytes,\n \"Invalid archive chunk\",\n );\n return {\n reference: transportReference(chunk.reference),\n sha256: chunk.sha256,\n size: chunk.size,\n };\n });\n invariant(\n chunks.reduce((sum, chunk) => sum + chunk.size, 0) === file.size,\n \"Archive file size mismatch\",\n );\n return {\n path: file.path,\n kind: file.kind,\n mode: file.mode,\n size: file.size,\n chunks,\n };\n });\n invariant(\n new Set(files.map((file) => file.path)).size === files.length,\n \"Duplicate archive paths\",\n );\n return { format: 1, files };\n}\n\nexport async function restoreArchiveFiles(\n transporter: Transport,\n reference: TransportReference,\n destination: string,\n maxBytes = defaults.maxBytes,\n): Promise<void> {\n positive(maxBytes, \"maxBytes\");\n const manifest = archiveManifest(\n jsonObject(\n await readReference(transporter, reference, defaults.manifestBytes),\n ),\n );\n const total = manifest.files.reduce((sum, file) => sum + file.size, 0);\n invariant(\n Number.isSafeInteger(total) && total <= maxBytes,\n \"Archive exceeds byte limit\",\n );\n for (const file of manifest.files)\n await safeDestination(destination, file.path);\n await mkdir(destination, { mode: 0o700 });\n for (const entry of manifest.files) {\n const target = await safeDestination(destination, entry.path);\n await mkdir(dirname(target), { recursive: true, mode: 0o700 });\n const data = async (chunk: ArchiveChunk) => {\n const value = await readReference(\n transporter,\n chunk.reference,\n chunk.size,\n );\n invariant(\n value.bytes.length === chunk.size &&\n digest(value.bytes) === chunk.sha256,\n \"Archive chunk integrity mismatch\",\n );\n return value.bytes;\n };\n if (entry.kind === \"symlink\") {\n invariant(\n entry.size <= defaults.chunkBytes,\n \"Archive symlink exceeds limit\",\n );\n const chunks = [];\n for (const chunk of entry.chunks) chunks.push(await data(chunk));\n await symlink(Buffer.concat(chunks).toString(), target);\n continue;\n }\n const file = await open(target, \"wx\", 0o600);\n try {\n for (const chunk of entry.chunks) await file.writeFile(await data(chunk));\n await file.sync();\n } finally {\n await file.close();\n }\n await chmod(target, entry.mode);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transport-archive.js","sourceRoot":"","sources":["../../src/infrastructure/transport-archive.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { lstat, open, readlink, mkdir, symlink, chmod } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { inspectionFileFlags } from \"./inspection-file.constants.ts\";\nimport type {\n Transport,\n TransportReference,\n} from \"../domain/transport.types.ts\";\nimport { invariant, positive } from \"../domain/errors.ts\";\nimport { fileDestination, safeDestination } from \"./files.ts\";\nimport {\n jsonBytes,\n jsonObject,\n readReference,\n transportReference,\n} from \"./transport-json.ts\";\nimport type {\n ArchiveManifest,\n ArchiveFile,\n ArchiveChunk,\n ArchivePathOptions,\n} from \"./transport-archive.types.ts\";\nimport { archiveDefaults as defaults } from \"./transport-archive.constants.ts\";\n\nconst digest = (bytes: Uint8Array) =>\n createHash(\"sha256\").update(bytes).digest(\"hex\");\n\nexport async function archiveFiles(\n transporter: Transport,\n root: string,\n paths: readonly string[],\n prefix: string,\n maxBytes = defaults.maxBytes,\n options: ArchivePathOptions = {},\n): Promise<TransportReference> {\n const destination = options.allowGitMetadata\n ? fileDestination\n : safeDestination;\n positive(maxBytes, \"maxBytes\");\n invariant(\n paths.length <= defaults.maxFiles && new Set(paths).size === paths.length,\n \"Invalid archive path count\",\n );\n const files: ArchiveFile[] = [];\n const published: TransportReference[] = [];\n let total = 0;\n try {\n for (const path of paths) {\n const source = await destination(root, path);\n const info = await lstat(source);\n invariant(\n info.isFile() || info.isSymbolicLink(),\n \"Unsupported archive entry\",\n );\n const chunks: ArchiveChunk[] = [];\n let size = 0;\n const publish = async (bytes: Uint8Array) => {\n total += bytes.length;\n size += bytes.length;\n invariant(total <= maxBytes, \"Archive exceeds byte limit\");\n const reference = await transporter.write(\n `${prefix}/files/${files.length}/${chunks.length}`,\n bytes,\n { ifRevision: null },\n );\n published.push(reference);\n chunks.push({ reference, sha256: digest(bytes), size: bytes.length });\n };\n if (info.isSymbolicLink())\n await publish(Buffer.from(await readlink(source)));\n if (info.isFile()) {\n const file = await open(source, inspectionFileFlags);\n try {\n const buffer = Buffer.alloc(defaults.chunkBytes);\n for (;;) {\n const { bytesRead } = await file.read(\n buffer,\n 0,\n buffer.length,\n null,\n );\n if (!bytesRead) break;\n await publish(Buffer.from(buffer.subarray(0, bytesRead)));\n }\n const after = await file.stat();\n invariant(\n info.ino === after.ino &&\n info.dev === after.dev &&\n info.size === size &&\n info.mtimeMs === after.mtimeMs &&\n info.ctimeMs === after.ctimeMs,\n \"Archive source changed\",\n );\n } finally {\n await file.close();\n }\n }\n files.push({\n path,\n kind: info.isSymbolicLink() ? \"symlink\" : \"file\",\n mode: info.mode & 0o777,\n size,\n chunks,\n });\n }\n const manifest: ArchiveManifest = { format: 1, files };\n const bytes = jsonBytes(manifest);\n invariant(\n bytes.length <= defaults.manifestBytes,\n \"Archive manifest exceeds byte limit\",\n );\n return await transporter.write(`${prefix}/manifest`, bytes, {\n ifRevision: null,\n });\n } catch (cause) {\n // Preserve chunks if publication may have succeeded before its acknowledgement failed.\n const manifest = await transporter\n .read(`${prefix}/manifest`)\n .catch(() => true);\n if (!manifest) {\n const cleanup = await Promise.allSettled(\n published.map((reference) =>\n transporter.remove(reference.key, { ifRevision: reference.revision }),\n ),\n );\n const failures = cleanup.flatMap((result) =>\n result.status === \"rejected\" ? [result.reason] : [],\n );\n if (failures.length)\n throw new AggregateError(\n [cause, ...failures],\n \"Archive publication and cleanup failed\",\n );\n }\n throw cause;\n }\n}\n\nexport function archiveManifest(value: unknown): ArchiveManifest {\n invariant(\n value &&\n typeof value === \"object\" &&\n \"format\" in value &&\n value.format === 1 &&\n \"files\" in value &&\n Array.isArray(value.files) &&\n value.files.length <= defaults.maxFiles,\n \"Invalid archive manifest\",\n );\n const files: ArchiveFile[] = value.files.map((file: unknown) => {\n invariant(\n file &&\n typeof file === \"object\" &&\n \"path\" in file &&\n typeof file.path === \"string\" &&\n \"kind\" in file &&\n (file.kind === \"file\" || file.kind === \"symlink\") &&\n \"mode\" in file &&\n typeof file.mode === \"number\" &&\n Number.isInteger(file.mode) &&\n file.mode >= 0 &&\n file.mode <= 0o777 &&\n \"size\" in file &&\n typeof file.size === \"number\" &&\n Number.isSafeInteger(file.size) &&\n file.size >= 0 &&\n \"chunks\" in file &&\n Array.isArray(file.chunks),\n \"Invalid archive file\",\n );\n const chunks = file.chunks.map((chunk: unknown): ArchiveChunk => {\n invariant(\n chunk &&\n typeof chunk === \"object\" &&\n \"reference\" in chunk &&\n \"sha256\" in chunk &&\n typeof chunk.sha256 === \"string\" &&\n /^[a-f0-9]{64}$/.test(chunk.sha256) &&\n \"size\" in chunk &&\n typeof chunk.size === \"number\" &&\n Number.isSafeInteger(chunk.size) &&\n chunk.size >= 0 &&\n chunk.size <= defaults.chunkBytes,\n \"Invalid archive chunk\",\n );\n return {\n reference: transportReference(chunk.reference),\n sha256: chunk.sha256,\n size: chunk.size,\n };\n });\n invariant(\n chunks.reduce((sum, chunk) => sum + chunk.size, 0) === file.size,\n \"Archive file size mismatch\",\n );\n return {\n path: file.path,\n kind: file.kind,\n mode: file.mode,\n size: file.size,\n chunks,\n };\n });\n invariant(\n new Set(files.map((file) => file.path)).size === files.length,\n \"Duplicate archive paths\",\n );\n return { format: 1, files };\n}\n\nexport async function restoreArchiveFiles(\n transporter: Transport,\n reference: TransportReference,\n destination: string,\n maxBytes = defaults.maxBytes,\n options: ArchivePathOptions = {},\n): Promise<void> {\n const path = options.allowGitMetadata ? fileDestination : safeDestination;\n positive(maxBytes, \"maxBytes\");\n const manifest = archiveManifest(\n jsonObject(\n await readReference(transporter, reference, defaults.manifestBytes),\n ),\n );\n const total = manifest.files.reduce((sum, file) => sum + file.size, 0);\n invariant(\n Number.isSafeInteger(total) && total <= maxBytes,\n \"Archive exceeds byte limit\",\n );\n for (const file of manifest.files) await path(destination, file.path);\n await mkdir(destination, { mode: 0o700 });\n for (const entry of manifest.files) {\n const target = await path(destination, entry.path);\n await mkdir(dirname(target), { recursive: true, mode: 0o700 });\n const data = async (chunk: ArchiveChunk) => {\n const value = await readReference(\n transporter,\n chunk.reference,\n chunk.size,\n );\n invariant(\n value.bytes.length === chunk.size &&\n digest(value.bytes) === chunk.sha256,\n \"Archive chunk integrity mismatch\",\n );\n return value.bytes;\n };\n if (entry.kind === \"symlink\") {\n invariant(\n entry.size <= defaults.chunkBytes,\n \"Archive symlink exceeds limit\",\n );\n const chunks = [];\n for (const chunk of entry.chunks) chunks.push(await data(chunk));\n await symlink(Buffer.concat(chunks).toString(), target);\n continue;\n }\n const file = await open(target, \"wx\", 0o600);\n try {\n for (const chunk of entry.chunks) await file.writeFile(await data(chunk));\n await file.sync();\n } finally {\n await file.close();\n }\n await chmod(target, entry.mode);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport-archive.types.js","sourceRoot":"","sources":["../../src/infrastructure/transport-archive.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { TransportReference } from \"../domain/transport.types.ts\";\nexport interface ArchiveChunk {\n readonly reference: TransportReference;\n readonly sha256: string;\n readonly size: number;\n}\nexport interface ArchiveFile {\n readonly path: string;\n readonly kind: \"file\" | \"symlink\";\n readonly mode: number;\n readonly size: number;\n readonly chunks: readonly ArchiveChunk[];\n}\nexport interface ArchiveManifest {\n readonly format: 1;\n readonly files: readonly ArchiveFile[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transport-archive.types.js","sourceRoot":"","sources":["../../src/infrastructure/transport-archive.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { TransportReference } from \"../domain/transport.types.ts\";\nexport interface ArchivePathOptions {\n readonly allowGitMetadata?: boolean;\n}\nexport interface ArchiveChunk {\n readonly reference: TransportReference;\n readonly sha256: string;\n readonly size: number;\n}\nexport interface ArchiveFile {\n readonly path: string;\n readonly kind: \"file\" | \"symlink\";\n readonly mode: number;\n readonly size: number;\n readonly chunks: readonly ArchiveChunk[];\n}\nexport interface ArchiveManifest {\n readonly format: 1;\n readonly files: readonly ArchiveFile[];\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { TransportConflict } from "../domain/transport.js";
|
|
3
3
|
import { jsonBytes, jsonObject } from "./transport-json.js";
|
|
4
|
-
import { workflowCheckpointMaxBytes } from "./workflow-checkpoint.constants.js";
|
|
4
|
+
import { workflowCheckpointMaxBytes, workflowCheckpointReleaseAttempts, } from "./workflow-checkpoint.constants.js";
|
|
5
5
|
export function workflowCheckpointKey(runId) {
|
|
6
6
|
if (!runId.trim())
|
|
7
7
|
throw new Error("Checkpoint runId must not be empty");
|
|
@@ -80,9 +80,19 @@ export function createWorkflowCheckpointStore(options) {
|
|
|
80
80
|
await pending;
|
|
81
81
|
if (released)
|
|
82
82
|
return;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
for (let attempt = 1;; attempt++) {
|
|
84
|
+
try {
|
|
85
|
+
await owned();
|
|
86
|
+
await transporter.write(target, jsonBytes({ ...data, owner: null }), { ifRevision: revision });
|
|
87
|
+
released = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error instanceof TransportConflict ||
|
|
92
|
+
attempt >= workflowCheckpointReleaseAttempts)
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
86
96
|
})();
|
|
87
97
|
return closing;
|
|
88
98
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport-checkpoint.js","sourceRoot":"","sources":["../../src/infrastructure/transport-checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"transport-checkpoint.js","sourceRoot":"","sources":["../../src/infrastructure/transport-checkpoint.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { WorkflowCheckpointStore } from \"../domain/workflow/checkpoint.types.ts\";\nimport type { TransportStoreOptions } from \"../domain/transport.types.ts\";\nimport { TransportConflict } from \"../domain/transport.ts\";\nimport { jsonBytes, jsonObject } from \"./transport-json.ts\";\nimport {\n workflowCheckpointMaxBytes,\n workflowCheckpointReleaseAttempts,\n} from \"./workflow-checkpoint.constants.ts\";\nimport type {\n CheckpointEnvelope,\n CheckpointRecoveryOptions,\n} from \"./transport-checkpoint.types.ts\";\n\nexport function workflowCheckpointKey(runId: string): string {\n if (!runId.trim()) throw new Error(\"Checkpoint runId must not be empty\");\n return `checkpoints/${createHash(\"sha256\").update(runId).digest(\"hex\")}.json`;\n}\n\nfunction envelope(value: unknown): CheckpointEnvelope {\n if (value === undefined) return { owner: null };\n if (\n !value ||\n typeof value !== \"object\" ||\n !(\"owner\" in value) ||\n (value.owner !== null && typeof value.owner !== \"string\")\n )\n throw new Error(\"Invalid checkpoint ownership envelope\");\n return {\n owner: value.owner,\n ...(\"checkpoint\" in value ? { checkpoint: value.checkpoint } : {}),\n };\n}\n\nexport function createWorkflowCheckpointStore(\n options: TransportStoreOptions,\n): WorkflowCheckpointStore {\n const { transporter } = options;\n return {\n async acquire(runId) {\n const target = workflowCheckpointKey(runId);\n const previous = await transporter.read(target, {\n maxBytes: workflowCheckpointMaxBytes + 1024,\n });\n let data = envelope(jsonObject(previous));\n if (data.owner !== null)\n throw new Error(\"Workflow is already in use or ownership is unknown\");\n const owner = randomUUID();\n data = { ...data, owner };\n let revision = (\n await transporter.write(target, jsonBytes(data), {\n ifRevision: previous?.revision ?? null,\n })\n ).revision;\n let released = false;\n let pending = Promise.resolve();\n let closing: Promise<void> | undefined;\n async function owned() {\n if (released) throw new Error(\"Checkpoint lease has been released\");\n const current = await transporter.read(target, {\n maxBytes: workflowCheckpointMaxBytes + 1024,\n });\n if (\n current?.revision !== revision ||\n envelope(jsonObject(current)).owner !== owner\n )\n throw new TransportConflict(target);\n }\n return {\n async read() {\n await pending;\n await owned();\n return structuredClone(data.checkpoint);\n },\n async write(checkpoint) {\n if (closing)\n return Promise.reject(\n new Error(\"Checkpoint lease has been released\"),\n );\n const bytes = jsonBytes(checkpoint);\n if (bytes.byteLength > workflowCheckpointMaxBytes)\n return Promise.reject(\n new Error(\n \"Workflow checkpoint exceeds 16 MiB; store large artifacts separately\",\n ),\n );\n const next = {\n owner,\n checkpoint: JSON.parse(Buffer.from(bytes).toString()) as unknown,\n };\n const operation = pending.then(async () => {\n await owned();\n revision = (\n await transporter.write(target, jsonBytes(next), {\n ifRevision: revision,\n })\n ).revision;\n data = next;\n });\n pending = operation.catch(() => {});\n return operation;\n },\n release() {\n closing ??= (async () => {\n await pending;\n if (released) return;\n for (let attempt = 1; ; attempt++) {\n try {\n await owned();\n await transporter.write(\n target,\n jsonBytes({ ...data, owner: null }),\n { ifRevision: revision },\n );\n released = true;\n return;\n } catch (error) {\n if (\n error instanceof TransportConflict ||\n attempt >= workflowCheckpointReleaseAttempts\n )\n throw error;\n }\n }\n })();\n return closing;\n },\n };\n },\n };\n}\n\nexport async function recoverWorkflowCheckpoint(\n options: CheckpointRecoveryOptions,\n): Promise<void> {\n const target = workflowCheckpointKey(options.runId);\n const current = await options.transporter.read(target, {\n maxBytes: workflowCheckpointMaxBytes + 1024,\n });\n if (!current || current.revision !== options.revision)\n throw new TransportConflict(target);\n const data = envelope(jsonObject(current));\n await options.transporter.write(target, jsonBytes({ ...data, owner: null }), {\n ifRevision: current.revision,\n });\n}\n"]}
|