@zackbart/connecta 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +363 -0
- package/README.md +62 -106
- package/SECURITY.md +3 -2
- package/bin/connecta.mjs +41 -16
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +2 -1
- package/dist/access-tokens.js +66 -30
- package/dist/activity.d.ts +1 -1
- package/dist/activity.js +2 -0
- package/dist/agent-config.d.ts +23 -0
- package/dist/agent-config.js +59 -0
- package/dist/agent-description.js +16 -0
- package/dist/agent-identity.js +4 -4
- package/dist/agent-observation.js +39 -0
- package/dist/auth/clerk-sdk/client.d.ts +2 -0
- package/dist/auth/clerk-sdk/generated.js +554 -563
- package/dist/auth/clerk-transport.js +70 -3
- package/dist/auth/clerk.d.ts +13 -0
- package/dist/auth/clerk.js +653 -246
- package/dist/auth/cloudflare-access.js +46 -42
- package/dist/auth/downstream-client-metadata.js +2 -2
- package/dist/auth/downstream-oauth.js +2051 -1160
- package/dist/auth/downstream-revocation.js +88 -88
- package/dist/auth/native-call.js +20 -0
- package/dist/auth/native.js +184 -0
- package/dist/auth/oauth-start-reset.js +11 -5
- package/dist/auth/oauth-token-response.js +121 -102
- package/dist/auth/oauth-v2-migration.js +97 -78
- package/dist/auth/renewable-grant.js +10 -0
- package/dist/auth/static-oauth.js +663 -271
- package/dist/auth/vercel-agent.js +266 -0
- package/dist/auth-admission.js +204 -0
- package/dist/auth-elicitation.js +350 -241
- package/dist/authorization-handoff.js +164 -90
- package/dist/call-admission-context.js +3 -10
- package/dist/call-admission.js +2 -0
- package/dist/call-classification.js +9 -0
- package/dist/call-recovery.js +32 -13
- package/dist/canonical-json.js +8 -0
- package/dist/catalog-cache.js +369 -213
- package/dist/catalog-service.js +659 -530
- package/dist/catalog.js +5 -1
- package/dist/concurrency.js +0 -25
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +3 -1
- package/dist/config-schema.js +13 -3
- package/dist/config.d.ts +40 -6
- package/dist/config.js +40 -26
- package/dist/connector-caller.d.ts +4 -0
- package/dist/connector-caller.js +17 -4
- package/dist/connectors/api-connector.d.ts +12 -7
- package/dist/connectors/api-connector.js +142 -56
- package/dist/connectors/guarded-fetch.d.ts +51 -17
- package/dist/connectors/guarded-fetch.js +215 -121
- package/dist/connectors/negotiation-cache.js +20 -10
- package/dist/connectors/remote-mcp.d.ts +10 -30
- package/dist/connectors/remote-mcp.js +955 -563
- package/dist/d1.js +10 -243
- package/dist/derived-context.js +5 -0
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +2 -0
- package/dist/downstream-input.js +347 -260
- package/dist/errors.d.ts +87 -6
- package/dist/errors.js +114 -4
- package/dist/execute.js +87 -50
- package/dist/executor-admission.js +3 -0
- package/dist/executors/quickjs.js +163 -112
- package/dist/guarded-transport.js +50 -0
- package/dist/guest-runtime.js +1 -1
- package/dist/http/client.js +439 -0
- package/dist/http/errors.js +558 -0
- package/dist/http/fields.js +49 -0
- package/dist/http/hatch-paging.js +318 -0
- package/dist/http/hatch.js +1279 -0
- package/dist/http/json-schema.js +267 -0
- package/dist/http/node-transport.js +241 -0
- package/dist/http/oauth.js +162 -0
- package/dist/http/paging.js +228 -0
- package/dist/http/provider.js +221 -0
- package/dist/http/safety.js +82 -0
- package/dist/http/services.js +79 -0
- package/dist/http/tools.js +383 -0
- package/dist/http/vendor-index.js +138 -0
- package/dist/http/vendor.js +448 -0
- package/dist/http/write-request.js +66 -0
- package/dist/http-policy.d.ts +11 -0
- package/dist/http-policy.js +45 -0
- package/dist/inbound-credential.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/invocation-auth.js +30 -19
- package/dist/invocation.js +218 -85
- package/dist/mcp-result.js +0 -4
- package/dist/membership.d.ts +4 -0
- package/dist/meta-output.js +1 -1
- package/dist/meta-tools.js +531 -406
- package/dist/native-recovery.js +52 -0
- package/dist/native-tools.js +382 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +45 -8
- package/dist/oauth-handoff.js +177 -104
- package/dist/oauth-partition.js +6 -20
- package/dist/oauth-sealing.js +6 -7
- package/dist/operation-vocabulary.js +101 -0
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +17 -4
- package/dist/operator-ui/agents.d.ts +5 -4
- package/dist/operator-ui/agents.js +17 -7
- package/dist/operator-ui/config-summary.js +5 -4
- package/dist/operator-ui/connection-map.js +7 -5
- package/dist/operator-ui/contract.d.ts +7 -3
- package/dist/operator-ui/endpoints.js +4 -13
- package/dist/operator-ui/fix-prompts.d.ts +1 -1
- package/dist/operator-ui/fix-prompts.js +0 -8
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/model.d.ts +2 -3
- package/dist/operator-ui/overview.js +5 -2
- package/dist/operator-ui/setup-commands.js +0 -4
- package/dist/operator-ui/view.d.ts +2 -25
- package/dist/operator-ui/view.js +2 -76
- package/dist/program-writes.js +47 -26
- package/dist/provider-call-facts.d.ts +115 -0
- package/dist/provider-call-facts.js +182 -0
- package/dist/provider-call.d.ts +112 -0
- package/dist/provider-call.js +271 -0
- package/dist/provider.d.ts +65 -31
- package/dist/provider.js +116 -39
- package/dist/providers/_shared/google/hatch.js +91 -0
- package/dist/providers/_shared/google/kit.js +537 -0
- package/dist/providers/_shared/google/result-size.js +15 -2
- package/dist/providers/_shared/google/service-account.d.ts +2 -1
- package/dist/providers/_shared/google/service-account.js +8 -4
- package/dist/providers/_shared/google/workspace.d.ts +6 -210
- package/dist/providers/_shared/google/workspace.js +6 -425
- package/dist/providers/_shared/rest/compact-index.js +86 -0
- package/dist/providers/_shared/rest/dispatch.js +3 -23
- package/dist/providers/_shared/rest/kit.js +546 -0
- package/dist/providers/_shared/rest/operation-index.js +230 -14
- package/dist/providers/_shared/rest/response-media.js +11 -0
- package/dist/providers/_shared/rest/tools.js +101 -78
- package/dist/providers/_shared/rest/value-safety.js +353 -195
- package/dist/providers/_shared/sensitive-fields.js +27 -0
- package/dist/providers/breeze/api.js +584 -0
- package/dist/providers/breeze/index.d.ts +1 -3
- package/dist/providers/breeze/index.js +67 -1420
- package/dist/providers/breeze/openapi.generated.js +60 -0
- package/dist/providers/breeze/skill.generated.js +2 -6
- package/dist/providers/breeze/tools.js +696 -0
- package/dist/providers/ccb/api.js +496 -0
- package/dist/providers/ccb/index.d.ts +15 -37
- package/dist/providers/ccb/index.js +77 -1702
- package/dist/providers/ccb/lib/constants.d.ts +34 -0
- package/dist/providers/ccb/lib/constants.js +74 -0
- package/dist/providers/ccb/lib/guide.js +9 -0
- package/dist/providers/ccb/lib/verdicts.js +387 -0
- package/dist/providers/ccb/openapi.generated.js +397 -0
- package/dist/providers/ccb/safety.js +295 -0
- package/dist/providers/ccb/skill.generated.js +2 -6
- package/dist/providers/ccb/tools.js +853 -0
- package/dist/providers/cloudflare/api.js +1066 -0
- package/dist/providers/cloudflare/index.d.ts +5 -22
- package/dist/providers/cloudflare/index.js +79 -197
- package/dist/providers/cloudflare/lib/config.d.ts +16 -0
- package/dist/providers/cloudflare/lib/config.js +2 -0
- package/dist/providers/cloudflare/lib/discovery.js +29 -0
- package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
- package/dist/providers/cloudflare/lib/hatch.js +103 -0
- package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
- package/dist/providers/cloudflare/lib/response-media.js +75 -0
- package/dist/providers/cloudflare/openapi.generated.js +4 -3
- package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -10
- package/dist/providers/cloudflare/tools.js +360 -0
- package/dist/providers/docs/api.js +51 -0
- package/dist/providers/docs/index.d.ts +1 -12
- package/dist/providers/docs/index.js +53 -1165
- package/dist/providers/docs/lib/constants.d.ts +18 -0
- package/dist/providers/docs/lib/constants.js +18 -0
- package/dist/providers/docs/lib/discovery.js +26 -0
- package/dist/providers/docs/lib/requests.js +48 -0
- package/dist/providers/docs/lib/schemas.js +168 -0
- package/dist/providers/docs/openapi.generated.js +16 -0
- package/dist/providers/docs/safety.js +54 -0
- package/dist/providers/docs/skill.generated.js +2 -6
- package/dist/providers/docs/tools.js +802 -0
- package/dist/providers/drive/api.js +177 -0
- package/dist/providers/drive/index.d.ts +1 -12
- package/dist/providers/drive/index.js +38 -1448
- package/dist/providers/drive/lib/constants.d.ts +6 -0
- package/dist/providers/drive/lib/constants.js +6 -0
- package/dist/providers/drive/lib/contracts.js +532 -0
- package/dist/providers/drive/lib/hatch.js +197 -0
- package/dist/providers/drive/openapi.generated.js +77 -0
- package/dist/providers/drive/safety.js +90 -0
- package/dist/providers/drive/skill.generated.js +2 -6
- package/dist/providers/drive/tools.js +1290 -0
- package/dist/providers/forms/api.js +71 -0
- package/dist/providers/forms/index.d.ts +1 -12
- package/dist/providers/forms/index.js +49 -1145
- package/dist/providers/forms/lib/constants.d.ts +14 -0
- package/dist/providers/forms/lib/constants.js +19 -0
- package/dist/providers/forms/lib/hatch.js +72 -0
- package/dist/providers/forms/openapi.generated.js +23 -0
- package/dist/providers/forms/safety.js +34 -0
- package/dist/providers/forms/skill.generated.js +2 -8
- package/dist/providers/forms/tools.js +1024 -0
- package/dist/providers/github/api.js +150 -0
- package/dist/providers/github/index.d.ts +4 -5
- package/dist/providers/github/index.js +30 -87
- package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
- package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
- package/dist/providers/github/lib/discovery.js +132 -0
- package/dist/providers/github/lib/operations.js +58 -0
- package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
- package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
- package/dist/providers/github/openapi.generated.js +1216 -0
- package/dist/providers/github/safety.js +216 -0
- package/dist/providers/github/skill.generated.js +2 -5
- package/dist/providers/github/tools.js +506 -0
- package/dist/providers/gmail/api.js +114 -0
- package/dist/providers/gmail/index.d.ts +1 -12
- package/dist/providers/gmail/index.js +73 -1621
- package/dist/providers/gmail/lib/constants.d.ts +13 -0
- package/dist/providers/gmail/lib/constants.js +16 -0
- package/dist/providers/gmail/lib/index.js +173 -0
- package/dist/providers/gmail/lib/schemas.js +176 -0
- package/dist/providers/gmail/lib/threading.js +120 -0
- package/dist/providers/gmail/openapi.generated.js +92 -0
- package/dist/providers/gmail/safety.js +21 -0
- package/dist/providers/gmail/skill.generated.js +2 -6
- package/dist/providers/gmail/tools.js +1435 -0
- package/dist/providers/overflow/api.js +1008 -0
- package/dist/providers/overflow/index.d.ts +1 -17
- package/dist/providers/overflow/index.js +60 -1488
- package/dist/providers/overflow/skill.generated.js +2 -5
- package/dist/providers/overflow/tools.js +873 -0
- package/dist/providers/planning-center/api.js +467 -0
- package/dist/providers/planning-center/index.d.ts +4 -35
- package/dist/providers/planning-center/index.js +69 -2445
- package/dist/providers/planning-center/lib/constants.d.ts +58 -0
- package/dist/providers/planning-center/lib/constants.js +83 -0
- package/dist/providers/planning-center/lib/discovery.js +40 -0
- package/dist/providers/planning-center/lib/guide.js +39 -0
- package/dist/providers/planning-center/lib/json-api.js +83 -0
- package/dist/providers/planning-center/lib/verdicts.js +1758 -0
- package/dist/providers/planning-center/openapi.generated.js +1764 -0
- package/dist/providers/planning-center/safety.js +188 -0
- package/dist/providers/planning-center/skill.generated.js +2 -27
- package/dist/providers/planning-center/tools.js +1010 -0
- package/dist/providers/sheets/api.js +133 -0
- package/dist/providers/sheets/index.d.ts +4 -16
- package/dist/providers/sheets/index.js +61 -1172
- package/dist/providers/sheets/lib/constants.d.ts +14 -0
- package/dist/providers/sheets/lib/constants.js +14 -0
- package/dist/providers/sheets/lib/hatch.js +163 -0
- package/dist/providers/sheets/lib/requests.js +105 -0
- package/dist/providers/sheets/lib/schemas.js +138 -0
- package/dist/providers/sheets/openapi.generated.js +30 -0
- package/dist/providers/sheets/safety.js +27 -0
- package/dist/providers/sheets/skill.generated.js +2 -7
- package/dist/providers/sheets/tools.js +734 -0
- package/dist/providers/slides/api.js +147 -0
- package/dist/providers/slides/index.d.ts +2 -10
- package/dist/providers/slides/index.js +48 -2555
- package/dist/providers/slides/lib/constants.d.ts +11 -0
- package/dist/providers/slides/lib/constants.js +11 -0
- package/dist/providers/slides/lib/requests.js +60 -0
- package/dist/providers/slides/lib/schemas.js +565 -0
- package/dist/providers/slides/openapi.generated.js +18 -0
- package/dist/providers/slides/skill.generated.js +2 -6
- package/dist/providers/slides/tools.js +1968 -0
- package/dist/providers/stripe/api.js +401 -0
- package/dist/providers/stripe/index.d.ts +5 -27
- package/dist/providers/stripe/index.js +74 -210
- package/dist/providers/stripe/lib/config.d.ts +4 -0
- package/dist/providers/stripe/lib/config.js +2 -0
- package/dist/providers/stripe/openapi.generated.js +4 -3
- package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
- package/dist/providers/stripe/skill.generated.js +2 -11
- package/dist/providers/stripe/tools.js +77 -0
- package/dist/providers/tithely/api.js +531 -0
- package/dist/providers/tithely/index.d.ts +17 -0
- package/dist/providers/tithely/index.js +86 -965
- package/dist/providers/tithely/skill.generated.js +2 -5
- package/dist/providers/tithely/tools.js +426 -0
- package/dist/providers/vercel/api.js +551 -0
- package/dist/providers/vercel/index.d.ts +9 -25
- package/dist/providers/vercel/index.js +66 -260
- package/dist/providers/vercel/lib/config.d.ts +2 -0
- package/dist/providers/vercel/lib/config.js +2 -0
- package/dist/providers/vercel/lib/index.js +117 -0
- package/dist/providers/vercel/lib/logs.js +100 -0
- package/dist/providers/vercel/lib/pin.js +270 -0
- package/dist/providers/vercel/openapi.generated.js +10 -5
- package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
- package/dist/providers/vercel/skill.generated.js +2 -7
- package/dist/providers/vercel/tools.js +642 -0
- package/dist/redaction-identity.js +47 -0
- package/dist/redaction-marker.js +125 -0
- package/dist/registry.d.ts +21 -15
- package/dist/registry.js +409 -376
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/actor-labels.js +1 -1
- package/dist/routes/connect.js +1 -1
- package/dist/routes/dispatch.js +103 -0
- package/dist/routes/mcp.js +170 -79
- package/dist/routes/oauth.js +3 -1
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +5 -4
- package/dist/routes/shared.js +244 -177
- package/dist/routes/skills.js +10 -8
- package/dist/routes/ui-config.js +27 -3
- package/dist/routes/ui-data.js +21 -20
- package/dist/routes/ui.js +1 -1
- package/dist/runtime/call-admission.js +0 -26
- package/dist/runtime/call-entry.js +30 -0
- package/dist/runtime/call.js +556 -0
- package/dist/runtime/connector-scope.js +7 -0
- package/dist/runtime/credential-probe.js +34 -0
- package/dist/runtime/executor.js +40 -0
- package/dist/runtime/native-storage.js +63 -0
- package/dist/runtime/registry.js +27 -0
- package/dist/runtime/request-scope.js +72 -0
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/shared-read.js +34 -5
- package/dist/runtime/storage.js +34 -21
- package/dist/sent-secrets.d.ts +194 -37
- package/dist/sent-secrets.js +1743 -340
- package/dist/server.js +5 -56
- package/dist/skills.js +322 -222
- package/dist/sqlite.js +15 -14
- package/dist/storage/d1-copy.js +255 -0
- package/dist/storage/d1-driver.js +15 -0
- package/dist/storage/keys.js +28 -0
- package/dist/storage/sql-driver.js +88 -0
- package/dist/storage/sql-native.js +544 -0
- package/dist/storage/sql-schema.d.ts +6 -0
- package/dist/storage/sql-schema.js +15 -0
- package/dist/storage/sql.d.ts +14 -20
- package/dist/storage/sql.js +29 -535
- package/dist/timeout.js +0 -26
- package/dist/tool-safety.js +20 -10
- package/dist/types.d.ts +7 -109
- package/dist/upward.js +3 -0
- package/dist/usage-guide.js +3 -1
- package/dist/validate.d.ts +4 -2
- package/dist/validate.js +86 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +312 -292
- package/dist/workspace-subject.js +5 -1
- package/dist/write-outcome.js +454 -34
- package/documentation/README.md +27 -7
- package/documentation/architecture.md +2547 -226
- package/documentation/auth.md +821 -98
- package/documentation/code-mode.md +49 -16
- package/documentation/deploying.md +164 -20
- package/documentation/hosted-mcp-migration.md +460 -0
- package/documentation/integrating.md +129 -144
- package/documentation/meta-tools.md +409 -32
- package/documentation/operating.md +27 -3
- package/documentation/operator-tests.md +3 -0
- package/documentation/operator-ui.md +68 -45
- package/documentation/provider-auth-migration.md +16 -63
- package/documentation/provider-migration-0.29.md +10 -5
- package/documentation/provider-migration-0.35.md +975 -0
- package/documentation/providers.md +965 -0
- package/examples/worker/AGENTS.md +23 -9
- package/examples/worker/README.md +254 -135
- package/examples/worker/alchemy.run.ts +116 -0
- package/examples/worker/gitignore +10 -0
- package/examples/worker/src/connecta.config.ts +23 -21
- package/examples/worker/wrangler.jsonc +14 -5
- package/guides/connecting-agents.md +157 -0
- package/guides/how-it-works.md +156 -0
- package/guides/safety-and-trust.md +153 -0
- package/package.json +27 -46
- package/templates/node/.env.example +3 -0
- package/templates/node/README.md +22 -2
- package/templates/node/docker-compose.yml +1 -0
- package/templates/node/package.json +3 -2
- package/templates/node/src/connecta.config.ts +8 -0
- package/dist/catalog-drift.d.ts +0 -138
- package/dist/catalog-drift.js +0 -578
- package/dist/providers/_shared/request-token.js +0 -83
- package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
- package/dist/providers/_shared/rest/tools.d.ts +0 -141
- package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
- package/dist/providers/basecamp/index.d.ts +0 -45
- package/dist/providers/basecamp/index.js +0 -997
- package/dist/providers/basecamp/skill.generated.js +0 -9
- package/dist/providers/cloudflare/rest.d.ts +0 -37
- package/dist/providers/cloudflare/rest.js +0 -1097
- package/dist/providers/github/hosted.js +0 -218
- package/dist/providers/github/rest.js +0 -321
- package/dist/providers/infisical/index.d.ts +0 -7
- package/dist/providers/infisical/index.js +0 -713
- package/dist/providers/infisical/skill.generated.js +0 -8
- package/dist/providers/intercom/index.d.ts +0 -24
- package/dist/providers/intercom/index.js +0 -121
- package/dist/providers/intercom/skill.generated.js +0 -8
- package/dist/providers/linear/index.d.ts +0 -55
- package/dist/providers/linear/index.js +0 -537
- package/dist/providers/linear/skill.generated.js +0 -8
- package/dist/providers/mixpanel/index.d.ts +0 -59
- package/dist/providers/mixpanel/index.js +0 -472
- package/dist/providers/mixpanel/operations.js +0 -20
- package/dist/providers/mixpanel/rest.js +0 -603
- package/dist/providers/mixpanel/skill.generated.js +0 -10
- package/dist/providers/mixpanel/value-safety.js +0 -16
- package/dist/providers/notion/index.d.ts +0 -67
- package/dist/providers/notion/index.js +0 -368
- package/dist/providers/notion/openapi.generated.js +0 -75
- package/dist/providers/notion/rest.d.ts +0 -24
- package/dist/providers/notion/rest.js +0 -1226
- package/dist/providers/notion/skill.generated.js +0 -11
- package/dist/providers/notion/value-safety.js +0 -56
- package/dist/providers/revenuecat/index.d.ts +0 -51
- package/dist/providers/revenuecat/index.js +0 -697
- package/dist/providers/revenuecat/skill.generated.js +0 -11
- package/dist/providers/stripe/rest.d.ts +0 -14
- package/dist/providers/stripe/rest.js +0 -402
- package/dist/providers/vercel/rest.d.ts +0 -20
- package/dist/providers/vercel/rest.js +0 -1020
- package/dist/providers/wispr-flow/index.d.ts +0 -14
- package/dist/providers/wispr-flow/index.js +0 -117
- package/dist/providers/wispr-flow/skill.generated.js +0 -8
- /package/dist/providers/github/{key.js → lib/key.js} +0 -0
package/dist/oauth-handoff.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { upwardStep } from "./upward.js";
|
|
2
|
+
import { authCall, authInvoke, authFlow, authStorage, registerAuth } from "./auth/native.js";
|
|
3
|
+
import { runEdge } from "./runtime/run.js";
|
|
1
4
|
import { oauthConnectKeys } from "./storage/keys.js";
|
|
2
5
|
const HANDOFF_TTL_MS = 15 * 60_000;
|
|
3
6
|
const INTERACTIVE_OAUTH_REQUIRED = "An interactive provider (Clerk or Cloudflare Access) is required to connect OAuth connectors.";
|
|
@@ -71,124 +74,194 @@ export function oauthConnectUnavailable(opts) {
|
|
|
71
74
|
}
|
|
72
75
|
return undefined;
|
|
73
76
|
}
|
|
74
|
-
export
|
|
75
|
-
return (
|
|
77
|
+
export function oauthConnectUrl(opts, baseUrl, connectorId, principal, force = false, opaque = false) {
|
|
78
|
+
return runEdge(oauthConnectUrlNative(opts, baseUrl, connectorId, principal, force, opaque));
|
|
76
79
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
force,
|
|
92
|
-
...(after ? { after } : {}),
|
|
93
|
-
};
|
|
94
|
-
const vault = opts.config.vault;
|
|
95
|
-
if (opaque && (!vault.seal || !vault.open))
|
|
96
|
-
throw new Error("URL elicitation requires a sealing credential vault.");
|
|
97
|
-
const compact = encodeHandoff(handoff);
|
|
98
|
-
const payload = opaque
|
|
99
|
-
? `v4.${encodeSegment(await vault.seal(connectorId, "connecta:connect-link:v4", compact))}`
|
|
100
|
-
: `v3.${encodeSegment(compact)}`;
|
|
101
|
-
// Preserve the vault's entire signature and its representation, including
|
|
102
|
-
// custom vaults, while keeping every URL segment unpadded base64url.
|
|
103
|
-
const signature = encodeSegment(await vault.signOAuthHandoff(payload));
|
|
104
|
-
const url = new URL(`/connect/${connectorId}`, baseUrl);
|
|
105
|
-
url.searchParams.set("h", `${payload}.${signature}`);
|
|
106
|
-
return { url: url.toString(), nonce: handoff.nonce };
|
|
80
|
+
const oauthConnectUrlNative = /* @__PURE__ */ registerAuth(oauthConnectUrl, function (opts, baseUrl, connectorId, principal, force = false, opaque = false) {
|
|
81
|
+
return authFlow(function* () {
|
|
82
|
+
return (yield* upwardStep(authCall(oauthConnectLink, [
|
|
83
|
+
opts,
|
|
84
|
+
baseUrl,
|
|
85
|
+
connectorId,
|
|
86
|
+
principal,
|
|
87
|
+
force,
|
|
88
|
+
opaque,
|
|
89
|
+
]))).url;
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
export function oauthConnectLink(opts, baseUrl, connectorId, principal, force = false, opaque = false, after) {
|
|
93
|
+
return runEdge(oauthConnectLinkNative(opts, baseUrl, connectorId, principal, force, opaque, after));
|
|
107
94
|
}
|
|
95
|
+
const oauthConnectLinkNative = /* @__PURE__ */ registerAuth(oauthConnectLink, function (opts, baseUrl, connectorId, principal, force = false, opaque = false, after) {
|
|
96
|
+
return authFlow(function* () {
|
|
97
|
+
const unavailable = oauthConnectUnavailable(opts);
|
|
98
|
+
if (unavailable)
|
|
99
|
+
throw new Error(unavailable);
|
|
100
|
+
const connector = opts.registry.getConnector(connectorId);
|
|
101
|
+
if (!connector || !principal)
|
|
102
|
+
throw new Error("OAuth connection requires an initiating user identity.");
|
|
103
|
+
const handoff = {
|
|
104
|
+
connector: connectorId,
|
|
105
|
+
owner: connector.authScope === "personal" ? principal : "shared",
|
|
106
|
+
principal,
|
|
107
|
+
origin: new URL(baseUrl).origin,
|
|
108
|
+
expiresAt: Date.now() + HANDOFF_TTL_MS,
|
|
109
|
+
nonce: crypto.randomUUID(),
|
|
110
|
+
force,
|
|
111
|
+
...(after ? { after } : {}),
|
|
112
|
+
};
|
|
113
|
+
const vault = opts.config.vault;
|
|
114
|
+
if (opaque && (!vault.seal || !vault.open))
|
|
115
|
+
throw new Error("URL elicitation requires a sealing credential vault.");
|
|
116
|
+
const compact = encodeHandoff(handoff);
|
|
117
|
+
const payload = opaque
|
|
118
|
+
? `v4.${encodeSegment(yield* upwardStep(authInvoke(vault, "seal", [connectorId, "connecta:connect-link:v4", compact])))}`
|
|
119
|
+
: `v3.${encodeSegment(compact)}`;
|
|
120
|
+
// Preserve the vault's entire signature and its representation, including
|
|
121
|
+
// custom vaults, while keeping every URL segment unpadded base64url.
|
|
122
|
+
const signature = encodeSegment(yield* upwardStep(authInvoke(vault, "signOAuthHandoff", [payload])));
|
|
123
|
+
const url = new URL(`/connect/${connectorId}`, baseUrl);
|
|
124
|
+
url.searchParams.set("h", `${payload}.${signature}`);
|
|
125
|
+
return { url: url.toString(), nonce: handoff.nonce };
|
|
126
|
+
});
|
|
127
|
+
});
|
|
108
128
|
/** A claim precedes OAuth reset; only a completed start can prove recovery. */
|
|
109
|
-
export
|
|
110
|
-
|
|
111
|
-
return stage === "used" ? "claimed" : stage === "started" || stage === "failed" ? stage : undefined;
|
|
129
|
+
export function oauthConnectLinkProgress(opts, baseUrl, id, nonce) {
|
|
130
|
+
return runEdge(oauthConnectLinkProgressNative(opts, baseUrl, id, nonce));
|
|
112
131
|
}
|
|
132
|
+
const oauthConnectLinkProgressNative = /* @__PURE__ */ registerAuth(oauthConnectLinkProgress, function (opts, baseUrl, id, nonce) {
|
|
133
|
+
return authFlow(function* () {
|
|
134
|
+
const stage = yield* upwardStep(authStorage(opts.registry.contextFor(id, baseUrl).storage, "get", [
|
|
135
|
+
oauthConnectKeys.used(nonce),
|
|
136
|
+
]));
|
|
137
|
+
return stage === "used" ? "claimed" : stage === "started" || stage === "failed" ? stage : undefined;
|
|
138
|
+
});
|
|
139
|
+
});
|
|
113
140
|
/** Make outstanding flow links single-use refusals before a completion check. */
|
|
114
|
-
export
|
|
115
|
-
|
|
116
|
-
for (const nonce of nonces) {
|
|
117
|
-
await storage.compareAndSet(oauthConnectKeys.used(nonce), null, "closed", { ttlSeconds: HANDOFF_TTL_MS / 1000 });
|
|
118
|
-
}
|
|
141
|
+
export function closeOAuthConnectLinks(opts, baseUrl, id, nonces) {
|
|
142
|
+
return runEdge(closeOAuthConnectLinksNative(opts, baseUrl, id, nonces));
|
|
119
143
|
}
|
|
144
|
+
const closeOAuthConnectLinksNative = /* @__PURE__ */ registerAuth(closeOAuthConnectLinks, function (opts, baseUrl, id, nonces) {
|
|
145
|
+
return authFlow(function* () {
|
|
146
|
+
const storage = opts.registry.contextFor(id, baseUrl).storage;
|
|
147
|
+
for (const nonce of nonces) {
|
|
148
|
+
yield* upwardStep(authStorage(storage, "compareAndSet", [
|
|
149
|
+
oauthConnectKeys.used(nonce),
|
|
150
|
+
null,
|
|
151
|
+
"closed",
|
|
152
|
+
{ ttlSeconds: HANDOFF_TTL_MS / 1000 },
|
|
153
|
+
]));
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
120
157
|
/** Consuming a round prevents replay from creating sibling retry lineages. */
|
|
121
|
-
export
|
|
122
|
-
return opts
|
|
123
|
-
|
|
124
|
-
|
|
158
|
+
export function claimAuthRetry(opts, baseUrl, id, nonce) {
|
|
159
|
+
return runEdge(claimAuthRetryNative(opts, baseUrl, id, nonce));
|
|
160
|
+
}
|
|
161
|
+
const claimAuthRetryNative = /* @__PURE__ */ registerAuth(claimAuthRetry, function (opts, baseUrl, id, nonce) {
|
|
162
|
+
return authFlow(function* () {
|
|
163
|
+
return yield* upwardStep(authStorage(opts.registry.contextFor(id, baseUrl).storage, "compareAndSet", [
|
|
164
|
+
oauthConnectKeys.retry(nonce),
|
|
165
|
+
null,
|
|
166
|
+
"used",
|
|
167
|
+
{ ttlSeconds: HANDOFF_TTL_MS / 1000 },
|
|
168
|
+
]));
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
export function verifyOAuthHandoff(opts, baseUrl, connectorId, token) {
|
|
172
|
+
return runEdge(verifyOAuthHandoffNative(opts, baseUrl, connectorId, token));
|
|
125
173
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
174
|
+
const verifyOAuthHandoffNative = /* @__PURE__ */ registerAuth(verifyOAuthHandoff, function (opts, baseUrl, connectorId, token) {
|
|
175
|
+
return authFlow(function* () {
|
|
176
|
+
if (!token || token.length > 4096)
|
|
177
|
+
return null;
|
|
178
|
+
try {
|
|
179
|
+
const parts = token.split(".");
|
|
180
|
+
let h;
|
|
181
|
+
if (parts.length === 3 && (parts[0] === "v3" || parts[0] === "v4")) {
|
|
182
|
+
const [version, body, signature] = parts;
|
|
183
|
+
const payload = `${version}.${body}`;
|
|
184
|
+
if (!(yield* upwardStep(authInvoke(opts.config.vault, "verifyOAuthHandoff", [
|
|
185
|
+
payload,
|
|
186
|
+
decodeSegment(signature),
|
|
187
|
+
]))))
|
|
188
|
+
return null;
|
|
189
|
+
const decoded = decodeSegment(body);
|
|
190
|
+
h = decodeHandoff(version === "v4"
|
|
191
|
+
? yield* upwardStep(authInvoke(opts.config.vault, "open", [connectorId, "connecta:connect-link:v4", decoded]))
|
|
192
|
+
: decoded);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// Keep pre-upgrade JSON and sealed v2 links until their original expiry.
|
|
196
|
+
// Authenticate their exact original text; never normalize before verifying.
|
|
197
|
+
const [payload, signature, ...rest] = parts;
|
|
198
|
+
if (!payload || !signature || rest.length)
|
|
199
|
+
return null;
|
|
200
|
+
if (!(yield* upwardStep(authInvoke(opts.config.vault, "verifyOAuthHandoff", [payload, signature]))))
|
|
201
|
+
return null;
|
|
202
|
+
h = JSON.parse(payload.startsWith("v2:")
|
|
203
|
+
? yield* upwardStep(authInvoke(opts.config.vault, "open", [
|
|
204
|
+
connectorId,
|
|
205
|
+
"connecta:connect-link:v2",
|
|
206
|
+
atob(payload.slice(3)),
|
|
207
|
+
]))
|
|
208
|
+
: atob(payload));
|
|
209
|
+
}
|
|
210
|
+
const connector = opts.registry.getConnector(connectorId);
|
|
211
|
+
if (!connector ||
|
|
212
|
+
h.connector !== connectorId ||
|
|
213
|
+
typeof h.principal !== "string" ||
|
|
214
|
+
!h.principal ||
|
|
215
|
+
h.owner !== (connector.authScope === "personal" ? h.principal : "shared") ||
|
|
216
|
+
h.origin !== new URL(baseUrl).origin ||
|
|
217
|
+
!Number.isSafeInteger(h.expiresAt) ||
|
|
218
|
+
h.expiresAt <= Date.now() ||
|
|
219
|
+
h.expiresAt > Date.now() + HANDOFF_TTL_MS ||
|
|
220
|
+
typeof h.nonce !== "string" ||
|
|
221
|
+
!h.nonce ||
|
|
222
|
+
typeof h.force !== "boolean")
|
|
136
223
|
return null;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
// Keep pre-upgrade JSON and sealed v2 links until their original expiry.
|
|
142
|
-
// Authenticate their exact original text; never normalize before verifying.
|
|
143
|
-
const [payload, signature, ...rest] = parts;
|
|
144
|
-
if (!payload || !signature || rest.length)
|
|
224
|
+
if (h.after !== undefined &&
|
|
225
|
+
(typeof h.after !== "string" || !/^[a-f0-9-]{36}$/.test(h.after) || h.after === h.nonce || h.force))
|
|
145
226
|
return null;
|
|
146
|
-
if (
|
|
227
|
+
if (yield* upwardStep(authStorage(opts.registry.contextFor(connectorId, baseUrl).storage, "get", [
|
|
228
|
+
oauthConnectKeys.used(h.nonce),
|
|
229
|
+
])))
|
|
147
230
|
return null;
|
|
148
|
-
h
|
|
149
|
-
? await opts.config.vault.open(connectorId, "connecta:connect-link:v2", atob(payload.slice(3)))
|
|
150
|
-
: atob(payload));
|
|
231
|
+
return h;
|
|
151
232
|
}
|
|
152
|
-
|
|
153
|
-
if (!connector ||
|
|
154
|
-
h.connector !== connectorId ||
|
|
155
|
-
typeof h.principal !== "string" ||
|
|
156
|
-
!h.principal ||
|
|
157
|
-
h.owner !== (connector.authScope === "personal" ? h.principal : "shared") ||
|
|
158
|
-
h.origin !== new URL(baseUrl).origin ||
|
|
159
|
-
!Number.isSafeInteger(h.expiresAt) ||
|
|
160
|
-
h.expiresAt <= Date.now() ||
|
|
161
|
-
h.expiresAt > Date.now() + HANDOFF_TTL_MS ||
|
|
162
|
-
typeof h.nonce !== "string" ||
|
|
163
|
-
!h.nonce ||
|
|
164
|
-
typeof h.force !== "boolean")
|
|
165
|
-
return null;
|
|
166
|
-
if (h.after !== undefined &&
|
|
167
|
-
(typeof h.after !== "string" || !/^[a-f0-9-]{36}$/.test(h.after) || h.after === h.nonce || h.force))
|
|
233
|
+
catch {
|
|
168
234
|
return null;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
177
238
|
/** Claim only after browser identity and permissions pass, before starting OAuth. */
|
|
178
|
-
export
|
|
179
|
-
|
|
180
|
-
return false;
|
|
181
|
-
const storage = opts.registry.contextFor(handoff.connector, handoff.origin).storage;
|
|
182
|
-
if (handoff.after && (await storage.get(oauthConnectKeys.used(handoff.after))) !== "started")
|
|
183
|
-
return false;
|
|
184
|
-
const expiry = { ttlSeconds: Math.max(1, Math.ceil((handoff.expiresAt - Date.now()) / 1000)) };
|
|
185
|
-
return storage.compareAndSet(oauthConnectKeys.used(handoff.nonce), null, "used", expiry);
|
|
239
|
+
export function consumeOAuthConnectLink(opts, handoff) {
|
|
240
|
+
return runEdge(consumeOAuthConnectLinkNative(opts, handoff));
|
|
186
241
|
}
|
|
242
|
+
const consumeOAuthConnectLinkNative = /* @__PURE__ */ registerAuth(consumeOAuthConnectLink, function (opts, handoff) {
|
|
243
|
+
return authFlow(function* () {
|
|
244
|
+
if (handoff.expiresAt <= Date.now())
|
|
245
|
+
return false;
|
|
246
|
+
const storage = opts.registry.contextFor(handoff.connector, handoff.origin).storage;
|
|
247
|
+
if (handoff.after &&
|
|
248
|
+
(yield* upwardStep(authStorage(storage, "get", [oauthConnectKeys.used(handoff.after)]))) !==
|
|
249
|
+
"started")
|
|
250
|
+
return false;
|
|
251
|
+
const expiry = { ttlSeconds: Math.max(1, Math.ceil((handoff.expiresAt - Date.now()) / 1000)) };
|
|
252
|
+
return yield* upwardStep(authStorage(storage, "compareAndSet", [oauthConnectKeys.used(handoff.nonce), null, "used", expiry]));
|
|
253
|
+
});
|
|
254
|
+
});
|
|
187
255
|
/** Terminal marker only after the start and every tracked reset have settled. */
|
|
188
|
-
export
|
|
189
|
-
|
|
190
|
-
return false;
|
|
191
|
-
const storage = opts.registry.contextFor(handoff.connector, handoff.origin).storage;
|
|
192
|
-
const expiry = { ttlSeconds: Math.max(1, Math.ceil((handoff.expiresAt - Date.now()) / 1000)) };
|
|
193
|
-
return storage.compareAndSet(oauthConnectKeys.used(handoff.nonce), "used", outcome, expiry);
|
|
256
|
+
export function finishOAuthConnectStart(opts, handoff, outcome) {
|
|
257
|
+
return runEdge(finishOAuthConnectStartNative(opts, handoff, outcome));
|
|
194
258
|
}
|
|
259
|
+
const finishOAuthConnectStartNative = /* @__PURE__ */ registerAuth(finishOAuthConnectStart, function (opts, handoff, outcome) {
|
|
260
|
+
return authFlow(function* () {
|
|
261
|
+
if (handoff.expiresAt <= Date.now())
|
|
262
|
+
return false;
|
|
263
|
+
const storage = opts.registry.contextFor(handoff.connector, handoff.origin).storage;
|
|
264
|
+
const expiry = { ttlSeconds: Math.max(1, Math.ceil((handoff.expiresAt - Date.now()) / 1000)) };
|
|
265
|
+
return yield* upwardStep(authStorage(storage, "compareAndSet", [oauthConnectKeys.used(handoff.nonce), "used", outcome, expiry]));
|
|
266
|
+
});
|
|
267
|
+
});
|
package/dist/oauth-partition.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
import { serviceFor, setViewService } from "./runtime/call.js";
|
|
1
3
|
// Registry-owned identity for downstream credential coordination. Storage
|
|
2
4
|
// wrappers are rebuilt per context, and epochs can be identical across owners.
|
|
3
5
|
// Keep this beside the context rather than widening the public connector API.
|
|
4
|
-
|
|
6
|
+
class OAuthPartition extends Context.Service()("connecta/operation/OAuthPartition") {
|
|
7
|
+
}
|
|
5
8
|
const activeWork = new WeakMap();
|
|
6
9
|
export function retainOAuthPartition(partition) {
|
|
7
10
|
if (!partition)
|
|
@@ -19,31 +22,14 @@ export function retainOAuthPartition(partition) {
|
|
|
19
22
|
activeWork.set(partition, remaining);
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Wrap one connector operation so its owner partition stays pinned from the
|
|
24
|
-
* first asynchronous storage read to the end, not only once a refresh flight
|
|
25
|
-
* exists: an idle personal registry can otherwise be evicted mid-flow. Status
|
|
26
|
-
* and auth operations take no call admission, so nothing else holds it.
|
|
27
|
-
*/
|
|
28
|
-
export function retainingOAuthPartition(operation, contextIndex) {
|
|
29
|
-
return async (...args) => {
|
|
30
|
-
const release = retainOAuthPartition(oauthPartitionFor(args[contextIndex]));
|
|
31
|
-
try {
|
|
32
|
-
return await operation(...args);
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
release();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
25
|
export function oauthPartitionIdle(partition) {
|
|
40
26
|
return !activeWork.has(partition);
|
|
41
27
|
}
|
|
42
28
|
export function attachOAuthPartition(ctx, partition) {
|
|
43
29
|
if (partition)
|
|
44
|
-
|
|
30
|
+
setViewService(ctx, OAuthPartition, partition);
|
|
45
31
|
return ctx;
|
|
46
32
|
}
|
|
47
33
|
export function oauthPartitionFor(ctx) {
|
|
48
|
-
return
|
|
34
|
+
return serviceFor(ctx, OAuthPartition);
|
|
49
35
|
}
|
package/dist/oauth-sealing.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
import { serviceFor, setViewService } from "./runtime/call.js";
|
|
1
3
|
/**
|
|
2
4
|
* Contexts the registry built with a sealing vault. Internal on purpose: the
|
|
3
5
|
* public ConnectorContext type does not change, and a connector cannot hand
|
|
4
6
|
* itself a sealer another connector's context carries.
|
|
5
7
|
*/
|
|
6
|
-
|
|
8
|
+
class OAuthSealer extends Context.Service()("connecta/operation/OAuthSealer") {
|
|
9
|
+
}
|
|
7
10
|
/** A sealer over the vault, or undefined when the vault cannot seal. */
|
|
8
11
|
export function vaultOAuthSealer(vault, connectorId, owner, logger) {
|
|
9
12
|
const { seal, open } = vault;
|
|
@@ -17,15 +20,11 @@ export function vaultOAuthSealer(vault, connectorId, owner, logger) {
|
|
|
17
20
|
}
|
|
18
21
|
export function attachOAuthSealer(ctx, sealer) {
|
|
19
22
|
if (sealer)
|
|
20
|
-
|
|
23
|
+
setViewService(ctx, OAuthSealer, sealer);
|
|
21
24
|
return ctx;
|
|
22
25
|
}
|
|
23
26
|
export function oauthSealerFor(ctx) {
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
/** Carry the sealer onto a context derived by spreading another. */
|
|
27
|
-
export function inheritOAuthSealer(from, to) {
|
|
28
|
-
return attachOAuthSealer(to, sealers.get(from));
|
|
27
|
+
return serviceFor(ctx, OAuthSealer);
|
|
29
28
|
}
|
|
30
29
|
/** A separate HMAC key for browser handoffs, derived from the credential key. */
|
|
31
30
|
export async function deriveOAuthHandoffKey(raw) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Private, request-free discovery facts. No provider, schema, handler or I/O dependency.
|
|
2
|
+
import { lexicalSearchContent, lexicalQueryTerms } from "./catalog.js";
|
|
3
|
+
const tools = new WeakMap();
|
|
4
|
+
const connectors = new WeakMap();
|
|
5
|
+
export const OPERATION_VOCABULARY_BOUNDS = {
|
|
6
|
+
operations: 8192,
|
|
7
|
+
rowChars: 1024,
|
|
8
|
+
chars: 2_097_152,
|
|
9
|
+
terms: 8,
|
|
10
|
+
queryChars: 200,
|
|
11
|
+
cache: 128,
|
|
12
|
+
};
|
|
13
|
+
const token = (word) => word.endsWith("ies")
|
|
14
|
+
? `${word.slice(0, -3)}y`
|
|
15
|
+
: word.endsWith("s") && !word.endsWith("ss")
|
|
16
|
+
? word.slice(0, -1)
|
|
17
|
+
: word;
|
|
18
|
+
const aliases = {
|
|
19
|
+
retrieve: "get",
|
|
20
|
+
read: "get",
|
|
21
|
+
fetch: "get",
|
|
22
|
+
find: "list",
|
|
23
|
+
search: "list",
|
|
24
|
+
add: "create",
|
|
25
|
+
new: "create",
|
|
26
|
+
edit: "update",
|
|
27
|
+
change: "update",
|
|
28
|
+
modify: "update",
|
|
29
|
+
remove: "delete",
|
|
30
|
+
};
|
|
31
|
+
const terms = (text) => [
|
|
32
|
+
...new Set(lexicalQueryTerms(lexicalSearchContent(text)).map((word) => aliases[word] ?? token(word))),
|
|
33
|
+
];
|
|
34
|
+
/** Register only construction-authored compact summaries; never whole request/response schemas. */
|
|
35
|
+
export function registerOperationVocabulary(tool, rows) {
|
|
36
|
+
let postings;
|
|
37
|
+
const routes = [];
|
|
38
|
+
const cache = new Map();
|
|
39
|
+
tools.set(tool, (query, visible, safety, readOnly) => {
|
|
40
|
+
if (query.length > OPERATION_VOCABULARY_BOUNDS.queryChars)
|
|
41
|
+
return false;
|
|
42
|
+
const wanted = terms(query);
|
|
43
|
+
if (!wanted.length || wanted.length > OPERATION_VOCABULARY_BOUNDS.terms)
|
|
44
|
+
return false;
|
|
45
|
+
if (!postings) {
|
|
46
|
+
postings = new Map();
|
|
47
|
+
let chars = 0;
|
|
48
|
+
for (const row of rows()) {
|
|
49
|
+
const text = row.text.slice(0, OPERATION_VOCABULARY_BOUNDS.rowChars);
|
|
50
|
+
if (routes.length >= OPERATION_VOCABULARY_BOUNDS.operations ||
|
|
51
|
+
chars + text.length > OPERATION_VOCABULARY_BOUNDS.chars)
|
|
52
|
+
break;
|
|
53
|
+
chars += text.length;
|
|
54
|
+
const id = routes.length;
|
|
55
|
+
routes.push({ route: row.route, write: row.write });
|
|
56
|
+
for (const word of terms(text)) {
|
|
57
|
+
const hits = postings.get(word);
|
|
58
|
+
if (hits)
|
|
59
|
+
hits.push(id);
|
|
60
|
+
else
|
|
61
|
+
postings.set(word, [id]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const key = wanted.join(" ");
|
|
66
|
+
let found = cache.get(key);
|
|
67
|
+
if (!found) {
|
|
68
|
+
const lists = wanted.map((word) => postings.get(word) ?? []).sort((a, b) => a.length - b.length);
|
|
69
|
+
const rest = lists.slice(1).map((list) => new Set(list));
|
|
70
|
+
found = lists[0].filter((id) => rest.every((set) => set.has(id)));
|
|
71
|
+
if (cache.size >= OPERATION_VOCABULARY_BOUNDS.cache)
|
|
72
|
+
cache.delete(cache.keys().next().value);
|
|
73
|
+
cache.set(key, found);
|
|
74
|
+
}
|
|
75
|
+
return found.some((id) => {
|
|
76
|
+
const row = routes[id];
|
|
77
|
+
if (row.write && readOnly)
|
|
78
|
+
return false;
|
|
79
|
+
if ((safety === "readOnly" && row.write) || (safety === "approvalRequired" && !row.write))
|
|
80
|
+
return false;
|
|
81
|
+
return row.route !== undefined && visible.has(row.route);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** Move private facts across the public ApiTool -> Connector construction boundary. */
|
|
86
|
+
export function registerConnectorVocabulary(connector, declarations) {
|
|
87
|
+
const entries = declarations.flatMap((tool) => {
|
|
88
|
+
const vocabulary = tools.get(tool);
|
|
89
|
+
return vocabulary ? [[tool.name, vocabulary]] : [];
|
|
90
|
+
});
|
|
91
|
+
if (entries.length)
|
|
92
|
+
connectors.set(connector, new Map(entries));
|
|
93
|
+
}
|
|
94
|
+
export function operationVocabularyMatches(connector, catalog, query, safety, readOnly) {
|
|
95
|
+
const registered = connectors.get(connector);
|
|
96
|
+
if (!registered)
|
|
97
|
+
return [];
|
|
98
|
+
const visible = new Set(catalog.map((tool) => tool.name));
|
|
99
|
+
return catalog.filter((tool) => tool.classification === "read" &&
|
|
100
|
+
registered.get(tool.name)?.(query, visible, safety, readOnly || connector.readOnly === true));
|
|
101
|
+
}
|
|
@@ -8,7 +8,7 @@ type FailureStep = "MCP handshake" | "tools/list" | "tools/call" | "resources/re
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const OAUTH_ERROR_CODES: ReadonlySet<string>;
|
|
10
10
|
/** Inbound denial facts, never identity text or provider diagnostics. */
|
|
11
|
-
export type AuthDenialReason = "authentication_failed" | "authorization_header_invalid" | "token_type_mismatch" | "oauth_verification_failed" | "oauth_client_not_allowed" | "oauth_binding_mismatch" | "session_origin_mismatch" | "user_missing" | "email_lookup_failed" | "verified_email_invalid" | "email_domain_denied" | "gate_denied" | "gate_failed" | "not_maintainer" | "pool_not_declared" | "not_pool_member";
|
|
11
|
+
export type AuthDenialReason = "authentication_abandoned_saturated" | "authentication_busy" | "authentication_failed" | "authentication_unreachable" | "authorization_header_invalid" | "token_type_mismatch" | "oauth_verification_failed" | "oauth_client_not_allowed" | "oauth_binding_mismatch" | "session_origin_mismatch" | "token_identity_mismatch" | "organization_membership_denied" | "organization_lookup_failed" | "organization_lookup_busy" | "user_missing" | "email_lookup_failed" | "verified_email_invalid" | "email_domain_denied" | "gate_denied" | "gate_failed" | "not_maintainer" | "pool_not_declared" | "not_pool_member";
|
|
12
12
|
/**
|
|
13
13
|
* Name `ctor`'s instances `label` in records and fixed text. Call once, at
|
|
14
14
|
* module scope, beside the class. An error whose prototype chain reaches no
|
|
@@ -104,7 +104,7 @@ export declare function failureRecord(subject: FailureSubject, failure?: unknown
|
|
|
104
104
|
* The events an operator log reports failures as. Closed: a new one is a
|
|
105
105
|
* reviewed line here, never a string assembled at the call site.
|
|
106
106
|
*/
|
|
107
|
-
export type FailureEvent = "negotiation cache read failed" | "negotiation cache write failed" | "pool tool grant is unreachable" | "call failed" | "input schema unusable; arguments are not validated" | "catalog read failed" | "catalog persistence failed" | "catalog invalidation failed" | "catalog refresh observation failed" | "catalog refresh failed; serving stale catalog" | "deferred catalog refresh failed" | "deferred catalog refresh could not attach to the runtime" | "catalog drift check failed" | "schema digest check failed; serving digested reviews as writes" | "credential shape read failed" | "credential test failed" | "credential test threw" | "session termination refused or failed; the downstream session may remain until its provider timeout" | "activity record failed" | "catalog drift record failed" | "activity read failed" | "OAuth start failed" | "OAuth disconnect failed" | "OAuth callback verifyState threw; no authorization code was exchanged" | "OAuth callback handoff could not be consumed; no authorization code was exchanged" | "MCP default endpoint request denied" | "MCP pool request denied" | "MCP agent endpoint request denied" | "OAuth error callback consent could not be consumed; no authorization code was exchanged" | "MCP handler error" | "operator status" | "operator agent slots read failed" | "result paging unavailable" | "request failed" | "Clerk request denied" | "Clerk authentication failed";
|
|
107
|
+
export type FailureEvent = "Authentication admission refused" | "Authentication released after its pending work became unreachable" | "negotiation cache read failed" | "negotiation cache write failed" | "pool tool grant is unreachable" | "call failed" | "input schema unusable; arguments are not validated" | "catalog read failed" | "catalog persistence failed" | "catalog invalidation failed" | "catalog refresh observation failed" | "catalog refresh failed; serving stale catalog" | "deferred catalog refresh failed" | "deferred catalog refresh could not attach to the runtime" | "catalog drift check failed" | "schema digest check failed; serving digested reviews as writes" | "credential shape read failed" | "credential test failed" | "credential test threw" | "session termination refused or failed; the downstream session may remain until its provider timeout" | "activity record failed" | "catalog drift record failed" | "activity read failed" | "OAuth start failed" | "OAuth disconnect failed" | "OAuth callback verifyState threw; no authorization code was exchanged" | "OAuth callback handoff could not be consumed; no authorization code was exchanged" | "MCP default endpoint request denied" | "MCP pool request denied" | "MCP agent endpoint request denied" | "OAuth error callback consent could not be consumed; no authorization code was exchanged" | "MCP handler error" | "operator status" | "operator agent slots read failed" | "result paging unavailable" | "native catalog unavailable" | "native name collision" | "native tools/list refused a pool over the 1,000-tool native cap" | "request failed" | "Clerk request denied" | "Clerk authentication failed";
|
|
108
108
|
/**
|
|
109
109
|
* Log one failure as `[connecta] <event>` and its record. A record
|
|
110
110
|
* `failureRecord` did not build is replaced, whatever its shape, by a fixed
|
package/dist/operator-record.js
CHANGED
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
// documentation/architecture.md, "Errors and records"). Core's sent-secrets
|
|
48
48
|
// boundary removes credentials used by the upstream request from those answers, including
|
|
49
49
|
// allowed diagnostics, tool results and nested error causes/data (INV-5).
|
|
50
|
+
import { carryProviderFailure } from "./provider-call-facts.js";
|
|
51
|
+
import { followRedactedCopies, keepErrorName } from "./redaction-identity.js";
|
|
50
52
|
import { classificationCode, ConnectorCallError, NETWORK_ERROR_CODES, WithheldTextError, } from "./errors.js";
|
|
51
53
|
const STEPS = new Set([
|
|
52
54
|
"MCP handshake",
|
|
@@ -88,6 +90,7 @@ export const OAUTH_ERROR_CODES = new Set([
|
|
|
88
90
|
"unapproved_software_statement",
|
|
89
91
|
]);
|
|
90
92
|
const SOURCES = new Set([
|
|
93
|
+
"native",
|
|
91
94
|
"call_tool",
|
|
92
95
|
"call_destructive_tool",
|
|
93
96
|
"batch_call",
|
|
@@ -99,13 +102,20 @@ const STATUS_STATES = new Set(["ok", "auth_required", "credential_required", "er
|
|
|
99
102
|
/** A status state, or `failed` for a status that could not be read at all. */
|
|
100
103
|
const STATES = new Set([...STATUS_STATES, "failed"]);
|
|
101
104
|
const AUTH_DENIAL_REASONS = new Set([
|
|
105
|
+
"authentication_abandoned_saturated",
|
|
106
|
+
"authentication_busy",
|
|
102
107
|
"authentication_failed",
|
|
108
|
+
"authentication_unreachable",
|
|
103
109
|
"authorization_header_invalid",
|
|
104
110
|
"token_type_mismatch",
|
|
105
111
|
"oauth_verification_failed",
|
|
106
112
|
"oauth_client_not_allowed",
|
|
107
113
|
"oauth_binding_mismatch",
|
|
108
114
|
"session_origin_mismatch",
|
|
115
|
+
"token_identity_mismatch",
|
|
116
|
+
"organization_membership_denied",
|
|
117
|
+
"organization_lookup_failed",
|
|
118
|
+
"organization_lookup_busy",
|
|
109
119
|
"user_missing",
|
|
110
120
|
"email_lookup_failed",
|
|
111
121
|
"verified_email_invalid",
|
|
@@ -146,6 +156,8 @@ export function labelErrorClass(ctor, label) {
|
|
|
146
156
|
throw new TypeError(`Invalid error class label "${label}"`);
|
|
147
157
|
labels.set(ctor.prototype, label);
|
|
148
158
|
labelNames.add(label);
|
|
159
|
+
// A label is a literal and the class's own name, so redaction keeps it as its instances' name.
|
|
160
|
+
keepErrorName(ctor, label);
|
|
149
161
|
}
|
|
150
162
|
for (const [ctor, label] of [
|
|
151
163
|
[Error, "Error"],
|
|
@@ -224,7 +236,7 @@ function defined(key, value) {
|
|
|
224
236
|
* Facts attached to failures by identity, never as properties: nothing about
|
|
225
237
|
* the failure's own shape changes, and nothing a caller can see grows.
|
|
226
238
|
*/
|
|
227
|
-
const attached = new WeakMap();
|
|
239
|
+
const attached = followRedactedCopies(new WeakMap());
|
|
228
240
|
/** Note what connecta knows about `failure` for any record made of it later. */
|
|
229
241
|
export function attachFailureFacts(failure, facts) {
|
|
230
242
|
if (failure !== null && typeof failure === "object") {
|
|
@@ -234,6 +246,7 @@ export function attachFailureFacts(failure, facts) {
|
|
|
234
246
|
}
|
|
235
247
|
/** The facts and class label of `from`, kept for `to` (its classified form, say). */
|
|
236
248
|
export function carryFailureFacts(from, to) {
|
|
249
|
+
carryProviderFailure(from, to);
|
|
237
250
|
if (to === null || typeof to !== "object")
|
|
238
251
|
return to;
|
|
239
252
|
const facts = from !== null && typeof from === "object" ? attached.get(from) : undefined;
|
|
@@ -243,7 +256,7 @@ export function carryFailureFacts(from, to) {
|
|
|
243
256
|
attached.set(to, { ...carried, ...attached.get(to) });
|
|
244
257
|
return to;
|
|
245
258
|
}
|
|
246
|
-
const built = new WeakSet();
|
|
259
|
+
const built = followRedactedCopies(new WeakSet());
|
|
247
260
|
function count(value, max) {
|
|
248
261
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= max
|
|
249
262
|
? Math.round(value)
|
|
@@ -282,7 +295,7 @@ function checkedSubject(subject) {
|
|
|
282
295
|
* identity. A plain object is read as one only when registered here: a thrown
|
|
283
296
|
* object shaped like a classification is just a thrown object.
|
|
284
297
|
*/
|
|
285
|
-
const classifications = new WeakSet();
|
|
298
|
+
const classifications = followRedactedCopies(new WeakSet());
|
|
286
299
|
/** Vouch for `details` as a classification connecta itself computed. */
|
|
287
300
|
export function classifiedFailure(details) {
|
|
288
301
|
classifications.add(details);
|
|
@@ -363,7 +376,7 @@ export function describeFailure(connectorId, failure) {
|
|
|
363
376
|
const kind = [record.errorClass, record.code, record.errno].filter(Boolean).join(", ");
|
|
364
377
|
return `Connector "${record.connector}"${where} failed${status}${oauth}${kind ? ` (${kind})` : ""}.`;
|
|
365
378
|
}
|
|
366
|
-
const ownStatuses = new WeakMap();
|
|
379
|
+
const ownStatuses = followRedactedCopies(new WeakMap());
|
|
367
380
|
function snapshot(status, failure) {
|
|
368
381
|
return {
|
|
369
382
|
state: member(status.state, STATUS_STATES) ?? "error",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OperatorUiContractV2 } from "./contract.js";
|
|
1
|
+
import type { OperatorAgent, OperatorUiContractV2 } from "./contract.js";
|
|
2
2
|
import { type Notice, type RequestFailureFacts, type Tone } from "./view.js";
|
|
3
3
|
/**
|
|
4
4
|
* The Tokens page's rows, computed from the slot list and contract v2 alone.
|
|
@@ -30,7 +30,8 @@ export interface AgentRow {
|
|
|
30
30
|
name: string;
|
|
31
31
|
/** Pools it belongs to, from `agents[].pools`; empty when the contract does not list it. */
|
|
32
32
|
pools: string[];
|
|
33
|
-
|
|
33
|
+
credentialTypes: OperatorAgent["credentialTypes"];
|
|
34
|
+
slot: AgentSlot | null;
|
|
34
35
|
/**
|
|
35
36
|
* Whether `slot` comes from the live slot list. Only a live slot can be
|
|
36
37
|
* acted on: one known only from the contract may be stale, so its controls
|
|
@@ -58,12 +59,12 @@ export declare function resolveSlot(contract: OperatorUiContractV2 | null, slots
|
|
|
58
59
|
* the contract lists. The contract adds pools and last seen.
|
|
59
60
|
*/
|
|
60
61
|
export declare function agentRows(contract: OperatorUiContractV2 | null, slots: AgentSlot[], unresolved?: readonly string[]): AgentRow[];
|
|
61
|
-
export declare function slotBadge(slot: Pick<AgentSlot, "state">): {
|
|
62
|
+
export declare function slotBadge(slot: Pick<AgentSlot, "state"> | null): {
|
|
62
63
|
label: string;
|
|
63
64
|
tone: Tone;
|
|
64
65
|
};
|
|
65
66
|
/** When the slot last changed, issued or revoked (its badge says which); a dash when it never has. */
|
|
66
|
-
export declare function slotSince(slot: AgentSlot, format: (value: string) => string): string;
|
|
67
|
+
export declare function slotSince(slot: AgentSlot | null, format: (value: string) => string): string;
|
|
67
68
|
export declare function lastSeenLabel(lastSeenAt: string | null, format: (value: string) => string): string;
|
|
68
69
|
/**
|
|
69
70
|
* Why a token operation did not land, in fixed sentences keyed by status.
|