@kindgi/api 0.1.4 → 0.1.5
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/dist/agent-binding.d.ts +13 -0
- package/dist/agent-binding.d.ts.map +1 -1
- package/dist/agent-pins.d.ts.map +1 -1
- package/dist/agent-pins.js +18 -2
- package/dist/agent-pins.js.map +1 -1
- package/dist/app.d.ts +110 -17
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +163 -35
- package/dist/app.js.map +1 -1
- package/dist/block-publish.d.ts +56 -0
- package/dist/block-publish.d.ts.map +1 -0
- package/dist/block-publish.js +132 -0
- package/dist/block-publish.js.map +1 -0
- package/dist/caller.d.ts +24 -0
- package/dist/caller.d.ts.map +1 -0
- package/dist/caller.js +47 -0
- package/dist/caller.js.map +1 -0
- package/dist/capability-binding.d.ts +11 -0
- package/dist/capability-binding.d.ts.map +1 -1
- package/dist/cost-binding.d.ts +19 -0
- package/dist/cost-binding.d.ts.map +1 -1
- package/dist/cost-binding.js.map +1 -1
- package/dist/derive-agent-version.d.ts +7 -0
- package/dist/derive-agent-version.d.ts.map +1 -1
- package/dist/derive-agent-version.js +4 -0
- package/dist/derive-agent-version.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +75 -7
- package/dist/errors.js.map +1 -1
- package/dist/eval-case-binding.d.ts +16 -4
- package/dist/eval-case-binding.d.ts.map +1 -1
- package/dist/eval-run-binding.d.ts +31 -0
- package/dist/eval-run-binding.d.ts.map +1 -1
- package/dist/eval-run-dispatcher.d.ts +7 -0
- package/dist/eval-run-dispatcher.d.ts.map +1 -1
- package/dist/eval-run-dispatcher.js.map +1 -1
- package/dist/eval-sample.d.ts +11 -0
- package/dist/eval-sample.d.ts.map +1 -0
- package/dist/eval-sample.js +34 -0
- package/dist/eval-sample.js.map +1 -0
- package/dist/eval-suite-binding.d.ts +13 -0
- package/dist/eval-suite-binding.d.ts.map +1 -1
- package/dist/eval-suite-binding.js.map +1 -1
- package/dist/flow-binding.d.ts +18 -0
- package/dist/flow-binding.d.ts.map +1 -1
- package/dist/flow-pins.d.ts.map +1 -1
- package/dist/flow-pins.js +18 -2
- package/dist/flow-pins.js.map +1 -1
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +25 -0
- package/dist/gate.js.map +1 -1
- package/dist/handler-binding.d.ts +32 -0
- package/dist/handler-binding.d.ts.map +1 -1
- package/dist/hitl-binding.d.ts +23 -0
- package/dist/hitl-binding.d.ts.map +1 -1
- package/dist/identity-directory-binding.d.ts +82 -4
- package/dist/identity-directory-binding.d.ts.map +1 -1
- package/dist/identity-provider-binding.d.ts +197 -17
- package/dist/identity-provider-binding.d.ts.map +1 -1
- package/dist/improvement-pass-binding.d.ts +196 -0
- package/dist/improvement-pass-binding.d.ts.map +1 -0
- package/dist/improvement-pass-binding.js +9 -0
- package/dist/improvement-pass-binding.js.map +1 -0
- package/dist/index.d.ts +35 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/judged-dispatcher.d.ts +25 -4
- package/dist/judged-dispatcher.d.ts.map +1 -1
- package/dist/judged-dispatcher.js +43 -6
- package/dist/judged-dispatcher.js.map +1 -1
- package/dist/judgment-binding.d.ts +25 -1
- package/dist/judgment-binding.d.ts.map +1 -1
- package/dist/mcp-endpoint-binding.d.ts +7 -0
- package/dist/mcp-endpoint-binding.d.ts.map +1 -1
- package/dist/memory-binding.d.ts +124 -17
- package/dist/memory-binding.d.ts.map +1 -1
- package/dist/memory-erasure-binding.d.ts +145 -0
- package/dist/memory-erasure-binding.d.ts.map +1 -0
- package/dist/memory-erasure-binding.js +4 -0
- package/dist/memory-erasure-binding.js.map +1 -0
- package/dist/middleware/auth.d.ts +66 -13
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +150 -50
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/authorize.d.ts +9 -0
- package/dist/middleware/authorize.d.ts.map +1 -1
- package/dist/middleware/authorize.js +122 -4
- package/dist/middleware/authorize.js.map +1 -1
- package/dist/middleware/error-mapper.d.ts +5 -0
- package/dist/middleware/error-mapper.d.ts.map +1 -1
- package/dist/middleware/error-mapper.js +14 -1
- package/dist/middleware/error-mapper.js.map +1 -1
- package/dist/middleware/idempotency.d.ts +70 -5
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +174 -41
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/middleware/key-project.d.ts +17 -0
- package/dist/middleware/key-project.d.ts.map +1 -0
- package/dist/middleware/key-project.js +75 -0
- package/dist/middleware/key-project.js.map +1 -0
- package/dist/middleware/principal.d.ts.map +1 -1
- package/dist/middleware/principal.js +9 -5
- package/dist/middleware/principal.js.map +1 -1
- package/dist/middleware/project-ref.d.ts.map +1 -1
- package/dist/middleware/project-ref.js +3 -0
- package/dist/middleware/project-ref.js.map +1 -1
- package/dist/openapi/generate.d.ts.map +1 -1
- package/dist/openapi/generate.js +5 -2
- package/dist/openapi/generate.js.map +1 -1
- package/dist/openapi/operations.d.ts.map +1 -1
- package/dist/openapi/operations.js +919 -140
- package/dist/openapi/operations.js.map +1 -1
- package/dist/openapi/schemas.d.ts +90 -13
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +2081 -359
- package/dist/openapi/schemas.js.map +1 -1
- package/dist/person-grants-binding.d.ts +77 -0
- package/dist/person-grants-binding.d.ts.map +1 -0
- package/dist/person-grants-binding.js +4 -0
- package/dist/person-grants-binding.js.map +1 -0
- package/dist/proposal-service.d.ts +121 -0
- package/dist/proposal-service.d.ts.map +1 -0
- package/dist/proposal-service.js +447 -0
- package/dist/proposal-service.js.map +1 -0
- package/dist/proposal-status.d.ts +60 -0
- package/dist/proposal-status.d.ts.map +1 -0
- package/dist/proposal-status.js +109 -0
- package/dist/proposal-status.js.map +1 -0
- package/dist/provider-binding.d.ts +10 -0
- package/dist/provider-binding.d.ts.map +1 -1
- package/dist/publish-refused.d.ts +28 -3
- package/dist/publish-refused.d.ts.map +1 -1
- package/dist/publish-refused.js +24 -3
- package/dist/publish-refused.js.map +1 -1
- package/dist/routes/adapters.d.ts +2 -1
- package/dist/routes/adapters.d.ts.map +1 -1
- package/dist/routes/adapters.js +3 -1
- package/dist/routes/adapters.js.map +1 -1
- package/dist/routes/agent-releases.d.ts +55 -3
- package/dist/routes/agent-releases.d.ts.map +1 -1
- package/dist/routes/agent-releases.js +85 -55
- package/dist/routes/agent-releases.js.map +1 -1
- package/dist/routes/agents.d.ts +12 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +100 -2
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts +15 -3
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +133 -122
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/artifacts.d.ts +25 -3
- package/dist/routes/artifacts.d.ts.map +1 -1
- package/dist/routes/artifacts.js +116 -7
- package/dist/routes/artifacts.js.map +1 -1
- package/dist/routes/auth.d.ts +25 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/auth.js +655 -281
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/blocks.d.ts.map +1 -1
- package/dist/routes/blocks.js +2 -42
- package/dist/routes/blocks.js.map +1 -1
- package/dist/routes/capabilities.d.ts +2 -1
- package/dist/routes/capabilities.d.ts.map +1 -1
- package/dist/routes/capabilities.js +6 -1
- package/dist/routes/capabilities.js.map +1 -1
- package/dist/routes/compliance.d.ts +9 -10
- package/dist/routes/compliance.d.ts.map +1 -1
- package/dist/routes/compliance.js +42 -61
- package/dist/routes/compliance.js.map +1 -1
- package/dist/routes/conversations.d.ts +8 -1
- package/dist/routes/conversations.d.ts.map +1 -1
- package/dist/routes/conversations.js +87 -8
- package/dist/routes/conversations.js.map +1 -1
- package/dist/routes/cost.d.ts +12 -1
- package/dist/routes/cost.d.ts.map +1 -1
- package/dist/routes/cost.js +74 -3
- package/dist/routes/cost.js.map +1 -1
- package/dist/routes/denied.d.ts +11 -0
- package/dist/routes/denied.d.ts.map +1 -0
- package/dist/routes/denied.js +17 -0
- package/dist/routes/denied.js.map +1 -0
- package/dist/routes/deployments.d.ts +10 -1
- package/dist/routes/deployments.d.ts.map +1 -1
- package/dist/routes/deployments.js +171 -13
- package/dist/routes/deployments.js.map +1 -1
- package/dist/routes/eval-comparison.d.ts.map +1 -1
- package/dist/routes/eval-comparison.js +69 -1
- package/dist/routes/eval-comparison.js.map +1 -1
- package/dist/routes/eval-overrides.d.ts +18 -0
- package/dist/routes/eval-overrides.d.ts.map +1 -0
- package/dist/routes/eval-overrides.js +81 -0
- package/dist/routes/eval-overrides.js.map +1 -0
- package/dist/routes/eval-runs.d.ts +10 -1
- package/dist/routes/eval-runs.d.ts.map +1 -1
- package/dist/routes/eval-runs.js +66 -7
- package/dist/routes/eval-runs.js.map +1 -1
- package/dist/routes/eval-suites.d.ts.map +1 -1
- package/dist/routes/eval-suites.js +28 -2
- package/dist/routes/eval-suites.js.map +1 -1
- package/dist/routes/event-triggers.d.ts +5 -1
- package/dist/routes/event-triggers.d.ts.map +1 -1
- package/dist/routes/event-triggers.js +26 -3
- package/dist/routes/event-triggers.js.map +1 -1
- package/dist/routes/export-signing-keys.d.ts +12 -0
- package/dist/routes/export-signing-keys.d.ts.map +1 -0
- package/dist/routes/export-signing-keys.js +24 -0
- package/dist/routes/export-signing-keys.js.map +1 -0
- package/dist/routes/flows.d.ts.map +1 -1
- package/dist/routes/flows.js +9 -1
- package/dist/routes/flows.js.map +1 -1
- package/dist/routes/gate-policies.d.ts +4 -1
- package/dist/routes/gate-policies.d.ts.map +1 -1
- package/dist/routes/gate-policies.js +29 -5
- package/dist/routes/gate-policies.js.map +1 -1
- package/dist/routes/guardrails.d.ts +16 -14
- package/dist/routes/guardrails.d.ts.map +1 -1
- package/dist/routes/guardrails.js +39 -3
- package/dist/routes/guardrails.js.map +1 -1
- package/dist/routes/identity.d.ts +20 -4
- package/dist/routes/identity.d.ts.map +1 -1
- package/dist/routes/identity.js +246 -6
- package/dist/routes/identity.js.map +1 -1
- package/dist/routes/improvement-passes.d.ts +52 -0
- package/dist/routes/improvement-passes.d.ts.map +1 -0
- package/dist/routes/improvement-passes.js +449 -0
- package/dist/routes/improvement-passes.js.map +1 -0
- package/dist/routes/judged-suites.d.ts +50 -0
- package/dist/routes/judged-suites.d.ts.map +1 -1
- package/dist/routes/judged-suites.js +66 -27
- package/dist/routes/judged-suites.js.map +1 -1
- package/dist/routes/judgment-context.d.ts +18 -2
- package/dist/routes/judgment-context.d.ts.map +1 -1
- package/dist/routes/judgment-context.js +63 -5
- package/dist/routes/judgment-context.js.map +1 -1
- package/dist/routes/judgment-flow-context.d.ts.map +1 -1
- package/dist/routes/judgment-flow-context.js +14 -2
- package/dist/routes/judgment-flow-context.js.map +1 -1
- package/dist/routes/judgments.d.ts.map +1 -1
- package/dist/routes/judgments.js +8 -1
- package/dist/routes/judgments.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +26 -1
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/memory-access.d.ts +43 -0
- package/dist/routes/memory-access.d.ts.map +1 -0
- package/dist/routes/memory-access.js +114 -0
- package/dist/routes/memory-access.js.map +1 -0
- package/dist/routes/memory-erasures.d.ts +25 -0
- package/dist/routes/memory-erasures.d.ts.map +1 -0
- package/dist/routes/memory-erasures.js +214 -0
- package/dist/routes/memory-erasures.js.map +1 -0
- package/dist/routes/memory-parse.d.ts +38 -0
- package/dist/routes/memory-parse.d.ts.map +1 -0
- package/dist/routes/memory-parse.js +199 -0
- package/dist/routes/memory-parse.js.map +1 -0
- package/dist/routes/memory.d.ts +9 -4
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +226 -234
- package/dist/routes/memory.js.map +1 -1
- package/dist/routes/observations.d.ts +4 -1
- package/dist/routes/observations.d.ts.map +1 -1
- package/dist/routes/observations.js +8 -2
- package/dist/routes/observations.js.map +1 -1
- package/dist/routes/pagination.d.ts +7 -0
- package/dist/routes/pagination.d.ts.map +1 -1
- package/dist/routes/pagination.js +10 -0
- package/dist/routes/pagination.js.map +1 -1
- package/dist/routes/policies.d.ts +2 -1
- package/dist/routes/policies.d.ts.map +1 -1
- package/dist/routes/policies.js +3 -1
- package/dist/routes/policies.js.map +1 -1
- package/dist/routes/project-mismatch.d.ts +22 -0
- package/dist/routes/project-mismatch.d.ts.map +1 -0
- package/dist/routes/project-mismatch.js +34 -0
- package/dist/routes/project-mismatch.js.map +1 -0
- package/dist/routes/projects.d.ts +15 -4
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +100 -9
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/proposals.d.ts +25 -17
- package/dist/routes/proposals.d.ts.map +1 -1
- package/dist/routes/proposals.js +340 -507
- package/dist/routes/proposals.js.map +1 -1
- package/dist/routes/provenance.d.ts +17 -3
- package/dist/routes/provenance.d.ts.map +1 -1
- package/dist/routes/provenance.js +79 -103
- package/dist/routes/provenance.js.map +1 -1
- package/dist/routes/providers.d.ts +17 -1
- package/dist/routes/providers.d.ts.map +1 -1
- package/dist/routes/providers.js +110 -3
- package/dist/routes/providers.js.map +1 -1
- package/dist/routes/public-run-tokens.d.ts.map +1 -1
- package/dist/routes/public-run-tokens.js +3 -0
- package/dist/routes/public-run-tokens.js.map +1 -1
- package/dist/routes/readable-projects.d.ts +26 -0
- package/dist/routes/readable-projects.d.ts.map +1 -0
- package/dist/routes/readable-projects.js +39 -0
- package/dist/routes/readable-projects.js.map +1 -0
- package/dist/routes/runs.d.ts +10 -0
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +92 -42
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/schedules.d.ts +21 -13
- package/dist/routes/schedules.d.ts.map +1 -1
- package/dist/routes/schedules.js +416 -55
- package/dist/routes/schedules.js.map +1 -1
- package/dist/routes/segments.d.ts +2 -0
- package/dist/routes/segments.d.ts.map +1 -1
- package/dist/routes/segments.js +5 -0
- package/dist/routes/segments.js.map +1 -1
- package/dist/routes/service-accounts.d.ts +17 -0
- package/dist/routes/service-accounts.d.ts.map +1 -0
- package/dist/routes/service-accounts.js +212 -0
- package/dist/routes/service-accounts.js.map +1 -0
- package/dist/routes/sign-in-options.d.ts +50 -0
- package/dist/routes/sign-in-options.d.ts.map +1 -0
- package/dist/routes/sign-in-options.js +98 -0
- package/dist/routes/sign-in-options.js.map +1 -0
- package/dist/routes/signing-keys.d.ts +2 -1
- package/dist/routes/signing-keys.d.ts.map +1 -1
- package/dist/routes/signing-keys.js +3 -1
- package/dist/routes/signing-keys.js.map +1 -1
- package/dist/routes/tenant-access.d.ts +13 -0
- package/dist/routes/tenant-access.d.ts.map +1 -0
- package/dist/routes/tenant-access.js +26 -0
- package/dist/routes/tenant-access.js.map +1 -0
- package/dist/routes/tenant.d.ts.map +1 -1
- package/dist/routes/tenant.js +4 -0
- package/dist/routes/tenant.js.map +1 -1
- package/dist/routes/token-sign-in.d.ts +36 -0
- package/dist/routes/token-sign-in.d.ts.map +1 -0
- package/dist/routes/token-sign-in.js +101 -0
- package/dist/routes/token-sign-in.js.map +1 -0
- package/dist/routes/tokens.d.ts +15 -5
- package/dist/routes/tokens.d.ts.map +1 -1
- package/dist/routes/tokens.js +162 -43
- package/dist/routes/tokens.js.map +1 -1
- package/dist/routes/tools.d.ts.map +1 -1
- package/dist/routes/tools.js +9 -1
- package/dist/routes/tools.js.map +1 -1
- package/dist/routes/trigger-access.d.ts +29 -0
- package/dist/routes/trigger-access.d.ts.map +1 -0
- package/dist/routes/trigger-access.js +33 -0
- package/dist/routes/trigger-access.js.map +1 -0
- package/dist/routes/webhook-endpoints.d.ts +2 -1
- package/dist/routes/webhook-endpoints.d.ts.map +1 -1
- package/dist/routes/webhook-endpoints.js +11 -3
- package/dist/routes/webhook-endpoints.js.map +1 -1
- package/dist/routes/webhooks.d.ts +5 -1
- package/dist/routes/webhooks.d.ts.map +1 -1
- package/dist/routes/webhooks.js +25 -2
- package/dist/routes/webhooks.js.map +1 -1
- package/dist/run-failure.d.ts +21 -0
- package/dist/run-failure.d.ts.map +1 -0
- package/dist/run-failure.js +29 -0
- package/dist/run-failure.js.map +1 -0
- package/dist/service-account-binding.d.ts +108 -0
- package/dist/service-account-binding.d.ts.map +1 -0
- package/dist/service-account-binding.js +4 -0
- package/dist/service-account-binding.js.map +1 -0
- package/dist/session-store-binding.d.ts +61 -13
- package/dist/session-store-binding.d.ts.map +1 -1
- package/dist/signed-export.d.ts +89 -0
- package/dist/signed-export.d.ts.map +1 -0
- package/dist/signed-export.js +149 -0
- package/dist/signed-export.js.map +1 -0
- package/dist/supervisor-binding.d.ts +186 -413
- package/dist/supervisor-binding.d.ts.map +1 -1
- package/dist/testing/app-bindings.d.ts +33 -0
- package/dist/testing/app-bindings.d.ts.map +1 -0
- package/dist/testing/app-bindings.js +112 -0
- package/dist/testing/app-bindings.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +13 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/stub-binding.d.ts +34 -0
- package/dist/testing/stub-binding.d.ts.map +1 -0
- package/dist/testing/stub-binding.js +42 -0
- package/dist/testing/stub-binding.js.map +1 -0
- package/dist/testing/trigger-registry.d.ts +21 -0
- package/dist/testing/trigger-registry.d.ts.map +1 -0
- package/dist/testing/trigger-registry.js +206 -0
- package/dist/testing/trigger-registry.js.map +1 -0
- package/dist/token-admin.d.ts +46 -8
- package/dist/token-admin.d.ts.map +1 -1
- package/dist/token-admin.js.map +1 -1
- package/dist/tool-binding.d.ts +13 -0
- package/dist/tool-binding.d.ts.map +1 -1
- package/dist/trigger-binding.d.ts +2 -2
- package/dist/trigger-binding.d.ts.map +1 -1
- package/dist/trigger-binding.js +1 -1
- package/dist/trigger-binding.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.d.ts +19 -3
- package/dist/webhook-endpoint-binding.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.js +1 -1
- package/dist/webhook-endpoint-binding.js.map +1 -1
- package/openapi.json +15387 -8789
- package/package.json +26 -22
- package/src/agent-binding.ts +14 -0
- package/src/agent-pins.ts +18 -2
- package/src/app.ts +366 -80
- package/src/block-publish.ts +181 -0
- package/src/caller.ts +62 -0
- package/src/capability-binding.ts +12 -0
- package/src/cost-binding.ts +19 -0
- package/src/derive-agent-version.ts +9 -1
- package/src/errors.ts +75 -7
- package/src/eval-case-binding.ts +17 -1
- package/src/eval-run-binding.ts +31 -0
- package/src/eval-run-dispatcher.ts +7 -0
- package/src/eval-sample.ts +42 -0
- package/src/eval-suite-binding.ts +14 -0
- package/src/flow-binding.ts +19 -0
- package/src/flow-pins.ts +18 -2
- package/src/gate.ts +26 -0
- package/src/handler-binding.ts +36 -1
- package/src/hitl-binding.ts +24 -0
- package/src/identity-directory-binding.ts +85 -4
- package/src/identity-provider-binding.ts +216 -21
- package/src/improvement-pass-binding.ts +193 -0
- package/src/index.ts +153 -31
- package/src/judged-dispatcher.ts +78 -11
- package/src/judgment-binding.ts +25 -1
- package/src/mcp-endpoint-binding.ts +7 -0
- package/src/memory-binding.ts +121 -17
- package/src/memory-erasure-binding.ts +156 -0
- package/src/middleware/auth.ts +228 -62
- package/src/middleware/authorize.ts +155 -3
- package/src/middleware/error-mapper.ts +15 -1
- package/src/middleware/idempotency.ts +230 -43
- package/src/middleware/key-project.ts +86 -0
- package/src/middleware/principal.ts +9 -5
- package/src/middleware/project-ref.ts +3 -0
- package/src/openapi/generate.ts +11 -2
- package/src/openapi/operations.ts +1060 -143
- package/src/openapi/schemas.ts +2387 -469
- package/src/person-grants-binding.ts +76 -0
- package/src/proposal-service.ts +640 -0
- package/src/proposal-status.ts +176 -0
- package/src/provider-binding.ts +10 -0
- package/src/publish-refused.ts +56 -9
- package/src/routes/adapters.ts +4 -0
- package/src/routes/agent-releases.ts +121 -69
- package/src/routes/agents.ts +128 -1
- package/src/routes/approvals.ts +164 -154
- package/src/routes/artifacts.ts +181 -11
- package/src/routes/auth.ts +804 -336
- package/src/routes/blocks.ts +2 -53
- package/src/routes/capabilities.ts +10 -1
- package/src/routes/compliance.ts +60 -99
- package/src/routes/conversations.ts +101 -7
- package/src/routes/cost.ts +110 -4
- package/src/routes/denied.ts +28 -0
- package/src/routes/deployments.ts +210 -17
- package/src/routes/eval-comparison.ts +81 -2
- package/src/routes/eval-overrides.ts +108 -0
- package/src/routes/eval-runs.ts +89 -6
- package/src/routes/eval-suites.ts +32 -2
- package/src/routes/event-triggers.ts +27 -3
- package/src/routes/export-signing-keys.ts +31 -0
- package/src/routes/flows.ts +12 -1
- package/src/routes/gate-policies.ts +37 -6
- package/src/routes/guardrails.ts +61 -2
- package/src/routes/identity.ts +328 -10
- package/src/routes/improvement-passes.ts +541 -0
- package/src/routes/judged-suites.ts +113 -43
- package/src/routes/judgment-context.ts +76 -5
- package/src/routes/judgment-flow-context.ts +16 -2
- package/src/routes/judgments.ts +17 -3
- package/src/routes/mcp.ts +29 -1
- package/src/routes/memory-access.ts +160 -0
- package/src/routes/memory-erasures.ts +240 -0
- package/src/routes/memory-parse.ts +250 -0
- package/src/routes/memory.ts +244 -286
- package/src/routes/observations.ts +15 -2
- package/src/routes/pagination.ts +12 -0
- package/src/routes/policies.ts +7 -1
- package/src/routes/project-mismatch.ts +56 -0
- package/src/routes/projects.ts +107 -13
- package/src/routes/proposals.ts +370 -611
- package/src/routes/provenance.ts +99 -133
- package/src/routes/providers.ts +142 -3
- package/src/routes/public-run-tokens.ts +3 -0
- package/src/routes/readable-projects.ts +66 -0
- package/src/routes/runs.ts +119 -39
- package/src/routes/schedules.ts +472 -60
- package/src/routes/segments.ts +11 -0
- package/src/routes/service-accounts.ts +262 -0
- package/src/routes/sign-in-options.ts +168 -0
- package/src/routes/signing-keys.ts +7 -1
- package/src/routes/tenant-access.ts +37 -0
- package/src/routes/tenant.ts +6 -0
- package/src/routes/token-sign-in.ts +161 -0
- package/src/routes/tokens.ts +172 -46
- package/src/routes/tools.ts +12 -1
- package/src/routes/trigger-access.ts +72 -0
- package/src/routes/webhook-endpoints.ts +15 -3
- package/src/routes/webhooks.ts +26 -2
- package/src/run-failure.ts +42 -0
- package/src/service-account-binding.ts +113 -0
- package/src/session-store-binding.ts +65 -13
- package/src/signed-export.ts +250 -0
- package/src/supervisor-binding.ts +169 -409
- package/src/testing/app-bindings.ts +142 -0
- package/src/testing/index.ts +21 -0
- package/src/testing/stub-binding.ts +53 -0
- package/src/testing/trigger-registry.ts +259 -0
- package/src/token-admin.ts +40 -8
- package/src/tool-binding.ts +14 -0
- package/src/trigger-binding.ts +8 -1
- package/src/types.ts +16 -0
- package/src/webhook-endpoint-binding.ts +18 -3
package/src/routes/guardrails.ts
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
5
|
|
|
6
6
|
import { type Principal, ref, tuplesForCreate } from '@kindgi/authz';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type Guardrail,
|
|
9
|
+
type GuardrailConfigProblem,
|
|
10
|
+
createCheckRegistry,
|
|
11
|
+
describeGuardrailConfigProblems,
|
|
12
|
+
validateGuardrailSpec,
|
|
13
|
+
} from '@kindgi/guardrails';
|
|
8
14
|
import type { Cursor, GuardrailId, ProjectId, TenantId, UserId } from '@kindgi/types';
|
|
9
15
|
|
|
10
16
|
import { statusFor, toWireError } from '../errors.js';
|
|
@@ -28,6 +34,9 @@ import { parseScopeParams } from './scope-params.js';
|
|
|
28
34
|
* guardrails whose check code is already bundled server-side. Check
|
|
29
35
|
* code is not uploaded over this surface.
|
|
30
36
|
*/
|
|
37
|
+
/** The built-in checks, for the config of a guardrail naming one. */
|
|
38
|
+
const builtInChecks = createCheckRegistry();
|
|
39
|
+
|
|
31
40
|
/**
|
|
32
41
|
* Optional side-effect callback fired after a successful write
|
|
33
42
|
* (register, unregister) to a guardrail. Lets an in-process
|
|
@@ -42,10 +51,26 @@ export type GuardrailWriteHook = (params: {
|
|
|
42
51
|
readonly kind: 'register' | 'unregister';
|
|
43
52
|
}) => void | Promise<void>;
|
|
44
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Checks a guardrail being registered against the check it names: every
|
|
56
|
+
* way its `config` breaks that check's `configSchema` (a pack check's,
|
|
57
|
+
* from its deployment), via `guardrailConfigProblems`. Any problem
|
|
58
|
+
* refuses the registration with `422 guardrail-config-invalid`
|
|
59
|
+
* (`details.issues`). Absent, or nothing to check against (an unknown
|
|
60
|
+
* check, a check without a schema): no problems. A guardrail naming a
|
|
61
|
+
* built-in check is checked by the route itself, against the built-in's
|
|
62
|
+
* schema, whatever the runtime passes here.
|
|
63
|
+
*/
|
|
64
|
+
export type GuardrailConfigCheck = (input: {
|
|
65
|
+
readonly tenantId: TenantId;
|
|
66
|
+
readonly guardrail: Guardrail;
|
|
67
|
+
}) => Promise<readonly GuardrailConfigProblem[]>;
|
|
68
|
+
|
|
45
69
|
export function guardrailsRouter(
|
|
46
70
|
binding: GuardrailRegistryBinding,
|
|
47
71
|
authorizer?: Authorizer,
|
|
48
72
|
onWrite?: GuardrailWriteHook,
|
|
73
|
+
checkConfig?: GuardrailConfigCheck,
|
|
49
74
|
): Hono<AppEnv> {
|
|
50
75
|
const r = new Hono<AppEnv>();
|
|
51
76
|
// A read-only registry (under `kindgi dev`, the pack's files) refuses
|
|
@@ -127,8 +152,15 @@ export function guardrailsRouter(
|
|
|
127
152
|
...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
|
|
128
153
|
...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
|
|
129
154
|
});
|
|
155
|
+
// Only what the caller may read (T243 A), as `GET …/:id` asks.
|
|
156
|
+
const visible =
|
|
157
|
+
authorizer === undefined
|
|
158
|
+
? page.data
|
|
159
|
+
: await authorizer.filterByCan(c, 'read', page.data, (a) =>
|
|
160
|
+
ref('guardrail', a.id as unknown as string),
|
|
161
|
+
);
|
|
130
162
|
return c.json({
|
|
131
|
-
data:
|
|
163
|
+
data: visible.map(serializeGuardrail),
|
|
132
164
|
hasMore: page.nextCursor !== undefined,
|
|
133
165
|
...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
|
|
134
166
|
});
|
|
@@ -210,6 +242,33 @@ export function guardrailsRouter(
|
|
|
210
242
|
);
|
|
211
243
|
}
|
|
212
244
|
|
|
245
|
+
// A built-in check's config is the route's to check (its schema ships with
|
|
246
|
+
// `@kindgi/guardrails`); any other check's, the runtime's.
|
|
247
|
+
const builtIn = builtInChecks.get(validated.value.check);
|
|
248
|
+
const problems =
|
|
249
|
+
builtIn?.configProblems !== undefined
|
|
250
|
+
? builtIn.configProblems(validated.value.config)
|
|
251
|
+
: await checkConfig?.({ tenantId, guardrail: validated.value });
|
|
252
|
+
if (problems !== undefined && problems.length > 0) {
|
|
253
|
+
c.status(statusFor('guardrail-config-invalid') as never);
|
|
254
|
+
return c.json(
|
|
255
|
+
toWireError(
|
|
256
|
+
{
|
|
257
|
+
code: 'guardrail-config-invalid',
|
|
258
|
+
message: describeGuardrailConfigProblems(
|
|
259
|
+
{
|
|
260
|
+
guardrailId: validated.value.id as unknown as string,
|
|
261
|
+
check: validated.value.check,
|
|
262
|
+
},
|
|
263
|
+
problems,
|
|
264
|
+
),
|
|
265
|
+
issues: problems as unknown as Record<string, unknown>[],
|
|
266
|
+
},
|
|
267
|
+
requestId,
|
|
268
|
+
),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
213
272
|
const principal = c.get('principal') as Principal | undefined;
|
|
214
273
|
const creatorUserId =
|
|
215
274
|
principal?.actor.kind === 'user' ? (principal.actor.id as UserId) : undefined;
|
package/src/routes/identity.ts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
3
|
|
|
4
|
+
import type { Context } from 'hono';
|
|
4
5
|
import { Hono } from 'hono';
|
|
5
6
|
|
|
6
7
|
import type { Cursor, SessionId, TenantId, UserId } from '@kindgi/types';
|
|
7
8
|
|
|
9
|
+
import { callerPrincipal, callerRef, isTenantAdmin, principalToWire } from '../caller.js';
|
|
8
10
|
import { statusFor, toWireError } from '../errors.js';
|
|
9
11
|
import type {
|
|
10
12
|
IdentityDirectoryBinding,
|
|
11
13
|
SessionSummary,
|
|
12
14
|
UserRecord,
|
|
13
15
|
} from '../identity-directory-binding.js';
|
|
16
|
+
import type { Authorizer } from '../middleware/authorize.js';
|
|
17
|
+
import type {
|
|
18
|
+
PersonGrant,
|
|
19
|
+
PersonGrantError,
|
|
20
|
+
PersonGrants,
|
|
21
|
+
PersonGrantsBinding,
|
|
22
|
+
} from '../person-grants-binding.js';
|
|
14
23
|
import type { ReviewerBinding } from '../reviewer-binding.js';
|
|
15
24
|
import { callerReviewerRole } from '../reviewer-role.js';
|
|
16
25
|
import type { SessionStoreBinding } from '../session-store-binding.js';
|
|
@@ -24,12 +33,16 @@ import { clampLimit } from './pagination.js';
|
|
|
24
33
|
* `whoami` endpoint, so clients have exactly one whoami endpoint to
|
|
25
34
|
* consume.
|
|
26
35
|
*
|
|
27
|
-
*
|
|
36
|
+
* Six routes:
|
|
28
37
|
* - `GET /v1/identity/whoami` (self — always mounted)
|
|
29
|
-
* - `GET /v1/identity/users` (cursor-paginated list)
|
|
30
|
-
* - `
|
|
31
|
-
* - `GET /v1/identity/users/:userId
|
|
38
|
+
* - `GET /v1/identity/users` (cursor-paginated list; tenant admins)
|
|
39
|
+
* - `POST /v1/identity/users` (add a person; admin, when the directory can)
|
|
40
|
+
* - `GET /v1/identity/users/:userId` (get; a tenant admin, or your own)
|
|
41
|
+
* - `GET /v1/identity/users/:userId/sessions` (active sessions; a tenant admin, or your own)
|
|
32
42
|
* - `POST /v1/identity/users/:userId/revoke-sessions` (admin op)
|
|
43
|
+
* - `GET /v1/identity/users/:userId/grants` (admin, or your own)
|
|
44
|
+
* - `POST /v1/identity/users/:userId/grant|ungrant` (tenant admin, tenant admins only)
|
|
45
|
+
* - `POST /v1/identity/users/:userId/unregister` (remove a person; tenant admins, when the directory can)
|
|
33
46
|
*
|
|
34
47
|
* `directory` is optional: deployments without an `IdentityDirectoryBinding`
|
|
35
48
|
* still get `whoami` (returning the minimal `{ tenantId, scopes, ... }` shape
|
|
@@ -55,10 +68,20 @@ export interface IdentityRouterOptions {
|
|
|
55
68
|
* routes resolve it.
|
|
56
69
|
*/
|
|
57
70
|
readonly reviewerBinding?: ReviewerBinding;
|
|
71
|
+
/**
|
|
72
|
+
* Optional. Decides who is a tenant admin for `POST /users`; without
|
|
73
|
+
* one, the `tenant-admin` scope does.
|
|
74
|
+
*/
|
|
75
|
+
readonly authorizer?: Authorizer;
|
|
76
|
+
/**
|
|
77
|
+
* Optional. A person's grants: the read, and tenant admin given or
|
|
78
|
+
* taken. Without it, those routes answer `501 person-grants-unsupported`.
|
|
79
|
+
*/
|
|
80
|
+
readonly personGrants?: PersonGrantsBinding;
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv> {
|
|
61
|
-
const { directory, sessionStore, reviewerBinding } = options;
|
|
84
|
+
const { directory, sessionStore, reviewerBinding, authorizer, personGrants } = options;
|
|
62
85
|
const r = new Hono<AppEnv>();
|
|
63
86
|
|
|
64
87
|
// ---------- GET / whoami ----------
|
|
@@ -76,7 +99,21 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
76
99
|
if (sessionId !== undefined) body.sessionId = sessionId;
|
|
77
100
|
if (providerId !== undefined) body.providerId = providerId;
|
|
78
101
|
body.scopes = scopes;
|
|
102
|
+
// Whether the caller is a tenant admin, as the admin routes decide it:
|
|
103
|
+
// what a console shows admin pages by (scopes are what a credential may
|
|
104
|
+
// do; admin is a role, from the authorizer when there is one). whoami
|
|
105
|
+
// is how a console starts, so an authorization store that can't answer
|
|
106
|
+
// leaves the field out (a console then reads `scopes`) instead of
|
|
107
|
+
// failing the whole answer.
|
|
108
|
+
try {
|
|
109
|
+
body.tenantAdmin = await isTenantAdmin(c, authorizer);
|
|
110
|
+
} catch (cause) {
|
|
111
|
+
c.get('log').warn(
|
|
112
|
+
`whoami: couldn't tell whether the caller is a tenant admin, so tenantAdmin is left out: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
79
115
|
if (reviewerRole !== undefined) body.reviewerRole = reviewerRole;
|
|
116
|
+
Object.assign(body, keyFacts(c));
|
|
80
117
|
|
|
81
118
|
if (userId !== undefined) {
|
|
82
119
|
body.userId = userId;
|
|
@@ -99,18 +136,25 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
99
136
|
return r;
|
|
100
137
|
}
|
|
101
138
|
|
|
102
|
-
// ---------- GET /users (list) ----------
|
|
139
|
+
// ---------- GET /users (list; tenant admins) ----------
|
|
140
|
+
// The people of a tenant, with their emails: a tenant admin's to read.
|
|
141
|
+
// Anyone else adds a person to a project by their email or id.
|
|
103
142
|
r.get('/users', async (c) => {
|
|
143
|
+
if (!(await isTenantAdmin(c, authorizer))) {
|
|
144
|
+
return denied(c, "Only a tenant admin lists the tenant's people");
|
|
145
|
+
}
|
|
104
146
|
const tenantId = c.get('tenantId') as TenantId;
|
|
105
147
|
const limit = clampLimit(c.req.query('limit'));
|
|
106
148
|
const cursorRaw = c.req.query('cursor');
|
|
107
149
|
const queryRaw = c.req.query('query');
|
|
150
|
+
const includeUnregistered = c.req.query('includeUnregistered') === 'true';
|
|
108
151
|
|
|
109
152
|
const page = await directory.listUsers({
|
|
110
153
|
tenantId,
|
|
111
154
|
limit,
|
|
112
155
|
...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
|
|
113
156
|
...(queryRaw !== undefined && queryRaw.length > 0 && { query: queryRaw }),
|
|
157
|
+
...(includeUnregistered && { includeUnregistered: true }),
|
|
114
158
|
});
|
|
115
159
|
return c.json({
|
|
116
160
|
data: page.data.map(serializeUser),
|
|
@@ -119,11 +163,57 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
119
163
|
});
|
|
120
164
|
});
|
|
121
165
|
|
|
122
|
-
// ----------
|
|
166
|
+
// ---------- POST /users (add a person; tenant admins) ----------
|
|
167
|
+
const createUser = directory.createUser?.bind(directory);
|
|
168
|
+
if (createUser !== undefined) {
|
|
169
|
+
r.post('/users', async (c) => {
|
|
170
|
+
const requestId = c.get('requestId');
|
|
171
|
+
if (!(await isTenantAdmin(c, authorizer))) {
|
|
172
|
+
c.status(statusFor('permission-denied') as never);
|
|
173
|
+
return c.json(
|
|
174
|
+
toWireError(
|
|
175
|
+
{ code: 'permission-denied', message: 'Only a tenant admin adds people' },
|
|
176
|
+
requestId,
|
|
177
|
+
),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const parsed = parseCreateUser(await c.req.json().catch(() => undefined));
|
|
181
|
+
if (typeof parsed === 'string') {
|
|
182
|
+
c.status(statusFor('bad-input') as never);
|
|
183
|
+
return c.json(toWireError({ code: 'bad-input', message: parsed }, requestId));
|
|
184
|
+
}
|
|
185
|
+
const createdBy = callerRef(c);
|
|
186
|
+
const outcome = await createUser({
|
|
187
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
188
|
+
...parsed,
|
|
189
|
+
...(createdBy !== undefined && { createdBy }),
|
|
190
|
+
});
|
|
191
|
+
if (outcome.kind === 'email-taken') {
|
|
192
|
+
c.status(statusFor('identity-user-email-taken') as never);
|
|
193
|
+
return c.json(
|
|
194
|
+
toWireError(
|
|
195
|
+
{
|
|
196
|
+
code: 'identity-user-email-taken',
|
|
197
|
+
message: 'Another person of this tenant has that email',
|
|
198
|
+
userId: outcome.userId as unknown as string,
|
|
199
|
+
},
|
|
200
|
+
requestId,
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
c.status(201);
|
|
205
|
+
return c.json(serializeUser(outcome.user));
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ---------- GET /users/:userId (get; a tenant admin, or your own) ----------
|
|
123
210
|
r.get('/users/:userId', async (c) => {
|
|
124
211
|
const requestId = c.get('requestId');
|
|
125
212
|
const tenantId = c.get('tenantId') as TenantId;
|
|
126
213
|
const userId = c.req.param('userId') as UserId;
|
|
214
|
+
if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
|
|
215
|
+
return denied(c, "Only a tenant admin reads someone else's record");
|
|
216
|
+
}
|
|
127
217
|
|
|
128
218
|
const user = await directory.getUser({ tenantId, userId });
|
|
129
219
|
if (user === null) {
|
|
@@ -142,10 +232,13 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
142
232
|
return c.json(serializeUser(user));
|
|
143
233
|
});
|
|
144
234
|
|
|
145
|
-
// ---------- GET /users/:userId/sessions (
|
|
235
|
+
// ---------- GET /users/:userId/sessions (a tenant admin, or your own) ----------
|
|
146
236
|
r.get('/users/:userId/sessions', async (c) => {
|
|
147
237
|
const tenantId = c.get('tenantId') as TenantId;
|
|
148
238
|
const userId = c.req.param('userId') as UserId;
|
|
239
|
+
if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
|
|
240
|
+
return denied(c, "Only a tenant admin reads someone else's sessions");
|
|
241
|
+
}
|
|
149
242
|
|
|
150
243
|
const page = await directory.listSessions({ tenantId, userId });
|
|
151
244
|
return c.json({
|
|
@@ -155,15 +248,84 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
155
248
|
});
|
|
156
249
|
});
|
|
157
250
|
|
|
158
|
-
// ---------- POST /users/:userId/
|
|
251
|
+
// ---------- POST /users/:userId/unregister (remove a person; tenant admins) ----------
|
|
252
|
+
const unregisterUser = directory.unregisterUser?.bind(directory);
|
|
253
|
+
if (unregisterUser !== undefined) {
|
|
254
|
+
r.post('/users/:userId/unregister', async (c) => {
|
|
255
|
+
const requestId = c.get('requestId');
|
|
256
|
+
const userId = c.req.param('userId') as UserId;
|
|
257
|
+
if (!(await isTenantAdmin(c, authorizer))) {
|
|
258
|
+
c.status(statusFor('permission-denied') as never);
|
|
259
|
+
return c.json(
|
|
260
|
+
toWireError(
|
|
261
|
+
{ code: 'permission-denied', message: 'Only a tenant admin removes people' },
|
|
262
|
+
requestId,
|
|
263
|
+
),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const unregisteredBy = callerRef(c);
|
|
267
|
+
const outcome = await unregisterUser({
|
|
268
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
269
|
+
userId,
|
|
270
|
+
...(unregisteredBy !== undefined && { unregisteredBy }),
|
|
271
|
+
});
|
|
272
|
+
if (outcome.kind === 'not-found') {
|
|
273
|
+
c.status(statusFor('identity-user-not-found') as never);
|
|
274
|
+
return c.json(
|
|
275
|
+
toWireError(
|
|
276
|
+
{
|
|
277
|
+
code: 'identity-user-not-found',
|
|
278
|
+
message: `No user with id "${userId as unknown as string}" under this tenant`,
|
|
279
|
+
userId: userId as unknown as string,
|
|
280
|
+
},
|
|
281
|
+
requestId,
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (outcome.kind === 'refused') {
|
|
286
|
+
const code =
|
|
287
|
+
outcome.reason === 'last-tenant-admin'
|
|
288
|
+
? 'last-tenant-admin'
|
|
289
|
+
: 'identity-user-unregister-refused';
|
|
290
|
+
c.status(statusFor(code) as never);
|
|
291
|
+
return c.json(
|
|
292
|
+
toWireError(
|
|
293
|
+
{
|
|
294
|
+
code,
|
|
295
|
+
message: outcome.message,
|
|
296
|
+
reason: outcome.reason,
|
|
297
|
+
userId: userId as unknown as string,
|
|
298
|
+
},
|
|
299
|
+
requestId,
|
|
300
|
+
),
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return c.json({
|
|
304
|
+
user: serializeUser(outcome.user),
|
|
305
|
+
keysRevoked: outcome.keysRevoked,
|
|
306
|
+
sessionsRevoked: outcome.sessionsRevoked,
|
|
307
|
+
grantsRemoved: outcome.grantsRemoved,
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---------- POST /users/:userId/revoke-sessions (admin, or your own) ----------
|
|
159
313
|
r.post('/users/:userId/revoke-sessions', async (c) => {
|
|
160
314
|
const requestId = c.get('requestId');
|
|
161
315
|
const tenantId = c.get('tenantId') as TenantId;
|
|
162
316
|
const userId = c.req.param('userId') as UserId;
|
|
317
|
+
if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
|
|
318
|
+
return denied(c, "Only a tenant admin revokes someone else's sessions");
|
|
319
|
+
}
|
|
163
320
|
|
|
164
321
|
let outcome: Awaited<ReturnType<IdentityDirectoryBinding['revokeAllSessions']>>;
|
|
165
322
|
try {
|
|
166
|
-
|
|
323
|
+
const revokedBy = callerRef(c);
|
|
324
|
+
outcome = await directory.revokeAllSessions({
|
|
325
|
+
tenantId,
|
|
326
|
+
userId,
|
|
327
|
+
...(revokedBy !== undefined && { revokedBy }),
|
|
328
|
+
});
|
|
167
329
|
} catch (err) {
|
|
168
330
|
c.status(statusFor('identity-revoke-failed') as never);
|
|
169
331
|
return c.json(
|
|
@@ -183,9 +345,123 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
|
|
|
183
345
|
});
|
|
184
346
|
});
|
|
185
347
|
|
|
348
|
+
mountPersonGrants(r, personGrants, authorizer);
|
|
186
349
|
return r;
|
|
187
350
|
}
|
|
188
351
|
|
|
352
|
+
const unsupported = (c: Context<AppEnv>) => {
|
|
353
|
+
c.status(statusFor('person-grants-unsupported') as never);
|
|
354
|
+
return c.json(
|
|
355
|
+
toWireError(
|
|
356
|
+
{
|
|
357
|
+
code: 'person-grants-unsupported',
|
|
358
|
+
message:
|
|
359
|
+
"This runtime doesn't read or write a person's grants: it runs without an authorization store",
|
|
360
|
+
},
|
|
361
|
+
c.get('requestId'),
|
|
362
|
+
),
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
/** Whether `userId` is the caller's own. */
|
|
367
|
+
const isOwn = (c: Context<AppEnv>, userId: string) =>
|
|
368
|
+
(c.get('userId') as unknown as string | undefined) === userId;
|
|
369
|
+
|
|
370
|
+
const denied = (c: Context<AppEnv>, message: string) => {
|
|
371
|
+
c.status(statusFor('permission-denied') as never);
|
|
372
|
+
return c.json(toWireError({ code: 'permission-denied', message }, c.get('requestId')));
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* `GET /users/:userId/grants` (a tenant admin, or the person), and
|
|
377
|
+
* `POST /users/:userId/grant|ungrant` (tenant admins): `{kind:
|
|
378
|
+
* 'tenant-admin'}`, written before the call answers.
|
|
379
|
+
*/
|
|
380
|
+
function mountPersonGrants(
|
|
381
|
+
r: Hono<AppEnv>,
|
|
382
|
+
binding: PersonGrantsBinding | undefined,
|
|
383
|
+
authorizer: Authorizer | undefined,
|
|
384
|
+
): void {
|
|
385
|
+
r.get('/users/:userId/grants', async (c) => {
|
|
386
|
+
const userId = c.req.param('userId');
|
|
387
|
+
if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
|
|
388
|
+
return denied(c, "Only a tenant admin reads someone else's grants");
|
|
389
|
+
}
|
|
390
|
+
if (binding === undefined) return unsupported(c);
|
|
391
|
+
const grants = await binding.read({ tenantId: c.get('tenantId') as TenantId, userId });
|
|
392
|
+
if (grants === null) return notFound(c, userId);
|
|
393
|
+
return c.json(serializePersonGrants(grants));
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
for (const action of ['grant', 'ungrant'] as const) {
|
|
397
|
+
r.post(`/users/:userId/${action}`, async (c) => {
|
|
398
|
+
if (!(await isTenantAdmin(c, authorizer))) {
|
|
399
|
+
return denied(c, "Only a tenant admin changes a person's grants");
|
|
400
|
+
}
|
|
401
|
+
const grant = parsePersonGrant(await c.req.json().catch(() => undefined));
|
|
402
|
+
if (typeof grant === 'string') {
|
|
403
|
+
c.status(statusFor('bad-input') as never);
|
|
404
|
+
return c.json(toWireError({ code: 'bad-input', message: grant }, c.get('requestId')));
|
|
405
|
+
}
|
|
406
|
+
if (binding === undefined) return unsupported(c);
|
|
407
|
+
const by = callerRef(c);
|
|
408
|
+
const result = await binding[action]({
|
|
409
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
410
|
+
userId: c.req.param('userId'),
|
|
411
|
+
grant,
|
|
412
|
+
...(by !== undefined && { by }),
|
|
413
|
+
});
|
|
414
|
+
if (result.kind === 'err') return grantError(c, result.error);
|
|
415
|
+
return c.json(serializePersonGrants(result.value));
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function notFound(c: Context<AppEnv>, userId: string) {
|
|
421
|
+
c.status(statusFor('identity-user-not-found') as never);
|
|
422
|
+
return c.json(
|
|
423
|
+
toWireError(
|
|
424
|
+
{
|
|
425
|
+
code: 'identity-user-not-found',
|
|
426
|
+
message: `No user with id "${userId}" under this tenant`,
|
|
427
|
+
userId,
|
|
428
|
+
},
|
|
429
|
+
c.get('requestId'),
|
|
430
|
+
),
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function grantError(c: Context<AppEnv>, error: PersonGrantError) {
|
|
435
|
+
c.status(statusFor(error.code) as never);
|
|
436
|
+
return c.json(toWireError({ code: error.code, message: error.message }, c.get('requestId')));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** `{kind: 'tenant-admin'}`, or why not. */
|
|
440
|
+
function parsePersonGrant(raw: unknown): PersonGrant | string {
|
|
441
|
+
const b = raw as { kind?: unknown } | null | undefined;
|
|
442
|
+
if (
|
|
443
|
+
typeof b !== 'object' ||
|
|
444
|
+
b === null ||
|
|
445
|
+
Array.isArray(b) ||
|
|
446
|
+
b.kind !== 'tenant-admin' ||
|
|
447
|
+
Object.keys(b).length !== 1
|
|
448
|
+
) {
|
|
449
|
+
return "The body must be { kind: 'tenant-admin' }: a person's project and team roles have their own routes (`/v1/projects/{projectId}/memberships`, `/v1/teams/{teamId}/memberships`)";
|
|
450
|
+
}
|
|
451
|
+
return { kind: 'tenant-admin' };
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function serializePersonGrants(g: PersonGrants): Record<string, unknown> {
|
|
455
|
+
return {
|
|
456
|
+
userId: g.userId,
|
|
457
|
+
...(g.tenantAdmin !== undefined && { tenantAdmin: g.tenantAdmin }),
|
|
458
|
+
...(g.tenantMember !== undefined && { tenantMember: g.tenantMember }),
|
|
459
|
+
projects: g.projects.map((p) => ({ projectId: p.projectId, role: p.role })),
|
|
460
|
+
teams: g.teams.map((t) => ({ teamId: t.teamId, role: t.role })),
|
|
461
|
+
...(g.reviewer !== undefined && { reviewer: { role: g.reviewer.role } }),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
189
465
|
function serializeUser(u: UserRecord): Record<string, unknown> {
|
|
190
466
|
return {
|
|
191
467
|
userId: u.userId as unknown as string,
|
|
@@ -194,6 +470,9 @@ function serializeUser(u: UserRecord): Record<string, unknown> {
|
|
|
194
470
|
...(u.displayName !== undefined && { displayName: u.displayName }),
|
|
195
471
|
createdAt: u.createdAt as unknown as string,
|
|
196
472
|
...(u.lastActiveAt !== undefined && { lastActiveAt: u.lastActiveAt as unknown as string }),
|
|
473
|
+
...(u.unregisteredAt !== undefined && {
|
|
474
|
+
unregisteredAt: u.unregisteredAt as unknown as string,
|
|
475
|
+
}),
|
|
197
476
|
...(u.metadata !== undefined && { metadata: u.metadata }),
|
|
198
477
|
};
|
|
199
478
|
}
|
|
@@ -209,3 +488,42 @@ function serializeSession(s: SessionSummary): Record<string, unknown> {
|
|
|
209
488
|
...(s.revokedAt !== undefined && { revokedAt: s.revokedAt as unknown as string }),
|
|
210
489
|
};
|
|
211
490
|
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* What an API key adds to whoami: whom it acts for, its id, and what it's
|
|
494
|
+
* limited to, so a client can say "you're acme-ci, limited to project X".
|
|
495
|
+
*/
|
|
496
|
+
function keyFacts(c: Context<AppEnv>): Record<string, unknown> {
|
|
497
|
+
const principal = callerPrincipal(c);
|
|
498
|
+
const tokenId = c.get('tokenId') as string | undefined;
|
|
499
|
+
const role = c.get('tokenRole');
|
|
500
|
+
const projectId = c.get('tokenProjectId');
|
|
501
|
+
return {
|
|
502
|
+
...(principal !== undefined && { principal: principalToWire(principal) }),
|
|
503
|
+
...(tokenId !== undefined && { tokenId }),
|
|
504
|
+
...(role !== undefined && { role }),
|
|
505
|
+
...(projectId !== undefined && { projectId }),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const MAX_NAME_LENGTH = 200;
|
|
510
|
+
|
|
511
|
+
/** `{displayName, primaryEmail?}`, or why not. */
|
|
512
|
+
function parseCreateUser(raw: unknown): { displayName: string; primaryEmail?: string } | string {
|
|
513
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
514
|
+
return 'Request body must be a JSON object';
|
|
515
|
+
}
|
|
516
|
+
const b = raw as { displayName?: unknown; primaryEmail?: unknown };
|
|
517
|
+
if (
|
|
518
|
+
typeof b.displayName !== 'string' ||
|
|
519
|
+
b.displayName.trim() === '' ||
|
|
520
|
+
b.displayName.length > MAX_NAME_LENGTH
|
|
521
|
+
) {
|
|
522
|
+
return `\`displayName\` must be a non-empty string of at most ${MAX_NAME_LENGTH} characters`;
|
|
523
|
+
}
|
|
524
|
+
if (b.primaryEmail === undefined) return { displayName: b.displayName.trim() };
|
|
525
|
+
if (typeof b.primaryEmail !== 'string' || !/^[^@\s]+@[^@\s]+$/.test(b.primaryEmail)) {
|
|
526
|
+
return '`primaryEmail` must be an email address';
|
|
527
|
+
}
|
|
528
|
+
return { displayName: b.displayName.trim(), primaryEmail: b.primaryEmail };
|
|
529
|
+
}
|