@cuna_labs/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +169 -0
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/dist/api/capability-evidence.d.ts +19 -0
- package/dist/api/capability-evidence.d.ts.map +1 -0
- package/dist/api/capability-evidence.js +38 -0
- package/dist/api/capability-evidence.js.map +1 -0
- package/dist/api/client.d.ts +167 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +937 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/contracts.d.ts +345 -0
- package/dist/api/contracts.d.ts.map +1 -0
- package/dist/api/contracts.js +976 -0
- package/dist/api/contracts.js.map +1 -0
- package/dist/api/execution-workspaces.d.ts +17 -0
- package/dist/api/execution-workspaces.d.ts.map +1 -0
- package/dist/api/execution-workspaces.js +46 -0
- package/dist/api/execution-workspaces.js.map +1 -0
- package/dist/api/http.d.ts +76 -0
- package/dist/api/http.d.ts.map +1 -0
- package/dist/api/http.js +838 -0
- package/dist/api/http.js.map +1 -0
- package/dist/api/managed-executions.d.ts +37 -0
- package/dist/api/managed-executions.d.ts.map +1 -0
- package/dist/api/managed-executions.js +80 -0
- package/dist/api/managed-executions.js.map +1 -0
- package/dist/api/observer-v2-schema.d.ts +206 -0
- package/dist/api/observer-v2-schema.d.ts.map +1 -0
- package/dist/api/observer-v2-schema.js +264 -0
- package/dist/api/observer-v2-schema.js.map +1 -0
- package/dist/api/observer-v2.d.ts +33 -0
- package/dist/api/observer-v2.d.ts.map +1 -0
- package/dist/api/observer-v2.js +60 -0
- package/dist/api/observer-v2.js.map +1 -0
- package/dist/api/owner-observe-grants-v2-schema.d.ts +678 -0
- package/dist/api/owner-observe-grants-v2-schema.d.ts.map +1 -0
- package/dist/api/owner-observe-grants-v2-schema.js +887 -0
- package/dist/api/owner-observe-grants-v2-schema.js.map +1 -0
- package/dist/api/owner-observe-grants-v2.d.ts +268 -0
- package/dist/api/owner-observe-grants-v2.d.ts.map +1 -0
- package/dist/api/owner-observe-grants-v2.js +604 -0
- package/dist/api/owner-observe-grants-v2.js.map +1 -0
- package/dist/api/provider-v2-schema.d.ts +293 -0
- package/dist/api/provider-v2-schema.d.ts.map +1 -0
- package/dist/api/provider-v2-schema.js +381 -0
- package/dist/api/provider-v2-schema.js.map +1 -0
- package/dist/api/provider-v2.d.ts +55 -0
- package/dist/api/provider-v2.d.ts.map +1 -0
- package/dist/api/provider-v2.js +54 -0
- package/dist/api/provider-v2.js.map +1 -0
- package/dist/api/remote-workspace.d.ts +29 -0
- package/dist/api/remote-workspace.d.ts.map +1 -0
- package/dist/api/remote-workspace.js +62 -0
- package/dist/api/remote-workspace.js.map +1 -0
- package/dist/api/supervisor-live-update.d.ts +248 -0
- package/dist/api/supervisor-live-update.d.ts.map +1 -0
- package/dist/api/supervisor-live-update.js +434 -0
- package/dist/api/supervisor-live-update.js.map +1 -0
- package/dist/api/terminal-reason.d.ts +5 -0
- package/dist/api/terminal-reason.d.ts.map +1 -0
- package/dist/api/terminal-reason.js +12 -0
- package/dist/api/terminal-reason.js.map +1 -0
- package/dist/auth/browser-handoff.d.ts +59 -0
- package/dist/auth/browser-handoff.d.ts.map +1 -0
- package/dist/auth/browser-handoff.js +33 -0
- package/dist/auth/browser-handoff.js.map +1 -0
- package/dist/auth/browser.d.ts +11 -0
- package/dist/auth/browser.d.ts.map +1 -0
- package/dist/auth/browser.js +47 -0
- package/dist/auth/browser.js.map +1 -0
- package/dist/auth/human-client.d.ts +39 -0
- package/dist/auth/human-client.d.ts.map +1 -0
- package/dist/auth/human-client.js +66 -0
- package/dist/auth/human-client.js.map +1 -0
- package/dist/auth/human-contracts.d.ts +52 -0
- package/dist/auth/human-contracts.d.ts.map +1 -0
- package/dist/auth/human-contracts.js +229 -0
- package/dist/auth/human-contracts.js.map +1 -0
- package/dist/auth/human-session.d.ts +48 -0
- package/dist/auth/human-session.d.ts.map +1 -0
- package/dist/auth/human-session.js +902 -0
- package/dist/auth/human-session.js.map +1 -0
- package/dist/auth/onboarding-state.d.ts +31 -0
- package/dist/auth/onboarding-state.d.ts.map +1 -0
- package/dist/auth/onboarding-state.js +52 -0
- package/dist/auth/onboarding-state.js.map +1 -0
- package/dist/auth/pkce.d.ts +15 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +19 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/bin/cuna.d.ts +3 -0
- package/dist/bin/cuna.d.ts.map +1 -0
- package/dist/bin/cuna.js +4 -0
- package/dist/bin/cuna.js.map +1 -0
- package/dist/build-identity.d.ts +20 -0
- package/dist/build-identity.d.ts.map +1 -0
- package/dist/build-identity.js +77 -0
- package/dist/build-identity.js.map +1 -0
- package/dist/cli/command-help.d.ts +9 -0
- package/dist/cli/command-help.d.ts.map +1 -0
- package/dist/cli/command-help.js +324 -0
- package/dist/cli/command-help.js.map +1 -0
- package/dist/cli/help.d.ts +40 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +256 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/output.d.ts +28 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +95 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/parser.d.ts +65 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +294 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/process-entrypoint.d.ts +18 -0
- package/dist/cli/process-entrypoint.d.ts.map +1 -0
- package/dist/cli/process-entrypoint.js +26 -0
- package/dist/cli/process-entrypoint.js.map +1 -0
- package/dist/cli/run.d.ts +114 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +1969 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/commands/commands.d.ts +74 -0
- package/dist/commands/commands.d.ts.map +1 -0
- package/dist/commands/commands.js +2814 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/config/config.d.ts +86 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +323 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/infra-contract-witness.d.ts +29 -0
- package/dist/config/infra-contract-witness.d.ts.map +1 -0
- package/dist/config/infra-contract-witness.js +30 -0
- package/dist/config/infra-contract-witness.js.map +1 -0
- package/dist/core/browser-url.d.ts +2 -0
- package/dist/core/browser-url.d.ts.map +1 -0
- package/dist/core/browser-url.js +20 -0
- package/dist/core/browser-url.js.map +1 -0
- package/dist/core/deployed-wire-compatibility.d.ts +15 -0
- package/dist/core/deployed-wire-compatibility.d.ts.map +1 -0
- package/dist/core/deployed-wire-compatibility.js +15 -0
- package/dist/core/deployed-wire-compatibility.js.map +1 -0
- package/dist/core/errors.d.ts +59 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +82 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/exit-codes.d.ts +68 -0
- package/dist/core/exit-codes.d.ts.map +1 -0
- package/dist/core/exit-codes.js +84 -0
- package/dist/core/exit-codes.js.map +1 -0
- package/dist/core/instant.d.ts +60 -0
- package/dist/core/instant.d.ts.map +1 -0
- package/dist/core/instant.js +90 -0
- package/dist/core/instant.js.map +1 -0
- package/dist/core/namespace.d.ts +171 -0
- package/dist/core/namespace.d.ts.map +1 -0
- package/dist/core/namespace.js +227 -0
- package/dist/core/namespace.js.map +1 -0
- package/dist/core/observation-budget.d.ts +185 -0
- package/dist/core/observation-budget.d.ts.map +1 -0
- package/dist/core/observation-budget.js +209 -0
- package/dist/core/observation-budget.js.map +1 -0
- package/dist/core/product-web.d.ts +81 -0
- package/dist/core/product-web.d.ts.map +1 -0
- package/dist/core/product-web.js +87 -0
- package/dist/core/product-web.js.map +1 -0
- package/dist/core/validation.d.ts +90 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +199 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/credentials/contracts.d.ts +82 -0
- package/dist/credentials/contracts.d.ts.map +1 -0
- package/dist/credentials/contracts.js +2 -0
- package/dist/credentials/contracts.js.map +1 -0
- package/dist/credentials/errors.d.ts +19 -0
- package/dist/credentials/errors.d.ts.map +1 -0
- package/dist/credentials/errors.js +16 -0
- package/dist/credentials/errors.js.map +1 -0
- package/dist/credentials/index.d.ts +6 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +6 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/local-session.d.ts +101 -0
- package/dist/credentials/local-session.d.ts.map +1 -0
- package/dist/credentials/local-session.js +839 -0
- package/dist/credentials/local-session.js.map +1 -0
- package/dist/credentials/secret-material.d.ts +17 -0
- package/dist/credentials/secret-material.d.ts.map +1 -0
- package/dist/credentials/secret-material.js +49 -0
- package/dist/credentials/secret-material.js.map +1 -0
- package/dist/credentials/vault.d.ts +41 -0
- package/dist/credentials/vault.d.ts.map +1 -0
- package/dist/credentials/vault.js +719 -0
- package/dist/credentials/vault.js.map +1 -0
- package/dist/daemon/endpoint.d.ts +29 -0
- package/dist/daemon/endpoint.d.ts.map +1 -0
- package/dist/daemon/endpoint.js +66 -0
- package/dist/daemon/endpoint.js.map +1 -0
- package/dist/daemon/journal.d.ts +62 -0
- package/dist/daemon/journal.d.ts.map +1 -0
- package/dist/daemon/journal.js +299 -0
- package/dist/daemon/journal.js.map +1 -0
- package/dist/daemon/lease.d.ts +25 -0
- package/dist/daemon/lease.d.ts.map +1 -0
- package/dist/daemon/lease.js +94 -0
- package/dist/daemon/lease.js.map +1 -0
- package/dist/daemon/lifecycle.d.ts +14 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +29 -0
- package/dist/daemon/lifecycle.js.map +1 -0
- package/dist/daemon/protocol.d.ts +65 -0
- package/dist/daemon/protocol.d.ts.map +1 -0
- package/dist/daemon/protocol.js +194 -0
- package/dist/daemon/protocol.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +29 -0
- package/dist/daemon/supervisor.d.ts.map +1 -0
- package/dist/daemon/supervisor.js +87 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/daemon/views.d.ts +31 -0
- package/dist/daemon/views.d.ts.map +1 -0
- package/dist/daemon/views.js +86 -0
- package/dist/daemon/views.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/journey/api-effects.d.ts +59 -0
- package/dist/journey/api-effects.d.ts.map +1 -0
- package/dist/journey/api-effects.js +386 -0
- package/dist/journey/api-effects.js.map +1 -0
- package/dist/journey/derived-identity.d.ts +91 -0
- package/dist/journey/derived-identity.d.ts.map +1 -0
- package/dist/journey/derived-identity.js +123 -0
- package/dist/journey/derived-identity.js.map +1 -0
- package/dist/journey/index.d.ts +6 -0
- package/dist/journey/index.d.ts.map +1 -0
- package/dist/journey/index.js +6 -0
- package/dist/journey/index.js.map +1 -0
- package/dist/journey/intent.d.ts +36 -0
- package/dist/journey/intent.d.ts.map +1 -0
- package/dist/journey/intent.js +191 -0
- package/dist/journey/intent.js.map +1 -0
- package/dist/journey/orchestrator.d.ts +140 -0
- package/dist/journey/orchestrator.d.ts.map +1 -0
- package/dist/journey/orchestrator.js +415 -0
- package/dist/journey/orchestrator.js.map +1 -0
- package/dist/journey/provider-launch-intent.d.ts +12 -0
- package/dist/journey/provider-launch-intent.d.ts.map +1 -0
- package/dist/journey/provider-launch-intent.js +40 -0
- package/dist/journey/provider-launch-intent.js.map +1 -0
- package/dist/journey/remote-workspace.d.ts +34 -0
- package/dist/journey/remote-workspace.d.ts.map +1 -0
- package/dist/journey/remote-workspace.js +91 -0
- package/dist/journey/remote-workspace.js.map +1 -0
- package/dist/journey/root-entry.d.ts +56 -0
- package/dist/journey/root-entry.d.ts.map +1 -0
- package/dist/journey/root-entry.js +19 -0
- package/dist/journey/root-entry.js.map +1 -0
- package/dist/journey/selection.d.ts +165 -0
- package/dist/journey/selection.d.ts.map +1 -0
- package/dist/journey/selection.js +565 -0
- package/dist/journey/selection.js.map +1 -0
- package/dist/journey/session-failure.d.ts +4 -0
- package/dist/journey/session-failure.d.ts.map +1 -0
- package/dist/journey/session-failure.js +28 -0
- package/dist/journey/session-failure.js.map +1 -0
- package/dist/journey/workspace-effects.d.ts +30 -0
- package/dist/journey/workspace-effects.d.ts.map +1 -0
- package/dist/journey/workspace-effects.js +348 -0
- package/dist/journey/workspace-effects.js.map +1 -0
- package/dist/local-actions/broker.d.ts +32 -0
- package/dist/local-actions/broker.d.ts.map +1 -0
- package/dist/local-actions/broker.js +400 -0
- package/dist/local-actions/broker.js.map +1 -0
- package/dist/local-actions/browser-action.d.ts +78 -0
- package/dist/local-actions/browser-action.d.ts.map +1 -0
- package/dist/local-actions/browser-action.js +307 -0
- package/dist/local-actions/browser-action.js.map +1 -0
- package/dist/local-actions/callback-relay.d.ts +45 -0
- package/dist/local-actions/callback-relay.d.ts.map +1 -0
- package/dist/local-actions/callback-relay.js +265 -0
- package/dist/local-actions/callback-relay.js.map +1 -0
- package/dist/local-actions/clipboard.d.ts +3 -0
- package/dist/local-actions/clipboard.d.ts.map +1 -0
- package/dist/local-actions/clipboard.js +18 -0
- package/dist/local-actions/clipboard.js.map +1 -0
- package/dist/local-actions/contracts.d.ts +63 -0
- package/dist/local-actions/contracts.d.ts.map +1 -0
- package/dist/local-actions/contracts.js +18 -0
- package/dist/local-actions/contracts.js.map +1 -0
- package/dist/local-actions/desktop-presentation.d.ts +141 -0
- package/dist/local-actions/desktop-presentation.d.ts.map +1 -0
- package/dist/local-actions/desktop-presentation.js +253 -0
- package/dist/local-actions/desktop-presentation.js.map +1 -0
- package/dist/local-actions/device-selection.d.ts +43 -0
- package/dist/local-actions/device-selection.d.ts.map +1 -0
- package/dist/local-actions/device-selection.js +154 -0
- package/dist/local-actions/device-selection.js.map +1 -0
- package/dist/local-actions/executor.d.ts +49 -0
- package/dist/local-actions/executor.d.ts.map +1 -0
- package/dist/local-actions/executor.js +329 -0
- package/dist/local-actions/executor.js.map +1 -0
- package/dist/local-actions/file-transfers.d.ts +141 -0
- package/dist/local-actions/file-transfers.d.ts.map +1 -0
- package/dist/local-actions/file-transfers.js +446 -0
- package/dist/local-actions/file-transfers.js.map +1 -0
- package/dist/local-actions/foreground-notifications.d.ts +45 -0
- package/dist/local-actions/foreground-notifications.d.ts.map +1 -0
- package/dist/local-actions/foreground-notifications.js +163 -0
- package/dist/local-actions/foreground-notifications.js.map +1 -0
- package/dist/local-actions/git-signing.d.ts +39 -0
- package/dist/local-actions/git-signing.d.ts.map +1 -0
- package/dist/local-actions/git-signing.js +135 -0
- package/dist/local-actions/git-signing.js.map +1 -0
- package/dist/local-actions/index.d.ts +19 -0
- package/dist/local-actions/index.d.ts.map +1 -0
- package/dist/local-actions/index.js +19 -0
- package/dist/local-actions/index.js.map +1 -0
- package/dist/local-actions/local-service-actions.d.ts +87 -0
- package/dist/local-actions/local-service-actions.d.ts.map +1 -0
- package/dist/local-actions/local-service-actions.js +347 -0
- package/dist/local-actions/local-service-actions.js.map +1 -0
- package/dist/local-actions/loopback.d.ts +5 -0
- package/dist/local-actions/loopback.d.ts.map +1 -0
- package/dist/local-actions/loopback.js +15 -0
- package/dist/local-actions/loopback.js.map +1 -0
- package/dist/local-actions/policy-store.d.ts +7 -0
- package/dist/local-actions/policy-store.d.ts.map +1 -0
- package/dist/local-actions/policy-store.js +70 -0
- package/dist/local-actions/policy-store.js.map +1 -0
- package/dist/local-actions/policy.d.ts +21 -0
- package/dist/local-actions/policy.d.ts.map +1 -0
- package/dist/local-actions/policy.js +43 -0
- package/dist/local-actions/policy.js.map +1 -0
- package/dist/local-actions/private-port-forward.d.ts +36 -0
- package/dist/local-actions/private-port-forward.d.ts.map +1 -0
- package/dist/local-actions/private-port-forward.js +154 -0
- package/dist/local-actions/private-port-forward.js.map +1 -0
- package/dist/local-actions/provider-mcp-config.d.ts +101 -0
- package/dist/local-actions/provider-mcp-config.d.ts.map +1 -0
- package/dist/local-actions/provider-mcp-config.js +166 -0
- package/dist/local-actions/provider-mcp-config.js.map +1 -0
- package/dist/local-actions/providers.d.ts +12 -0
- package/dist/local-actions/providers.d.ts.map +1 -0
- package/dist/local-actions/providers.js +42 -0
- package/dist/local-actions/providers.js.map +1 -0
- package/dist/local-actions/schemas.d.ts +4 -0
- package/dist/local-actions/schemas.d.ts.map +1 -0
- package/dist/local-actions/schemas.js +314 -0
- package/dist/local-actions/schemas.js.map +1 -0
- package/dist/local-actions/sensitive-consent.d.ts +32 -0
- package/dist/local-actions/sensitive-consent.d.ts.map +1 -0
- package/dist/local-actions/sensitive-consent.js +47 -0
- package/dist/local-actions/sensitive-consent.js.map +1 -0
- package/dist/machines/command-launch.d.ts +17 -0
- package/dist/machines/command-launch.d.ts.map +1 -0
- package/dist/machines/command-launch.js +52 -0
- package/dist/machines/command-launch.js.map +1 -0
- package/dist/machines/execution-receipt.d.ts +18 -0
- package/dist/machines/execution-receipt.d.ts.map +1 -0
- package/dist/machines/execution-receipt.js +87 -0
- package/dist/machines/execution-receipt.js.map +1 -0
- package/dist/machines/executions-screen.d.ts +6 -0
- package/dist/machines/executions-screen.d.ts.map +1 -0
- package/dist/machines/executions-screen.js +410 -0
- package/dist/machines/executions-screen.js.map +1 -0
- package/dist/machines/explorer.d.ts +75 -0
- package/dist/machines/explorer.d.ts.map +1 -0
- package/dist/machines/explorer.js +1726 -0
- package/dist/machines/explorer.js.map +1 -0
- package/dist/machines/live-supervisor-update.d.ts +325 -0
- package/dist/machines/live-supervisor-update.d.ts.map +1 -0
- package/dist/machines/live-supervisor-update.js +513 -0
- package/dist/machines/live-supervisor-update.js.map +1 -0
- package/dist/machines/machine-first.d.ts +123 -0
- package/dist/machines/machine-first.d.ts.map +1 -0
- package/dist/machines/machine-first.js +119 -0
- package/dist/machines/machine-first.js.map +1 -0
- package/dist/machines/opencode-supervisor.d.ts +51 -0
- package/dist/machines/opencode-supervisor.d.ts.map +1 -0
- package/dist/machines/opencode-supervisor.js +124 -0
- package/dist/machines/opencode-supervisor.js.map +1 -0
- package/dist/machines/pagination.d.ts +4 -0
- package/dist/machines/pagination.d.ts.map +1 -0
- package/dist/machines/pagination.js +7 -0
- package/dist/machines/pagination.js.map +1 -0
- package/dist/machines/provider-availability.d.ts +42 -0
- package/dist/machines/provider-availability.d.ts.map +1 -0
- package/dist/machines/provider-availability.js +82 -0
- package/dist/machines/provider-availability.js.map +1 -0
- package/dist/machines/provider-screen.d.ts +12 -0
- package/dist/machines/provider-screen.d.ts.map +1 -0
- package/dist/machines/provider-screen.js +129 -0
- package/dist/machines/provider-screen.js.map +1 -0
- package/dist/machines/session-actionability.d.ts +86 -0
- package/dist/machines/session-actionability.d.ts.map +1 -0
- package/dist/machines/session-actionability.js +125 -0
- package/dist/machines/session-actionability.js.map +1 -0
- package/dist/machines/session-visibility.d.ts +139 -0
- package/dist/machines/session-visibility.d.ts.map +1 -0
- package/dist/machines/session-visibility.js +109 -0
- package/dist/machines/session-visibility.js.map +1 -0
- package/dist/platform/adapter.d.ts +62 -0
- package/dist/platform/adapter.d.ts.map +1 -0
- package/dist/platform/adapter.js +240 -0
- package/dist/platform/adapter.js.map +1 -0
- package/dist/platform/support.d.ts +12 -0
- package/dist/platform/support.d.ts.map +1 -0
- package/dist/platform/support.js +25 -0
- package/dist/platform/support.js.map +1 -0
- package/dist/pty/contracts.d.ts +45 -0
- package/dist/pty/contracts.d.ts.map +1 -0
- package/dist/pty/contracts.js +2 -0
- package/dist/pty/contracts.js.map +1 -0
- package/dist/pty/evidence-gate.d.ts +14 -0
- package/dist/pty/evidence-gate.d.ts.map +1 -0
- package/dist/pty/evidence-gate.js +35 -0
- package/dist/pty/evidence-gate.js.map +1 -0
- package/dist/pty/index.d.ts +5 -0
- package/dist/pty/index.d.ts.map +1 -0
- package/dist/pty/index.js +5 -0
- package/dist/pty/index.js.map +1 -0
- package/dist/pty/node-host-terminal.d.ts +14 -0
- package/dist/pty/node-host-terminal.d.ts.map +1 -0
- package/dist/pty/node-host-terminal.js +173 -0
- package/dist/pty/node-host-terminal.js.map +1 -0
- package/dist/pty/node-process.d.ts +26 -0
- package/dist/pty/node-process.d.ts.map +1 -0
- package/dist/pty/node-process.js +102 -0
- package/dist/pty/node-process.js.map +1 -0
- package/dist/runtime/api-terminal-control-plane.d.ts +7 -0
- package/dist/runtime/api-terminal-control-plane.d.ts.map +1 -0
- package/dist/runtime/api-terminal-control-plane.js +95 -0
- package/dist/runtime/api-terminal-control-plane.js.map +1 -0
- package/dist/runtime/backoff.d.ts +17 -0
- package/dist/runtime/backoff.d.ts.map +1 -0
- package/dist/runtime/backoff.js +61 -0
- package/dist/runtime/backoff.js.map +1 -0
- package/dist/runtime/boundary.d.ts +171 -0
- package/dist/runtime/boundary.d.ts.map +1 -0
- package/dist/runtime/boundary.js +1858 -0
- package/dist/runtime/boundary.js.map +1 -0
- package/dist/runtime/capability-gate.d.ts +25 -0
- package/dist/runtime/capability-gate.d.ts.map +1 -0
- package/dist/runtime/capability-gate.js +77 -0
- package/dist/runtime/capability-gate.js.map +1 -0
- package/dist/runtime/contracts.d.ts +25 -0
- package/dist/runtime/contracts.d.ts.map +1 -0
- package/dist/runtime/contracts.js +66 -0
- package/dist/runtime/contracts.js.map +1 -0
- package/dist/runtime/errors.d.ts +20 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +22 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/node-foreground-session.d.ts +50 -0
- package/dist/runtime/node-foreground-session.d.ts.map +1 -0
- package/dist/runtime/node-foreground-session.js +431 -0
- package/dist/runtime/node-foreground-session.js.map +1 -0
- package/dist/runtime/node-websocket-connector.d.ts +6 -0
- package/dist/runtime/node-websocket-connector.d.ts.map +1 -0
- package/dist/runtime/node-websocket-connector.js +344 -0
- package/dist/runtime/node-websocket-connector.js.map +1 -0
- package/dist/runtime/observer-screen.d.ts +6 -0
- package/dist/runtime/observer-screen.d.ts.map +1 -0
- package/dist/runtime/observer-screen.js +144 -0
- package/dist/runtime/observer-screen.js.map +1 -0
- package/dist/runtime/owner-grant-operations.d.ts +136 -0
- package/dist/runtime/owner-grant-operations.d.ts.map +1 -0
- package/dist/runtime/owner-grant-operations.js +216 -0
- package/dist/runtime/owner-grant-operations.js.map +1 -0
- package/dist/runtime/owner-grants-screen.d.ts +40 -0
- package/dist/runtime/owner-grants-screen.d.ts.map +1 -0
- package/dist/runtime/owner-grants-screen.js +987 -0
- package/dist/runtime/owner-grants-screen.js.map +1 -0
- package/dist/runtime/terminal-transport.d.ts +85 -0
- package/dist/runtime/terminal-transport.d.ts.map +1 -0
- package/dist/runtime/terminal-transport.js +152 -0
- package/dist/runtime/terminal-transport.js.map +1 -0
- package/dist/sync/continuous-sync-supervisor.d.ts +107 -0
- package/dist/sync/continuous-sync-supervisor.d.ts.map +1 -0
- package/dist/sync/continuous-sync-supervisor.js +1117 -0
- package/dist/sync/continuous-sync-supervisor.js.map +1 -0
- package/dist/sync/index.d.ts +9 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +9 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/journal.d.ts +57 -0
- package/dist/sync/journal.d.ts.map +1 -0
- package/dist/sync/journal.js +685 -0
- package/dist/sync/journal.js.map +1 -0
- package/dist/sync/revisions.d.ts +118 -0
- package/dist/sync/revisions.d.ts.map +1 -0
- package/dist/sync/revisions.js +364 -0
- package/dist/sync/revisions.js.map +1 -0
- package/dist/sync/supervisor.d.ts +107 -0
- package/dist/sync/supervisor.d.ts.map +1 -0
- package/dist/sync/supervisor.js +246 -0
- package/dist/sync/supervisor.js.map +1 -0
- package/dist/sync/workspace-sync-client.d.ts +18 -0
- package/dist/sync/workspace-sync-client.d.ts.map +1 -0
- package/dist/sync/workspace-sync-client.js +150 -0
- package/dist/sync/workspace-sync-client.js.map +1 -0
- package/dist/sync/workspace-sync-coordinator.d.ts +76 -0
- package/dist/sync/workspace-sync-coordinator.d.ts.map +1 -0
- package/dist/sync/workspace-sync-coordinator.js +621 -0
- package/dist/sync/workspace-sync-coordinator.js.map +1 -0
- package/dist/sync/workspace-sync-product-service.d.ts +79 -0
- package/dist/sync/workspace-sync-product-service.d.ts.map +1 -0
- package/dist/sync/workspace-sync-product-service.js +398 -0
- package/dist/sync/workspace-sync-product-service.js.map +1 -0
- package/dist/sync/workspace-sync-protocol.d.ts +145 -0
- package/dist/sync/workspace-sync-protocol.d.ts.map +1 -0
- package/dist/sync/workspace-sync-protocol.js +347 -0
- package/dist/sync/workspace-sync-protocol.js.map +1 -0
- package/dist/terminal/appbar.d.ts +40 -0
- package/dist/terminal/appbar.d.ts.map +1 -0
- package/dist/terminal/appbar.js +55 -0
- package/dist/terminal/appbar.js.map +1 -0
- package/dist/terminal/cell-width.d.ts +3 -0
- package/dist/terminal/cell-width.d.ts.map +1 -0
- package/dist/terminal/cell-width.js +53 -0
- package/dist/terminal/cell-width.js.map +1 -0
- package/dist/terminal/codec.d.ts +107 -0
- package/dist/terminal/codec.d.ts.map +1 -0
- package/dist/terminal/codec.js +538 -0
- package/dist/terminal/codec.js.map +1 -0
- package/dist/terminal/foreground.d.ts +135 -0
- package/dist/terminal/foreground.d.ts.map +1 -0
- package/dist/terminal/foreground.js +2097 -0
- package/dist/terminal/foreground.js.map +1 -0
- package/dist/terminal/generated/terminal-wire-v1.d.ts +26 -0
- package/dist/terminal/generated/terminal-wire-v1.d.ts.map +1 -0
- package/dist/terminal/generated/terminal-wire-v1.js +27 -0
- package/dist/terminal/generated/terminal-wire-v1.js.map +1 -0
- package/dist/terminal/mode.d.ts +33 -0
- package/dist/terminal/mode.d.ts.map +1 -0
- package/dist/terminal/mode.js +99 -0
- package/dist/terminal/mode.js.map +1 -0
- package/dist/terminal/passthrough.d.ts +47 -0
- package/dist/terminal/passthrough.d.ts.map +1 -0
- package/dist/terminal/passthrough.js +666 -0
- package/dist/terminal/passthrough.js.map +1 -0
- package/dist/terminal/resume.d.ts +58 -0
- package/dist/terminal/resume.d.ts.map +1 -0
- package/dist/terminal/resume.js +185 -0
- package/dist/terminal/resume.js.map +1 -0
- package/dist/terminal/viewport.d.ts +93 -0
- package/dist/terminal/viewport.d.ts.map +1 -0
- package/dist/terminal/viewport.js +266 -0
- package/dist/terminal/viewport.js.map +1 -0
- package/dist/terminal/workbench.d.ts +32 -0
- package/dist/terminal/workbench.d.ts.map +1 -0
- package/dist/terminal/workbench.js +316 -0
- package/dist/terminal/workbench.js.map +1 -0
- package/dist/terminal/xterm-vte.d.ts +49 -0
- package/dist/terminal/xterm-vte.d.ts.map +1 -0
- package/dist/terminal/xterm-vte.js +602 -0
- package/dist/terminal/xterm-vte.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace/binding-path.d.ts +17 -0
- package/dist/workspace/binding-path.d.ts.map +1 -0
- package/dist/workspace/binding-path.js +91 -0
- package/dist/workspace/binding-path.js.map +1 -0
- package/dist/workspace/binding-store.d.ts +107 -0
- package/dist/workspace/binding-store.d.ts.map +1 -0
- package/dist/workspace/binding-store.js +783 -0
- package/dist/workspace/binding-store.js.map +1 -0
- package/dist/workspace/binding.d.ts +35 -0
- package/dist/workspace/binding.d.ts.map +1 -0
- package/dist/workspace/binding.js +70 -0
- package/dist/workspace/binding.js.map +1 -0
- package/dist/workspace/errors.d.ts +4 -0
- package/dist/workspace/errors.d.ts.map +1 -0
- package/dist/workspace/errors.js +19 -0
- package/dist/workspace/errors.js.map +1 -0
- package/dist/workspace/exclusion.d.ts +21 -0
- package/dist/workspace/exclusion.d.ts.map +1 -0
- package/dist/workspace/exclusion.js +243 -0
- package/dist/workspace/exclusion.js.map +1 -0
- package/dist/workspace/index.d.ts +8 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +8 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/lifecycle.d.ts +56 -0
- package/dist/workspace/lifecycle.d.ts.map +1 -0
- package/dist/workspace/lifecycle.js +94 -0
- package/dist/workspace/lifecycle.js.map +1 -0
- package/dist/workspace/manifest.d.ts +47 -0
- package/dist/workspace/manifest.d.ts.map +1 -0
- package/dist/workspace/manifest.js +243 -0
- package/dist/workspace/manifest.js.map +1 -0
- package/dist/workspace/paths.d.ts +14 -0
- package/dist/workspace/paths.d.ts.map +1 -0
- package/dist/workspace/paths.js +67 -0
- package/dist/workspace/paths.js.map +1 -0
- package/dist/workspace/schema.d.ts +15 -0
- package/dist/workspace/schema.d.ts.map +1 -0
- package/dist/workspace/schema.js +33 -0
- package/dist/workspace/schema.js.map +1 -0
- package/dist/workspace/selection-screen.d.ts +4 -0
- package/dist/workspace/selection-screen.d.ts.map +1 -0
- package/dist/workspace/selection-screen.js +216 -0
- package/dist/workspace/selection-screen.js.map +1 -0
- package/dist/workspace/selection-service.d.ts +16 -0
- package/dist/workspace/selection-service.d.ts.map +1 -0
- package/dist/workspace/selection-service.js +65 -0
- package/dist/workspace/selection-service.js.map +1 -0
- package/node_modules/@xterm/headless/README.md +31 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.js +2 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.js.map +1 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs.map +7 -0
- package/node_modules/@xterm/headless/package.json +29 -0
- package/node_modules/@xterm/headless/typings/xterm-headless.d.ts +1385 -0
- package/package.json +75 -0
package/dist/api/http.js
ADDED
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import { EXIT_CODES, CunaError } from "../core/errors.js";
|
|
2
|
+
import { CredentialBoundaryError } from "../credentials/errors.js";
|
|
3
|
+
import { INTERNAL_DEFECT_HINT, OFF_CONTRACT_RESPONSE_HINT, SUPPORT_URL, automationCredentialHint, } from "../core/product-web.js";
|
|
4
|
+
import { containsCredentialValue, isAccessToken, isProblemType, isProblemTypeForCode, isTransportCredential, } from "../core/namespace.js";
|
|
5
|
+
import { isIdempotencyKey, isObject, safeReasonCode } from "../core/validation.js";
|
|
6
|
+
import { isOpenCodeSupervisorUpgradeReason, openCodeSupervisorUpgradeRequired, } from "../machines/opencode-supervisor.js";
|
|
7
|
+
import { DEFAULT_REQUEST_BUDGET_MS, observationBudgetElapsed, } from "../core/observation-budget.js";
|
|
8
|
+
import { CLI_VERSION } from "../version.js";
|
|
9
|
+
const MAX_RESPONSE_BYTES = 16 * 1024 * 1024;
|
|
10
|
+
// A 16 MiB body is never a legitimate answer on this contract, so the remedy is
|
|
11
|
+
// to narrow the request rather than to retry it.
|
|
12
|
+
const OVERSIZED_RESPONSE_HINT = "Narrow the request with --limit or a cursor. Retrying unchanged returns the same oversized body.";
|
|
13
|
+
const PROBLEM_CODE = /^[a-z][a-z0-9_]{2,63}$/u;
|
|
14
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
15
|
+
const PROBLEM_ACTIONS = new Set(["retry", "sign_in", "open_web", "contact_support", "none"]);
|
|
16
|
+
// Server prose reaches a terminal, so it may not carry control or format
|
|
17
|
+
// characters, and it may never echo a credential back at the user. Same guard
|
|
18
|
+
// `safePublicString` applies in `api/contracts.ts`; it rejects here instead of
|
|
19
|
+
// throwing, because an unusable sentence must degrade to the generic message
|
|
20
|
+
// rather than turn a server refusal into a client crash.
|
|
21
|
+
const FORBIDDEN_PUBLIC_CHARACTER = /[\p{Cc}\p{Cf}]/u;
|
|
22
|
+
function publicProse(value) {
|
|
23
|
+
if (FORBIDDEN_PUBLIC_CHARACTER.test(value) || containsCredentialValue(value))
|
|
24
|
+
return undefined;
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
const AGENT_SESSION_CREATE_PATH = /^\/v1\/sessions\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/agent-sessions$/u;
|
|
28
|
+
const WORKSPACE_SYNC_CAPABILITIES = Object.freeze([
|
|
29
|
+
"atomic_generation_commit",
|
|
30
|
+
"bounded_manifest_pages",
|
|
31
|
+
"content_digest_verification",
|
|
32
|
+
"explicit_reconciliation",
|
|
33
|
+
"ordered_generation_changes",
|
|
34
|
+
"policy_bound_admission",
|
|
35
|
+
]);
|
|
36
|
+
const NO_WORKSPACE_SYNC_CAPABILITIES = Object.freeze([]);
|
|
37
|
+
function isOpenCodeAgentSessionCreate(input) {
|
|
38
|
+
return input.method === "POST" &&
|
|
39
|
+
AGENT_SESSION_CREATE_PATH.test(input.path) &&
|
|
40
|
+
isObject(input.requestBody) &&
|
|
41
|
+
input.requestBody.agent === "opencode";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The subject a request path names, when it names one.
|
|
45
|
+
*
|
|
46
|
+
* A 404 was answered two different ways depending on how the command reached
|
|
47
|
+
* the server: the ones that pass a resource-scoped capability check named the
|
|
48
|
+
* Machine or AgentSession, and the rest got "The requested Cuna resource or
|
|
49
|
+
* operation was not found." The vaguer half is the half a reader meets straight
|
|
50
|
+
* after typing an id, which is the worst place for it. One 404, one sentence.
|
|
51
|
+
*/
|
|
52
|
+
function notFoundSubject(path) {
|
|
53
|
+
// A resource-scoped capability query carries its subject in the query string
|
|
54
|
+
// rather than the path, and it is the one command whose entire job is a
|
|
55
|
+
// resource-scoped question -- so it is the last place that should answer with
|
|
56
|
+
// the general sentence.
|
|
57
|
+
const scoped = /^\/v\d+\/capabilities\?(?=.*\bscope=(machine|agent_session)\b)(?=.*\bresource_id=([^&]+))/u.exec(path);
|
|
58
|
+
if (scoped !== null && scoped[1] !== undefined && scoped[2] !== undefined) {
|
|
59
|
+
return {
|
|
60
|
+
subject: scoped[1] === "machine" ? "Machine" : "AgentSession",
|
|
61
|
+
id: decodeURIComponent(scoped[2]),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const match = /^\/v\d+\/(sessions|agent-sessions|api-keys|workspace-bindings)\/([^/?]+)/u.exec(path);
|
|
65
|
+
const id = match?.[2];
|
|
66
|
+
if (match === null || id === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
const subject = match[1] === "sessions"
|
|
69
|
+
? "Machine"
|
|
70
|
+
: match[1] === "agent-sessions"
|
|
71
|
+
? "AgentSession"
|
|
72
|
+
: match[1] === "api-keys"
|
|
73
|
+
? "API key"
|
|
74
|
+
: "workspace binding";
|
|
75
|
+
return { subject, id: decodeURIComponent(id) };
|
|
76
|
+
}
|
|
77
|
+
function machineIdFromAgentSessionCreatePath(path) {
|
|
78
|
+
return AGENT_SESSION_CREATE_PATH.exec(path)?.[1];
|
|
79
|
+
}
|
|
80
|
+
function problemMetadata(body, expectedStatus) {
|
|
81
|
+
if (!isObject(body))
|
|
82
|
+
return undefined;
|
|
83
|
+
if (Object.hasOwn(body, "selected_protocol") || Object.hasOwn(body, "capabilities")) {
|
|
84
|
+
return workspaceSyncProblemMetadata(body, expectedStatus);
|
|
85
|
+
}
|
|
86
|
+
const required = new Set(["type", "title", "status", "code", "request_id", "retryable"]);
|
|
87
|
+
const optional = new Set(["detail", "action"]);
|
|
88
|
+
const keys = Object.keys(body);
|
|
89
|
+
if ([...required].some((key) => !Object.hasOwn(body, key)) ||
|
|
90
|
+
keys.some((key) => !required.has(key) && !optional.has(key)) ||
|
|
91
|
+
typeof body.type !== "string" || !isProblemType(body.type) ||
|
|
92
|
+
typeof body.title !== "string" || body.title.length < 1 || body.title.length > 120 ||
|
|
93
|
+
!Number.isSafeInteger(body.status) || body.status !== expectedStatus ||
|
|
94
|
+
typeof body.code !== "string" || !PROBLEM_CODE.test(body.code) ||
|
|
95
|
+
typeof body.request_id !== "string" || !UUID.test(body.request_id) ||
|
|
96
|
+
typeof body.retryable !== "boolean" ||
|
|
97
|
+
(Object.hasOwn(body, "detail") &&
|
|
98
|
+
(typeof body.detail !== "string" || body.detail.length > 500)) ||
|
|
99
|
+
(Object.hasOwn(body, "action") &&
|
|
100
|
+
(typeof body.action !== "string" || !PROBLEM_ACTIONS.has(body.action)))) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const title = publicProse(body.title);
|
|
104
|
+
const detail = typeof body.detail === "string" ? publicProse(body.detail) : undefined;
|
|
105
|
+
return Object.freeze({
|
|
106
|
+
code: body.code,
|
|
107
|
+
requestId: body.request_id,
|
|
108
|
+
retryable: body.retryable,
|
|
109
|
+
...(title === undefined ? {} : { title }),
|
|
110
|
+
...(detail === undefined ? {} : { detail }),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function workspaceSyncProblemMetadata(body, expectedStatus) {
|
|
114
|
+
const required = new Set([
|
|
115
|
+
"type", "title", "status", "code", "request_id", "retryable", "action",
|
|
116
|
+
"selected_protocol", "capabilities", "detail",
|
|
117
|
+
]);
|
|
118
|
+
const keys = Object.keys(body);
|
|
119
|
+
const selectedProtocol = body.selected_protocol;
|
|
120
|
+
const capabilities = body.capabilities;
|
|
121
|
+
const hasSelectedProtocol = selectedProtocol === 1 || selectedProtocol === 2;
|
|
122
|
+
if (keys.length !== required.size ||
|
|
123
|
+
[...required].some((key) => !Object.hasOwn(body, key)) ||
|
|
124
|
+
typeof body.code !== "string" ||
|
|
125
|
+
!/^workspace_sync_[a-z0-9_]{2,48}$/u.test(body.code) ||
|
|
126
|
+
typeof body.type !== "string" || !isProblemTypeForCode(body.type, body.code) ||
|
|
127
|
+
typeof body.title !== "string" || body.title.length < 1 || body.title.length > 120 ||
|
|
128
|
+
!Number.isSafeInteger(body.status) || body.status !== expectedStatus ||
|
|
129
|
+
typeof body.request_id !== "string" || !UUID.test(body.request_id) ||
|
|
130
|
+
typeof body.retryable !== "boolean" ||
|
|
131
|
+
(body.action !== "retry" && body.action !== "none") ||
|
|
132
|
+
typeof body.detail !== "string" || body.detail.length < 1 || body.detail.length > 500 ||
|
|
133
|
+
(!hasSelectedProtocol && selectedProtocol !== null) ||
|
|
134
|
+
!Array.isArray(capabilities) ||
|
|
135
|
+
(selectedProtocol === null
|
|
136
|
+
? capabilities.length !== 0
|
|
137
|
+
: capabilities.length !== WORKSPACE_SYNC_CAPABILITIES.length ||
|
|
138
|
+
capabilities.some((value, index) => value !== WORKSPACE_SYNC_CAPABILITIES[index]))) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
code: body.code,
|
|
143
|
+
requestId: body.request_id,
|
|
144
|
+
retryable: body.retryable,
|
|
145
|
+
selectedProtocol: selectedProtocol,
|
|
146
|
+
capabilities: selectedProtocol === null
|
|
147
|
+
? NO_WORKSPACE_SYNC_CAPABILITIES
|
|
148
|
+
: WORKSPACE_SYNC_CAPABILITIES,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build the error for a non-2xx response.
|
|
153
|
+
*
|
|
154
|
+
* `body` is the decoded response body, or `undefined` when the body was not
|
|
155
|
+
* JSON at all. The distinction is load-bearing: a status this API produced
|
|
156
|
+
* always carries a JSON object (a Problem document, or at minimum
|
|
157
|
+
* `{"error":"…"}`), because the request asks for exactly
|
|
158
|
+
* `application/json, application/problem+json`. A non-JSON body at an error
|
|
159
|
+
* status therefore came from a layer in front of the API that has no route for
|
|
160
|
+
* this path — measured: `GET https://api.getcuna.com/v1/capabilities` answers
|
|
161
|
+
* `404` with `content-type: text/plain` and the body `404 Not Found`, while
|
|
162
|
+
* `GET /v1/me` answers `401` with `application/json`.
|
|
163
|
+
*/
|
|
164
|
+
function apiError(input) {
|
|
165
|
+
const { status, requestId, body, credentialKind } = input;
|
|
166
|
+
const problem = problemMetadata(body, status);
|
|
167
|
+
const reason = problem?.code ??
|
|
168
|
+
(isObject(body) ? safeReasonCode(body.code) ?? safeReasonCode(body.error) : undefined);
|
|
169
|
+
// Not every route on this API answers with a Problem document. `sessions.start`
|
|
170
|
+
// is declared `"errorModel": "legacy"` in the contract and answers
|
|
171
|
+
// `{"error": "<sentence>"}` with no code, title or detail — so `problem` is
|
|
172
|
+
// undefined and the sentence is not a reason code either, because it has
|
|
173
|
+
// spaces. Measured: the web console renders exactly this string while the CLI
|
|
174
|
+
// showed only `http_status: 409`. The sentence is the whole actionable
|
|
175
|
+
// content, so read it as prose under the same terminal-safety guard.
|
|
176
|
+
const legacyReason = isObject(body) && typeof body.error === "string" &&
|
|
177
|
+
body.error.length >= 1 && body.error.length <= 500
|
|
178
|
+
? publicProse(body.error)
|
|
179
|
+
: undefined;
|
|
180
|
+
const effectiveRequestId = problem?.requestId ?? requestId;
|
|
181
|
+
const details = {
|
|
182
|
+
http_status: status,
|
|
183
|
+
...(effectiveRequestId === undefined ? {} : { request_id: effectiveRequestId }),
|
|
184
|
+
...(reason === undefined ? {} : { reason }),
|
|
185
|
+
...(problem?.selectedProtocol === undefined
|
|
186
|
+
? {}
|
|
187
|
+
: { selected_protocol: problem.selectedProtocol }),
|
|
188
|
+
...(problem?.capabilities === undefined
|
|
189
|
+
? {}
|
|
190
|
+
: { capabilities: problem.capabilities }),
|
|
191
|
+
};
|
|
192
|
+
if (status === 401) {
|
|
193
|
+
return new CunaError({
|
|
194
|
+
code: "cuna.auth.rejected",
|
|
195
|
+
message: "Cuna rejected the current credential.",
|
|
196
|
+
exitCode: EXIT_CODES.auth,
|
|
197
|
+
// The api_key arm used to say "Replace CUNA_API_KEY with a valid
|
|
198
|
+
// automation credential" and name no source, which is the same dead end
|
|
199
|
+
// the sign-in path had. It now shares the one sentence that does.
|
|
200
|
+
hint: credentialKind === "interactive"
|
|
201
|
+
? "The access token was refused. Retry the command so Cuna can obtain a fresh token from the encrypted local session."
|
|
202
|
+
: credentialKind === "api_key"
|
|
203
|
+
? `The current automation credential was refused. ${automationCredentialHint()}`
|
|
204
|
+
: `Run \`cuna login\`, or provide an automation credential. ${automationCredentialHint()}`,
|
|
205
|
+
...(problem === undefined ? {} : { retryable: problem.retryable }),
|
|
206
|
+
details,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (status === 403) {
|
|
210
|
+
return new CunaError({
|
|
211
|
+
code: "cuna.policy.denied",
|
|
212
|
+
message: "Cuna denied this operation.",
|
|
213
|
+
exitCode: EXIT_CODES.policy,
|
|
214
|
+
hint: "The credential authenticated but is not permitted to do this. Retrying will not change the outcome.",
|
|
215
|
+
...(problem === undefined ? {} : { retryable: problem.retryable }),
|
|
216
|
+
details,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// This is not a retryable state conflict: the durable create authority
|
|
220
|
+
// rejected an AgentSession because its provider is not installed on the
|
|
221
|
+
// selected Machine. Keep the server's reason in safe details, but turn it
|
|
222
|
+
// into the same actionable provider-selection result the CLI emits when its
|
|
223
|
+
// immediately preceding machine observation detects the mismatch.
|
|
224
|
+
if (status === 409 && reason === "agent_session_provider_unavailable") {
|
|
225
|
+
return new CunaError({
|
|
226
|
+
code: "cuna.agent.provider_not_installed",
|
|
227
|
+
message: "OpenCode is not installed on the selected Machine.",
|
|
228
|
+
exitCode: EXIT_CODES.unsupported,
|
|
229
|
+
hint: "Choose a running Machine configured for OpenCode, or create one with `cuna machines create --agent opencode --name NAME --yes`.",
|
|
230
|
+
...(problem === undefined ? {} : { retryable: problem.retryable }),
|
|
231
|
+
details,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// This is an authoritative refusal from the durable AgentSession create
|
|
235
|
+
// authority, not an attach failure and not an ambiguous network outcome.
|
|
236
|
+
// Keep the route fence here: `supervisor_upgrade_required` also occurs on
|
|
237
|
+
// terminal connections, where saying that no AgentSession was created would
|
|
238
|
+
// be false.
|
|
239
|
+
if (status === 409 &&
|
|
240
|
+
problem !== undefined &&
|
|
241
|
+
isOpenCodeAgentSessionCreate(input) &&
|
|
242
|
+
isOpenCodeSupervisorUpgradeReason(problem.code)) {
|
|
243
|
+
const machineId = machineIdFromAgentSessionCreatePath(input.path);
|
|
244
|
+
return openCodeSupervisorUpgradeRequired({
|
|
245
|
+
details,
|
|
246
|
+
...(machineId === undefined ? {} : { machineId }),
|
|
247
|
+
retryable: problem.retryable,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (status === 409) {
|
|
251
|
+
// The server names which state conflicts; this used to answer every 409
|
|
252
|
+
// with one fixed sentence and throw that away, so the user was told a
|
|
253
|
+
// conflict exists but never which one. Prefer the server's own words and
|
|
254
|
+
// keep the generic sentence only as the fallback for a refusal that
|
|
255
|
+
// carried none.
|
|
256
|
+
return new CunaError({
|
|
257
|
+
code: "cuna.remote.conflict",
|
|
258
|
+
message: problem?.title ?? legacyReason ??
|
|
259
|
+
"Cuna could not apply the operation because current state conflicts with it.",
|
|
260
|
+
exitCode: EXIT_CODES.conflict,
|
|
261
|
+
hint: problem?.detail ??
|
|
262
|
+
"Re-read the resource and decide again from its current state. Repeating this request unchanged repeats this answer.",
|
|
263
|
+
...(problem === undefined ? {} : { retryable: problem.retryable }),
|
|
264
|
+
details,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// A 5xx the server marked final. `retryable` is the server's own word, so it
|
|
268
|
+
// decides, not the status class: exit 5 tells a caller to wait, and a refusal
|
|
269
|
+
// is not something waiting can clear.
|
|
270
|
+
//
|
|
271
|
+
// `hint: problem.detail` is load-bearing. One `code` may be emitted from
|
|
272
|
+
// several sites and the catalogue fixes `title` per code, so `detail` is the
|
|
273
|
+
// only field that distinguishes them.
|
|
274
|
+
if (status >= 500 && problem !== undefined && !problem.retryable) {
|
|
275
|
+
return new CunaError({
|
|
276
|
+
code: "cuna.remote.rejected",
|
|
277
|
+
message: problem.title ?? "Cuna rejected the request.",
|
|
278
|
+
exitCode: EXIT_CODES.remote,
|
|
279
|
+
hint: problem.detail ?? OFF_CONTRACT_RESPONSE_HINT,
|
|
280
|
+
retryable: false,
|
|
281
|
+
details,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
if (status === 429 || status >= 500) {
|
|
285
|
+
// A 5xx that carries the server's own sentence (a Problem title, or the
|
|
286
|
+
// legacy `{"error": "<sentence>"}` that `sessions.start` answers) must
|
|
287
|
+
// show it: nine identical "temporarily unavailable" answers in a row on
|
|
288
|
+
// 2026-09-02 hid a reason the server had named every time.
|
|
289
|
+
const serverSentence = status === 429 ? undefined : problem?.title ?? legacyReason;
|
|
290
|
+
return new CunaError({
|
|
291
|
+
code: status === 429 ? "cuna.network.rate_limited" : "cuna.network.service_unavailable",
|
|
292
|
+
message: status === 429
|
|
293
|
+
? "Cuna is rate limiting this request."
|
|
294
|
+
: serverSentence ?? "The Cuna service is temporarily unavailable.",
|
|
295
|
+
exitCode: EXIT_CODES.network,
|
|
296
|
+
hint: status === 429
|
|
297
|
+
? "Wait before retrying. No change was applied by this request."
|
|
298
|
+
: problem?.detail ??
|
|
299
|
+
"No authoritative answer was received. Retry a read; do not assume a write was applied.",
|
|
300
|
+
retryable: problem?.retryable ?? true,
|
|
301
|
+
details: {
|
|
302
|
+
...details,
|
|
303
|
+
...(legacyReason === undefined || problem !== undefined ? {} : { server_error: legacyReason }),
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (status === 404 && !input.apiEncodedBody) {
|
|
308
|
+
// Reporting this as `cuna.remote.malformed_response` — which is what
|
|
309
|
+
// happened while the body was parsed before the status was read — names the
|
|
310
|
+
// layer that noticed the failure instead of the layer that caused it. The
|
|
311
|
+
// response is not malformed; this deployment does not implement the
|
|
312
|
+
// operation. Production serves 26 of the 57 operations this build knows, so
|
|
313
|
+
// this is the majority answer today, not an edge case.
|
|
314
|
+
return new CunaError({
|
|
315
|
+
code: "cuna.remote.operation_not_served",
|
|
316
|
+
message: `The Cuna API at ${input.origin} does not serve ${input.method} ${input.path}.`,
|
|
317
|
+
exitCode: EXIT_CODES.unsupported,
|
|
318
|
+
hint: `The deployed Cuna API does not implement this operation. Run \`cuna version --json\` and report it with that record at ${SUPPORT_URL} if it should be available.`,
|
|
319
|
+
details: {
|
|
320
|
+
http_status: status,
|
|
321
|
+
...(requestId === undefined ? {} : { request_id: requestId }),
|
|
322
|
+
method: input.method,
|
|
323
|
+
path: input.path,
|
|
324
|
+
api_origin: input.origin,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
const subject = status === 404 ? notFoundSubject(input.path) : undefined;
|
|
329
|
+
return new CunaError({
|
|
330
|
+
code: status === 404 ? "cuna.remote.not_found" : "cuna.remote.rejected",
|
|
331
|
+
message: status !== 404
|
|
332
|
+
? "Cuna rejected the request."
|
|
333
|
+
: subject === undefined
|
|
334
|
+
? "The requested Cuna resource or operation was not found."
|
|
335
|
+
: `${subject.subject} ${subject.id} is not available to this account.`,
|
|
336
|
+
exitCode: EXIT_CODES.remote,
|
|
337
|
+
hint: status === 404
|
|
338
|
+
? subject?.subject === "Machine"
|
|
339
|
+
? "Run `cuna machines list` to see the Machines on this account."
|
|
340
|
+
: subject?.subject === "AgentSession"
|
|
341
|
+
? "Run `cuna agent-sessions list --machine <id>` to see the AgentSessions on a Machine."
|
|
342
|
+
: "The identifier does not name a resource this account can see. Re-list to get a current one."
|
|
343
|
+
: OFF_CONTRACT_RESPONSE_HINT,
|
|
344
|
+
...(problem === undefined ? {} : { retryable: problem.retryable }),
|
|
345
|
+
details,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async function readLimited(response) {
|
|
349
|
+
const declared = response.headers.get("content-length");
|
|
350
|
+
if (declared !== null && Number(declared) > MAX_RESPONSE_BYTES) {
|
|
351
|
+
throw new CunaError({
|
|
352
|
+
code: "cuna.remote.response_too_large",
|
|
353
|
+
message: "Cuna returned an oversized response.",
|
|
354
|
+
exitCode: EXIT_CODES.remote,
|
|
355
|
+
hint: OVERSIZED_RESPONSE_HINT,
|
|
356
|
+
details: { predicate: "response_within_size_limit", limit_bytes: MAX_RESPONSE_BYTES },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (response.body === null)
|
|
360
|
+
return new Uint8Array();
|
|
361
|
+
const reader = response.body.getReader();
|
|
362
|
+
const chunks = [];
|
|
363
|
+
let total = 0;
|
|
364
|
+
for (;;) {
|
|
365
|
+
const { done, value } = await reader.read();
|
|
366
|
+
if (done)
|
|
367
|
+
break;
|
|
368
|
+
if (value !== undefined) {
|
|
369
|
+
total += value.byteLength;
|
|
370
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
371
|
+
await reader.cancel();
|
|
372
|
+
throw new CunaError({
|
|
373
|
+
code: "cuna.remote.response_too_large",
|
|
374
|
+
message: "Cuna returned an oversized response.",
|
|
375
|
+
exitCode: EXIT_CODES.remote,
|
|
376
|
+
hint: OVERSIZED_RESPONSE_HINT,
|
|
377
|
+
details: { predicate: "response_within_size_limit", limit_bytes: MAX_RESPONSE_BYTES },
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
chunks.push(value);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const result = new Uint8Array(total);
|
|
384
|
+
let offset = 0;
|
|
385
|
+
for (const chunk of chunks) {
|
|
386
|
+
result.set(chunk, offset);
|
|
387
|
+
offset += chunk.byteLength;
|
|
388
|
+
}
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
function decodeJson(bytes) {
|
|
392
|
+
if (bytes.byteLength === 0)
|
|
393
|
+
return { decoded: true, value: null };
|
|
394
|
+
try {
|
|
395
|
+
return {
|
|
396
|
+
decoded: true,
|
|
397
|
+
value: JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
catch (cause) {
|
|
401
|
+
return { decoded: false, cause };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Decode a 2xx body, or fail naming the operation whose body would not parse.
|
|
406
|
+
*
|
|
407
|
+
* This is the other half of `cuna.remote.malformed_response`: not "the shape is
|
|
408
|
+
* wrong" but "these bytes are not JSON at all". The two are worth telling apart
|
|
409
|
+
* — a `predicate` of `response_body_is_json` points at a proxy or a truncated
|
|
410
|
+
* transfer, while a field-level predicate points at the API itself — so the
|
|
411
|
+
* detail says which, using the same key names the decode path uses.
|
|
412
|
+
*/
|
|
413
|
+
function parseJson(bytes, request) {
|
|
414
|
+
const result = decodeJson(bytes);
|
|
415
|
+
if (result.decoded)
|
|
416
|
+
return result.value;
|
|
417
|
+
throw new CunaError({
|
|
418
|
+
code: "cuna.remote.malformed_response",
|
|
419
|
+
message: "Cuna returned a response body that is not valid JSON.",
|
|
420
|
+
exitCode: EXIT_CODES.remote,
|
|
421
|
+
hint: OFF_CONTRACT_RESPONSE_HINT,
|
|
422
|
+
details: {
|
|
423
|
+
operation: `${request.method} ${request.path}`,
|
|
424
|
+
predicate: "response_body_is_json",
|
|
425
|
+
// A byte count is a property of the transfer, not of the payload's
|
|
426
|
+
// contents, so it discloses nothing a proxy log would not already show.
|
|
427
|
+
response_bytes: bytes.byteLength,
|
|
428
|
+
},
|
|
429
|
+
cause: result.cause,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
const CONNECT_PHASE_CODES = new Set([
|
|
433
|
+
// undici gave up waiting for the socket to connect.
|
|
434
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
435
|
+
// The kernel refused or could not route the connect.
|
|
436
|
+
"ECONNREFUSED",
|
|
437
|
+
"EHOSTUNREACH",
|
|
438
|
+
"ENETUNREACH",
|
|
439
|
+
"EADDRNOTAVAIL",
|
|
440
|
+
// Name resolution failed, permanently or transiently.
|
|
441
|
+
"ENOTFOUND",
|
|
442
|
+
"EAI_AGAIN",
|
|
443
|
+
"EAI_FAIL",
|
|
444
|
+
"EAI_NONAME",
|
|
445
|
+
// The TLS handshake was refused. Node reports certificate faults with the
|
|
446
|
+
// OpenSSL verify-result names, and handshake protocol faults as `EPROTO`
|
|
447
|
+
// (only trusted here when the syscall confirms it happened during connect)
|
|
448
|
+
// or `ERR_TLS_*`.
|
|
449
|
+
"CERT_HAS_EXPIRED",
|
|
450
|
+
"CERT_NOT_YET_VALID",
|
|
451
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
452
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
453
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
454
|
+
"UNABLE_TO_GET_ISSUER_CERT",
|
|
455
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
456
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
457
|
+
"ERR_TLS_HANDSHAKE_TIMEOUT",
|
|
458
|
+
"ERR_SSL_WRONG_VERSION_NUMBER",
|
|
459
|
+
]);
|
|
460
|
+
// A syscall name is stronger evidence than a code: `ETIMEDOUT` on `connect`
|
|
461
|
+
// never wrote a byte, while `ETIMEDOUT` on `write` may have.
|
|
462
|
+
const CONNECT_PHASE_SYSCALLS = new Set(["connect", "getaddrinfo"]);
|
|
463
|
+
/**
|
|
464
|
+
* Walk the error, its `cause` chain and any `AggregateError.errors` (Node's
|
|
465
|
+
* Happy Eyeballs connect reports one `ECONNREFUSED` per address family that
|
|
466
|
+
* way) looking for a `connect`-phase witness. Anything else — headers or body
|
|
467
|
+
* timeouts, a reset after the request was written, a bare `fetch failed` with
|
|
468
|
+
* no cause — is `response`: the fail-closed answer, because the CLI cannot
|
|
469
|
+
* prove the request was not sent.
|
|
470
|
+
*/
|
|
471
|
+
function transportFailurePhase(error) {
|
|
472
|
+
const seen = new Set();
|
|
473
|
+
const pending = [error];
|
|
474
|
+
while (pending.length > 0) {
|
|
475
|
+
const current = pending.pop();
|
|
476
|
+
if (typeof current !== "object" || current === null || seen.has(current))
|
|
477
|
+
continue;
|
|
478
|
+
seen.add(current);
|
|
479
|
+
if (seen.size > 16)
|
|
480
|
+
break;
|
|
481
|
+
const record = current;
|
|
482
|
+
if (typeof record.syscall === "string" && CONNECT_PHASE_SYSCALLS.has(record.syscall))
|
|
483
|
+
return "connect";
|
|
484
|
+
if (typeof record.code === "string" && CONNECT_PHASE_CODES.has(record.code))
|
|
485
|
+
return "connect";
|
|
486
|
+
if (Array.isArray(record.errors))
|
|
487
|
+
pending.push(...record.errors);
|
|
488
|
+
pending.push(record.cause);
|
|
489
|
+
}
|
|
490
|
+
return "response";
|
|
491
|
+
}
|
|
492
|
+
function transportFailure(input) {
|
|
493
|
+
const { request, phase, attempts } = input;
|
|
494
|
+
if (phase === "connect") {
|
|
495
|
+
// Never sent, so nothing was applied, so every method may retry. The
|
|
496
|
+
// sentence says so: the old one ("failed before an authoritative result
|
|
497
|
+
// was received") left the user unable to tell a lost answer from a lost
|
|
498
|
+
// connection, and those call for different next steps.
|
|
499
|
+
return new CunaError({
|
|
500
|
+
code: "cuna.network.failed",
|
|
501
|
+
message: `Cuna could not be reached: the connection was not established after ${attempts} attempts, so the request was never sent.`,
|
|
502
|
+
exitCode: EXIT_CODES.network,
|
|
503
|
+
hint: "No change was applied by this request. Check connectivity to the API origin shown by `cuna config get`, then retry.",
|
|
504
|
+
retryable: true,
|
|
505
|
+
details: {
|
|
506
|
+
method: request.method,
|
|
507
|
+
path: request.path,
|
|
508
|
+
phase,
|
|
509
|
+
remote_outcome: "not_sent",
|
|
510
|
+
attempts,
|
|
511
|
+
},
|
|
512
|
+
cause: input.cause,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
// A failure after the request may have been written cannot prove whether a
|
|
516
|
+
// mutating request reached the authority. An idempotency key alone is not
|
|
517
|
+
// reconciliation evidence, so mutations stay fail-closed until a producer
|
|
518
|
+
// contract exposes authoritative operation-status reconciliation.
|
|
519
|
+
//
|
|
520
|
+
// This does not govern the timeout arm, and that separation is deliberate.
|
|
521
|
+
// A budget elapsing is not a transport failure: nothing failed, the CLI
|
|
522
|
+
// stopped waiting. `core/observation-budget.ts` owns that answer and makes it
|
|
523
|
+
// retryable at a single site.
|
|
524
|
+
return new CunaError({
|
|
525
|
+
code: "cuna.network.failed",
|
|
526
|
+
message: "The Cuna request was sent, but the connection failed before an authoritative result was received.",
|
|
527
|
+
exitCode: EXIT_CODES.network,
|
|
528
|
+
hint: request.settleWith === undefined
|
|
529
|
+
? "Check connectivity to the API origin shown by `cuna config get`. A mutating request may still have been applied; re-read the resource before re-issuing it."
|
|
530
|
+
: `The operation may still have been applied. Inspect its outcome with \`${request.settleWith}\` before re-issuing it.`,
|
|
531
|
+
retryable: request.method === "GET",
|
|
532
|
+
details: {
|
|
533
|
+
method: request.method,
|
|
534
|
+
path: request.path,
|
|
535
|
+
phase,
|
|
536
|
+
remote_outcome: "unobserved",
|
|
537
|
+
attempts,
|
|
538
|
+
},
|
|
539
|
+
cause: input.cause,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
export function createHttpTransport(input) {
|
|
543
|
+
const credentialAuthorities = [
|
|
544
|
+
input.apiKey,
|
|
545
|
+
input.bearerToken,
|
|
546
|
+
input.bearerTokenProvider,
|
|
547
|
+
].filter((value) => value !== undefined).length;
|
|
548
|
+
if (credentialAuthorities > 1) {
|
|
549
|
+
throw new TypeError("HTTP transport accepts exactly one credential authority.");
|
|
550
|
+
}
|
|
551
|
+
const credential = input.apiKey ?? input.bearerToken;
|
|
552
|
+
const credentialKind = input.apiKey !== undefined
|
|
553
|
+
? "api_key"
|
|
554
|
+
: input.bearerToken !== undefined || input.bearerTokenProvider !== undefined
|
|
555
|
+
? "interactive"
|
|
556
|
+
: "anonymous";
|
|
557
|
+
if (credential !== undefined && !isTransportCredential(credential)) {
|
|
558
|
+
throw new TypeError("HTTP transport credential is invalid.");
|
|
559
|
+
}
|
|
560
|
+
const fetcher = input.fetch ?? globalThis.fetch;
|
|
561
|
+
// Precedence, in one expression so it cannot be spelled differently twice:
|
|
562
|
+
// what the user typed, then what the operation declares it is worth, then the
|
|
563
|
+
// one default.
|
|
564
|
+
const budgetFor = (request) => input.timeoutMs ?? request.budgetMs ?? DEFAULT_REQUEST_BUDGET_MS;
|
|
565
|
+
return Object.freeze({
|
|
566
|
+
async request(request) {
|
|
567
|
+
if (request.signal?.aborted === true) {
|
|
568
|
+
throw new CunaError({
|
|
569
|
+
code: "cuna.network.cancelled",
|
|
570
|
+
message: "The Cuna request was cancelled.",
|
|
571
|
+
exitCode: EXIT_CODES.network,
|
|
572
|
+
hint: "The request was cancelled before dispatch, so nothing was sent.",
|
|
573
|
+
retryable: false,
|
|
574
|
+
cause: request.signal.reason,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
if (!request.path.startsWith("/v1/") || request.path.includes("..") || request.path.includes("?")) {
|
|
578
|
+
throw new CunaError({
|
|
579
|
+
code: "cuna.internal.invalid_api_path",
|
|
580
|
+
message: "Cuna refused an invalid API operation.",
|
|
581
|
+
exitCode: EXIT_CODES.internal,
|
|
582
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
if (request.machineCreateRequestId !== undefined &&
|
|
586
|
+
!UUID.test(request.machineCreateRequestId)) {
|
|
587
|
+
throw new CunaError({
|
|
588
|
+
code: "cuna.internal.invalid_machine_create_request_id",
|
|
589
|
+
message: "Cuna refused an invalid machine-create request identity.",
|
|
590
|
+
exitCode: EXIT_CODES.internal,
|
|
591
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const target = new URL(request.path, `${input.baseUrl}/`);
|
|
595
|
+
if (target.origin !== input.baseUrl || target.pathname !== request.path) {
|
|
596
|
+
throw new CunaError({
|
|
597
|
+
code: "cuna.internal.invalid_api_origin",
|
|
598
|
+
message: "Cuna refused an invalid API origin.",
|
|
599
|
+
exitCode: EXIT_CODES.internal,
|
|
600
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
for (const [key, value] of Object.entries(request.query ?? {})) {
|
|
604
|
+
if (value !== undefined)
|
|
605
|
+
target.searchParams.set(key, value);
|
|
606
|
+
}
|
|
607
|
+
const budgetMs = budgetFor(request);
|
|
608
|
+
let body;
|
|
609
|
+
let contentType;
|
|
610
|
+
let contentLength;
|
|
611
|
+
if (request.body !== undefined) {
|
|
612
|
+
if (request.body instanceof Uint8Array) {
|
|
613
|
+
if (request.contentType !== "application/octet-stream") {
|
|
614
|
+
throw new CunaError({
|
|
615
|
+
code: "cuna.usage.invalid_body",
|
|
616
|
+
message: "Binary request bodies require application/octet-stream.",
|
|
617
|
+
exitCode: EXIT_CODES.usage,
|
|
618
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
const binary = new Uint8Array(request.body.byteLength);
|
|
622
|
+
binary.set(request.body);
|
|
623
|
+
body = binary.buffer;
|
|
624
|
+
contentType = request.contentType;
|
|
625
|
+
contentLength = request.body.byteLength;
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
if (request.contentType !== undefined &&
|
|
629
|
+
request.contentType !== "application/json; charset=utf-8") {
|
|
630
|
+
throw new CunaError({
|
|
631
|
+
code: "cuna.usage.invalid_body",
|
|
632
|
+
message: "Structured request bodies require JSON content type.",
|
|
633
|
+
exitCode: EXIT_CODES.usage,
|
|
634
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
try {
|
|
638
|
+
body = JSON.stringify(request.body);
|
|
639
|
+
contentType = "application/json; charset=utf-8";
|
|
640
|
+
}
|
|
641
|
+
catch (cause) {
|
|
642
|
+
throw new CunaError({
|
|
643
|
+
code: "cuna.usage.invalid_body",
|
|
644
|
+
message: "The request body cannot be encoded.",
|
|
645
|
+
exitCode: EXIT_CODES.usage,
|
|
646
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
647
|
+
cause,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else if (request.contentType !== undefined) {
|
|
653
|
+
throw new CunaError({
|
|
654
|
+
code: "cuna.usage.invalid_body",
|
|
655
|
+
message: "A request content type requires a request body.",
|
|
656
|
+
exitCode: EXIT_CODES.usage,
|
|
657
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const controller = new AbortController();
|
|
661
|
+
let timeout;
|
|
662
|
+
// Dispatches actually started, reported in `details.attempts` so a
|
|
663
|
+
// transport failure says how many connections were tried.
|
|
664
|
+
let attempts = 0;
|
|
665
|
+
const onAbort = () => controller.abort(request.signal?.reason);
|
|
666
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
667
|
+
try {
|
|
668
|
+
const requestCredential = input.bearerTokenProvider === undefined
|
|
669
|
+
? credential
|
|
670
|
+
: await input.bearerTokenProvider(controller.signal);
|
|
671
|
+
if (requestCredential !== undefined &&
|
|
672
|
+
(input.bearerTokenProvider === undefined
|
|
673
|
+
? !isTransportCredential(requestCredential)
|
|
674
|
+
: !isAccessToken(requestCredential))) {
|
|
675
|
+
throw new CunaError({
|
|
676
|
+
code: "cuna.internal.invalid_transport_credential",
|
|
677
|
+
message: "Cuna refused an invalid HTTP credential authority.",
|
|
678
|
+
exitCode: EXIT_CODES.internal,
|
|
679
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
// The response-observation budget starts at dispatch. Credential
|
|
683
|
+
// acquisition is a distinct local security boundary and may wait for
|
|
684
|
+
// another healthy CLI process to finish a revision-fenced refresh.
|
|
685
|
+
// The caller's AbortSignal remains live throughout both phases.
|
|
686
|
+
timeout = setTimeout(() => controller.abort(new DOMException("Timed out", "TimeoutError")), budgetMs);
|
|
687
|
+
const dispatch = async (bearer) => {
|
|
688
|
+
const response = await fetcher(target, {
|
|
689
|
+
method: request.method,
|
|
690
|
+
headers: {
|
|
691
|
+
Accept: "application/json, application/problem+json",
|
|
692
|
+
...(bearer === undefined ? {} : { Authorization: `Bearer ${bearer}` }),
|
|
693
|
+
"User-Agent": `cuna-cli/${CLI_VERSION}`,
|
|
694
|
+
...(contentType === undefined ? {} : { "Content-Type": contentType }),
|
|
695
|
+
...(contentLength === undefined ? {} : { "Content-Length": String(contentLength) }),
|
|
696
|
+
...(request.idempotencyKey === undefined ? {} : { "Idempotency-Key": request.idempotencyKey }),
|
|
697
|
+
...(request.machineCreateRequestId === undefined
|
|
698
|
+
? {}
|
|
699
|
+
: { "X-Cuna-Machine-Create-Request-Id": request.machineCreateRequestId }),
|
|
700
|
+
},
|
|
701
|
+
...(body === undefined ? {} : { body }),
|
|
702
|
+
signal: controller.signal,
|
|
703
|
+
redirect: "error",
|
|
704
|
+
});
|
|
705
|
+
const bytes = await readLimited(response);
|
|
706
|
+
if (!response.ok) {
|
|
707
|
+
const decoded = decodeJson(bytes);
|
|
708
|
+
throw apiError({
|
|
709
|
+
status: response.status,
|
|
710
|
+
requestId: response.headers.get("x-request-id") ?? undefined,
|
|
711
|
+
body: decoded.decoded ? decoded.value : undefined,
|
|
712
|
+
apiEncodedBody: decoded.decoded && isObject(decoded.value),
|
|
713
|
+
credentialKind,
|
|
714
|
+
method: request.method,
|
|
715
|
+
path: request.path,
|
|
716
|
+
requestBody: request.body,
|
|
717
|
+
origin: input.baseUrl,
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
return parseJson(bytes, request);
|
|
721
|
+
};
|
|
722
|
+
// One immediate retry when the connection was never established. The
|
|
723
|
+
// request was not sent, so this is safe for every method, and the
|
|
724
|
+
// budget is respected without splitting it: both attempts share
|
|
725
|
+
// `controller`, so the one `setTimeout` above bounds their sum, and an
|
|
726
|
+
// attempt that fails after the budget fired is reported as the budget,
|
|
727
|
+
// never retried. Counted so the final error can say how many times.
|
|
728
|
+
const dispatchWithConnectRetry = async (bearer) => {
|
|
729
|
+
attempts += 1;
|
|
730
|
+
try {
|
|
731
|
+
return await dispatch(bearer);
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
if (error instanceof CunaError || error instanceof CredentialBoundaryError)
|
|
735
|
+
throw error;
|
|
736
|
+
if (controller.signal.aborted)
|
|
737
|
+
throw error;
|
|
738
|
+
// Checked BEFORE the phase classification, deliberately. For an
|
|
739
|
+
// operation that opted out there is no error shape that licenses a
|
|
740
|
+
// second send, so the walk over `cause`/`errors` never runs and
|
|
741
|
+
// cannot decide anything.
|
|
742
|
+
if (request.automaticRedispatch === false)
|
|
743
|
+
throw error;
|
|
744
|
+
if (transportFailurePhase(error) !== "connect")
|
|
745
|
+
throw error;
|
|
746
|
+
attempts += 1;
|
|
747
|
+
return await dispatch(bearer);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
try {
|
|
751
|
+
return await dispatchWithConnectRetry(requestCredential);
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
const canRetryUnauthorized = error instanceof CunaError &&
|
|
755
|
+
error.code === "cuna.auth.rejected" &&
|
|
756
|
+
input.bearerTokenProvider !== undefined &&
|
|
757
|
+
requestCredential !== undefined &&
|
|
758
|
+
(request.method === "GET" ||
|
|
759
|
+
(request.idempotencyKey !== undefined && isIdempotencyKey(request.idempotencyKey)));
|
|
760
|
+
if (!canRetryUnauthorized)
|
|
761
|
+
throw error;
|
|
762
|
+
const refreshedCredential = await input.bearerTokenProvider(controller.signal, {
|
|
763
|
+
reason: "unauthorized",
|
|
764
|
+
rejectedToken: requestCredential,
|
|
765
|
+
});
|
|
766
|
+
if (!isAccessToken(refreshedCredential)) {
|
|
767
|
+
throw new CunaError({
|
|
768
|
+
code: "cuna.internal.invalid_transport_credential",
|
|
769
|
+
message: "Cuna refused an invalid refreshed HTTP credential authority.",
|
|
770
|
+
exitCode: EXIT_CODES.internal,
|
|
771
|
+
hint: INTERNAL_DEFECT_HINT,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
// Deliberately outside a retry loop: a second 401 is authoritative.
|
|
775
|
+
// The same serialized body and Idempotency-Key are reused verbatim.
|
|
776
|
+
// The connect retry still applies: the first exchange proved the
|
|
777
|
+
// origin reachable, and a fresh connection can still stall.
|
|
778
|
+
return await dispatchWithConnectRetry(refreshedCredential);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
if (error instanceof CunaError)
|
|
783
|
+
throw error;
|
|
784
|
+
// Token acquisition is a local credential boundary, not a network
|
|
785
|
+
// dispatch. Preserve its typed failure so the CLI can name the exact
|
|
786
|
+
// authentication repair instead of misreporting connectivity.
|
|
787
|
+
if (error instanceof CredentialBoundaryError)
|
|
788
|
+
throw error;
|
|
789
|
+
if (controller.signal.aborted) {
|
|
790
|
+
// TWO DIFFERENT DETECTORS ARRIVE AT THIS ONE BRANCH, and collapsing
|
|
791
|
+
// them is the defect. The caller pressing Ctrl-C is a decision by a
|
|
792
|
+
// person; this process's own `setTimeout` firing is a decision by
|
|
793
|
+
// this process's configuration. Only the first is `cancelled`.
|
|
794
|
+
//
|
|
795
|
+
// Read through a binding rather than compared inline: the pre-dispatch
|
|
796
|
+
// guard above narrowed `request.signal?.aborted` to `false | undefined`
|
|
797
|
+
// for the rest of the function, and the whole point here is that it may
|
|
798
|
+
// have become `true` in the meantime.
|
|
799
|
+
const cancelledByCaller = request.signal?.aborted ?? false;
|
|
800
|
+
if (cancelledByCaller) {
|
|
801
|
+
throw new CunaError({
|
|
802
|
+
code: "cuna.network.cancelled",
|
|
803
|
+
message: "The Cuna request was cancelled.",
|
|
804
|
+
exitCode: EXIT_CODES.network,
|
|
805
|
+
hint: "No authoritative answer was received, so a mutating request may still have been applied.",
|
|
806
|
+
retryable: false,
|
|
807
|
+
cause: error,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
// Named `cuna.network.timeout` until 2026-08-19, which named the
|
|
811
|
+
// network. The network was fine: `cuna machines create` reported it
|
|
812
|
+
// for a machine that reached `running` five seconds later. What
|
|
813
|
+
// elapsed was this budget.
|
|
814
|
+
throw observationBudgetElapsed({
|
|
815
|
+
kind: "response",
|
|
816
|
+
operation: `${request.method} ${request.path}`,
|
|
817
|
+
budgetMs,
|
|
818
|
+
...(request.settleWith === undefined ? {} : { settleWith: request.settleWith }),
|
|
819
|
+
details: { method: request.method, path: request.path },
|
|
820
|
+
cause: error,
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
throw transportFailure({
|
|
824
|
+
request,
|
|
825
|
+
phase: transportFailurePhase(error),
|
|
826
|
+
attempts,
|
|
827
|
+
cause: error,
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
finally {
|
|
831
|
+
if (timeout !== undefined)
|
|
832
|
+
clearTimeout(timeout);
|
|
833
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
//# sourceMappingURL=http.js.map
|