@kindgi/api 0.0.0-bootstrap.0 → 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/README.md +216 -1
- package/dist/adapter-binding.d.ts +177 -0
- package/dist/adapter-binding.d.ts.map +1 -0
- package/dist/adapter-binding.js +12 -0
- package/dist/adapter-binding.js.map +1 -0
- package/dist/agent-binding.d.ts +207 -0
- package/dist/agent-binding.d.ts.map +1 -0
- package/dist/agent-binding.js +4 -0
- package/dist/agent-binding.js.map +1 -0
- package/dist/app.d.ts +770 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +396 -0
- package/dist/app.js.map +1 -0
- package/dist/capability-binding.d.ts +76 -0
- package/dist/capability-binding.d.ts.map +1 -0
- package/dist/capability-binding.js +4 -0
- package/dist/capability-binding.js.map +1 -0
- package/dist/cost-binding.d.ts +175 -0
- package/dist/cost-binding.d.ts.map +1 -0
- package/dist/cost-binding.js +13 -0
- package/dist/cost-binding.js.map +1 -0
- package/dist/deployment-binding.d.ts +201 -0
- package/dist/deployment-binding.d.ts.map +1 -0
- package/dist/deployment-binding.js +4 -0
- package/dist/deployment-binding.js.map +1 -0
- package/dist/env-binding.d.ts +132 -0
- package/dist/env-binding.d.ts.map +1 -0
- package/dist/env-binding.js +4 -0
- package/dist/env-binding.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +264 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval-run-binding.d.ts +196 -0
- package/dist/eval-run-binding.d.ts.map +1 -0
- package/dist/eval-run-binding.js +16 -0
- package/dist/eval-run-binding.js.map +1 -0
- package/dist/eval-run-dispatcher.d.ts +131 -0
- package/dist/eval-run-dispatcher.d.ts.map +1 -0
- package/dist/eval-run-dispatcher.js +368 -0
- package/dist/eval-run-dispatcher.js.map +1 -0
- package/dist/eval-suite-binding.d.ts +227 -0
- package/dist/eval-suite-binding.d.ts.map +1 -0
- package/dist/eval-suite-binding.js +24 -0
- package/dist/eval-suite-binding.js.map +1 -0
- package/dist/event-bus-binding.d.ts +107 -0
- package/dist/event-bus-binding.d.ts.map +1 -0
- package/dist/event-bus-binding.js +5 -0
- package/dist/event-bus-binding.js.map +1 -0
- package/dist/flow-binding.d.ts +194 -0
- package/dist/flow-binding.d.ts.map +1 -0
- package/dist/flow-binding.js +4 -0
- package/dist/flow-binding.js.map +1 -0
- package/dist/guardrail-binding.d.ts +138 -0
- package/dist/guardrail-binding.d.ts.map +1 -0
- package/dist/guardrail-binding.js +4 -0
- package/dist/guardrail-binding.js.map +1 -0
- package/dist/handler-binding.d.ts +126 -0
- package/dist/handler-binding.d.ts.map +1 -0
- package/dist/handler-binding.js +4 -0
- package/dist/handler-binding.js.map +1 -0
- package/dist/hitl-binding.d.ts +115 -0
- package/dist/hitl-binding.d.ts.map +1 -0
- package/dist/hitl-binding.js +4 -0
- package/dist/hitl-binding.js.map +1 -0
- package/dist/identity-directory-binding.d.ts +116 -0
- package/dist/identity-directory-binding.d.ts.map +1 -0
- package/dist/identity-directory-binding.js +4 -0
- package/dist/identity-directory-binding.js.map +1 -0
- package/dist/identity-provider-binding.d.ts +184 -0
- package/dist/identity-provider-binding.d.ts.map +1 -0
- package/dist/identity-provider-binding.js +4 -0
- package/dist/identity-provider-binding.js.map +1 -0
- package/dist/image-registry-binding.d.ts +165 -0
- package/dist/image-registry-binding.d.ts.map +1 -0
- package/dist/image-registry-binding.js +4 -0
- package/dist/image-registry-binding.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/key-management-binding.d.ts +101 -0
- package/dist/key-management-binding.d.ts.map +1 -0
- package/dist/key-management-binding.js +4 -0
- package/dist/key-management-binding.js.map +1 -0
- package/dist/mcp-endpoint-binding.d.ts +312 -0
- package/dist/mcp-endpoint-binding.d.ts.map +1 -0
- package/dist/mcp-endpoint-binding.js +12 -0
- package/dist/mcp-endpoint-binding.js.map +1 -0
- package/dist/memory-binding.d.ts +209 -0
- package/dist/memory-binding.d.ts.map +1 -0
- package/dist/memory-binding.js +4 -0
- package/dist/memory-binding.js.map +1 -0
- package/dist/middleware/auth.d.ts +168 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +247 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/authorize.d.ts +26 -0
- package/dist/middleware/authorize.d.ts.map +1 -0
- package/dist/middleware/authorize.js +65 -0
- package/dist/middleware/authorize.js.map +1 -0
- package/dist/middleware/error-mapper.d.ts +13 -0
- package/dist/middleware/error-mapper.d.ts.map +1 -0
- package/dist/middleware/error-mapper.js +32 -0
- package/dist/middleware/error-mapper.js.map +1 -0
- package/dist/middleware/idempotency.d.ts +43 -0
- package/dist/middleware/idempotency.d.ts.map +1 -0
- package/dist/middleware/idempotency.js +118 -0
- package/dist/middleware/idempotency.js.map +1 -0
- package/dist/middleware/principal.d.ts +4 -0
- package/dist/middleware/principal.d.ts.map +1 -0
- package/dist/middleware/principal.js +42 -0
- package/dist/middleware/principal.js.map +1 -0
- package/dist/middleware/public-run-routes.d.ts +16 -0
- package/dist/middleware/public-run-routes.d.ts.map +1 -0
- package/dist/middleware/public-run-routes.js +43 -0
- package/dist/middleware/public-run-routes.js.map +1 -0
- package/dist/middleware/request-id.d.ts +14 -0
- package/dist/middleware/request-id.d.ts.map +1 -0
- package/dist/middleware/request-id.js +24 -0
- package/dist/middleware/request-id.js.map +1 -0
- package/dist/middleware/sigv4-verify.d.ts +64 -0
- package/dist/middleware/sigv4-verify.d.ts.map +1 -0
- package/dist/middleware/sigv4-verify.js +286 -0
- package/dist/middleware/sigv4-verify.js.map +1 -0
- package/dist/middleware/sigv4.d.ts +25 -0
- package/dist/middleware/sigv4.d.ts.map +1 -0
- package/dist/middleware/sigv4.js +120 -0
- package/dist/middleware/sigv4.js.map +1 -0
- package/dist/openapi/generate.d.ts +15 -0
- package/dist/openapi/generate.d.ts.map +1 -0
- package/dist/openapi/generate.js +241 -0
- package/dist/openapi/generate.js.map +1 -0
- package/dist/openapi/operations.d.ts +70 -0
- package/dist/openapi/operations.d.ts.map +1 -0
- package/dist/openapi/operations.js +5051 -0
- package/dist/openapi/operations.js.map +1 -0
- package/dist/openapi/schemas.d.ts +590 -0
- package/dist/openapi/schemas.d.ts.map +1 -0
- package/dist/openapi/schemas.js +5820 -0
- package/dist/openapi/schemas.js.map +1 -0
- package/dist/provenance-binding.d.ts +116 -0
- package/dist/provenance-binding.d.ts.map +1 -0
- package/dist/provenance-binding.js +31 -0
- package/dist/provenance-binding.js.map +1 -0
- package/dist/provider-binding.d.ts +174 -0
- package/dist/provider-binding.d.ts.map +1 -0
- package/dist/provider-binding.js +4 -0
- package/dist/provider-binding.js.map +1 -0
- package/dist/public-run-token.d.ts +97 -0
- package/dist/public-run-token.d.ts.map +1 -0
- package/dist/public-run-token.js +167 -0
- package/dist/public-run-token.js.map +1 -0
- package/dist/retention-binding.d.ts +69 -0
- package/dist/retention-binding.d.ts.map +1 -0
- package/dist/retention-binding.js +4 -0
- package/dist/retention-binding.js.map +1 -0
- package/dist/reviewer-binding.d.ts +84 -0
- package/dist/reviewer-binding.d.ts.map +1 -0
- package/dist/reviewer-binding.js +4 -0
- package/dist/reviewer-binding.js.map +1 -0
- package/dist/rotation-status-store.d.ts +96 -0
- package/dist/rotation-status-store.d.ts.map +1 -0
- package/dist/rotation-status-store.js +106 -0
- package/dist/rotation-status-store.js.map +1 -0
- package/dist/routes/adapters.d.ts +29 -0
- package/dist/routes/adapters.d.ts.map +1 -0
- package/dist/routes/adapters.js +230 -0
- package/dist/routes/adapters.js.map +1 -0
- package/dist/routes/agents.d.ts +17 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +342 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/approvals.d.ts +37 -0
- package/dist/routes/approvals.d.ts.map +1 -0
- package/dist/routes/approvals.js +551 -0
- package/dist/routes/approvals.js.map +1 -0
- package/dist/routes/artifacts.d.ts +25 -0
- package/dist/routes/artifacts.d.ts.map +1 -0
- package/dist/routes/artifacts.js +265 -0
- package/dist/routes/artifacts.js.map +1 -0
- package/dist/routes/audit.d.ts +6 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +162 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/auth.d.ts +47 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +630 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/capabilities.d.ts +14 -0
- package/dist/routes/capabilities.d.ts.map +1 -0
- package/dist/routes/capabilities.js +62 -0
- package/dist/routes/capabilities.js.map +1 -0
- package/dist/routes/compliance.d.ts +37 -0
- package/dist/routes/compliance.d.ts.map +1 -0
- package/dist/routes/compliance.js +321 -0
- package/dist/routes/compliance.js.map +1 -0
- package/dist/routes/conversations.d.ts +13 -0
- package/dist/routes/conversations.d.ts.map +1 -0
- package/dist/routes/conversations.js +295 -0
- package/dist/routes/conversations.js.map +1 -0
- package/dist/routes/cost.d.ts +24 -0
- package/dist/routes/cost.d.ts.map +1 -0
- package/dist/routes/cost.js +243 -0
- package/dist/routes/cost.js.map +1 -0
- package/dist/routes/deployments.d.ts +122 -0
- package/dist/routes/deployments.d.ts.map +1 -0
- package/dist/routes/deployments.js +1036 -0
- package/dist/routes/deployments.js.map +1 -0
- package/dist/routes/env.d.ts +62 -0
- package/dist/routes/env.d.ts.map +1 -0
- package/dist/routes/env.js +311 -0
- package/dist/routes/env.js.map +1 -0
- package/dist/routes/eval-runs.d.ts +28 -0
- package/dist/routes/eval-runs.d.ts.map +1 -0
- package/dist/routes/eval-runs.js +479 -0
- package/dist/routes/eval-runs.js.map +1 -0
- package/dist/routes/eval-suites.d.ts +24 -0
- package/dist/routes/eval-suites.d.ts.map +1 -0
- package/dist/routes/eval-suites.js +381 -0
- package/dist/routes/eval-suites.js.map +1 -0
- package/dist/routes/event-triggers.d.ts +12 -0
- package/dist/routes/event-triggers.d.ts.map +1 -0
- package/dist/routes/event-triggers.js +218 -0
- package/dist/routes/event-triggers.js.map +1 -0
- package/dist/routes/flows.d.ts +23 -0
- package/dist/routes/flows.d.ts.map +1 -0
- package/dist/routes/flows.js +344 -0
- package/dist/routes/flows.js.map +1 -0
- package/dist/routes/guardrails.d.ts +33 -0
- package/dist/routes/guardrails.d.ts.map +1 -0
- package/dist/routes/guardrails.js +236 -0
- package/dist/routes/guardrails.js.map +1 -0
- package/dist/routes/identity.d.ts +39 -0
- package/dist/routes/identity.d.ts.map +1 -0
- package/dist/routes/identity.js +138 -0
- package/dist/routes/identity.js.map +1 -0
- package/dist/routes/mcp.d.ts +23 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +727 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/memory.d.ts +16 -0
- package/dist/routes/memory.d.ts.map +1 -0
- package/dist/routes/memory.js +353 -0
- package/dist/routes/memory.js.map +1 -0
- package/dist/routes/observations.d.ts +19 -0
- package/dist/routes/observations.d.ts.map +1 -0
- package/dist/routes/observations.js +135 -0
- package/dist/routes/observations.js.map +1 -0
- package/dist/routes/orgs.d.ts +24 -0
- package/dist/routes/orgs.d.ts.map +1 -0
- package/dist/routes/orgs.js +208 -0
- package/dist/routes/orgs.js.map +1 -0
- package/dist/routes/pagination.d.ts +18 -0
- package/dist/routes/pagination.d.ts.map +1 -0
- package/dist/routes/pagination.js +35 -0
- package/dist/routes/pagination.js.map +1 -0
- package/dist/routes/policies.d.ts +23 -0
- package/dist/routes/policies.d.ts.map +1 -0
- package/dist/routes/policies.js +314 -0
- package/dist/routes/policies.js.map +1 -0
- package/dist/routes/projects.d.ts +45 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +485 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/proposals.d.ts +24 -0
- package/dist/routes/proposals.d.ts.map +1 -0
- package/dist/routes/proposals.js +581 -0
- package/dist/routes/proposals.js.map +1 -0
- package/dist/routes/provenance.d.ts +34 -0
- package/dist/routes/provenance.d.ts.map +1 -0
- package/dist/routes/provenance.js +274 -0
- package/dist/routes/provenance.js.map +1 -0
- package/dist/routes/providers.d.ts +32 -0
- package/dist/routes/providers.d.ts.map +1 -0
- package/dist/routes/providers.js +490 -0
- package/dist/routes/providers.js.map +1 -0
- package/dist/routes/public-run-tokens.d.ts +17 -0
- package/dist/routes/public-run-tokens.d.ts.map +1 -0
- package/dist/routes/public-run-tokens.js +69 -0
- package/dist/routes/public-run-tokens.js.map +1 -0
- package/dist/routes/retention.d.ts +19 -0
- package/dist/routes/retention.d.ts.map +1 -0
- package/dist/routes/retention.js +152 -0
- package/dist/routes/retention.js.map +1 -0
- package/dist/routes/reviewers.d.ts +5 -0
- package/dist/routes/reviewers.d.ts.map +1 -0
- package/dist/routes/reviewers.js +161 -0
- package/dist/routes/reviewers.js.map +1 -0
- package/dist/routes/runs.d.ts +34 -0
- package/dist/routes/runs.d.ts.map +1 -0
- package/dist/routes/runs.js +773 -0
- package/dist/routes/runs.js.map +1 -0
- package/dist/routes/s3.d.ts +31 -0
- package/dist/routes/s3.d.ts.map +1 -0
- package/dist/routes/s3.js +433 -0
- package/dist/routes/s3.js.map +1 -0
- package/dist/routes/schedules.d.ts +20 -0
- package/dist/routes/schedules.d.ts.map +1 -0
- package/dist/routes/schedules.js +234 -0
- package/dist/routes/schedules.js.map +1 -0
- package/dist/routes/scope-params.d.ts +57 -0
- package/dist/routes/scope-params.d.ts.map +1 -0
- package/dist/routes/scope-params.js +105 -0
- package/dist/routes/scope-params.js.map +1 -0
- package/dist/routes/secret-ref.d.ts +20 -0
- package/dist/routes/secret-ref.d.ts.map +1 -0
- package/dist/routes/secret-ref.js +42 -0
- package/dist/routes/secret-ref.js.map +1 -0
- package/dist/routes/secrets.d.ts +53 -0
- package/dist/routes/secrets.d.ts.map +1 -0
- package/dist/routes/secrets.js +715 -0
- package/dist/routes/secrets.js.map +1 -0
- package/dist/routes/signing-keys.d.ts +23 -0
- package/dist/routes/signing-keys.d.ts.map +1 -0
- package/dist/routes/signing-keys.js +212 -0
- package/dist/routes/signing-keys.js.map +1 -0
- package/dist/routes/sse.d.ts +58 -0
- package/dist/routes/sse.d.ts.map +1 -0
- package/dist/routes/sse.js +92 -0
- package/dist/routes/sse.js.map +1 -0
- package/dist/routes/teams.d.ts +30 -0
- package/dist/routes/teams.d.ts.map +1 -0
- package/dist/routes/teams.js +443 -0
- package/dist/routes/teams.js.map +1 -0
- package/dist/routes/tenant.d.ts +57 -0
- package/dist/routes/tenant.d.ts.map +1 -0
- package/dist/routes/tenant.js +352 -0
- package/dist/routes/tenant.js.map +1 -0
- package/dist/routes/tokens.d.ts +14 -0
- package/dist/routes/tokens.d.ts.map +1 -0
- package/dist/routes/tokens.js +231 -0
- package/dist/routes/tokens.js.map +1 -0
- package/dist/routes/tools.d.ts +33 -0
- package/dist/routes/tools.d.ts.map +1 -0
- package/dist/routes/tools.js +380 -0
- package/dist/routes/tools.js.map +1 -0
- package/dist/routes/webhook-endpoints.d.ts +10 -0
- package/dist/routes/webhook-endpoints.d.ts.map +1 -0
- package/dist/routes/webhook-endpoints.js +407 -0
- package/dist/routes/webhook-endpoints.js.map +1 -0
- package/dist/routes/webhooks.d.ts +21 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +228 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/s3-credential-binding.d.ts +54 -0
- package/dist/s3-credential-binding.d.ts.map +1 -0
- package/dist/s3-credential-binding.js +4 -0
- package/dist/s3-credential-binding.js.map +1 -0
- package/dist/secrets-binding.d.ts +281 -0
- package/dist/secrets-binding.d.ts.map +1 -0
- package/dist/secrets-binding.js +10 -0
- package/dist/secrets-binding.js.map +1 -0
- package/dist/secrets-provider-binding.d.ts +164 -0
- package/dist/secrets-provider-binding.d.ts.map +1 -0
- package/dist/secrets-provider-binding.js +4 -0
- package/dist/secrets-provider-binding.js.map +1 -0
- package/dist/session-store-binding.d.ts +148 -0
- package/dist/session-store-binding.d.ts.map +1 -0
- package/dist/session-store-binding.js +4 -0
- package/dist/session-store-binding.js.map +1 -0
- package/dist/signing-key-binding.d.ts +210 -0
- package/dist/signing-key-binding.d.ts.map +1 -0
- package/dist/signing-key-binding.js +4 -0
- package/dist/signing-key-binding.js.map +1 -0
- package/dist/state-store-binding.d.ts +53 -0
- package/dist/state-store-binding.d.ts.map +1 -0
- package/dist/state-store-binding.js +38 -0
- package/dist/state-store-binding.js.map +1 -0
- package/dist/supervisor-binding.d.ts +526 -0
- package/dist/supervisor-binding.d.ts.map +1 -0
- package/dist/supervisor-binding.js +4 -0
- package/dist/supervisor-binding.js.map +1 -0
- package/dist/tenant-host-access.d.ts +41 -0
- package/dist/tenant-host-access.d.ts.map +1 -0
- package/dist/tenant-host-access.js +42 -0
- package/dist/tenant-host-access.js.map +1 -0
- package/dist/token-admin.d.ts +99 -0
- package/dist/token-admin.d.ts.map +1 -0
- package/dist/token-admin.js +4 -0
- package/dist/token-admin.js.map +1 -0
- package/dist/tool-binding.d.ts +247 -0
- package/dist/tool-binding.d.ts.map +1 -0
- package/dist/tool-binding.js +4 -0
- package/dist/tool-binding.js.map +1 -0
- package/dist/trigger-binding.d.ts +13 -0
- package/dist/trigger-binding.d.ts.map +1 -0
- package/dist/trigger-binding.js +4 -0
- package/dist/trigger-binding.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook-endpoint-binding.d.ts +230 -0
- package/dist/webhook-endpoint-binding.d.ts.map +1 -0
- package/dist/webhook-endpoint-binding.js +12 -0
- package/dist/webhook-endpoint-binding.js.map +1 -0
- package/dist/xml.d.ts +79 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +115 -0
- package/dist/xml.js.map +1 -0
- package/openapi.json +29659 -0
- package/package.json +76 -4
- package/scripts/emit-openapi.mjs +44 -0
- package/src/adapter-binding.ts +204 -0
- package/src/agent-binding.ts +231 -0
- package/src/app.ts +1285 -0
- package/src/capability-binding.ts +83 -0
- package/src/cost-binding.ts +213 -0
- package/src/deployment-binding.ts +225 -0
- package/src/env-binding.ts +140 -0
- package/src/errors.ts +291 -0
- package/src/eval-run-binding.ts +209 -0
- package/src/eval-run-dispatcher.ts +497 -0
- package/src/eval-suite-binding.ts +257 -0
- package/src/event-bus-binding.ts +127 -0
- package/src/flow-binding.ts +213 -0
- package/src/guardrail-binding.ts +151 -0
- package/src/handler-binding.ts +132 -0
- package/src/hitl-binding.ts +172 -0
- package/src/identity-directory-binding.ts +128 -0
- package/src/identity-provider-binding.ts +203 -0
- package/src/image-registry-binding.ts +193 -0
- package/src/index.ts +606 -0
- package/src/key-management-binding.ts +91 -0
- package/src/mcp-endpoint-binding.ts +311 -0
- package/src/memory-binding.ts +239 -0
- package/src/middleware/auth.ts +437 -0
- package/src/middleware/authorize.ts +125 -0
- package/src/middleware/error-mapper.ts +37 -0
- package/src/middleware/idempotency.ts +160 -0
- package/src/middleware/principal.ts +64 -0
- package/src/middleware/public-run-routes.ts +50 -0
- package/src/middleware/request-id.ts +28 -0
- package/src/middleware/sigv4-verify.ts +374 -0
- package/src/middleware/sigv4.ts +165 -0
- package/src/openapi/generate.ts +307 -0
- package/src/openapi/operations.ts +5445 -0
- package/src/openapi/schemas.ts +6363 -0
- package/src/provenance-binding.ts +169 -0
- package/src/provider-binding.ts +196 -0
- package/src/public-run-token.ts +263 -0
- package/src/retention-binding.ts +78 -0
- package/src/reviewer-binding.ts +99 -0
- package/src/rotation-status-store.ts +209 -0
- package/src/routes/adapters.ts +319 -0
- package/src/routes/agents.ts +424 -0
- package/src/routes/approvals.ts +759 -0
- package/src/routes/artifacts.ts +317 -0
- package/src/routes/audit.ts +219 -0
- package/src/routes/auth.ts +806 -0
- package/src/routes/capabilities.ts +79 -0
- package/src/routes/compliance.ts +482 -0
- package/src/routes/conversations.ts +369 -0
- package/src/routes/cost.ts +327 -0
- package/src/routes/deployments.ts +1493 -0
- package/src/routes/env.ts +429 -0
- package/src/routes/eval-runs.ts +631 -0
- package/src/routes/eval-suites.ts +490 -0
- package/src/routes/event-triggers.ts +283 -0
- package/src/routes/flows.ts +423 -0
- package/src/routes/guardrails.ts +320 -0
- package/src/routes/identity.ts +203 -0
- package/src/routes/mcp.ts +940 -0
- package/src/routes/memory.ts +430 -0
- package/src/routes/observations.ts +175 -0
- package/src/routes/orgs.ts +273 -0
- package/src/routes/pagination.ts +51 -0
- package/src/routes/policies.ts +413 -0
- package/src/routes/projects.ts +634 -0
- package/src/routes/proposals.ts +719 -0
- package/src/routes/provenance.ts +406 -0
- package/src/routes/providers.ts +624 -0
- package/src/routes/public-run-tokens.ts +104 -0
- package/src/routes/retention.ts +193 -0
- package/src/routes/reviewers.ts +209 -0
- package/src/routes/runs.ts +981 -0
- package/src/routes/s3.ts +519 -0
- package/src/routes/schedules.ts +300 -0
- package/src/routes/scope-params.ts +133 -0
- package/src/routes/secret-ref.ts +52 -0
- package/src/routes/secrets.ts +975 -0
- package/src/routes/signing-keys.ts +259 -0
- package/src/routes/sse.ts +148 -0
- package/src/routes/teams.ts +584 -0
- package/src/routes/tenant.ts +530 -0
- package/src/routes/tokens.ts +285 -0
- package/src/routes/tools.ts +491 -0
- package/src/routes/webhook-endpoints.ts +534 -0
- package/src/routes/webhooks.ts +296 -0
- package/src/s3-credential-binding.ts +58 -0
- package/src/secrets-binding.ts +308 -0
- package/src/secrets-provider-binding.ts +167 -0
- package/src/session-store-binding.ts +163 -0
- package/src/signing-key-binding.ts +235 -0
- package/src/state-store-binding.ts +84 -0
- package/src/supervisor-binding.ts +554 -0
- package/src/tenant-host-access.ts +62 -0
- package/src/token-admin.ts +104 -0
- package/src/tool-binding.ts +274 -0
- package/src/trigger-binding.ts +38 -0
- package/src/types.ts +74 -0
- package/src/webhook-endpoint-binding.ts +246 -0
- package/src/xml.ts +196 -0
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
|
|
6
|
+
import type { AgentId } from '@kindgi/agents';
|
|
7
|
+
import type { KernelRunRecord, ListRunsInput, RunBinding } from '@kindgi/runtime';
|
|
8
|
+
import type { FlowId, OrgId, ProjectId, RunId, Semver, TenantId } from '@kindgi/types';
|
|
9
|
+
|
|
10
|
+
import { ref } from '@kindgi/authz';
|
|
11
|
+
|
|
12
|
+
import { type WireErrorBody, statusFor, toWireError } from '../errors.js';
|
|
13
|
+
import type { EventBusBinding, EventPayload, Subscription } from '../event-bus-binding.js';
|
|
14
|
+
import type {
|
|
15
|
+
RunHandlerBinding,
|
|
16
|
+
RunHandlerFailure,
|
|
17
|
+
RunHandlerOutcome,
|
|
18
|
+
} from '../handler-binding.js';
|
|
19
|
+
import type { Authorizer } from '../middleware/authorize.js';
|
|
20
|
+
import type { MintPublicRunTokenResult } from '../public-run-token.js';
|
|
21
|
+
import type { AppEnv } from '../types.js';
|
|
22
|
+
import type { DecodedCursor } from './pagination.js';
|
|
23
|
+
import { clampLimit, decodeCursor } from './pagination.js';
|
|
24
|
+
import { type ParseScopeParamsOutcome, parseScopeParams } from './scope-params.js';
|
|
25
|
+
import {
|
|
26
|
+
formatSseFrame,
|
|
27
|
+
isTerminalWireKind,
|
|
28
|
+
parseLastEventId,
|
|
29
|
+
projectJournalEntry,
|
|
30
|
+
toRunProgressEvent,
|
|
31
|
+
} from './sse.js';
|
|
32
|
+
|
|
33
|
+
const KERNEL_RUN_CHANNEL_PREFIX = 'kernel:run:';
|
|
34
|
+
|
|
35
|
+
/** Options for `runsRouter`. */
|
|
36
|
+
export interface RunsRouterOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Optional push-based event bus. When present, `GET /:runId/stream`
|
|
39
|
+
* subscribes on `kernel:run:<runId>` and delivers events
|
|
40
|
+
* push-mode. When absent, the route polls the run's journal every
|
|
41
|
+
* 200 ms — wire shape identical either way.
|
|
42
|
+
*/
|
|
43
|
+
readonly eventBus?: EventBusBinding;
|
|
44
|
+
/**
|
|
45
|
+
* Issue public run tokens: `POST /` adds a `publicAccessToken` for the
|
|
46
|
+
* new run, for its progress routes.
|
|
47
|
+
*/
|
|
48
|
+
readonly publicRunTokens?: {
|
|
49
|
+
readonly mint: (tenantId: TenantId, runIds: readonly RunId[]) => MintPublicRunTokenResult;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** How far up the parent chain a public token's grant reaches. */
|
|
54
|
+
const MAX_RUN_ANCESTRY = 16;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Runs resource routes (per `docs/API-ROUTE-CONVENTIONS.md` §7).
|
|
58
|
+
*
|
|
59
|
+
* Routes: `POST /` (start), `GET /:runId`, `GET /` (list),
|
|
60
|
+
* `POST /:runId/cancel`, `POST /:runId/resume`, `GET /:runId/stream`
|
|
61
|
+
* (SSE), `GET /:runId/journal`.
|
|
62
|
+
*/
|
|
63
|
+
export function runsRouter(
|
|
64
|
+
binding: RunHandlerBinding,
|
|
65
|
+
runBinding: RunBinding,
|
|
66
|
+
options: RunsRouterOptions = {},
|
|
67
|
+
authorizer?: Authorizer,
|
|
68
|
+
): Hono<AppEnv> {
|
|
69
|
+
const r = new Hono<AppEnv>();
|
|
70
|
+
const eventBus = options.eventBus;
|
|
71
|
+
|
|
72
|
+
// Authorization — a run inherits its permissions from its project.
|
|
73
|
+
// Each check resolves the run's project (by run id) and asks the
|
|
74
|
+
// authorizer about that project:
|
|
75
|
+
//
|
|
76
|
+
// POST / — no route-level check; `execute` on the agent
|
|
77
|
+
// or flow is up to the run handler binding.
|
|
78
|
+
// GET /:runId — read on the run's project
|
|
79
|
+
// POST /:runId/cancel — write on the run's project
|
|
80
|
+
// POST /:runId/resume — execute on the run's project
|
|
81
|
+
// GET /:runId/journal — read on the run's project
|
|
82
|
+
// GET /:runId/stream — read on the run's project
|
|
83
|
+
// GET / — list; tenant-scoped, the scope filter narrows
|
|
84
|
+
// further (rows are not filtered per permission)
|
|
85
|
+
if (authorizer !== undefined) {
|
|
86
|
+
const projectFromRun = async (c: import('hono').Context<AppEnv>) => {
|
|
87
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
88
|
+
const runId = c.req.param('runId') ?? '';
|
|
89
|
+
if (runId.length === 0) return ref('tenant', tenantId as unknown as string);
|
|
90
|
+
const row = await runBinding.getRun(tenantId, runId as RunId);
|
|
91
|
+
if (row === null) {
|
|
92
|
+
// Fall back to tenant so the underlying handler surfaces
|
|
93
|
+
// 404 rather than the middleware masking it as 403.
|
|
94
|
+
return ref('tenant', tenantId as unknown as string);
|
|
95
|
+
}
|
|
96
|
+
return ref('project', row.projectId as unknown as string);
|
|
97
|
+
};
|
|
98
|
+
r.use('/:runId', async (c, next) => {
|
|
99
|
+
if (c.req.method !== 'GET') return next();
|
|
100
|
+
const mw = authorizer.authorize('read', projectFromRun);
|
|
101
|
+
return mw(c, next);
|
|
102
|
+
});
|
|
103
|
+
r.use('/:runId/journal', async (c, next) => {
|
|
104
|
+
const mw = authorizer.authorize('read', projectFromRun);
|
|
105
|
+
return mw(c, next);
|
|
106
|
+
});
|
|
107
|
+
r.use('/:runId/stream', async (c, next) => {
|
|
108
|
+
const mw = authorizer.authorize('read', projectFromRun);
|
|
109
|
+
return mw(c, next);
|
|
110
|
+
});
|
|
111
|
+
// Progress: `read` for API tokens. A public run token has no
|
|
112
|
+
// principal; the handlers check that its grant covers the run.
|
|
113
|
+
const progressAuth = async (c: import('hono').Context<AppEnv>, next: import('hono').Next) => {
|
|
114
|
+
if (c.get('tokenKind') === 'public-run') return next();
|
|
115
|
+
const mw = authorizer.authorize('read', projectFromRun);
|
|
116
|
+
return mw(c, next);
|
|
117
|
+
};
|
|
118
|
+
r.use('/:runId/progress', progressAuth);
|
|
119
|
+
r.use('/:runId/progress/stream', progressAuth);
|
|
120
|
+
r.use('/:runId/cancel', async (c, next) => {
|
|
121
|
+
const mw = authorizer.authorize('write', projectFromRun);
|
|
122
|
+
return mw(c, next);
|
|
123
|
+
});
|
|
124
|
+
r.use('/:runId/resume', async (c, next) => {
|
|
125
|
+
const mw = authorizer.authorize('execute', projectFromRun);
|
|
126
|
+
return mw(c, next);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ---------- POST / (start a run — agent | flow) ----------
|
|
131
|
+
r.post('/', async (c) => {
|
|
132
|
+
const requestId = c.get('requestId');
|
|
133
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
134
|
+
|
|
135
|
+
let body: unknown;
|
|
136
|
+
try {
|
|
137
|
+
body = await c.req.json();
|
|
138
|
+
} catch {
|
|
139
|
+
c.status(statusFor('bad-input') as never);
|
|
140
|
+
return c.json(
|
|
141
|
+
toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const parsed = parseStartRunBody(body);
|
|
145
|
+
if (parsed.kind === 'err') {
|
|
146
|
+
c.status(statusFor(parsed.error.code) as never);
|
|
147
|
+
return c.json(toWireError(parsed.error, requestId));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const invocation = await invokeFromBody(binding, tenantId, parsed.value);
|
|
151
|
+
|
|
152
|
+
if (invocation.kind === 'err') {
|
|
153
|
+
c.status(statusFor(invocation.error.code) as never);
|
|
154
|
+
return c.json(runFailureToWire(invocation.error, requestId));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const runId = invocation.runId;
|
|
158
|
+
const loaded = await runBinding.getRun(tenantId, runId);
|
|
159
|
+
if (loaded === null) {
|
|
160
|
+
c.status(statusFor('journal-error') as never);
|
|
161
|
+
return c.json(
|
|
162
|
+
toWireError(
|
|
163
|
+
{ code: 'journal-error', message: `Run ${runId} started but row not readable` },
|
|
164
|
+
requestId,
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
// `wait: false` → the binding handed the run id back before the
|
|
169
|
+
// run finished: 202, and the caller polls `GET /v1/runs/:runId`.
|
|
170
|
+
c.status(parsed.value.wait === false ? 202 : 201);
|
|
171
|
+
const minted = options.publicRunTokens?.mint(tenantId, [runId]);
|
|
172
|
+
return c.json({
|
|
173
|
+
...serializeRun(loaded, { output: true }),
|
|
174
|
+
...(minted?.kind === 'ok' && {
|
|
175
|
+
publicAccessToken: minted.token,
|
|
176
|
+
publicAccessTokenExpiresAt: minted.expiresAt.toISOString(),
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ---------- GET /:runId ----------
|
|
182
|
+
r.get('/:runId', async (c) => {
|
|
183
|
+
const requestId = c.get('requestId');
|
|
184
|
+
const runId = c.req.param('runId') as RunId;
|
|
185
|
+
|
|
186
|
+
const loaded = await runBinding.getRun(c.get('tenantId') as TenantId, runId);
|
|
187
|
+
if (loaded === null) {
|
|
188
|
+
c.status(statusFor('run-not-found') as never);
|
|
189
|
+
return c.json(
|
|
190
|
+
toWireError({ code: 'run-not-found', message: `No run with id ${runId}` }, requestId),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return c.json(serializeRun(loaded, { output: true }));
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ---------- GET /:runId/progress ----------
|
|
197
|
+
r.get('/:runId/progress', async (c) => {
|
|
198
|
+
const requestId = c.get('requestId');
|
|
199
|
+
const runId = c.req.param('runId') as RunId;
|
|
200
|
+
const loaded = await readableRun(runBinding, c, runId);
|
|
201
|
+
if (loaded === null) {
|
|
202
|
+
c.status(statusFor('run-not-found') as never);
|
|
203
|
+
return c.json(
|
|
204
|
+
toWireError({ code: 'run-not-found', message: `No run with id ${runId}` }, requestId),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return c.json(serializeRunProgress(loaded));
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ---------- GET / (list, cursor-paginated) ----------
|
|
211
|
+
//
|
|
212
|
+
// Content-scoped list; scope filter threaded via `parseScopeParams`.
|
|
213
|
+
// Every run belongs to exactly one project; `scope.kind === 'project'`
|
|
214
|
+
// narrows to that project, `scope.kind === 'org'` to the org's
|
|
215
|
+
// projects, and tenant / undefined behave as documented in
|
|
216
|
+
// scope-params.ts.
|
|
217
|
+
r.get('/', async (c) => {
|
|
218
|
+
const requestId = c.get('requestId');
|
|
219
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
220
|
+
const limit = clampLimit(c.req.query('limit'));
|
|
221
|
+
|
|
222
|
+
let cursorFilter: DecodedCursor | null = null;
|
|
223
|
+
const rawCursor = c.req.query('cursor');
|
|
224
|
+
if (rawCursor !== undefined && rawCursor.length > 0) {
|
|
225
|
+
const decoded = decodeCursor(rawCursor);
|
|
226
|
+
if (decoded === null) {
|
|
227
|
+
c.status(statusFor('bad-input') as never);
|
|
228
|
+
return c.json(
|
|
229
|
+
toWireError({ code: 'bad-input', message: '`cursor` is malformed' }, requestId),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
cursorFilter = decoded;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const scopeParsed = parseScopeParams(c.req.query(), { tenantId });
|
|
236
|
+
if (scopeParsed.kind === 'err') {
|
|
237
|
+
c.status(statusFor('scope-invalid') as never);
|
|
238
|
+
return c.json(
|
|
239
|
+
toWireError({ code: 'scope-invalid', message: scopeParsed.message }, requestId),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
const listFilter = parseRunListFilter(c.req.query());
|
|
243
|
+
if (listFilter.kind === 'err') {
|
|
244
|
+
c.status(statusFor('bad-input') as never);
|
|
245
|
+
return c.json(toWireError({ code: 'bad-input', message: listFilter.message }, requestId));
|
|
246
|
+
}
|
|
247
|
+
const includeOutput = listFilter.value.includeOutput;
|
|
248
|
+
|
|
249
|
+
const page = await runBinding.listRuns(
|
|
250
|
+
listRunsInput({
|
|
251
|
+
tenantId,
|
|
252
|
+
scope: scopeParsed.scope,
|
|
253
|
+
limit,
|
|
254
|
+
cursor: cursorFilter,
|
|
255
|
+
filter: listFilter.value,
|
|
256
|
+
}),
|
|
257
|
+
);
|
|
258
|
+
return c.json({
|
|
259
|
+
data: page.data.map((row) => serializeRun(row, { output: includeOutput })),
|
|
260
|
+
hasMore: page.nextCursor !== undefined,
|
|
261
|
+
...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// ---------- POST /:runId/cancel ----------
|
|
266
|
+
r.post('/:runId/cancel', async (c) => {
|
|
267
|
+
const requestId = c.get('requestId');
|
|
268
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
269
|
+
const runId = c.req.param('runId') as RunId;
|
|
270
|
+
|
|
271
|
+
const cancelled = await runBinding.cancelRun(tenantId, runId, eventBus);
|
|
272
|
+
if (cancelled.kind === 'err') {
|
|
273
|
+
const err = cancelled.error;
|
|
274
|
+
c.status(statusFor(err.code) as never);
|
|
275
|
+
return c.json(toWireError(err as never, requestId));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Re-read to return the updated row.
|
|
279
|
+
const reloaded = await runBinding.getRun(tenantId, runId);
|
|
280
|
+
if (reloaded === null) {
|
|
281
|
+
c.status(statusFor('journal-error') as never);
|
|
282
|
+
return c.json(
|
|
283
|
+
toWireError(
|
|
284
|
+
{
|
|
285
|
+
code: 'journal-error',
|
|
286
|
+
message: 'Cancelled OK but failed to reload run row',
|
|
287
|
+
},
|
|
288
|
+
requestId,
|
|
289
|
+
),
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return c.json(serializeRun(reloaded, { output: true }));
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// ---------- POST /:runId/resume ----------
|
|
296
|
+
r.post('/:runId/resume', async (c) => {
|
|
297
|
+
const requestId = c.get('requestId');
|
|
298
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
299
|
+
const runId = c.req.param('runId') as RunId;
|
|
300
|
+
|
|
301
|
+
let body: unknown = {};
|
|
302
|
+
const hasBody =
|
|
303
|
+
(c.req.header('content-type') ?? '').includes('json') ||
|
|
304
|
+
(c.req.header('content-length') !== undefined && c.req.header('content-length') !== '0');
|
|
305
|
+
if (hasBody) {
|
|
306
|
+
try {
|
|
307
|
+
const text = await c.req.text();
|
|
308
|
+
body = text.length > 0 ? JSON.parse(text) : {};
|
|
309
|
+
} catch {
|
|
310
|
+
c.status(statusFor('bad-input') as never);
|
|
311
|
+
return c.json(
|
|
312
|
+
toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const parsed = parseResumeBody(body);
|
|
317
|
+
if (parsed.kind === 'err') {
|
|
318
|
+
c.status(statusFor(parsed.error.code) as never);
|
|
319
|
+
return c.json(toWireError(parsed.error, requestId));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Resume completes a waitpoint (`completeToken`) and then drives the
|
|
323
|
+
// run on. Resuming without a `waitpointId` is not supported.
|
|
324
|
+
if (parsed.value.waitpointId === undefined) {
|
|
325
|
+
c.status(statusFor('bad-input') as never);
|
|
326
|
+
return c.json(
|
|
327
|
+
toWireError(
|
|
328
|
+
{
|
|
329
|
+
code: 'bad-input',
|
|
330
|
+
message:
|
|
331
|
+
'Programmatic resume without `waitpointId` is not supported yet — supply a waitpoint id to complete a wait token.',
|
|
332
|
+
},
|
|
333
|
+
requestId,
|
|
334
|
+
),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const completed = await runBinding.completeToken(
|
|
339
|
+
tenantId,
|
|
340
|
+
runId,
|
|
341
|
+
parsed.value.waitpointId,
|
|
342
|
+
parsed.value.value,
|
|
343
|
+
eventBus,
|
|
344
|
+
);
|
|
345
|
+
if (completed.kind === 'err') {
|
|
346
|
+
const err = completed.error;
|
|
347
|
+
c.status(statusFor(err.code) as never);
|
|
348
|
+
return c.json(toWireError(err as never, requestId));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Drive the run on in the same request, as approvals do. Soft on
|
|
352
|
+
// failure: the token completion is already durable, so the run is
|
|
353
|
+
// resumable later; the response shows the status it reached.
|
|
354
|
+
await binding.resumeRun({ tenantId, runId }).catch(() => undefined);
|
|
355
|
+
|
|
356
|
+
const reloaded = await runBinding.getRun(tenantId, runId);
|
|
357
|
+
if (reloaded === null) {
|
|
358
|
+
c.status(statusFor('run-not-found') as never);
|
|
359
|
+
return c.json(
|
|
360
|
+
toWireError({ code: 'run-not-found', message: `No run with id ${runId}` }, requestId),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
return c.json(serializeRun(reloaded, { output: true }));
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// ---------- GET /:runId/stream and /:runId/progress/stream (SSE) ----------
|
|
367
|
+
// One stream, two views: every event in full, or progress only (no
|
|
368
|
+
// payloads), which a public run token may follow.
|
|
369
|
+
const streamRun = async (c: import('hono').Context<AppEnv>, view: 'full' | 'progress') => {
|
|
370
|
+
const requestId = c.get('requestId');
|
|
371
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
372
|
+
const runId = c.req.param('runId') as RunId;
|
|
373
|
+
const since = parseLastEventId(c.req.header('last-event-id'));
|
|
374
|
+
|
|
375
|
+
// Verify the run belongs to this tenant before opening the stream —
|
|
376
|
+
// otherwise a cross-tenant probe learns run existence via the stream
|
|
377
|
+
// hanging vs 404-ing.
|
|
378
|
+
const run = await readableRun(runBinding, c, runId);
|
|
379
|
+
if (run === null) {
|
|
380
|
+
c.status(statusFor('run-not-found') as never);
|
|
381
|
+
return c.json(
|
|
382
|
+
toWireError({ code: 'run-not-found', message: `No run with id ${runId}` }, requestId),
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const encoder = new TextEncoder();
|
|
387
|
+
let lastEmittedSeq = since ?? -1;
|
|
388
|
+
const POLL_MS = 200;
|
|
389
|
+
const MAX_WAIT_MS = 5 * 60_000;
|
|
390
|
+
|
|
391
|
+
const body = new ReadableStream<Uint8Array>({
|
|
392
|
+
async start(controller) {
|
|
393
|
+
const startedAt = Date.now();
|
|
394
|
+
let closed = false;
|
|
395
|
+
const close = (): void => {
|
|
396
|
+
if (closed) return;
|
|
397
|
+
closed = true;
|
|
398
|
+
try {
|
|
399
|
+
controller.close();
|
|
400
|
+
} catch {
|
|
401
|
+
/* already closed */
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Shared projection helper: emit one journal-entry-shaped
|
|
406
|
+
// record to the stream. Returns true iff the entry was a
|
|
407
|
+
// terminal wire kind so the caller can close.
|
|
408
|
+
const emitEntry = (entry: {
|
|
409
|
+
sequence: number;
|
|
410
|
+
kind: string;
|
|
411
|
+
nodeId?: string;
|
|
412
|
+
payload?: unknown;
|
|
413
|
+
timestamp: string;
|
|
414
|
+
}): boolean => {
|
|
415
|
+
if (entry.sequence <= lastEmittedSeq) return false;
|
|
416
|
+
const projected = projectJournalEntry(
|
|
417
|
+
{
|
|
418
|
+
sequence: entry.sequence,
|
|
419
|
+
kind: entry.kind as never,
|
|
420
|
+
...(entry.nodeId !== undefined && { nodeId: entry.nodeId as never }),
|
|
421
|
+
...(entry.payload !== undefined && { payload: entry.payload }),
|
|
422
|
+
timestamp: entry.timestamp as never,
|
|
423
|
+
},
|
|
424
|
+
runId,
|
|
425
|
+
tenantId,
|
|
426
|
+
);
|
|
427
|
+
lastEmittedSeq = entry.sequence;
|
|
428
|
+
if (projected === null) return false;
|
|
429
|
+
controller.enqueue(
|
|
430
|
+
encoder.encode(
|
|
431
|
+
formatSseFrame({
|
|
432
|
+
id: projected.event.eventId,
|
|
433
|
+
event: projected.wireKind,
|
|
434
|
+
data: view === 'progress' ? toRunProgressEvent(projected.event) : projected.event,
|
|
435
|
+
}),
|
|
436
|
+
),
|
|
437
|
+
);
|
|
438
|
+
return isTerminalWireKind(projected.wireKind);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// Push-mode: prefer the event bus when wired. The bus delivers
|
|
442
|
+
// journal entries as `doc` payloads; we backfill via journal
|
|
443
|
+
// reads from `since` first so `Last-Event-Id` resumes work
|
|
444
|
+
// whether we're on the push or poll path.
|
|
445
|
+
if (eventBus !== undefined) {
|
|
446
|
+
const abortCtrl = new AbortController();
|
|
447
|
+
let subscription: Subscription | null = null;
|
|
448
|
+
let fallBackToPoll = false;
|
|
449
|
+
try {
|
|
450
|
+
// Backfill: read persisted journal up to now first, so a
|
|
451
|
+
// reconnect with `Last-Event-Id` doesn't miss events that
|
|
452
|
+
// landed while the client was disconnected. Then subscribe
|
|
453
|
+
// for the tail push-side.
|
|
454
|
+
const backfill = await runBinding.readJournal(tenantId, runId);
|
|
455
|
+
if (backfill.kind === 'err') {
|
|
456
|
+
controller.enqueue(
|
|
457
|
+
encoder.encode(
|
|
458
|
+
formatSseFrame({
|
|
459
|
+
id: `${runId}:error`,
|
|
460
|
+
event: 'error',
|
|
461
|
+
data: {
|
|
462
|
+
code: 'journal-error',
|
|
463
|
+
message: backfill.error.message,
|
|
464
|
+
requestId,
|
|
465
|
+
},
|
|
466
|
+
}),
|
|
467
|
+
),
|
|
468
|
+
);
|
|
469
|
+
close();
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
let sawTerminal = false;
|
|
473
|
+
for (const entry of backfill.value) {
|
|
474
|
+
if (
|
|
475
|
+
emitEntry({
|
|
476
|
+
sequence: entry.sequence,
|
|
477
|
+
kind: entry.kind,
|
|
478
|
+
...(entry.nodeId !== undefined && { nodeId: entry.nodeId as string }),
|
|
479
|
+
...(entry.payload !== undefined && { payload: entry.payload }),
|
|
480
|
+
timestamp: entry.timestamp as string,
|
|
481
|
+
})
|
|
482
|
+
) {
|
|
483
|
+
sawTerminal = true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (sawTerminal) {
|
|
487
|
+
close();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const subOutcome = await eventBus.subscribe(
|
|
491
|
+
tenantId,
|
|
492
|
+
`${KERNEL_RUN_CHANNEL_PREFIX}${runId}`,
|
|
493
|
+
(payload: EventPayload) => {
|
|
494
|
+
const doc = payload.doc as
|
|
495
|
+
| {
|
|
496
|
+
sequence?: unknown;
|
|
497
|
+
kind?: unknown;
|
|
498
|
+
nodeId?: unknown;
|
|
499
|
+
payload?: unknown;
|
|
500
|
+
timestamp?: unknown;
|
|
501
|
+
}
|
|
502
|
+
| null
|
|
503
|
+
| undefined;
|
|
504
|
+
if (doc === null || doc === undefined) return;
|
|
505
|
+
if (typeof doc.sequence !== 'number' || typeof doc.kind !== 'string') return;
|
|
506
|
+
if (typeof doc.timestamp !== 'string') return;
|
|
507
|
+
const wasTerminal = emitEntry({
|
|
508
|
+
sequence: doc.sequence,
|
|
509
|
+
kind: doc.kind,
|
|
510
|
+
...(typeof doc.nodeId === 'string' && { nodeId: doc.nodeId }),
|
|
511
|
+
...('payload' in doc && doc.payload !== undefined && { payload: doc.payload }),
|
|
512
|
+
timestamp: doc.timestamp,
|
|
513
|
+
});
|
|
514
|
+
if (wasTerminal) {
|
|
515
|
+
abortCtrl.abort();
|
|
516
|
+
close();
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
abortCtrl.signal,
|
|
520
|
+
{ sinceSeq: lastEmittedSeq < 0 ? 0 : lastEmittedSeq },
|
|
521
|
+
);
|
|
522
|
+
if (subOutcome.kind === 'err') {
|
|
523
|
+
// Subscription setup failed. Not fatal: leave the stream
|
|
524
|
+
// open and continue with the poll path below.
|
|
525
|
+
fallBackToPoll = true;
|
|
526
|
+
} else {
|
|
527
|
+
subscription = subOutcome.value;
|
|
528
|
+
// Wait until close or max-wait window elapses. We rely
|
|
529
|
+
// on the subscription callback to write frames.
|
|
530
|
+
await new Promise<void>((resolve) => {
|
|
531
|
+
const timer = setTimeout(() => resolve(), MAX_WAIT_MS);
|
|
532
|
+
const onDone = (): void => {
|
|
533
|
+
clearTimeout(timer);
|
|
534
|
+
resolve();
|
|
535
|
+
};
|
|
536
|
+
abortCtrl.signal.addEventListener('abort', onDone, { once: true });
|
|
537
|
+
});
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
} finally {
|
|
541
|
+
abortCtrl.abort();
|
|
542
|
+
if (subscription !== null) {
|
|
543
|
+
try {
|
|
544
|
+
await subscription.unsubscribe();
|
|
545
|
+
} catch {
|
|
546
|
+
/* cleanup best-effort */
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (!fallBackToPoll) close();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Poll mode: no event bus wired, or subscribing failed. Poll the
|
|
554
|
+
// journal every 200 ms until a terminal entry or the max-wait
|
|
555
|
+
// window elapses.
|
|
556
|
+
const pollEmit = async (): Promise<{ terminal: boolean }> => {
|
|
557
|
+
const journal = await runBinding.readJournal(tenantId, runId);
|
|
558
|
+
if (journal.kind === 'err') {
|
|
559
|
+
const frame = formatSseFrame({
|
|
560
|
+
id: `${runId}:error`,
|
|
561
|
+
event: 'error',
|
|
562
|
+
data: {
|
|
563
|
+
code: 'journal-error',
|
|
564
|
+
message: journal.error.message,
|
|
565
|
+
requestId,
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
controller.enqueue(encoder.encode(frame));
|
|
569
|
+
return { terminal: true };
|
|
570
|
+
}
|
|
571
|
+
let sawTerminal = false;
|
|
572
|
+
for (const entry of journal.value) {
|
|
573
|
+
if (
|
|
574
|
+
emitEntry({
|
|
575
|
+
sequence: entry.sequence,
|
|
576
|
+
kind: entry.kind,
|
|
577
|
+
...(entry.nodeId !== undefined && { nodeId: entry.nodeId as string }),
|
|
578
|
+
...(entry.payload !== undefined && { payload: entry.payload }),
|
|
579
|
+
timestamp: entry.timestamp as string,
|
|
580
|
+
})
|
|
581
|
+
) {
|
|
582
|
+
sawTerminal = true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return { terminal: sawTerminal };
|
|
586
|
+
};
|
|
587
|
+
try {
|
|
588
|
+
const first = await pollEmit();
|
|
589
|
+
if (first.terminal) {
|
|
590
|
+
close();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
while (Date.now() - startedAt < MAX_WAIT_MS) {
|
|
594
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
595
|
+
const step = await pollEmit();
|
|
596
|
+
if (step.terminal) break;
|
|
597
|
+
}
|
|
598
|
+
} finally {
|
|
599
|
+
close();
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
return new Response(body, {
|
|
605
|
+
status: 200,
|
|
606
|
+
headers: {
|
|
607
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
608
|
+
'Cache-Control': 'no-cache',
|
|
609
|
+
Connection: 'keep-alive',
|
|
610
|
+
'X-Request-Id': requestId,
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
r.get('/:runId/stream', (c) => streamRun(c, 'full'));
|
|
615
|
+
r.get('/:runId/progress/stream', (c) => streamRun(c, 'progress'));
|
|
616
|
+
|
|
617
|
+
// ---------- GET /:runId/journal ----------
|
|
618
|
+
r.get('/:runId/journal', async (c) => {
|
|
619
|
+
const requestId = c.get('requestId');
|
|
620
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
621
|
+
const runId = c.req.param('runId') as RunId;
|
|
622
|
+
const sinceRaw = c.req.query('since');
|
|
623
|
+
const since = sinceRaw !== undefined ? Number.parseInt(sinceRaw, 10) : undefined;
|
|
624
|
+
|
|
625
|
+
const journal = await runBinding.readJournal(tenantId, runId);
|
|
626
|
+
if (journal.kind === 'err') {
|
|
627
|
+
const err = journal.error;
|
|
628
|
+
c.status(statusFor(err.code) as never);
|
|
629
|
+
return c.json(toWireError(err as never, requestId));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const filtered =
|
|
633
|
+
since !== undefined && Number.isFinite(since)
|
|
634
|
+
? journal.value.filter((e) => e.sequence > since)
|
|
635
|
+
: journal.value;
|
|
636
|
+
return c.json({
|
|
637
|
+
data: filtered,
|
|
638
|
+
hasMore: false,
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
return r;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Wire body for a binding failure. `RunHandlerFailure.details` become
|
|
647
|
+
* the wire error's `details` (not nested under `details.details`);
|
|
648
|
+
* any other field a binding attaches rides along, and `code` /
|
|
649
|
+
* `message` always come from the failure itself.
|
|
650
|
+
*/
|
|
651
|
+
function runFailureToWire(failure: RunHandlerFailure, requestId: string): WireErrorBody {
|
|
652
|
+
const { details, ...rest } = failure as RunHandlerFailure & Readonly<Record<string, unknown>>;
|
|
653
|
+
return toWireError({ ...details, ...rest }, requestId);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** Hand a parsed start body to the binding: an agent turn or a flow run. */
|
|
657
|
+
function invokeFromBody(
|
|
658
|
+
binding: RunHandlerBinding,
|
|
659
|
+
tenantId: TenantId,
|
|
660
|
+
body: ParsedStartRunBody,
|
|
661
|
+
): Promise<RunHandlerOutcome> {
|
|
662
|
+
const common = {
|
|
663
|
+
tenantId,
|
|
664
|
+
...(body.projectId !== undefined && { projectId: body.projectId }),
|
|
665
|
+
input: body.input,
|
|
666
|
+
...(body.dryRun !== undefined && { dryRun: body.dryRun }),
|
|
667
|
+
...(body.wait !== undefined && { wait: body.wait }),
|
|
668
|
+
};
|
|
669
|
+
return body.kind === 'agent'
|
|
670
|
+
? binding.invokeAgent({
|
|
671
|
+
...common,
|
|
672
|
+
agentId: body.agentId,
|
|
673
|
+
...(body.agentVersion !== undefined && { agentVersion: body.agentVersion }),
|
|
674
|
+
})
|
|
675
|
+
: binding.invokeFlow({
|
|
676
|
+
...common,
|
|
677
|
+
flowId: body.flowId,
|
|
678
|
+
...(body.flowVersion !== undefined && { flowVersion: body.flowVersion }),
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Wire row for a run. `output` is the run's output once it completed;
|
|
684
|
+
* single-run responses carry it, lists only with `?include=output`
|
|
685
|
+
* (outputs can be large). Child runs carry their parent's run + node.
|
|
686
|
+
*/
|
|
687
|
+
function serializeRun(
|
|
688
|
+
row: KernelRunRecord,
|
|
689
|
+
opts: { readonly output: boolean },
|
|
690
|
+
): Record<string, unknown> {
|
|
691
|
+
return {
|
|
692
|
+
id: row.runId as unknown as string,
|
|
693
|
+
tenantId: row.tenantId as unknown as string,
|
|
694
|
+
projectId: row.projectId as unknown as string,
|
|
695
|
+
flowId: row.flowId,
|
|
696
|
+
flowVersion: row.flowVersion,
|
|
697
|
+
status: row.status,
|
|
698
|
+
dryRun: row.dryRun,
|
|
699
|
+
createdAt: row.createdAt as unknown as string,
|
|
700
|
+
updatedAt: row.updatedAt as unknown as string,
|
|
701
|
+
completedAt: row.completedAt ?? undefined,
|
|
702
|
+
failureMessage: row.failureMessage ?? undefined,
|
|
703
|
+
...(opts.output && row.output !== undefined && { output: row.output }),
|
|
704
|
+
...(row.parentRunId != null && { parentRunId: row.parentRunId as unknown as string }),
|
|
705
|
+
...(row.parentNodeId != null && { parentNodeId: row.parentNodeId as unknown as string }),
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/** A run's progress: status and timing, no data. */
|
|
710
|
+
function serializeRunProgress(row: KernelRunRecord): Record<string, unknown> {
|
|
711
|
+
return {
|
|
712
|
+
id: row.runId as unknown as string,
|
|
713
|
+
flowId: row.flowId,
|
|
714
|
+
flowVersion: row.flowVersion,
|
|
715
|
+
status: row.status,
|
|
716
|
+
createdAt: row.createdAt as unknown as string,
|
|
717
|
+
updatedAt: row.updatedAt as unknown as string,
|
|
718
|
+
...(row.completedAt != null && { completedAt: row.completedAt as unknown as string }),
|
|
719
|
+
...(row.parentRunId != null && { parentRunId: row.parentRunId as unknown as string }),
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* The run when the caller may read it: any run of the tenant for an API
|
|
725
|
+
* token; for a public run token, a run the token names or a descendant
|
|
726
|
+
* of one. `null` otherwise, which the routes answer as 404 so a token
|
|
727
|
+
* learns nothing about other runs.
|
|
728
|
+
*/
|
|
729
|
+
async function readableRun(
|
|
730
|
+
runBinding: RunBinding,
|
|
731
|
+
c: import('hono').Context<AppEnv>,
|
|
732
|
+
runId: RunId,
|
|
733
|
+
): Promise<KernelRunRecord | null> {
|
|
734
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
735
|
+
const row = await runBinding.getRun(tenantId, runId);
|
|
736
|
+
if (row === null || c.get('tokenKind') !== 'public-run') return row;
|
|
737
|
+
const granted = c.get('publicRunIds') ?? [];
|
|
738
|
+
let current: KernelRunRecord | null = row;
|
|
739
|
+
for (let depth = 0; current !== null && depth < MAX_RUN_ANCESTRY; depth += 1) {
|
|
740
|
+
if (granted.includes(current.runId)) return row;
|
|
741
|
+
if (current.parentRunId == null) return null;
|
|
742
|
+
current = await runBinding.getRun(tenantId, current.parentRunId);
|
|
743
|
+
}
|
|
744
|
+
return null;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** The binding query for `GET /v1/runs`: scope, page, and the parent filters. */
|
|
748
|
+
function listRunsInput(input: {
|
|
749
|
+
readonly tenantId: TenantId;
|
|
750
|
+
readonly scope: Extract<ParseScopeParamsOutcome, { kind: 'ok' }>['scope'];
|
|
751
|
+
readonly limit: number;
|
|
752
|
+
readonly cursor: DecodedCursor | null;
|
|
753
|
+
readonly filter: RunListFilter;
|
|
754
|
+
}): ListRunsInput {
|
|
755
|
+
const { tenantId, scope, limit, cursor, filter } = input;
|
|
756
|
+
return {
|
|
757
|
+
tenantId,
|
|
758
|
+
...(scope?.kind === 'project' && {
|
|
759
|
+
scope: { kind: 'project' as const, projectId: scope.projectId as ProjectId },
|
|
760
|
+
}),
|
|
761
|
+
...(scope?.kind === 'org' && {
|
|
762
|
+
scope: { kind: 'org' as const, orgId: scope.orgId as unknown as OrgId },
|
|
763
|
+
}),
|
|
764
|
+
limit,
|
|
765
|
+
...(cursor !== null && {
|
|
766
|
+
cursor: { createdAt: cursor.createdAt as never, id: cursor.id as RunId },
|
|
767
|
+
}),
|
|
768
|
+
...(filter.parentRunId !== undefined && { parent: { runId: filter.parentRunId } }),
|
|
769
|
+
...(filter.topLevelOnly && { topLevelOnly: true }),
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** Waitpoint ids the runtime reserves for a parent waiting on a child run. */
|
|
774
|
+
const RESERVED_WAITPOINT_PREFIX = 'child:';
|
|
775
|
+
|
|
776
|
+
interface RunListFilter {
|
|
777
|
+
readonly parentRunId?: RunId;
|
|
778
|
+
readonly topLevelOnly: boolean;
|
|
779
|
+
readonly includeOutput: boolean;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/** `?parentRunId=` (children of a run), `?topLevel=true`, `?include=output`. */
|
|
783
|
+
function parseRunListFilter(
|
|
784
|
+
query: Readonly<Record<string, string>>,
|
|
785
|
+
): { kind: 'ok'; value: RunListFilter } | { kind: 'err'; message: string } {
|
|
786
|
+
const { parentRunId, topLevel, include } = query;
|
|
787
|
+
if (topLevel !== undefined && topLevel !== 'true' && topLevel !== 'false') {
|
|
788
|
+
return { kind: 'err', message: '`topLevel` must be `true` or `false`' };
|
|
789
|
+
}
|
|
790
|
+
const topLevelOnly = topLevel === 'true';
|
|
791
|
+
if (parentRunId !== undefined && parentRunId.length === 0) {
|
|
792
|
+
return { kind: 'err', message: '`parentRunId` must be a run id' };
|
|
793
|
+
}
|
|
794
|
+
if (parentRunId !== undefined && topLevelOnly) {
|
|
795
|
+
return { kind: 'err', message: '`parentRunId` and `topLevel=true` cannot be combined' };
|
|
796
|
+
}
|
|
797
|
+
const includes = include === undefined ? [] : include.split(',').map((i) => i.trim());
|
|
798
|
+
const unknown = includes.filter((i) => i !== 'output');
|
|
799
|
+
if (unknown.length > 0) {
|
|
800
|
+
return { kind: 'err', message: `Unknown \`include\` value(s): ${unknown.join(', ')}` };
|
|
801
|
+
}
|
|
802
|
+
return {
|
|
803
|
+
kind: 'ok',
|
|
804
|
+
value: {
|
|
805
|
+
...(parentRunId !== undefined && { parentRunId: parentRunId as RunId }),
|
|
806
|
+
topLevelOnly,
|
|
807
|
+
includeOutput: includes.includes('output'),
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function parseResumeBody(
|
|
813
|
+
body: unknown,
|
|
814
|
+
):
|
|
815
|
+
| { kind: 'ok'; value: { waitpointId?: string; value?: unknown } }
|
|
816
|
+
| { kind: 'err'; error: { code: string; message: string } } {
|
|
817
|
+
if (body === null || typeof body !== 'object') {
|
|
818
|
+
return { kind: 'err', error: { code: 'bad-input', message: 'Request body must be an object' } };
|
|
819
|
+
}
|
|
820
|
+
const b = body as Record<string, unknown>;
|
|
821
|
+
const waitpointId = b.waitpointId;
|
|
822
|
+
if (waitpointId !== undefined && (typeof waitpointId !== 'string' || waitpointId.length === 0)) {
|
|
823
|
+
return {
|
|
824
|
+
kind: 'err',
|
|
825
|
+
error: {
|
|
826
|
+
code: 'bad-input',
|
|
827
|
+
message: '`waitpointId` must be a non-empty string when supplied',
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
if (typeof waitpointId === 'string' && waitpointId.startsWith(RESERVED_WAITPOINT_PREFIX)) {
|
|
832
|
+
return {
|
|
833
|
+
kind: 'err',
|
|
834
|
+
error: {
|
|
835
|
+
code: 'bad-input',
|
|
836
|
+
message: `Waitpoint ids starting with "${RESERVED_WAITPOINT_PREFIX}" belong to the runtime (a parent waiting on a child run) and cannot be completed by a caller.`,
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
return {
|
|
841
|
+
kind: 'ok',
|
|
842
|
+
value: {
|
|
843
|
+
...(waitpointId !== undefined && { waitpointId: waitpointId as string }),
|
|
844
|
+
...('value' in b && { value: b.value }),
|
|
845
|
+
},
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/** `options` of a start body: `dryRun?`, `wait?` (booleans). */
|
|
850
|
+
function parseStartOptions(
|
|
851
|
+
raw: unknown,
|
|
852
|
+
):
|
|
853
|
+
| { kind: 'ok'; value: { readonly dryRun?: boolean; readonly wait?: boolean } }
|
|
854
|
+
| { kind: 'err'; error: { code: string; message: string } } {
|
|
855
|
+
const options = (raw ?? {}) as Record<string, unknown>;
|
|
856
|
+
if (typeof options !== 'object' || options === null) {
|
|
857
|
+
return {
|
|
858
|
+
kind: 'err',
|
|
859
|
+
error: { code: 'bad-input', message: '`options` must be an object when supplied' },
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
for (const key of ['dryRun', 'wait'] as const) {
|
|
863
|
+
if (options[key] !== undefined && typeof options[key] !== 'boolean') {
|
|
864
|
+
return {
|
|
865
|
+
kind: 'err',
|
|
866
|
+
error: { code: 'bad-input', message: `\`options.${key}\` must be a boolean` },
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const { dryRun, wait } = options as { dryRun?: boolean; wait?: boolean };
|
|
871
|
+
return {
|
|
872
|
+
kind: 'ok',
|
|
873
|
+
value: { ...(dryRun !== undefined && { dryRun }), ...(wait !== undefined && { wait }) },
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
type ParsedStartRunBody =
|
|
878
|
+
| {
|
|
879
|
+
readonly kind: 'agent';
|
|
880
|
+
readonly agentId: AgentId;
|
|
881
|
+
readonly agentVersion?: Semver;
|
|
882
|
+
readonly projectId?: ProjectId;
|
|
883
|
+
readonly input: unknown;
|
|
884
|
+
readonly dryRun?: boolean;
|
|
885
|
+
readonly wait?: boolean;
|
|
886
|
+
}
|
|
887
|
+
| {
|
|
888
|
+
readonly kind: 'flow';
|
|
889
|
+
readonly flowId: FlowId;
|
|
890
|
+
readonly flowVersion?: Semver;
|
|
891
|
+
readonly projectId?: ProjectId;
|
|
892
|
+
readonly input: unknown;
|
|
893
|
+
readonly dryRun?: boolean;
|
|
894
|
+
readonly wait?: boolean;
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
function parseStartRunBody(
|
|
898
|
+
body: unknown,
|
|
899
|
+
):
|
|
900
|
+
| { kind: 'ok'; value: ParsedStartRunBody }
|
|
901
|
+
| { kind: 'err'; error: { code: string; message: string } } {
|
|
902
|
+
if (body === null || typeof body !== 'object') {
|
|
903
|
+
return { kind: 'err', error: { code: 'bad-input', message: 'Request body must be an object' } };
|
|
904
|
+
}
|
|
905
|
+
const b = body as Record<string, unknown>;
|
|
906
|
+
const hasAgent = typeof b.agent === 'string' && b.agent.length > 0;
|
|
907
|
+
const hasGraph = typeof b.flow === 'string' && b.flow.length > 0;
|
|
908
|
+
if (hasAgent && hasGraph) {
|
|
909
|
+
return {
|
|
910
|
+
kind: 'err',
|
|
911
|
+
error: { code: 'bad-input', message: 'Provide exactly one of `agent` or `flow`, not both' },
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
if (!hasAgent && !hasGraph) {
|
|
915
|
+
return {
|
|
916
|
+
kind: 'err',
|
|
917
|
+
error: { code: 'bad-input', message: 'Request body must include `agent` or `flow`' },
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
if (!('input' in b)) {
|
|
921
|
+
return { kind: 'err', error: { code: 'bad-input', message: '`input` is required' } };
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const options = parseStartOptions(b.options);
|
|
925
|
+
if (options.kind === 'err') return options;
|
|
926
|
+
const { dryRun, wait } = options.value;
|
|
927
|
+
if (hasAgent) {
|
|
928
|
+
const agentVersion = b.agentVersion;
|
|
929
|
+
if (agentVersion !== undefined && typeof agentVersion !== 'string') {
|
|
930
|
+
return {
|
|
931
|
+
kind: 'err',
|
|
932
|
+
error: { code: 'bad-input', message: '`agentVersion` must be a string when supplied' },
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
const agentProjectIdRaw = b.projectId;
|
|
936
|
+
if (agentProjectIdRaw !== undefined && typeof agentProjectIdRaw !== 'string') {
|
|
937
|
+
return {
|
|
938
|
+
kind: 'err',
|
|
939
|
+
error: { code: 'bad-input', message: '`projectId` must be a string when supplied' },
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
return {
|
|
943
|
+
kind: 'ok',
|
|
944
|
+
value: {
|
|
945
|
+
kind: 'agent',
|
|
946
|
+
agentId: b.agent as AgentId,
|
|
947
|
+
...(agentVersion !== undefined && { agentVersion: agentVersion as Semver }),
|
|
948
|
+
...(agentProjectIdRaw !== undefined && { projectId: agentProjectIdRaw as ProjectId }),
|
|
949
|
+
input: b.input,
|
|
950
|
+
...(dryRun !== undefined && { dryRun }),
|
|
951
|
+
...(wait !== undefined && { wait }),
|
|
952
|
+
},
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
const flowVersion = b.flowVersion;
|
|
956
|
+
if (flowVersion !== undefined && typeof flowVersion !== 'string') {
|
|
957
|
+
return {
|
|
958
|
+
kind: 'err',
|
|
959
|
+
error: { code: 'bad-input', message: '`flowVersion` must be a string when supplied' },
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const projectId = b.projectId;
|
|
963
|
+
if (projectId !== undefined && typeof projectId !== 'string') {
|
|
964
|
+
return {
|
|
965
|
+
kind: 'err',
|
|
966
|
+
error: { code: 'bad-input', message: '`projectId` must be a string when supplied' },
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
return {
|
|
970
|
+
kind: 'ok',
|
|
971
|
+
value: {
|
|
972
|
+
kind: 'flow',
|
|
973
|
+
flowId: b.flow as FlowId,
|
|
974
|
+
...(flowVersion !== undefined && { flowVersion: flowVersion as Semver }),
|
|
975
|
+
...(projectId !== undefined && { projectId: projectId as ProjectId }),
|
|
976
|
+
input: b.input,
|
|
977
|
+
...(dryRun !== undefined && { dryRun }),
|
|
978
|
+
...(wait !== undefined && { wait }),
|
|
979
|
+
},
|
|
980
|
+
};
|
|
981
|
+
}
|