@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,369 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
Conversation,
|
|
8
|
+
ConversationBinding,
|
|
9
|
+
ConversationId,
|
|
10
|
+
ConversationMessage,
|
|
11
|
+
ConversationPageCursor,
|
|
12
|
+
} from '@kindgi/agents';
|
|
13
|
+
import type { RunBinding } from '@kindgi/runtime';
|
|
14
|
+
import type { AgentId, Semver, TenantId } from '@kindgi/types';
|
|
15
|
+
|
|
16
|
+
import { statusFor, toWireError } from '../errors.js';
|
|
17
|
+
import type { AppEnv } from '../types.js';
|
|
18
|
+
import { clampLimit, decodeCursor, encodeCursor } from './pagination.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Conversations resource routes. All storage access goes through the
|
|
22
|
+
* `ConversationBinding` — the router never touches storage directly.
|
|
23
|
+
* Messages sort is `sequence asc` (natural conversation reading
|
|
24
|
+
* order); conversation list sort is `openedAt DESC, id DESC`
|
|
25
|
+
* (most-recently-opened first).
|
|
26
|
+
*/
|
|
27
|
+
export function conversationsRouter(
|
|
28
|
+
conversationBinding: ConversationBinding,
|
|
29
|
+
runBinding: RunBinding,
|
|
30
|
+
): Hono<AppEnv> {
|
|
31
|
+
const r = new Hono<AppEnv>();
|
|
32
|
+
|
|
33
|
+
// ---------- GET / (list, cursor-paginated) ----------
|
|
34
|
+
r.get('/', async (c) => {
|
|
35
|
+
const requestId = c.get('requestId');
|
|
36
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
37
|
+
const limit = clampLimit(c.req.query('limit'));
|
|
38
|
+
|
|
39
|
+
const statusRaw = c.req.query('status');
|
|
40
|
+
let statusFilter: 'open' | 'closed' | undefined;
|
|
41
|
+
if (statusRaw !== undefined && statusRaw.length > 0) {
|
|
42
|
+
if (statusRaw !== 'open' && statusRaw !== 'closed') {
|
|
43
|
+
c.status(statusFor('bad-input') as never);
|
|
44
|
+
return c.json(
|
|
45
|
+
toWireError(
|
|
46
|
+
{ code: 'bad-input', message: '`status` must be one of: open, closed' },
|
|
47
|
+
requestId,
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
statusFilter = statusRaw;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const agentIdRaw = c.req.query('agentId');
|
|
55
|
+
|
|
56
|
+
let before: ConversationPageCursor | undefined;
|
|
57
|
+
const rawCursor = c.req.query('cursor');
|
|
58
|
+
if (rawCursor !== undefined && rawCursor.length > 0) {
|
|
59
|
+
const decoded = decodeCursor(rawCursor);
|
|
60
|
+
if (decoded === null) {
|
|
61
|
+
c.status(statusFor('bad-input') as never);
|
|
62
|
+
return c.json(
|
|
63
|
+
toWireError({ code: 'bad-input', message: '`cursor` is malformed' }, requestId),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
before = { openedAt: decoded.createdAt, id: decoded.id as unknown as ConversationId };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const listResult = await conversationBinding.listConversationsPage({
|
|
70
|
+
tenantId,
|
|
71
|
+
...(agentIdRaw !== undefined && agentIdRaw.length > 0 && { agentId: agentIdRaw as AgentId }),
|
|
72
|
+
...(statusFilter !== undefined && { status: statusFilter }),
|
|
73
|
+
...(before !== undefined && { before }),
|
|
74
|
+
limit,
|
|
75
|
+
});
|
|
76
|
+
if (listResult.kind === 'err') {
|
|
77
|
+
c.status(statusFor(listResult.error.code) as never);
|
|
78
|
+
return c.json(toWireError(listResult.error as never, requestId));
|
|
79
|
+
}
|
|
80
|
+
const { data, hasMore } = listResult.value;
|
|
81
|
+
const last = data[data.length - 1];
|
|
82
|
+
const nextCursor =
|
|
83
|
+
hasMore && last !== undefined
|
|
84
|
+
? encodeCursor({
|
|
85
|
+
createdAt: last.openedAt as unknown as string,
|
|
86
|
+
id: last.id as unknown as string,
|
|
87
|
+
})
|
|
88
|
+
: undefined;
|
|
89
|
+
return c.json({
|
|
90
|
+
data: data.map(serializeConversation),
|
|
91
|
+
hasMore,
|
|
92
|
+
...(nextCursor !== undefined && { nextCursor }),
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ---------- GET /:conversationId ----------
|
|
97
|
+
r.get('/:conversationId', async (c) => {
|
|
98
|
+
const requestId = c.get('requestId');
|
|
99
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
100
|
+
const conversationId = c.req.param('conversationId') as ConversationId;
|
|
101
|
+
|
|
102
|
+
const got = await conversationBinding.getConversation(tenantId, conversationId);
|
|
103
|
+
if (got.kind === 'err') {
|
|
104
|
+
c.status(statusFor(got.error.code) as never);
|
|
105
|
+
return c.json(toWireError(got.error as never, requestId));
|
|
106
|
+
}
|
|
107
|
+
return c.json(serializeConversation(got.value));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ---------- POST / (open) ----------
|
|
111
|
+
r.post('/', async (c) => {
|
|
112
|
+
const requestId = c.get('requestId');
|
|
113
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
114
|
+
|
|
115
|
+
let body: unknown;
|
|
116
|
+
try {
|
|
117
|
+
body = await c.req.json();
|
|
118
|
+
} catch {
|
|
119
|
+
c.status(statusFor('bad-input') as never);
|
|
120
|
+
return c.json(
|
|
121
|
+
toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const parsed = parseOpenBody(body);
|
|
125
|
+
if (parsed.kind === 'err') {
|
|
126
|
+
c.status(statusFor(parsed.error.code) as never);
|
|
127
|
+
return c.json(toWireError(parsed.error, requestId));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const opened = await conversationBinding.openConversation({
|
|
131
|
+
tenantId,
|
|
132
|
+
agentId: parsed.value.agentId,
|
|
133
|
+
agentVersion: parsed.value.agentVersion,
|
|
134
|
+
title: parsed.value.title,
|
|
135
|
+
scope: parsed.value.scope,
|
|
136
|
+
...(parsed.value.participantId !== undefined && {
|
|
137
|
+
participantId: parsed.value.participantId,
|
|
138
|
+
}),
|
|
139
|
+
...(parsed.value.metadata !== undefined && { metadata: parsed.value.metadata }),
|
|
140
|
+
});
|
|
141
|
+
if (opened.kind === 'err') {
|
|
142
|
+
c.status(statusFor(opened.error.code) as never);
|
|
143
|
+
return c.json(toWireError(opened.error as never, requestId));
|
|
144
|
+
}
|
|
145
|
+
c.status(201);
|
|
146
|
+
return c.json(serializeConversation(opened.value));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ---------- POST /:conversationId/close (idempotent) ----------
|
|
150
|
+
r.post('/:conversationId/close', async (c) => {
|
|
151
|
+
const requestId = c.get('requestId');
|
|
152
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
153
|
+
const conversationId = c.req.param('conversationId') as ConversationId;
|
|
154
|
+
|
|
155
|
+
const existing = await conversationBinding.getConversation(tenantId, conversationId);
|
|
156
|
+
if (existing.kind === 'err') {
|
|
157
|
+
c.status(statusFor(existing.error.code) as never);
|
|
158
|
+
return c.json(toWireError(existing.error as never, requestId));
|
|
159
|
+
}
|
|
160
|
+
// Close is idempotent — closing an already-closed conversation
|
|
161
|
+
// returns 200 with the current row, no error and no timestamp bump.
|
|
162
|
+
if (existing.value.closedAt !== undefined) {
|
|
163
|
+
return c.json(serializeConversation(existing.value));
|
|
164
|
+
}
|
|
165
|
+
const closed = await conversationBinding.closeConversation(
|
|
166
|
+
tenantId,
|
|
167
|
+
conversationId,
|
|
168
|
+
runBinding,
|
|
169
|
+
);
|
|
170
|
+
if (closed.kind === 'err') {
|
|
171
|
+
c.status(statusFor(closed.error.code) as never);
|
|
172
|
+
return c.json(toWireError(closed.error as never, requestId));
|
|
173
|
+
}
|
|
174
|
+
return c.json(serializeConversation(closed.value));
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ---------- GET /:conversationId/messages (cursor-paginated, sequence asc) ----------
|
|
178
|
+
r.get('/:conversationId/messages', async (c) => {
|
|
179
|
+
const requestId = c.get('requestId');
|
|
180
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
181
|
+
const conversationId = c.req.param('conversationId') as ConversationId;
|
|
182
|
+
const limit = clampLimit(c.req.query('limit'));
|
|
183
|
+
|
|
184
|
+
const rawCursor = c.req.query('cursor');
|
|
185
|
+
let sinceSequence: number | undefined;
|
|
186
|
+
if (rawCursor !== undefined && rawCursor.length > 0) {
|
|
187
|
+
const decoded = decodeMessageCursor(rawCursor);
|
|
188
|
+
if (decoded === null) {
|
|
189
|
+
c.status(statusFor('bad-input') as never);
|
|
190
|
+
return c.json(
|
|
191
|
+
toWireError({ code: 'bad-input', message: '`cursor` is malformed' }, requestId),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
// Advance past the last-seen sequence. `readMessages`' sinceSequence
|
|
195
|
+
// is inclusive (`>= n`), so bump by one for exclusive resume.
|
|
196
|
+
sinceSequence = decoded + 1;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = await conversationBinding.readMessages({
|
|
200
|
+
tenantId,
|
|
201
|
+
conversationId,
|
|
202
|
+
...(sinceSequence !== undefined && { sinceSequence }),
|
|
203
|
+
limit: limit + 1,
|
|
204
|
+
});
|
|
205
|
+
if (result.kind === 'err') {
|
|
206
|
+
c.status(statusFor(result.error.code) as never);
|
|
207
|
+
return c.json(toWireError(result.error as never, requestId));
|
|
208
|
+
}
|
|
209
|
+
const all = result.value;
|
|
210
|
+
const hasMore = all.length > limit;
|
|
211
|
+
const page = hasMore ? all.slice(0, limit) : all;
|
|
212
|
+
const last = page[page.length - 1];
|
|
213
|
+
const nextCursor =
|
|
214
|
+
hasMore && last !== undefined ? encodeMessageCursor(last.sequence) : undefined;
|
|
215
|
+
return c.json({
|
|
216
|
+
data: page.map(serializeMessage),
|
|
217
|
+
hasMore,
|
|
218
|
+
...(nextCursor !== undefined && { nextCursor }),
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return r;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ============ serializers ============
|
|
226
|
+
|
|
227
|
+
function serializeConversation(c: Conversation): Record<string, unknown> {
|
|
228
|
+
return {
|
|
229
|
+
id: c.id as unknown as string,
|
|
230
|
+
tenantId: c.tenantId as unknown as string,
|
|
231
|
+
agentId: c.agentId as unknown as string,
|
|
232
|
+
agentVersion: c.agentVersion as unknown as string,
|
|
233
|
+
title: c.title,
|
|
234
|
+
...(c.participantId !== undefined && { participantId: c.participantId }),
|
|
235
|
+
scope: c.scope,
|
|
236
|
+
status: (c.closedAt === undefined ? 'open' : 'closed') as 'open' | 'closed',
|
|
237
|
+
openedAt: c.openedAt as unknown as string,
|
|
238
|
+
...(c.closedAt !== undefined && { closedAt: c.closedAt as unknown as string }),
|
|
239
|
+
turnCount: c.turnCount,
|
|
240
|
+
...(c.lastMessageAt !== undefined && { lastMessageAt: c.lastMessageAt as unknown as string }),
|
|
241
|
+
...(c.metadata !== undefined && { metadata: c.metadata }),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function serializeMessage(m: ConversationMessage): Record<string, unknown> {
|
|
246
|
+
return {
|
|
247
|
+
sequence: m.sequence,
|
|
248
|
+
role: m.role,
|
|
249
|
+
content: m.content,
|
|
250
|
+
...(m.toolCall !== undefined && { toolCall: m.toolCall }),
|
|
251
|
+
...(m.actor !== undefined && { actor: m.actor }),
|
|
252
|
+
createdAt: m.createdAt as unknown as string,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ============ cursor helpers ============
|
|
257
|
+
|
|
258
|
+
function encodeMessageCursor(sequence: number): string {
|
|
259
|
+
return Buffer.from(JSON.stringify({ sequence }), 'utf8').toString('base64url');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function decodeMessageCursor(raw: string): number | null {
|
|
263
|
+
try {
|
|
264
|
+
const json = Buffer.from(raw, 'base64url').toString('utf8');
|
|
265
|
+
const parsed = JSON.parse(json) as unknown;
|
|
266
|
+
if (
|
|
267
|
+
parsed === null ||
|
|
268
|
+
typeof parsed !== 'object' ||
|
|
269
|
+
typeof (parsed as { sequence?: unknown }).sequence !== 'number'
|
|
270
|
+
) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return (parsed as { sequence: number }).sequence;
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============ body parsers ============
|
|
280
|
+
|
|
281
|
+
interface ParsedOpenBody {
|
|
282
|
+
readonly agentId: AgentId;
|
|
283
|
+
readonly agentVersion: Semver;
|
|
284
|
+
readonly title: string;
|
|
285
|
+
readonly scope: Readonly<Record<string, unknown>>;
|
|
286
|
+
readonly participantId?: string;
|
|
287
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function parseOpenBody(
|
|
291
|
+
body: unknown,
|
|
292
|
+
):
|
|
293
|
+
| { kind: 'ok'; value: ParsedOpenBody }
|
|
294
|
+
| { kind: 'err'; error: { code: string; message: string } } {
|
|
295
|
+
if (body === null || typeof body !== 'object') {
|
|
296
|
+
return { kind: 'err', error: { code: 'bad-input', message: 'Request body must be an object' } };
|
|
297
|
+
}
|
|
298
|
+
const b = body as Record<string, unknown>;
|
|
299
|
+
const agentId = b.agentId;
|
|
300
|
+
if (typeof agentId !== 'string' || agentId.length === 0) {
|
|
301
|
+
return {
|
|
302
|
+
kind: 'err',
|
|
303
|
+
error: { code: 'bad-input', message: '`agentId` must be a non-empty string' },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const agentVersion = b.agentVersion;
|
|
307
|
+
if (typeof agentVersion !== 'string' || agentVersion.length === 0) {
|
|
308
|
+
return {
|
|
309
|
+
kind: 'err',
|
|
310
|
+
error: { code: 'bad-input', message: '`agentVersion` must be a non-empty string' },
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const titleRaw = b.title;
|
|
314
|
+
if (titleRaw !== undefined && typeof titleRaw !== 'string') {
|
|
315
|
+
return {
|
|
316
|
+
kind: 'err',
|
|
317
|
+
error: { code: 'bad-input', message: '`title` must be a string when supplied' },
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// Default title when omitted — the underlying runtime rejects blanks.
|
|
321
|
+
// Callers who want a first-turn auto-title supply that themselves.
|
|
322
|
+
const title =
|
|
323
|
+
titleRaw !== undefined && titleRaw.trim().length > 0 ? titleRaw : 'Untitled conversation';
|
|
324
|
+
|
|
325
|
+
const scopeRaw = b.scope;
|
|
326
|
+
let scope: Readonly<Record<string, unknown>>;
|
|
327
|
+
if (scopeRaw === undefined) {
|
|
328
|
+
scope = {};
|
|
329
|
+
} else if (scopeRaw === null || typeof scopeRaw !== 'object' || Array.isArray(scopeRaw)) {
|
|
330
|
+
return {
|
|
331
|
+
kind: 'err',
|
|
332
|
+
error: { code: 'bad-input', message: '`scope` must be an object when supplied' },
|
|
333
|
+
};
|
|
334
|
+
} else {
|
|
335
|
+
scope = scopeRaw as Readonly<Record<string, unknown>>;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const participantIdRaw = b.participantId;
|
|
339
|
+
if (participantIdRaw !== undefined && typeof participantIdRaw !== 'string') {
|
|
340
|
+
return {
|
|
341
|
+
kind: 'err',
|
|
342
|
+
error: { code: 'bad-input', message: '`participantId` must be a string when supplied' },
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const metadataRaw = b.metadata;
|
|
347
|
+
let metadata: Readonly<Record<string, unknown>> | undefined;
|
|
348
|
+
if (metadataRaw !== undefined) {
|
|
349
|
+
if (metadataRaw === null || typeof metadataRaw !== 'object' || Array.isArray(metadataRaw)) {
|
|
350
|
+
return {
|
|
351
|
+
kind: 'err',
|
|
352
|
+
error: { code: 'bad-input', message: '`metadata` must be an object when supplied' },
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
metadata = metadataRaw as Readonly<Record<string, unknown>>;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
kind: 'ok',
|
|
360
|
+
value: {
|
|
361
|
+
agentId: agentId as AgentId,
|
|
362
|
+
agentVersion: agentVersion as Semver,
|
|
363
|
+
title,
|
|
364
|
+
scope,
|
|
365
|
+
...(participantIdRaw !== undefined && { participantId: participantIdRaw }),
|
|
366
|
+
...(metadata !== undefined && { metadata }),
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
|
|
6
|
+
import type { Cursor, TenantId, Timestamp } from '@kindgi/types';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
COST_GROUP_DIMENSIONS,
|
|
10
|
+
type CostAggregateGroup,
|
|
11
|
+
type CostBinding,
|
|
12
|
+
type CostGroupDimension,
|
|
13
|
+
type CostRecord,
|
|
14
|
+
type CostRecordFilter,
|
|
15
|
+
} from '../cost-binding.js';
|
|
16
|
+
import { statusFor, toWireError } from '../errors.js';
|
|
17
|
+
import type { AppEnv } from '../types.js';
|
|
18
|
+
import { clampLimit } from './pagination.js';
|
|
19
|
+
import { parseScopeParams } from './scope-params.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cost readback routes — part of the admin control plane. Three
|
|
23
|
+
* surfaces:
|
|
24
|
+
*
|
|
25
|
+
* - `GET /v1/cost/records` — paginated raw records with filters
|
|
26
|
+
* - `GET /v1/cost/records/:id` — single record
|
|
27
|
+
* - `GET /v1/cost/aggregate` — multi-dim rollup over a required
|
|
28
|
+
* time window
|
|
29
|
+
*
|
|
30
|
+
* The aggregate endpoint is the primary consumer path (dashboards);
|
|
31
|
+
* the records list is for drilldown. Aggregate always runs over a
|
|
32
|
+
* bounded time range — without one, queries would scan unbounded rows.
|
|
33
|
+
* When the caller gives none, the route applies "last 30 days" and
|
|
34
|
+
* echoes it in the response `timeRange` field for round-trippability.
|
|
35
|
+
*
|
|
36
|
+
* Budgets are not part of this surface.
|
|
37
|
+
*/
|
|
38
|
+
export function costRouter(binding: CostBinding): Hono<AppEnv> {
|
|
39
|
+
const r = new Hono<AppEnv>();
|
|
40
|
+
|
|
41
|
+
const DEFAULT_AGGREGATE_WINDOW_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
42
|
+
|
|
43
|
+
// ---------- GET /records ----------
|
|
44
|
+
r.get('/records', async (c) => {
|
|
45
|
+
const requestId = c.get('requestId');
|
|
46
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
47
|
+
const limit = clampLimit(c.req.query('limit'));
|
|
48
|
+
|
|
49
|
+
const cursorRaw = c.req.query('cursor');
|
|
50
|
+
const filter = parseRecordFilter(c.req.query());
|
|
51
|
+
if (filter.kind === 'err') {
|
|
52
|
+
c.status(statusFor('bad-input') as never);
|
|
53
|
+
return c.json(toWireError({ code: 'bad-input', message: filter.error }, requestId));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Thread the ?scopeKind + ?scopeId + ?inherit
|
|
57
|
+
// triplet as a sibling of `filter` (matches the binding's shape:
|
|
58
|
+
// `scope` / `inherit` are top-level on `CostListRecordsInput`).
|
|
59
|
+
const scopeParsed = parseScopeParams(c.req.query(), { tenantId });
|
|
60
|
+
if (scopeParsed.kind === 'err') {
|
|
61
|
+
c.status(statusFor('scope-invalid') as never);
|
|
62
|
+
return c.json(
|
|
63
|
+
toWireError({ code: 'scope-invalid', message: scopeParsed.message }, requestId),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const page = await binding.listRecords({
|
|
68
|
+
tenantId,
|
|
69
|
+
limit,
|
|
70
|
+
...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
|
|
71
|
+
filter: filter.value,
|
|
72
|
+
...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
|
|
73
|
+
...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
|
|
74
|
+
});
|
|
75
|
+
return c.json({
|
|
76
|
+
data: page.data.map(serializeRecord),
|
|
77
|
+
hasMore: page.nextCursor !== undefined,
|
|
78
|
+
...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ---------- GET /records/:recordId ----------
|
|
83
|
+
r.get('/records/:recordId', async (c) => {
|
|
84
|
+
const requestId = c.get('requestId');
|
|
85
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
86
|
+
const recordId = c.req.param('recordId');
|
|
87
|
+
|
|
88
|
+
const record = await binding.getRecord({ tenantId, recordId });
|
|
89
|
+
if (record === null) {
|
|
90
|
+
c.status(statusFor('cost-record-not-found') as never);
|
|
91
|
+
return c.json(
|
|
92
|
+
toWireError(
|
|
93
|
+
{
|
|
94
|
+
code: 'cost-record-not-found',
|
|
95
|
+
message: `No cost record with id "${recordId}"`,
|
|
96
|
+
recordId,
|
|
97
|
+
},
|
|
98
|
+
requestId,
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return c.json(serializeRecord(record));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ---------- GET /aggregate ----------
|
|
106
|
+
r.get('/aggregate', async (c) => {
|
|
107
|
+
const requestId = c.get('requestId');
|
|
108
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
109
|
+
const query = c.req.query();
|
|
110
|
+
|
|
111
|
+
// groupBy — required, comma-separated, each value from the closed set.
|
|
112
|
+
const groupByRaw = query.groupBy;
|
|
113
|
+
if (groupByRaw === undefined || groupByRaw.length === 0) {
|
|
114
|
+
c.status(statusFor('bad-input') as never);
|
|
115
|
+
return c.json(
|
|
116
|
+
toWireError(
|
|
117
|
+
{ code: 'bad-input', message: '`groupBy` is required (comma-separated dimensions)' },
|
|
118
|
+
requestId,
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const rawDims = groupByRaw
|
|
123
|
+
.split(',')
|
|
124
|
+
.map((s) => s.trim())
|
|
125
|
+
.filter((s) => s.length > 0);
|
|
126
|
+
if (rawDims.length === 0) {
|
|
127
|
+
c.status(statusFor('bad-input') as never);
|
|
128
|
+
return c.json(
|
|
129
|
+
toWireError(
|
|
130
|
+
{ code: 'bad-input', message: '`groupBy` must contain at least one dimension' },
|
|
131
|
+
requestId,
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const groupBy: CostGroupDimension[] = [];
|
|
136
|
+
for (const raw of rawDims) {
|
|
137
|
+
if (!(COST_GROUP_DIMENSIONS as readonly string[]).includes(raw)) {
|
|
138
|
+
c.status(statusFor('bad-input') as never);
|
|
139
|
+
return c.json(
|
|
140
|
+
toWireError(
|
|
141
|
+
{
|
|
142
|
+
code: 'bad-input',
|
|
143
|
+
message: `\`groupBy\` value "${raw}" is not a known dimension (one of: ${(
|
|
144
|
+
COST_GROUP_DIMENSIONS as readonly string[]
|
|
145
|
+
).join(', ')})`,
|
|
146
|
+
},
|
|
147
|
+
requestId,
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (!groupBy.includes(raw as CostGroupDimension)) {
|
|
152
|
+
groupBy.push(raw as CostGroupDimension);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Time range — required. Default to last 30 days when both absent
|
|
157
|
+
// (documented in the response `timeRange`). If only one endpoint is
|
|
158
|
+
// supplied, reject — half-open defaults invite confusion.
|
|
159
|
+
const fromRaw = query.from;
|
|
160
|
+
const toRaw = query.to;
|
|
161
|
+
let from: Date;
|
|
162
|
+
let to: Date;
|
|
163
|
+
if (fromRaw === undefined && toRaw === undefined) {
|
|
164
|
+
to = new Date();
|
|
165
|
+
from = new Date(to.getTime() - DEFAULT_AGGREGATE_WINDOW_MS);
|
|
166
|
+
} else if (fromRaw === undefined || toRaw === undefined) {
|
|
167
|
+
c.status(statusFor('bad-input') as never);
|
|
168
|
+
return c.json(
|
|
169
|
+
toWireError(
|
|
170
|
+
{
|
|
171
|
+
code: 'bad-input',
|
|
172
|
+
message: '`from` and `to` must be provided together (or both omitted for the default)',
|
|
173
|
+
},
|
|
174
|
+
requestId,
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
const parsedFrom = parseIsoDate(fromRaw);
|
|
179
|
+
const parsedTo = parseIsoDate(toRaw);
|
|
180
|
+
if (parsedFrom === null || parsedTo === null) {
|
|
181
|
+
c.status(statusFor('bad-input') as never);
|
|
182
|
+
return c.json(
|
|
183
|
+
toWireError(
|
|
184
|
+
{ code: 'bad-input', message: '`from` / `to` must be ISO-8601 timestamps' },
|
|
185
|
+
requestId,
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (parsedFrom.getTime() > parsedTo.getTime()) {
|
|
190
|
+
c.status(statusFor('bad-input') as never);
|
|
191
|
+
return c.json(
|
|
192
|
+
toWireError(
|
|
193
|
+
{ code: 'bad-input', message: '`from` must be less than or equal to `to`' },
|
|
194
|
+
requestId,
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
from = parsedFrom;
|
|
199
|
+
to = parsedTo;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const filter = parseRecordFilter(query, { skipTime: true });
|
|
203
|
+
if (filter.kind === 'err') {
|
|
204
|
+
c.status(statusFor('bad-input') as never);
|
|
205
|
+
return c.json(toWireError({ code: 'bad-input', message: filter.error }, requestId));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Thread the scope triplet into aggregate so a
|
|
209
|
+
// scoped `/records` list and a scoped `/aggregate` reconcile to the
|
|
210
|
+
// same underlying row-set.
|
|
211
|
+
const scopeParsed = parseScopeParams(query, { tenantId });
|
|
212
|
+
if (scopeParsed.kind === 'err') {
|
|
213
|
+
c.status(statusFor('scope-invalid') as never);
|
|
214
|
+
return c.json(
|
|
215
|
+
toWireError({ code: 'scope-invalid', message: scopeParsed.message }, requestId),
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const result = await binding.aggregate({
|
|
220
|
+
tenantId,
|
|
221
|
+
groupBy,
|
|
222
|
+
from,
|
|
223
|
+
to,
|
|
224
|
+
...(Object.keys(filter.value).length > 0 && { filter: filter.value }),
|
|
225
|
+
...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
|
|
226
|
+
...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return c.json({
|
|
230
|
+
groups: result.groups.map(serializeGroup),
|
|
231
|
+
totalUsd: result.totalUsd,
|
|
232
|
+
totalRecords: result.totalRecords,
|
|
233
|
+
timeRange: {
|
|
234
|
+
from: result.timeRange.from,
|
|
235
|
+
to: result.timeRange.to,
|
|
236
|
+
},
|
|
237
|
+
groupBy,
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return r;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// -------- helpers --------
|
|
245
|
+
|
|
246
|
+
function parseRecordFilter(
|
|
247
|
+
query: Readonly<Record<string, string | undefined>>,
|
|
248
|
+
opts: { skipTime?: boolean } = {},
|
|
249
|
+
): { kind: 'ok'; value: CostRecordFilter } | { kind: 'err'; error: string } {
|
|
250
|
+
const out: {
|
|
251
|
+
runId?: string;
|
|
252
|
+
agentId?: string;
|
|
253
|
+
conversationId?: string;
|
|
254
|
+
category?: string;
|
|
255
|
+
providerId?: string;
|
|
256
|
+
from?: Date;
|
|
257
|
+
to?: Date;
|
|
258
|
+
} = {};
|
|
259
|
+
const strKeys: Array<
|
|
260
|
+
['runId' | 'agentId' | 'conversationId' | 'category' | 'providerId', string]
|
|
261
|
+
> = [
|
|
262
|
+
['runId', 'runId'],
|
|
263
|
+
['agentId', 'agentId'],
|
|
264
|
+
['conversationId', 'conversationId'],
|
|
265
|
+
['category', 'category'],
|
|
266
|
+
['providerId', 'providerId'],
|
|
267
|
+
];
|
|
268
|
+
for (const [outKey, qKey] of strKeys) {
|
|
269
|
+
const raw = query[qKey];
|
|
270
|
+
if (raw !== undefined && raw.length > 0) {
|
|
271
|
+
out[outKey] = raw;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (opts.skipTime !== true) {
|
|
275
|
+
const fromRaw = query.from;
|
|
276
|
+
if (fromRaw !== undefined && fromRaw.length > 0) {
|
|
277
|
+
const parsed = parseIsoDate(fromRaw);
|
|
278
|
+
if (parsed === null) return { kind: 'err', error: '`from` must be an ISO-8601 timestamp' };
|
|
279
|
+
out.from = parsed;
|
|
280
|
+
}
|
|
281
|
+
const toRaw = query.to;
|
|
282
|
+
if (toRaw !== undefined && toRaw.length > 0) {
|
|
283
|
+
const parsed = parseIsoDate(toRaw);
|
|
284
|
+
if (parsed === null) return { kind: 'err', error: '`to` must be an ISO-8601 timestamp' };
|
|
285
|
+
out.to = parsed;
|
|
286
|
+
}
|
|
287
|
+
if (out.from !== undefined && out.to !== undefined && out.from.getTime() > out.to.getTime()) {
|
|
288
|
+
return { kind: 'err', error: '`from` must be less than or equal to `to`' };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return { kind: 'ok', value: out };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function parseIsoDate(raw: string): Date | null {
|
|
295
|
+
const t = Date.parse(raw);
|
|
296
|
+
if (!Number.isFinite(t)) return null;
|
|
297
|
+
return new Date(t);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function serializeRecord(rec: CostRecord): Record<string, unknown> {
|
|
301
|
+
return {
|
|
302
|
+
id: rec.id,
|
|
303
|
+
tenantId: rec.tenantId as unknown as string,
|
|
304
|
+
category: rec.category,
|
|
305
|
+
...(rec.providerId !== undefined && { providerId: rec.providerId }),
|
|
306
|
+
...(rec.runId !== undefined && { runId: rec.runId }),
|
|
307
|
+
...(rec.agentId !== undefined && { agentId: rec.agentId }),
|
|
308
|
+
...(rec.conversationId !== undefined && { conversationId: rec.conversationId }),
|
|
309
|
+
quantity: rec.quantity,
|
|
310
|
+
unit: rec.unit,
|
|
311
|
+
...(rec.costUsd !== undefined && { costUsd: rec.costUsd }),
|
|
312
|
+
occurredAt: rec.occurredAt as unknown as string,
|
|
313
|
+
...(rec.metrics !== undefined && { metrics: rec.metrics }),
|
|
314
|
+
...(rec.attributes !== undefined && { attributes: rec.attributes }),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function serializeGroup(g: CostAggregateGroup): Record<string, unknown> {
|
|
319
|
+
return {
|
|
320
|
+
key: g.key,
|
|
321
|
+
count: g.count,
|
|
322
|
+
totalUsd: g.totalUsd,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Re-export for typing convenience in tests / callers.
|
|
327
|
+
export type { Timestamp };
|