@assemblyline-agents/runtime 5.0.9 → 6.0.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/README.md +18 -5
- package/dist/a2a-client.d.ts +1 -3
- package/dist/a2a-client.d.ts.map +1 -1
- package/dist/a2a-client.js +1 -4
- package/dist/a2a-client.js.map +1 -1
- package/dist/agent-event-handlers.d.ts +2 -0
- package/dist/agent-event-handlers.d.ts.map +1 -1
- package/dist/agent-event-handlers.js +1 -1
- package/dist/agent-event-handlers.js.map +1 -1
- package/dist/agent-hook-context.d.ts.map +1 -1
- package/dist/agent-hook-context.js +3 -1
- package/dist/agent-hook-context.js.map +1 -1
- package/dist/agent-hook-controller-factory.d.ts +5 -1
- package/dist/agent-hook-controller-factory.d.ts.map +1 -1
- package/dist/agent-hook-controller-factory.js +11 -9
- package/dist/agent-hook-controller-factory.js.map +1 -1
- package/dist/agent-hook-evaluation.d.ts +0 -2
- package/dist/agent-hook-evaluation.d.ts.map +1 -1
- package/dist/agent-hook-evaluation.js +10 -10
- package/dist/agent-hook-evaluation.js.map +1 -1
- package/dist/agent-hook-run-lifecycle.d.ts +1 -2
- package/dist/agent-hook-run-lifecycle.d.ts.map +1 -1
- package/dist/agent-hook-run-lifecycle.js +1 -2
- package/dist/agent-hook-run-lifecycle.js.map +1 -1
- package/dist/agent-surface.d.ts +5 -2
- package/dist/agent-surface.d.ts.map +1 -1
- package/dist/agent-surface.js +11 -3
- package/dist/agent-surface.js.map +1 -1
- package/dist/artifact-selection.js +1 -1
- package/dist/artifact-selection.js.map +1 -1
- package/dist/assembly-line-runtime.d.ts +9 -0
- package/dist/assembly-line-runtime.d.ts.map +1 -0
- package/dist/assembly-line-runtime.js +9 -0
- package/dist/assembly-line-runtime.js.map +1 -0
- package/dist/attachment-download.d.ts +17 -0
- package/dist/attachment-download.d.ts.map +1 -0
- package/dist/attachment-download.js +51 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachments.d.ts +2 -14
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +8 -80
- package/dist/attachments.js.map +1 -1
- package/dist/background-review-types.d.ts +2 -0
- package/dist/background-review-types.d.ts.map +1 -1
- package/dist/background-review.d.ts.map +1 -1
- package/dist/background-review.js +13 -0
- package/dist/background-review.js.map +1 -1
- package/dist/background-subagents.d.ts +1 -1
- package/dist/background-subagents.d.ts.map +1 -1
- package/dist/blob-storage.d.ts +12 -0
- package/dist/blob-storage.d.ts.map +1 -0
- package/dist/blob-storage.js +86 -0
- package/dist/blob-storage.js.map +1 -0
- package/dist/builtin-tool-executors.d.ts.map +1 -1
- package/dist/builtin-tool-executors.js +38 -29
- package/dist/builtin-tool-executors.js.map +1 -1
- package/dist/builtin-tools.d.ts +1 -2
- package/dist/builtin-tools.d.ts.map +1 -1
- package/dist/builtin-tools.js +20 -18
- package/dist/builtin-tools.js.map +1 -1
- package/dist/capability-inspection.d.ts +4 -0
- package/dist/capability-inspection.d.ts.map +1 -1
- package/dist/capability-inspection.js +10 -11
- package/dist/capability-inspection.js.map +1 -1
- package/dist/channel-credential-environment.d.ts +1 -1
- package/dist/channel-credential-environment.d.ts.map +1 -1
- package/dist/channel-credential-environment.js +1 -6
- package/dist/channel-credential-environment.js.map +1 -1
- package/dist/channel-ingress.d.ts +1 -2
- package/dist/channel-ingress.d.ts.map +1 -1
- package/dist/channel-ingress.js +2 -2
- package/dist/channel-ingress.js.map +1 -1
- package/dist/channel-operation.d.ts +0 -1
- package/dist/channel-operation.d.ts.map +1 -1
- package/dist/channel-operation.js +1 -2
- package/dist/channel-operation.js.map +1 -1
- package/dist/channel.d.ts +32 -3
- package/dist/channel.d.ts.map +1 -1
- package/dist/connection-assets.d.ts.map +1 -1
- package/dist/connection-assets.js.map +1 -1
- package/dist/connection-authorization-service.d.ts +52 -0
- package/dist/connection-authorization-service.d.ts.map +1 -0
- package/dist/connection-authorization-service.js +417 -0
- package/dist/connection-authorization-service.js.map +1 -0
- package/dist/connection-credential-materialization.d.ts +23 -0
- package/dist/connection-credential-materialization.d.ts.map +1 -0
- package/dist/connection-credential-materialization.js +223 -0
- package/dist/connection-credential-materialization.js.map +1 -0
- package/dist/connection-definition-loader.d.ts +6 -0
- package/dist/connection-definition-loader.d.ts.map +1 -0
- package/dist/connection-definition-loader.js +102 -0
- package/dist/connection-definition-loader.js.map +1 -0
- package/dist/connection-discovery.d.ts +11 -0
- package/dist/connection-discovery.d.ts.map +1 -0
- package/dist/connection-discovery.js +105 -0
- package/dist/connection-discovery.js.map +1 -0
- package/dist/connection-events.d.ts.map +1 -1
- package/dist/connection-events.js.map +1 -1
- package/dist/connection-oauth-client.d.ts +3 -0
- package/dist/connection-oauth-client.d.ts.map +1 -0
- package/dist/connection-oauth-client.js +64 -0
- package/dist/connection-oauth-client.js.map +1 -0
- package/dist/connection-runtime-types.d.ts +78 -0
- package/dist/connection-runtime-types.d.ts.map +1 -0
- package/dist/connection-runtime-types.js +31 -0
- package/dist/connection-runtime-types.js.map +1 -0
- package/dist/connection-search-scope.d.ts +13 -0
- package/dist/connection-search-scope.d.ts.map +1 -0
- package/dist/connection-search-scope.js +24 -0
- package/dist/connection-search-scope.js.map +1 -0
- package/dist/connections.d.ts +18 -167
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +71 -1017
- package/dist/connections.js.map +1 -1
- package/dist/context-policy-util.d.ts +0 -4
- package/dist/context-policy-util.d.ts.map +1 -1
- package/dist/context-policy-util.js +0 -10
- package/dist/context-policy-util.js.map +1 -1
- package/dist/conversation-context.d.ts +1 -1
- package/dist/conversation-context.d.ts.map +1 -1
- package/dist/conversation-context.js +12 -9
- package/dist/conversation-context.js.map +1 -1
- package/dist/default-outbound-route.d.ts +1 -0
- package/dist/default-outbound-route.d.ts.map +1 -1
- package/dist/default-outbound-route.js +1 -1
- package/dist/default-outbound-route.js.map +1 -1
- package/dist/delivery-queue.d.ts +1 -1
- package/dist/delivery-send.d.ts.map +1 -1
- package/dist/delivery-send.js +3 -2
- package/dist/delivery-send.js.map +1 -1
- package/dist/dynamic-schedule-execution.d.ts +4 -0
- package/dist/dynamic-schedule-execution.d.ts.map +1 -0
- package/dist/dynamic-schedule-execution.js +136 -0
- package/dist/dynamic-schedule-execution.js.map +1 -0
- package/dist/dynamic-schedule-runtime.d.ts +11 -20
- package/dist/dynamic-schedule-runtime.d.ts.map +1 -1
- package/dist/dynamic-schedule-runtime.js +27 -39
- package/dist/dynamic-schedule-runtime.js.map +1 -1
- package/dist/evolution-tracker.d.ts +52 -0
- package/dist/evolution-tracker.d.ts.map +1 -0
- package/dist/evolution-tracker.js +179 -0
- package/dist/evolution-tracker.js.map +1 -0
- package/dist/harness-capabilities.d.ts +7 -0
- package/dist/harness-capabilities.d.ts.map +1 -0
- package/dist/harness-capabilities.js +56 -0
- package/dist/harness-capabilities.js.map +1 -0
- package/dist/harness-continuation-persistence.d.ts +6 -0
- package/dist/harness-continuation-persistence.d.ts.map +1 -0
- package/dist/harness-continuation-persistence.js +11 -0
- package/dist/harness-continuation-persistence.js.map +1 -0
- package/dist/harness-execution-context.d.ts +10 -0
- package/dist/harness-execution-context.d.ts.map +1 -0
- package/dist/harness-execution-context.js +193 -0
- package/dist/harness-execution-context.js.map +1 -0
- package/dist/harness-loop-types.d.ts +67 -0
- package/dist/harness-loop-types.d.ts.map +1 -0
- package/dist/harness-loop-types.js +2 -0
- package/dist/harness-loop-types.js.map +1 -0
- package/dist/harness-loop.d.ts +13 -94
- package/dist/harness-loop.d.ts.map +1 -1
- package/dist/harness-loop.js +19 -551
- package/dist/harness-loop.js.map +1 -1
- package/dist/harness-tool-execution.d.ts +30 -0
- package/dist/harness-tool-execution.d.ts.map +1 -0
- package/dist/harness-tool-execution.js +397 -0
- package/dist/harness-tool-execution.js.map +1 -0
- package/dist/harness-transcript-resume.d.ts +18 -0
- package/dist/harness-transcript-resume.d.ts.map +1 -0
- package/dist/harness-transcript-resume.js +102 -0
- package/dist/harness-transcript-resume.js.map +1 -0
- package/dist/harness-turn-execution.d.ts +11 -0
- package/dist/harness-turn-execution.d.ts.map +1 -0
- package/dist/harness-turn-execution.js +157 -0
- package/dist/harness-turn-execution.js.map +1 -0
- package/dist/index.d.ts +10 -499
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2139
- package/dist/index.js.map +1 -1
- package/dist/local-sandbox.d.ts +2 -1
- package/dist/local-sandbox.d.ts.map +1 -1
- package/dist/mcp-client.d.ts +11 -1
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +39 -7
- package/dist/mcp-client.js.map +1 -1
- package/dist/media-processor.d.ts +3 -1
- package/dist/media-processor.d.ts.map +1 -1
- package/dist/media-processor.js +15 -1
- package/dist/media-processor.js.map +1 -1
- package/dist/memory-storage.d.ts +51 -0
- package/dist/memory-storage.d.ts.map +1 -0
- package/dist/memory-storage.js +324 -0
- package/dist/memory-storage.js.map +1 -0
- package/dist/model-credential-stores.d.ts.map +1 -1
- package/dist/model-credential-stores.js +6 -12
- package/dist/model-credential-stores.js.map +1 -1
- package/dist/model-provider-environment.d.ts +4 -0
- package/dist/model-provider-environment.d.ts.map +1 -0
- package/dist/model-provider-environment.js +36 -0
- package/dist/model-provider-environment.js.map +1 -0
- package/dist/plugin-embeddings.d.ts +4 -0
- package/dist/plugin-embeddings.d.ts.map +1 -0
- package/dist/plugin-embeddings.js +75 -0
- package/dist/plugin-embeddings.js.map +1 -0
- package/dist/public-api.d.ts +8 -5
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -4
- package/dist/public-api.js.map +1 -1
- package/dist/relay-mcp-client.js +1 -1
- package/dist/relay-mcp-client.js.map +1 -1
- package/dist/replay-resume-authorization.d.ts +8 -0
- package/dist/replay-resume-authorization.d.ts.map +1 -0
- package/dist/replay-resume-authorization.js +181 -0
- package/dist/replay-resume-authorization.js.map +1 -0
- package/dist/replay-resume-connection.d.ts +6 -0
- package/dist/replay-resume-connection.d.ts.map +1 -0
- package/dist/replay-resume-connection.js +75 -0
- package/dist/replay-resume-connection.js.map +1 -0
- package/dist/replay-resume-controls.d.ts +6 -0
- package/dist/replay-resume-controls.d.ts.map +1 -0
- package/dist/replay-resume-controls.js +157 -0
- package/dist/replay-resume-controls.js.map +1 -0
- package/dist/replay-resume-harness.d.ts +14 -0
- package/dist/replay-resume-harness.d.ts.map +1 -0
- package/dist/replay-resume-harness.js +158 -0
- package/dist/replay-resume-harness.js.map +1 -0
- package/dist/replay-resume-recovery.d.ts +10 -0
- package/dist/replay-resume-recovery.d.ts.map +1 -0
- package/dist/replay-resume-recovery.js +62 -0
- package/dist/replay-resume-recovery.js.map +1 -0
- package/dist/replay-resume-shared.d.ts +16 -0
- package/dist/replay-resume-shared.d.ts.map +1 -0
- package/dist/replay-resume-shared.js +55 -0
- package/dist/replay-resume-shared.js.map +1 -0
- package/dist/replay-resume-types.d.ts +85 -0
- package/dist/replay-resume-types.d.ts.map +1 -0
- package/dist/replay-resume-types.js +2 -0
- package/dist/replay-resume-types.js.map +1 -0
- package/dist/replay-resume.d.ts +7 -119
- package/dist/replay-resume.d.ts.map +1 -1
- package/dist/replay-resume.js +5 -715
- package/dist/replay-resume.js.map +1 -1
- package/dist/resource-projection-util.d.ts +1 -2
- package/dist/resource-projection-util.d.ts.map +1 -1
- package/dist/resource-projection-util.js +2 -11
- package/dist/resource-projection-util.js.map +1 -1
- package/dist/resource-projection.d.ts.map +1 -1
- package/dist/resource-projection.js +67 -22
- package/dist/resource-projection.js.map +1 -1
- package/dist/run-execution-host.d.ts +98 -0
- package/dist/run-execution-host.d.ts.map +1 -0
- package/dist/run-execution-host.js +2 -0
- package/dist/run-execution-host.js.map +1 -0
- package/dist/run-execution.d.ts +6 -0
- package/dist/run-execution.d.ts.map +1 -0
- package/dist/run-execution.js +313 -0
- package/dist/run-execution.js.map +1 -0
- package/dist/run-initialization.d.ts +19 -0
- package/dist/run-initialization.d.ts.map +1 -0
- package/dist/run-initialization.js +171 -0
- package/dist/run-initialization.js.map +1 -0
- package/dist/run-recovery.d.ts +1 -1
- package/dist/run-recovery.js.map +1 -1
- package/dist/runtime-connection-definitions.d.ts +35 -0
- package/dist/runtime-connection-definitions.d.ts.map +1 -0
- package/dist/runtime-connection-definitions.js +97 -0
- package/dist/runtime-connection-definitions.js.map +1 -0
- package/dist/runtime-engine-background.d.ts +64 -0
- package/dist/runtime-engine-background.d.ts.map +1 -0
- package/dist/runtime-engine-background.js +136 -0
- package/dist/runtime-engine-background.js.map +1 -0
- package/dist/runtime-engine-base.d.ts +60 -0
- package/dist/runtime-engine-base.d.ts.map +1 -0
- package/dist/runtime-engine-base.js +192 -0
- package/dist/runtime-engine-base.js.map +1 -0
- package/dist/runtime-engine-capabilities.d.ts +81 -0
- package/dist/runtime-engine-capabilities.d.ts.map +1 -0
- package/dist/runtime-engine-capabilities.js +198 -0
- package/dist/runtime-engine-capabilities.js.map +1 -0
- package/dist/runtime-engine-config.d.ts +77 -0
- package/dist/runtime-engine-config.d.ts.map +1 -0
- package/dist/runtime-engine-config.js +187 -0
- package/dist/runtime-engine-config.js.map +1 -0
- package/dist/runtime-engine-foundation.d.ts +11 -0
- package/dist/runtime-engine-foundation.d.ts.map +1 -0
- package/dist/runtime-engine-foundation.js +121 -0
- package/dist/runtime-engine-foundation.js.map +1 -0
- package/dist/runtime-engine-hooks.d.ts +33 -0
- package/dist/runtime-engine-hooks.d.ts.map +1 -0
- package/dist/runtime-engine-hooks.js +149 -0
- package/dist/runtime-engine-hooks.js.map +1 -0
- package/dist/runtime-engine-integrations.d.ts +16 -0
- package/dist/runtime-engine-integrations.d.ts.map +1 -0
- package/dist/runtime-engine-integrations.js +177 -0
- package/dist/runtime-engine-integrations.js.map +1 -0
- package/dist/runtime-engine-operations.d.ts +87 -0
- package/dist/runtime-engine-operations.d.ts.map +1 -0
- package/dist/runtime-engine-operations.js +204 -0
- package/dist/runtime-engine-operations.js.map +1 -0
- package/dist/runtime-engine-resources.d.ts +18 -0
- package/dist/runtime-engine-resources.d.ts.map +1 -0
- package/dist/runtime-engine-resources.js +49 -0
- package/dist/runtime-engine-resources.js.map +1 -0
- package/dist/runtime-engine-run.d.ts +70 -0
- package/dist/runtime-engine-run.d.ts.map +1 -0
- package/dist/runtime-engine-run.js +202 -0
- package/dist/runtime-engine-run.js.map +1 -0
- package/dist/runtime-engine-scheduling.d.ts +22 -0
- package/dist/runtime-engine-scheduling.d.ts.map +1 -0
- package/dist/runtime-engine-scheduling.js +45 -0
- package/dist/runtime-engine-scheduling.js.map +1 -0
- package/dist/runtime-engine-tools.d.ts +8 -0
- package/dist/runtime-engine-tools.d.ts.map +1 -0
- package/dist/runtime-engine-tools.js +80 -0
- package/dist/runtime-engine-tools.js.map +1 -0
- package/dist/runtime-evolution.d.ts +17 -0
- package/dist/runtime-evolution.d.ts.map +1 -0
- package/dist/runtime-evolution.js +51 -0
- package/dist/runtime-evolution.js.map +1 -0
- package/dist/runtime-learning.d.ts +4 -6
- package/dist/runtime-learning.d.ts.map +1 -1
- package/dist/runtime-learning.js +109 -99
- package/dist/runtime-learning.js.map +1 -1
- package/dist/runtime-types.d.ts +21 -7
- package/dist/runtime-types.d.ts.map +1 -1
- package/dist/runtime-util.d.ts +4 -3
- package/dist/runtime-util.d.ts.map +1 -1
- package/dist/runtime-util.js +0 -7
- package/dist/runtime-util.js.map +1 -1
- package/dist/sandbox-fs.d.ts +2 -7
- package/dist/sandbox-fs.d.ts.map +1 -1
- package/dist/sandbox-fs.js +6 -37
- package/dist/sandbox-fs.js.map +1 -1
- package/dist/sandbox-handle.d.ts +1 -3
- package/dist/sandbox-handle.d.ts.map +1 -1
- package/dist/sandbox-handle.js +2 -5
- package/dist/sandbox-handle.js.map +1 -1
- package/dist/sandbox-state-sync.d.ts +2 -0
- package/dist/sandbox-state-sync.d.ts.map +1 -1
- package/dist/sandbox-state-sync.js +26 -2
- package/dist/sandbox-state-sync.js.map +1 -1
- package/dist/sandbox-tool-bridge.js +2 -3
- package/dist/sandbox-tool-bridge.js.map +1 -1
- package/dist/schedule-execution.d.ts +4 -4
- package/dist/schedule-execution.d.ts.map +1 -1
- package/dist/schedule-execution.js +45 -440
- package/dist/schedule-execution.js.map +1 -1
- package/dist/schedule-trigger-context.d.ts +18 -0
- package/dist/schedule-trigger-context.d.ts.map +1 -0
- package/dist/schedule-trigger-context.js +276 -0
- package/dist/schedule-trigger-context.js.map +1 -0
- package/dist/scheduler.d.ts +1 -14
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +1 -37
- package/dist/scheduler.js.map +1 -1
- package/dist/security.d.ts +0 -4
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +0 -22
- package/dist/security.js.map +1 -1
- package/dist/self-improvement.d.ts +5 -21
- package/dist/self-improvement.d.ts.map +1 -1
- package/dist/self-improvement.js +10 -40
- package/dist/self-improvement.js.map +1 -1
- package/dist/source-loader.d.ts +0 -3
- package/dist/source-loader.d.ts.map +1 -1
- package/dist/source-loader.js +1 -10
- package/dist/source-loader.js.map +1 -1
- package/dist/state/conversation-state.d.ts +117 -0
- package/dist/state/conversation-state.d.ts.map +1 -0
- package/dist/state/conversation-state.js +2 -0
- package/dist/state/conversation-state.js.map +1 -0
- package/dist/state/run-state.d.ts +129 -0
- package/dist/state/run-state.d.ts.map +1 -0
- package/dist/state/run-state.js +3 -0
- package/dist/state/run-state.js.map +1 -0
- package/dist/state/sandbox-memory-state.d.ts +159 -0
- package/dist/state/sandbox-memory-state.d.ts.map +1 -0
- package/dist/state/sandbox-memory-state.js +2 -0
- package/dist/state/sandbox-memory-state.js.map +1 -0
- package/dist/state/store-contracts.d.ts +329 -0
- package/dist/state/store-contracts.d.ts.map +1 -0
- package/dist/state/store-contracts.js +26 -0
- package/dist/state/store-contracts.js.map +1 -0
- package/dist/state/usage-state.d.ts +111 -0
- package/dist/state/usage-state.d.ts.map +1 -0
- package/dist/state/usage-state.js +2 -0
- package/dist/state/usage-state.js.map +1 -0
- package/dist/state/workspace-state.d.ts +126 -0
- package/dist/state/workspace-state.d.ts.map +1 -0
- package/dist/state/workspace-state.js +16 -0
- package/dist/state/workspace-state.js.map +1 -0
- package/dist/state-stores.d.ts +3 -15
- package/dist/state-stores.d.ts.map +1 -1
- package/dist/state-stores.js +36 -82
- package/dist/state-stores.js.map +1 -1
- package/dist/state.d.ts +7 -1075
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +7 -88
- package/dist/state.js.map +1 -1
- package/dist/storage.d.ts +4 -64
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +5 -408
- package/dist/storage.js.map +1 -1
- package/dist/subagent-execution.d.ts +1 -2
- package/dist/subagent-execution.d.ts.map +1 -1
- package/dist/subagent-execution.js +2 -26
- package/dist/subagent-execution.js.map +1 -1
- package/dist/subagent-handoff.d.ts +13 -0
- package/dist/subagent-handoff.d.ts.map +1 -1
- package/dist/subagent-handoff.js +74 -9
- package/dist/subagent-handoff.js.map +1 -1
- package/dist/tool-apis.d.ts +2 -2
- package/dist/tool-apis.d.ts.map +1 -1
- package/dist/tool-apis.js +26 -14
- package/dist/tool-apis.js.map +1 -1
- package/dist/tool-execution-host.d.ts +121 -0
- package/dist/tool-execution-host.d.ts.map +1 -0
- package/dist/tool-execution-host.js +2 -0
- package/dist/tool-execution-host.js.map +1 -0
- package/dist/tool-execution.d.ts +5 -141
- package/dist/tool-execution.d.ts.map +1 -1
- package/dist/tool-execution.js +6 -395
- package/dist/tool-execution.js.map +1 -1
- package/dist/usage-reconciliation-support.d.ts +20 -0
- package/dist/usage-reconciliation-support.d.ts.map +1 -0
- package/dist/usage-reconciliation-support.js +95 -0
- package/dist/usage-reconciliation-support.js.map +1 -0
- package/dist/usage-reconciliation.d.ts +14 -35
- package/dist/usage-reconciliation.d.ts.map +1 -1
- package/dist/usage-reconciliation.js +17 -394
- package/dist/usage-reconciliation.js.map +1 -1
- package/dist/workspace-paths.d.ts +3 -0
- package/dist/workspace-paths.d.ts.map +1 -0
- package/dist/workspace-paths.js +13 -0
- package/dist/workspace-paths.js.map +1 -0
- package/dist/workspace-service.d.ts +0 -1
- package/dist/workspace-service.d.ts.map +1 -1
- package/dist/workspace-service.js +1 -4
- package/dist/workspace-service.js.map +1 -1
- package/package.json +3 -3
- package/dist/run-timeline.d.ts +0 -11
- package/dist/run-timeline.d.ts.map +0 -1
- package/dist/run-timeline.js +0 -2
- package/dist/run-timeline.js.map +0 -1
- package/dist/trusted-attachments.d.ts +0 -20
- package/dist/trusted-attachments.d.ts.map +0 -1
- package/dist/trusted-attachments.js +0 -276
- package/dist/trusted-attachments.js.map +0 -1
package/dist/connections.js
CHANGED
|
@@ -1,178 +1,52 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError, isConnectionAuthorizationFailedError, isConnectionAuthorizationRequiredError, resolveSandboxPath, shellQuote } from "@assemblyline-agents/core";
|
|
4
|
-
import { OAUTH_STANDARD_TOKEN_FIELDS, buildOAuthChallenge, connectionPrincipalFor, connectionPrincipalKey, grantToTokenResult, isTokenExpired, normalizeAuthorization, randomUrlToken, resolveHeadersDefinition, storedGrantExpired, tokenCacheKey } from "./connection-auth-util.js";
|
|
5
|
-
import { HttpApiConnectionClient, A2AConnectionClient, CredentialConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, SdkApiConnectionClient, readResponseBody } from "./connection-clients.js";
|
|
6
|
-
import { SandboxCliConnectionClient } from "./sandbox-cli-client.js";
|
|
7
1
|
import { audienceAllowsConnectionAccess } from "./audience.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
2
|
+
import { ConnectionAuthorizationService } from "./connection-authorization-service.js";
|
|
3
|
+
import { connectionPrincipalFor, connectionPrincipalKey, normalizeAuthorization } from "./connection-auth-util.js";
|
|
4
|
+
import { A2AConnectionClient, CredentialConnectionClient, HttpApiConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, SdkApiConnectionClient } from "./connection-clients.js";
|
|
5
|
+
import { materializeCredentialsWithContext } from "./connection-credential-materialization.js";
|
|
6
|
+
import { loadResolvedConnection } from "./connection-definition-loader.js";
|
|
7
|
+
import { searchConnections } from "./connection-discovery.js";
|
|
8
|
+
import { CONNECTION_PAIR_TOOL_NAME, ConnectionConfigurationError } from "./connection-runtime-types.js";
|
|
9
|
+
import { connectionToolAccess, connectionToolApproval, isLiveConnection, isRecord, liveDefinitionUrl, parseQualifiedName } from "./connection-util.js";
|
|
10
|
+
import { SandboxCliConnectionClient } from "./sandbox-cli-client.js";
|
|
11
11
|
import { CredentialTransferConnectionClient } from "./credential-transfer-client.js";
|
|
12
|
-
import { createConnectionAssetApi } from "./connection-assets.js";
|
|
13
12
|
export * from "./connection-auth-util.js";
|
|
14
13
|
export * from "./connection-clients.js";
|
|
14
|
+
export * from "./connection-credential-materialization.js";
|
|
15
|
+
export * from "./connection-runtime-types.js";
|
|
15
16
|
export * from "./connection-stores.js";
|
|
16
17
|
export * from "./connection-util.js";
|
|
17
18
|
export * from "./computer-relay-protocol.js";
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.name = "ConnectionConfigurationError";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
export function isConnectionConfigurationError(error) {
|
|
25
|
-
return error instanceof ConnectionConfigurationError;
|
|
26
|
-
}
|
|
27
|
-
const MATERIALIZED_CREDENTIAL_ROOT = "/workspace/.assembly-line/credentials/";
|
|
28
|
-
// Compose the channel-visible response for a run parked on connection
|
|
29
|
-
// authorization. The generic "authorization required" string is useless to the
|
|
30
|
-
// human — the challenge URL is the thing they need to click.
|
|
31
|
-
export async function connectionAuthorizationPrompt(store, authorizationSessionId, baseResponse) {
|
|
32
|
-
const session = authorizationSessionId ? await store.getSession(authorizationSessionId) : undefined;
|
|
33
|
-
const url = session && typeof session.challenge.url === "string" ? session.challenge.url : undefined;
|
|
34
|
-
if (!url)
|
|
35
|
-
return baseResponse || "Connection authorization required.";
|
|
36
|
-
const prompt = `Authorization required for ${session?.connectionName ?? "a connection"} — approve here: ${url}`;
|
|
37
|
-
return baseResponse ? `${baseResponse}\n\n${prompt}` : prompt;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Project declared connection credentials into a freshly acquired sandbox.
|
|
41
|
-
* Tokens resolve host-side (cached grant or refresh exchange — the grant
|
|
42
|
-
* store is updated before anything reaches the sandbox), so recycling the
|
|
43
|
-
* sandbox never loses a durable credential and never holds one. Subagent
|
|
44
|
-
* sandboxes are excluded: they receive only their engine harness credential,
|
|
45
|
-
* never the parent's connections. The emitted event carries paths only —
|
|
46
|
-
* credential contents never reach the event log.
|
|
47
|
-
*/
|
|
48
|
-
export async function materializeConnectionCredentialsIntoSandbox(run, registry, sandbox, appendEvent) {
|
|
49
|
-
if (run.input.channel === "subagent" || typeof run.input.parentRunId === "string")
|
|
50
|
-
return;
|
|
51
|
-
const { materialized, pending } = await registry.materializeCredentials({
|
|
52
|
-
id: run.id,
|
|
53
|
-
input: run.input
|
|
54
|
-
});
|
|
55
|
-
if (materialized.length === 0 && pending.length === 0)
|
|
56
|
-
return;
|
|
57
|
-
for (const credential of materialized) {
|
|
58
|
-
for (const file of credential.files) {
|
|
59
|
-
assertMaterializedCredentialPath(credential.connection, file.path);
|
|
60
|
-
await sandbox.writeFile(file.path, file.content);
|
|
61
|
-
const physicalPath = resolveSandboxPath(sandbox.root, file.path);
|
|
62
|
-
await sandbox.shell(`chmod ${file.mode.toString(8)} -- ${shellQuote(physicalPath)}`);
|
|
63
|
-
}
|
|
64
|
-
for (const command of credential.commands ?? []) {
|
|
65
|
-
await sandbox.shell(command);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
await appendEvent("connections.credentials_materialized", {
|
|
69
|
-
materialized: materialized.map((credential) => ({
|
|
70
|
-
connection: credential.connection,
|
|
71
|
-
files: credential.files.map((file) => file.path),
|
|
72
|
-
...(credential.audit ? { audit: credential.audit } : {})
|
|
73
|
-
})),
|
|
74
|
-
pending
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
function assertMaterializedCredentialPath(connectionName, path) {
|
|
78
|
-
const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
79
|
-
if (normalized.split("/").includes("..") || !normalized.startsWith(MATERIALIZED_CREDENTIAL_ROOT)) {
|
|
80
|
-
throw new ConnectionConfigurationError(`Connection ${connectionName} materialize files must be under ${MATERIALIZED_CREDENTIAL_ROOT}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function assertRedactedAuditMetadata(connectionName, audit) {
|
|
84
|
-
const visit = (value, path) => {
|
|
85
|
-
if (Array.isArray(value)) {
|
|
86
|
-
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (!value || typeof value !== "object")
|
|
90
|
-
return;
|
|
91
|
-
for (const [key, item] of Object.entries(value)) {
|
|
92
|
-
if (/(?:token|secret|password|authorization|credential)/iu.test(key)) {
|
|
93
|
-
throw new ConnectionConfigurationError(`Connection ${connectionName} audit metadata contains forbidden secret-like key ${path}.${key}.`);
|
|
94
|
-
}
|
|
95
|
-
visit(item, `${path}.${key}`);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
visit(audit, "audit");
|
|
99
|
-
}
|
|
100
|
-
export const CONNECTION_PAIR_TOOL_NAME = "pair";
|
|
101
|
-
// The synthetic host-side pairing tool advertised for connections that define
|
|
102
|
-
// redeemPairingCode. The input field is named `secret` so tool-call event
|
|
103
|
-
// logging redacts the pasted one-time code automatically.
|
|
104
|
-
function pairingToolItem(connection) {
|
|
105
|
-
return {
|
|
106
|
-
connection: connection.name,
|
|
107
|
-
qualifiedName: `${connection.name}__${CONNECTION_PAIR_TOOL_NAME}`,
|
|
108
|
-
tool: CONNECTION_PAIR_TOOL_NAME,
|
|
109
|
-
description: `Pair ${connection.name} by redeeming a one-time pairing packet or claim code. When the user pastes binding/pairing instructions for ${connection.name}, call this with the FULL pasted text as \`secret\`; the host exchanges it and stores the credential durably. Only call this for a supplied packet; the tool's presence does not mean an existing grant is missing.`,
|
|
110
|
-
inputSchema: {
|
|
111
|
-
type: "object",
|
|
112
|
-
additionalProperties: false,
|
|
113
|
-
properties: {
|
|
114
|
-
secret: {
|
|
115
|
-
type: "string",
|
|
116
|
-
description: "The full pasted pairing packet, binding instructions, or one-time claim code."
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
required: ["secret"]
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const CREDENTIAL_TEMPLATE_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_.]*)\s*\}\}/gu;
|
|
124
|
-
function templateUsesToken(template) {
|
|
125
|
-
return [...template.matchAll(CREDENTIAL_TEMPLATE_PATTERN)].some((match) => match[1] === "token");
|
|
126
|
-
}
|
|
127
|
-
// An unrendered placeholder in a credentials file is a silent misconfiguration
|
|
128
|
-
// (the CLI reads garbage), so unknown keys fail loudly instead of passing through.
|
|
129
|
-
function renderCredentialTemplate(template, values, connectionName) {
|
|
130
|
-
return template.replace(CREDENTIAL_TEMPLATE_PATTERN, (_match, key) => {
|
|
131
|
-
const value = values[key];
|
|
132
|
-
if (value === undefined) {
|
|
133
|
-
throw new Error(`Connection ${connectionName} materialize template references unknown placeholder {{${key}}}.`);
|
|
134
|
-
}
|
|
135
|
-
return value;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
19
|
+
export * from "./credential-broker.js";
|
|
20
|
+
export * from "./credential-transfer-client.js";
|
|
21
|
+
/** Registry for discovery, execution, credential materialization, and authorization. */
|
|
138
22
|
export class RuntimeConnectionRegistry {
|
|
139
23
|
options;
|
|
140
24
|
resolved = new Map();
|
|
141
25
|
clients = new Map();
|
|
142
|
-
|
|
143
|
-
executionContext = new AsyncLocalStorage();
|
|
144
|
-
authorizationIntent = new AsyncLocalStorage();
|
|
145
|
-
pendingAuthorizationCreations = new Map();
|
|
146
|
-
grantStore;
|
|
147
|
-
authorizationSessionStore;
|
|
148
|
-
credentialBroker;
|
|
26
|
+
authorization;
|
|
149
27
|
constructor(options) {
|
|
150
28
|
this.options = options;
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
});
|
|
156
|
-
this.authorizationSessionStore = options.authorizationSessionStore ?? new InMemoryConnectionAuthorizationSessionStore();
|
|
29
|
+
this.authorization = new ConnectionAuthorizationService(options, (connection, session) => this.resolveConnection(connection, session));
|
|
30
|
+
}
|
|
31
|
+
pluginRuntimePaths(connectionName) {
|
|
32
|
+
return this.options.pluginRuntime?.[connectionName];
|
|
157
33
|
}
|
|
158
34
|
hasLiveConnections() {
|
|
159
35
|
return this.options.connections.some(isLiveConnection);
|
|
160
36
|
}
|
|
161
37
|
liveConnectionNames() {
|
|
162
|
-
return this.options.connections
|
|
163
|
-
.filter(isLiveConnection)
|
|
164
|
-
.map((connection) => connection.name);
|
|
38
|
+
return this.options.connections.filter(isLiveConnection).map((connection) => connection.name);
|
|
165
39
|
}
|
|
166
|
-
/** Resolve the host-only event adapter without exposing it as a model tool. */
|
|
167
40
|
async eventBindingContext(connectionName) {
|
|
168
41
|
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
169
42
|
if (!compiled || !isLiveConnection(compiled)) {
|
|
170
43
|
throw new ConnectionConfigurationError(`Connection ${connectionName} is not registered as a live connection.`);
|
|
171
44
|
}
|
|
172
45
|
const definition = (await this.resolveConnection(compiled)).definition;
|
|
173
|
-
if (!definition.events)
|
|
46
|
+
if (!definition.events) {
|
|
174
47
|
throw new ConnectionConfigurationError(`Connection ${connectionName} does not accept provider events.`);
|
|
175
|
-
|
|
48
|
+
}
|
|
49
|
+
const context = this.authorization.resolverContext(compiled);
|
|
176
50
|
let principal;
|
|
177
51
|
if (definition.events.scope === "connection") {
|
|
178
52
|
principal = { type: "app" };
|
|
@@ -199,99 +73,26 @@ export class RuntimeConnectionRegistry {
|
|
|
199
73
|
config: context.config,
|
|
200
74
|
credentials: context.credentials,
|
|
201
75
|
getToken: async () => {
|
|
202
|
-
if (!definition.auth)
|
|
76
|
+
if (!definition.auth) {
|
|
203
77
|
throw new ConnectionConfigurationError(`Connection ${connectionName} event adapter requested a token without connection auth.`);
|
|
204
|
-
|
|
78
|
+
}
|
|
79
|
+
return this.authorization.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
205
80
|
}
|
|
206
81
|
};
|
|
207
82
|
}
|
|
208
83
|
async search(input, execution) {
|
|
209
|
-
return this.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const queryTokens = tokenize(keywords);
|
|
216
|
-
const scored = [];
|
|
217
|
-
const failures = [];
|
|
218
|
-
const privateOnly = [];
|
|
219
|
-
// Pairing tools surface even when the connection cannot authenticate yet —
|
|
220
|
-
// redeeming a pasted packet is exactly how an unauthorized connection
|
|
221
|
-
// becomes authorized, so it must not hide behind the failure path.
|
|
222
|
-
const pairable = [];
|
|
223
|
-
for (const connection of target) {
|
|
224
|
-
// A personal connection stays registered in a shared run, but its tool
|
|
225
|
-
// catalog, pairing, and authorization state are private-surface only.
|
|
226
|
-
if (!this.connectionAccessible(connection)) {
|
|
227
|
-
privateOnly.push({
|
|
228
|
-
connection: connection.name,
|
|
229
|
-
description: `${connection.name} is registered. Its personal tools, pairing, and authorization status are available only on a private surface such as a direct message; this does not mean a durable grant is missing.`,
|
|
230
|
-
requiresPrivateAudience: true
|
|
231
|
-
});
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
let metadata;
|
|
235
|
-
try {
|
|
236
|
-
if (await this.supportsPairing(connection)) {
|
|
237
|
-
pairable.push(pairingToolItem(connection));
|
|
238
|
-
}
|
|
239
|
-
metadata = await this.getClient(connection.name).getToolMetadata();
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
failures.push(this.connectionFailure(connection, error));
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
for (const tool of metadata) {
|
|
246
|
-
const score = keywords.trim() ? scoreMatch(queryTokens, tool) : 1;
|
|
247
|
-
if (score <= 0)
|
|
248
|
-
continue;
|
|
249
|
-
const item = {
|
|
250
|
-
connection: tool.connection,
|
|
251
|
-
description: tool.description,
|
|
252
|
-
inputSchema: tool.inputSchema,
|
|
253
|
-
qualifiedName: tool.qualifiedName,
|
|
254
|
-
tool: tool.name
|
|
255
|
-
};
|
|
256
|
-
if (tool.outputSchema)
|
|
257
|
-
item.outputSchema = tool.outputSchema;
|
|
258
|
-
scored.push({
|
|
259
|
-
score,
|
|
260
|
-
item
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
scored.sort((a, b) => b.score - a.score);
|
|
265
|
-
const matches = scored.slice(0, limit).map((entry) => entry.item);
|
|
266
|
-
if (matches.length > 0)
|
|
267
|
-
return [...pairable, ...matches, ...privateOnly, ...failures];
|
|
268
|
-
const privateConnectionNames = new Set(privateOnly.map((item) => item.connection));
|
|
269
|
-
return [
|
|
270
|
-
...pairable,
|
|
271
|
-
...privateOnly,
|
|
272
|
-
...target.map((connection) => ({
|
|
273
|
-
connection: connection.name,
|
|
274
|
-
description: connection.description ?? `${connection.name} connection`
|
|
275
|
-
})).filter((item) => !privateConnectionNames.has(item.connection)),
|
|
276
|
-
...failures
|
|
277
|
-
];
|
|
84
|
+
return this.authorization.runWithIntent("discover", () => this.authorization.withExecutionContext(execution, () => searchConnections(input, {
|
|
85
|
+
targetConnections: (name) => this.targetConnections(name),
|
|
86
|
+
connectionAccessible: (connection) => this.connectionAccessible(connection),
|
|
87
|
+
supportsPairing: (connection) => this.supportsPairing(connection),
|
|
88
|
+
toolMetadata: async (connectionName) => this.getClient(connectionName).getToolMetadata()
|
|
89
|
+
})));
|
|
278
90
|
}
|
|
279
|
-
// Deliberately does not swallow load errors: a definition module that fails
|
|
280
|
-
// to resolve (for example a deployed artifact missing the connection's
|
|
281
|
-
// package) must surface the real cause instead of silently hiding the
|
|
282
|
-
// pairing tool that would otherwise be the recovery path.
|
|
283
91
|
async supportsPairing(connection) {
|
|
284
92
|
return typeof (await this.resolveConnection(connection)).definition.redeemPairingCode === "function";
|
|
285
93
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Report which live connections can redeem a pairing code right now, and
|
|
288
|
-
* which cannot because their definition failed to resolve (missing package,
|
|
289
|
-
* unset env, platform mismatch). Backs the core `pair` tool: a connection
|
|
290
|
-
* that cannot even load must still be *diagnosable* from the model — the
|
|
291
|
-
* resolve failure is the answer the user needs, not a hidden tool.
|
|
292
|
-
*/
|
|
293
94
|
async pairingStatus() {
|
|
294
|
-
return this.
|
|
95
|
+
return this.authorization.runWithIntent("discover", async () => {
|
|
295
96
|
const pairable = [];
|
|
296
97
|
const unavailable = [];
|
|
297
98
|
for (const connection of this.options.connections) {
|
|
@@ -311,11 +112,6 @@ export class RuntimeConnectionRegistry {
|
|
|
311
112
|
return { pairable, unavailable };
|
|
312
113
|
});
|
|
313
114
|
}
|
|
314
|
-
/**
|
|
315
|
-
* Redeem a user-provided one-time pairing code host-side and persist the
|
|
316
|
-
* resulting credentials in the grant store. The durable credential never
|
|
317
|
-
* enters a sandbox; the model only relays the single-use code.
|
|
318
|
-
*/
|
|
319
115
|
async redeemPairingCode(connectionName, code) {
|
|
320
116
|
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
321
117
|
if (!compiled || !isLiveConnection(compiled)) {
|
|
@@ -330,15 +126,14 @@ export class RuntimeConnectionRegistry {
|
|
|
330
126
|
throw new Error(`Connection ${connectionName} does not support pairing codes.`);
|
|
331
127
|
if (!code.trim())
|
|
332
128
|
throw new Error("A pairing code is required.");
|
|
333
|
-
const context = this.resolverContext(compiled);
|
|
129
|
+
const context = this.authorization.resolverContext(compiled);
|
|
334
130
|
const token = await redeem({ code }, context);
|
|
335
131
|
const authorization = definition.auth
|
|
336
132
|
? await normalizeAuthorization(definition.auth, context)
|
|
337
133
|
: { kind: "token", principalType: "user", getToken: async () => token };
|
|
338
134
|
const principal = connectionPrincipalFor(compiled, authorization, context);
|
|
339
135
|
const principalKey = connectionPrincipalKey(principal);
|
|
340
|
-
await this.
|
|
341
|
-
this.tokenCache.delete(tokenCacheKey(compiled.name, principal));
|
|
136
|
+
await this.authorization.storeToken(compiled, principal, principalKey, token);
|
|
342
137
|
return {
|
|
343
138
|
connected: true,
|
|
344
139
|
connection: compiled.name,
|
|
@@ -346,7 +141,7 @@ export class RuntimeConnectionRegistry {
|
|
|
346
141
|
};
|
|
347
142
|
}
|
|
348
143
|
async metadataForQualifiedName(qualifiedName) {
|
|
349
|
-
return this.
|
|
144
|
+
return this.authorization.runWithIntent("discover", async () => {
|
|
350
145
|
const parsed = parseQualifiedName(qualifiedName);
|
|
351
146
|
if (!parsed)
|
|
352
147
|
return undefined;
|
|
@@ -365,16 +160,11 @@ export class RuntimeConnectionRegistry {
|
|
|
365
160
|
if (!compiled || !this.toolAllowed(compiled)) {
|
|
366
161
|
throw new Error(`Connection tool ${qualifiedName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
|
|
367
162
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (parsed.toolName === CONNECTION_PAIR_TOOL_NAME) {
|
|
372
|
-
if (compiled && await this.supportsPairing(compiled)) {
|
|
373
|
-
const secret = isRecord(input) && typeof input.secret === "string" ? input.secret : "";
|
|
374
|
-
return this.withExecutionContext(execution, () => this.redeemPairingCode(parsed.connectionName, secret));
|
|
375
|
-
}
|
|
163
|
+
if (parsed.toolName === CONNECTION_PAIR_TOOL_NAME && await this.supportsPairing(compiled)) {
|
|
164
|
+
const secret = isRecord(input) && typeof input.secret === "string" ? input.secret : "";
|
|
165
|
+
return this.authorization.withExecutionContext(execution, () => this.redeemPairingCode(parsed.connectionName, secret));
|
|
376
166
|
}
|
|
377
|
-
return this.withExecutionContext(execution, () => this.getClient(parsed.connectionName).executeTool(parsed.toolName, input));
|
|
167
|
+
return this.authorization.withExecutionContext(execution, () => this.getClient(parsed.connectionName).executeTool(parsed.toolName, input));
|
|
378
168
|
}
|
|
379
169
|
async getConnectionApproval(connectionName, toolName) {
|
|
380
170
|
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
@@ -383,10 +173,6 @@ export class RuntimeConnectionRegistry {
|
|
|
383
173
|
if (!this.toolAllowed(compiled)) {
|
|
384
174
|
throw new Error(`Connection tool ${connectionName}__${toolName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
|
|
385
175
|
}
|
|
386
|
-
// Pairing is a synthetic host-side bootstrap operation, not a provider
|
|
387
|
-
// tool. It must bypass the provider connection's ordinary read/write
|
|
388
|
-
// classification or discovery will advertise a tool that execution then
|
|
389
|
-
// rejects before the redeemer can run.
|
|
390
176
|
if (toolName === CONNECTION_PAIR_TOOL_NAME) {
|
|
391
177
|
if (!await this.supportsPairing(compiled)) {
|
|
392
178
|
throw new Error(`Connection ${connectionName} does not support pairing codes.`);
|
|
@@ -432,6 +218,12 @@ export class RuntimeConnectionRegistry {
|
|
|
432
218
|
this.clients.set(connectionName, client);
|
|
433
219
|
return client;
|
|
434
220
|
}
|
|
221
|
+
async executeSandboxCli(invocation) {
|
|
222
|
+
if (!this.options.executeSandboxCli) {
|
|
223
|
+
throw new Error("Sandbox CLI connection execution requires an active agent run sandbox.");
|
|
224
|
+
}
|
|
225
|
+
return this.options.executeSandboxCli(invocation, this.authorization.currentExecutionContext());
|
|
226
|
+
}
|
|
435
227
|
async transferCredential(input) {
|
|
436
228
|
const source = this.options.connections.find((connection) => connection.name === input.sourceConnection);
|
|
437
229
|
const sink = this.options.connections.find((connection) => connection.name === input.sinkConnection);
|
|
@@ -444,8 +236,8 @@ export class RuntimeConnectionRegistry {
|
|
|
444
236
|
const sinkCapability = "credentialSink" in sinkDefinition ? sinkDefinition.credentialSink : undefined;
|
|
445
237
|
if (!sourceCapability || !sinkCapability)
|
|
446
238
|
throw new Error("Credential transfer source or sink is not configured.");
|
|
447
|
-
const principal = this.connectionPrincipal(sink);
|
|
448
|
-
const execution = this.
|
|
239
|
+
const principal = this.authorization.connectionPrincipal(sink);
|
|
240
|
+
const execution = this.authorization.currentExecutionContext();
|
|
449
241
|
const runId = execution?.runId ?? this.options.runId;
|
|
450
242
|
const identity = {
|
|
451
243
|
principal,
|
|
@@ -462,7 +254,7 @@ export class RuntimeConnectionRegistry {
|
|
|
462
254
|
try {
|
|
463
255
|
const value = await sourceCapability.resolve(input.reference, context);
|
|
464
256
|
await sinkCapability.consume(value, input.target, context);
|
|
465
|
-
await this.credentialBroker.recordTransfer({
|
|
257
|
+
await this.authorization.credentialBroker.recordTransfer({
|
|
466
258
|
credential: input.reference,
|
|
467
259
|
connection: sink.name,
|
|
468
260
|
identity,
|
|
@@ -472,7 +264,7 @@ export class RuntimeConnectionRegistry {
|
|
|
472
264
|
return { status: "filled" };
|
|
473
265
|
}
|
|
474
266
|
catch {
|
|
475
|
-
await this.credentialBroker.recordTransfer({
|
|
267
|
+
await this.authorization.credentialBroker.recordTransfer({
|
|
476
268
|
credential: input.reference,
|
|
477
269
|
connection: sink.name,
|
|
478
270
|
identity,
|
|
@@ -482,303 +274,31 @@ export class RuntimeConnectionRegistry {
|
|
|
482
274
|
throw new Error(`Credential transfer from ${source.name} to ${sink.name} failed.`);
|
|
483
275
|
}
|
|
484
276
|
}
|
|
485
|
-
async executeSandboxCli(invocation) {
|
|
486
|
-
if (!this.options.executeSandboxCli) {
|
|
487
|
-
throw new Error("Sandbox CLI connection execution requires an active agent run sandbox.");
|
|
488
|
-
}
|
|
489
|
-
return this.options.executeSandboxCli(invocation, this.executionContext.getStore());
|
|
490
|
-
}
|
|
491
277
|
async resolveHeaders(connection, definition) {
|
|
492
|
-
|
|
493
|
-
const context = this.resolverContext(connection);
|
|
494
|
-
if (definition.auth !== undefined) {
|
|
495
|
-
const token = await this.resolveToken(connection, definition.auth, context, liveDefinitionUrl(definition));
|
|
496
|
-
const tokenType = token.tokenType?.trim() || "Bearer";
|
|
497
|
-
headers.Authorization = `${tokenType} ${token.token}`;
|
|
498
|
-
}
|
|
499
|
-
if (definition.headers !== undefined) {
|
|
500
|
-
const resolved = await resolveHeadersDefinition(definition.headers, context);
|
|
501
|
-
for (const [key, value] of Object.entries(resolved)) {
|
|
502
|
-
if (key.toLowerCase() === "authorization" && headers.Authorization !== undefined) {
|
|
503
|
-
throw new Error(`Connection ${connection.name} headers must not include Authorization when auth is configured.`);
|
|
504
|
-
}
|
|
505
|
-
headers[key] = value;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return headers;
|
|
278
|
+
return this.authorization.resolveHeaders(connection, definition);
|
|
509
279
|
}
|
|
510
|
-
/**
|
|
511
|
-
* Start (or resume) the authorization flow for a named connection and
|
|
512
|
-
* report where to send the user's browser. Backs the host's authenticated
|
|
513
|
-
* GET /assembly-line/connections/authorize endpoint (agent-control route class,
|
|
514
|
-
* JSON — deliberately not a public 302, so unauthenticated callers cannot
|
|
515
|
-
* trigger consent flows or enumerate connection names): the dashboard
|
|
516
|
-
* fetches the URL and opens it; the provider redirects back to the public,
|
|
517
|
-
* idempotency-claimed callback. Hitting it when a grant already exists
|
|
518
|
-
* reports "connected" instead of minting a redundant session; a pending
|
|
519
|
-
* session's challenge is reused.
|
|
520
|
-
*/
|
|
521
280
|
async beginAuthorization(connectionName) {
|
|
522
|
-
return this.
|
|
281
|
+
return this.authorization.beginAuthorization(connectionName);
|
|
523
282
|
}
|
|
524
|
-
async beginAuthorizationWithContext(connectionName) {
|
|
525
|
-
const compiled = this.options.connections.find((connection) => connection.name === connectionName);
|
|
526
|
-
if (!compiled || !isLiveConnection(compiled))
|
|
527
|
-
return { status: "unavailable", reason: "unknown_connection" };
|
|
528
|
-
let definition;
|
|
529
|
-
try {
|
|
530
|
-
definition = (await this.resolveConnection(compiled)).definition;
|
|
531
|
-
}
|
|
532
|
-
catch (error) {
|
|
533
|
-
return { status: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
534
|
-
}
|
|
535
|
-
if (!definition.auth)
|
|
536
|
-
return { status: "unavailable", reason: "connection_has_no_auth" };
|
|
537
|
-
try {
|
|
538
|
-
await this.resolveToken(compiled, definition.auth, this.resolverContext(compiled), liveDefinitionUrl(definition));
|
|
539
|
-
return { status: "connected" };
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
if (isConnectionAuthorizationRequiredError(error)) {
|
|
543
|
-
const url = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
|
|
544
|
-
return url ? { status: "authorize", url } : { status: "unavailable", reason: error.reason };
|
|
545
|
-
}
|
|
546
|
-
return { status: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Resolve every connection that declares `materialize` and render its
|
|
551
|
-
* credential files for injection into the agent sandbox. Tokens resolve
|
|
552
|
-
* through the normal host-side flow — cached grant, or a refresh exchange
|
|
553
|
-
* that persists the rotated grant to the grant store BEFORE anything is
|
|
554
|
-
* rendered — so the sandbox only ever sees the short-lived access token.
|
|
555
|
-
*
|
|
556
|
-
* A connection that cannot resolve is reported as pending unless the run
|
|
557
|
-
* explicitly requested that credential. Explicit requests fail closed so a
|
|
558
|
-
* run can never continue after a missing, revoked, or mismatched credential
|
|
559
|
-
* it declared as required. Merely making a credential connection available
|
|
560
|
-
* to the agent must not prevent unrelated sandbox work.
|
|
561
|
-
*/
|
|
562
283
|
async materializeCredentials(run) {
|
|
563
|
-
return this.
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (!this.toolAllowed(compiled))
|
|
570
|
-
continue;
|
|
571
|
-
const credentialRequested = sandboxCredentialWasRequested(run, compiled.name);
|
|
572
|
-
// Credential-file connections are leases, not ambient sandbox setup.
|
|
573
|
-
// Merely configuring one on a root agent must never resolve or project
|
|
574
|
-
// its secret into an unrelated sandbox acquisition.
|
|
575
|
-
if (compiled.protocol === "credential" && !credentialRequested)
|
|
576
|
-
continue;
|
|
577
|
-
const failClosed = compiled.protocol === "credential" && credentialRequested;
|
|
578
|
-
let context;
|
|
579
|
-
let definition;
|
|
580
|
-
try {
|
|
581
|
-
context = this.resolverContext(compiled);
|
|
582
|
-
definition = (await this.resolveConnection(compiled)).definition;
|
|
583
|
-
}
|
|
584
|
-
catch (error) {
|
|
585
|
-
if (failClosed)
|
|
586
|
-
throw error;
|
|
587
|
-
pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
const spec = definition.materialize;
|
|
591
|
-
if (!spec || ((spec.files?.length ?? 0) === 0 && !spec.resolve))
|
|
592
|
-
continue;
|
|
593
|
-
const staticFiles = spec.files ?? [];
|
|
594
|
-
const needsToken = staticFiles.some((file) => templateUsesToken(file.path) || templateUsesToken(file.content));
|
|
595
|
-
let resolvedToken = { token: "" };
|
|
596
|
-
if (needsToken) {
|
|
597
|
-
if (!definition.auth) {
|
|
598
|
-
pending.push({ connection: compiled.name, reason: "materialize templates use {{token}} but the connection has no auth" });
|
|
599
|
-
continue;
|
|
600
|
-
}
|
|
601
|
-
try {
|
|
602
|
-
resolvedToken = await this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
603
|
-
}
|
|
604
|
-
catch (error) {
|
|
605
|
-
if (failClosed)
|
|
606
|
-
throw error;
|
|
607
|
-
if (isConnectionAuthorizationRequiredError(error)) {
|
|
608
|
-
const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
|
|
609
|
-
pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
|
|
610
|
-
continue;
|
|
611
|
-
}
|
|
612
|
-
const reason = isConnectionAuthorizationFailedError(error)
|
|
613
|
-
? `${error.reason}: ${error.message}`
|
|
614
|
-
: error instanceof Error
|
|
615
|
-
? error.message
|
|
616
|
-
: String(error);
|
|
617
|
-
pending.push({ connection: compiled.name, reason });
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
let files = staticFiles;
|
|
622
|
-
let commands = [];
|
|
623
|
-
let audit;
|
|
624
|
-
if (spec.resolve) {
|
|
625
|
-
if (!run) {
|
|
626
|
-
pending.push({ connection: compiled.name, reason: "run_context_required" });
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
let lazyToken;
|
|
630
|
-
try {
|
|
631
|
-
const authorization = definition.auth
|
|
632
|
-
? await normalizeAuthorization(definition.auth, context)
|
|
633
|
-
: undefined;
|
|
634
|
-
const principal = authorization
|
|
635
|
-
? connectionPrincipalFor(compiled, authorization, context)
|
|
636
|
-
: { type: "app" };
|
|
637
|
-
const resolved = await spec.resolve({
|
|
638
|
-
connectionName: compiled.name,
|
|
639
|
-
agentId: this.options.agentId ?? "",
|
|
640
|
-
run,
|
|
641
|
-
principal,
|
|
642
|
-
session: context.session,
|
|
643
|
-
config: context.config,
|
|
644
|
-
credentials: context.credentials,
|
|
645
|
-
fetch: this.options.fetch,
|
|
646
|
-
getToken: async () => {
|
|
647
|
-
if (!definition.auth)
|
|
648
|
-
throw new Error(`Connection ${compiled.name} materialization requested a token without auth.`);
|
|
649
|
-
lazyToken ??= this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
|
|
650
|
-
return lazyToken;
|
|
651
|
-
}
|
|
652
|
-
});
|
|
653
|
-
if (!resolved)
|
|
654
|
-
continue;
|
|
655
|
-
files = resolved.files;
|
|
656
|
-
commands = resolved.commands ?? [];
|
|
657
|
-
audit = resolved.audit;
|
|
658
|
-
if (audit)
|
|
659
|
-
assertRedactedAuditMetadata(compiled.name, audit);
|
|
660
|
-
}
|
|
661
|
-
catch (error) {
|
|
662
|
-
if (failClosed)
|
|
663
|
-
throw error;
|
|
664
|
-
if (isConnectionAuthorizationRequiredError(error)) {
|
|
665
|
-
const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
|
|
666
|
-
pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
const reason = isConnectionAuthorizationFailedError(error)
|
|
670
|
-
? `${error.reason}: ${error.message}`
|
|
671
|
-
: error instanceof Error
|
|
672
|
-
? error.message
|
|
673
|
-
: String(error);
|
|
674
|
-
pending.push({ connection: compiled.name, reason });
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
const values = { token: resolvedToken.token };
|
|
679
|
-
// Extra token-response fields (captured into the grant's credential bag)
|
|
680
|
-
// are addressable as {{credential.<key>}} — e.g. a provider-issued user
|
|
681
|
-
// or account id a sandbox-side CLI needs to build API paths.
|
|
682
|
-
for (const [key, value] of Object.entries(resolvedToken.credential ?? {})) {
|
|
683
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
684
|
-
values[`credential.${key}`] = String(value);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
try {
|
|
688
|
-
materialized.push({
|
|
689
|
-
connection: compiled.name,
|
|
690
|
-
files: files.map((file) => ({
|
|
691
|
-
path: renderCredentialTemplate(file.path, values, compiled.name),
|
|
692
|
-
content: renderCredentialTemplate(file.content, values, compiled.name),
|
|
693
|
-
mode: file.mode ?? 0o600
|
|
694
|
-
})),
|
|
695
|
-
...(commands.length > 0 ? { commands } : {}),
|
|
696
|
-
...(audit ? { audit } : {})
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
catch (error) {
|
|
700
|
-
if (failClosed)
|
|
701
|
-
throw error;
|
|
702
|
-
pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return { materialized, pending };
|
|
284
|
+
return this.authorization.runWithIntent("discover", () => materializeCredentialsWithContext(this.options, run, {
|
|
285
|
+
toolAllowed: (connection) => this.toolAllowed(connection),
|
|
286
|
+
resolveConnection: (connection) => this.resolveConnection(connection),
|
|
287
|
+
resolveToken: (connection, auth, context, resolvedUrl) => this.authorization.resolveToken(connection, auth, context, resolvedUrl),
|
|
288
|
+
resolverContext: (connection) => this.authorization.resolverContext(connection)
|
|
289
|
+
}));
|
|
706
290
|
}
|
|
707
291
|
async evictToken(connection, definition) {
|
|
708
|
-
|
|
709
|
-
return;
|
|
710
|
-
const context = this.resolverContext(connection);
|
|
711
|
-
const authorization = await normalizeAuthorization(definition.auth, context);
|
|
712
|
-
const principal = connectionPrincipalFor(connection, authorization, context);
|
|
713
|
-
const cacheKey = tokenCacheKey(connection.name, principal);
|
|
714
|
-
const principalKey = connectionPrincipalKey(principal);
|
|
715
|
-
const cached = this.tokenCache.get(cacheKey);
|
|
716
|
-
this.tokenCache.delete(cacheKey);
|
|
717
|
-
if (cached?.authorization.kind === "oauth" || authorization.kind === "oauth") {
|
|
718
|
-
await this.grantStore.deleteGrant(connection.name, principalKey);
|
|
719
|
-
}
|
|
720
|
-
await (cached?.authorization ?? authorization).evict?.({ principal, connection: { url: connection.url ?? "" } });
|
|
292
|
+
return this.authorization.evictToken(connection, definition);
|
|
721
293
|
}
|
|
722
294
|
async completeAuthorization(callback) {
|
|
723
|
-
|
|
724
|
-
if (!state)
|
|
725
|
-
throw new ConnectionAuthorizationFailedError("unknown", { reason: "missing_state", retryable: false });
|
|
726
|
-
const session = await this.authorizationSessionStore.getSessionByState(state);
|
|
727
|
-
if (!session)
|
|
728
|
-
throw new ConnectionAuthorizationFailedError("unknown", { reason: "unknown_state", retryable: false });
|
|
729
|
-
// Provider callbacks are routinely retried. Once the grant was saved and
|
|
730
|
-
// the session completed, return the durable result without attempting to
|
|
731
|
-
// exchange the one-time authorization code again.
|
|
732
|
-
if (session.status === "completed")
|
|
733
|
-
return session;
|
|
734
|
-
if (session.expiresAt <= Date.now()) {
|
|
735
|
-
await this.authorizationSessionStore.updateSession(session.id, { status: "expired" });
|
|
736
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "authorization_expired", retryable: true });
|
|
737
|
-
}
|
|
738
|
-
if (callback.params.error) {
|
|
739
|
-
await this.authorizationSessionStore.updateSession(session.id, { status: "denied" });
|
|
740
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, {
|
|
741
|
-
reason: callback.params.error,
|
|
742
|
-
retryable: true,
|
|
743
|
-
message: callback.params.error_description ?? `Connection ${session.connectionName} authorization was denied.`
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
const compiled = this.options.connections.find((connection) => connection.name === session.connectionName);
|
|
747
|
-
if (!compiled || !isLiveConnection(compiled)) {
|
|
748
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "connection_not_found", retryable: false });
|
|
749
|
-
}
|
|
750
|
-
const resolved = await this.resolveConnection(compiled);
|
|
751
|
-
if (!resolved.definition.auth) {
|
|
752
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "auth_not_configured", retryable: false });
|
|
753
|
-
}
|
|
754
|
-
const authorization = await normalizeAuthorization(resolved.definition.auth, this.resolverContext(compiled, session.session));
|
|
755
|
-
const connectionContext = { url: compiled.url ?? "" };
|
|
756
|
-
let token;
|
|
757
|
-
if (authorization.kind === "oauth" && authorization.oauth) {
|
|
758
|
-
token = await this.exchangeOAuthAuthorizationCode(authorization.oauth, session, callback);
|
|
759
|
-
}
|
|
760
|
-
else if (authorization.completeAuthorization) {
|
|
761
|
-
const completeOptions = {
|
|
762
|
-
principal: session.principal,
|
|
763
|
-
connection: connectionContext,
|
|
764
|
-
callbackUrl: session.callbackUrl,
|
|
765
|
-
callback
|
|
766
|
-
};
|
|
767
|
-
if (session.resume !== undefined)
|
|
768
|
-
completeOptions.resume = session.resume;
|
|
769
|
-
token = await authorization.completeAuthorization(completeOptions);
|
|
770
|
-
}
|
|
771
|
-
else {
|
|
772
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "callback_not_supported", retryable: false });
|
|
773
|
-
}
|
|
774
|
-
await this.saveGrantFromToken(compiled.name, session.principal, session.principalKey, token);
|
|
775
|
-
return this.authorizationSessionStore.updateSession(session.id, { status: "completed" });
|
|
295
|
+
return this.authorization.completeAuthorization(callback);
|
|
776
296
|
}
|
|
777
297
|
async getAuthorizationSession(id) {
|
|
778
|
-
return this.
|
|
298
|
+
return this.authorization.getAuthorizationSession(id);
|
|
779
299
|
}
|
|
780
300
|
async updateAuthorizationSession(id, patch) {
|
|
781
|
-
return this.
|
|
301
|
+
return this.authorization.updateAuthorizationSession(id, patch);
|
|
782
302
|
}
|
|
783
303
|
async close() {
|
|
784
304
|
await Promise.allSettled([...this.clients.values()].map((client) => client.close()));
|
|
@@ -788,7 +308,7 @@ export class RuntimeConnectionRegistry {
|
|
|
788
308
|
return this.options.fetch(input, init);
|
|
789
309
|
}
|
|
790
310
|
async processEnvironment(connection) {
|
|
791
|
-
const context = this.factoryContext(connection);
|
|
311
|
+
const context = this.authorization.factoryContext(connection);
|
|
792
312
|
const environment = {};
|
|
793
313
|
for (const name of connection.requiredConfig ?? []) {
|
|
794
314
|
const value = context.config[name];
|
|
@@ -818,493 +338,27 @@ export class RuntimeConnectionRegistry {
|
|
|
818
338
|
return environment;
|
|
819
339
|
}
|
|
820
340
|
async credentialValue(connection, name, purpose) {
|
|
821
|
-
return this.factoryContext(connection).credentials.get(name, { purpose, delivery: "relay" });
|
|
341
|
+
return this.authorization.factoryContext(connection).credentials.get(name, { purpose, delivery: "relay" });
|
|
822
342
|
}
|
|
823
|
-
async resolveConnection(compiled) {
|
|
343
|
+
async resolveConnection(compiled, session) {
|
|
344
|
+
if (session) {
|
|
345
|
+
return loadResolvedConnection(this.options, compiled, this.authorization.factoryContext(compiled, session));
|
|
346
|
+
}
|
|
824
347
|
const existing = this.resolved.get(compiled.name);
|
|
825
348
|
if (existing)
|
|
826
349
|
return existing;
|
|
827
|
-
const promise = this.
|
|
350
|
+
const promise = loadResolvedConnection(this.options, compiled, this.authorization.factoryContext(compiled));
|
|
828
351
|
this.resolved.set(compiled.name, promise);
|
|
829
352
|
return promise;
|
|
830
353
|
}
|
|
831
|
-
async loadResolvedConnection(compiled) {
|
|
832
|
-
const platform = this.options.platform ?? process.platform;
|
|
833
|
-
if (compiled.hostRequirements?.platforms?.length && !compiled.hostRequirements.platforms.includes(platform)) {
|
|
834
|
-
throw new Error(`${compiled.hostRequirements.message} Current runtime platform: ${platform}.`);
|
|
835
|
-
}
|
|
836
|
-
const loaded = await this.options.loadDefinition(compiled);
|
|
837
|
-
const factoryContext = this.factoryContext(compiled);
|
|
838
|
-
const dynamic = compiled.source.path.startsWith("dynamic://");
|
|
839
|
-
const raw = !dynamic && isConnectionFactory(loaded)
|
|
840
|
-
? await loaded.create(factoryContext)
|
|
841
|
-
: loaded;
|
|
842
|
-
if (!isRecord(raw))
|
|
843
|
-
throw new Error(`Connection ${compiled.name} did not export an object.`);
|
|
844
|
-
if (compiled.protocol === "mcp") {
|
|
845
|
-
if (raw.transport === "stdio") {
|
|
846
|
-
if (compiled.source.path.startsWith("dynamic://")) {
|
|
847
|
-
throw new Error(`Dynamic MCP connection ${compiled.name} cannot launch a stdio process.`);
|
|
848
|
-
}
|
|
849
|
-
if (typeof raw.command !== "string" || !raw.command.trim()) {
|
|
850
|
-
throw new Error(`Stdio MCP connection ${compiled.name} must export command.`);
|
|
851
|
-
}
|
|
852
|
-
if (raw.args !== undefined && (!Array.isArray(raw.args) || raw.args.some((argument) => typeof argument !== "string"))) {
|
|
853
|
-
throw new Error(`Stdio MCP connection ${compiled.name} args must contain only strings.`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
else if (raw.transport === "relay") {
|
|
857
|
-
if (compiled.source.path.startsWith("dynamic://")) {
|
|
858
|
-
throw new Error(`Dynamic MCP connection ${compiled.name} cannot use a device relay.`);
|
|
859
|
-
}
|
|
860
|
-
if (typeof raw.url !== "string" || typeof raw.credential !== "string") {
|
|
861
|
-
throw new Error(`Relay MCP connection ${compiled.name} must export url and credential.`);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
else if (typeof raw.url !== "string") {
|
|
865
|
-
throw new Error(`HTTP MCP connection ${compiled.name} must export url.`);
|
|
866
|
-
}
|
|
867
|
-
if (typeof raw.description !== "string")
|
|
868
|
-
throw new Error(`MCP connection ${compiled.name} must export description.`);
|
|
869
|
-
return { compiled, definition: raw };
|
|
870
|
-
}
|
|
871
|
-
if (compiled.protocol === "openapi") {
|
|
872
|
-
if (raw.spec === undefined)
|
|
873
|
-
throw new Error(`OpenAPI connection ${compiled.name} must export spec.`);
|
|
874
|
-
if (typeof raw.description !== "string")
|
|
875
|
-
throw new Error(`OpenAPI connection ${compiled.name} must export description.`);
|
|
876
|
-
return { compiled, definition: raw };
|
|
877
|
-
}
|
|
878
|
-
if (compiled.protocol === "a2a") {
|
|
879
|
-
if (typeof raw.agentCardUrl !== "string")
|
|
880
|
-
throw new Error(`A2A connection ${compiled.name} must export agentCardUrl.`);
|
|
881
|
-
if (typeof raw.description !== "string")
|
|
882
|
-
throw new Error(`A2A connection ${compiled.name} must export description.`);
|
|
883
|
-
if (!raw.access || typeof raw.access !== "object")
|
|
884
|
-
throw new Error(`A2A connection ${compiled.name} must export access.`);
|
|
885
|
-
return { compiled, definition: raw };
|
|
886
|
-
}
|
|
887
|
-
if (compiled.protocol === "http") {
|
|
888
|
-
if (typeof raw.baseUrl !== "string")
|
|
889
|
-
throw new Error(`HTTP API connection ${compiled.name} must export baseUrl.`);
|
|
890
|
-
if (typeof raw.description !== "string")
|
|
891
|
-
throw new Error(`HTTP API connection ${compiled.name} must export description.`);
|
|
892
|
-
if (!Array.isArray(raw.operations))
|
|
893
|
-
throw new Error(`HTTP API connection ${compiled.name} must export operations.`);
|
|
894
|
-
return { compiled, definition: raw };
|
|
895
|
-
}
|
|
896
|
-
if (compiled.protocol === "sdk") {
|
|
897
|
-
if (compiled.source.path.startsWith("dynamic://")) {
|
|
898
|
-
throw new Error(`Dynamic SDK API connection ${compiled.name} is not allowed.`);
|
|
899
|
-
}
|
|
900
|
-
if (typeof raw.description !== "string" || !Array.isArray(raw.operations) || typeof raw.execute !== "function") {
|
|
901
|
-
throw new Error(`SDK API connection ${compiled.name} must export description, operations, and execute.`);
|
|
902
|
-
}
|
|
903
|
-
if (!raw.access || typeof raw.access !== "object") {
|
|
904
|
-
throw new Error(`SDK API connection ${compiled.name} must export access.`);
|
|
905
|
-
}
|
|
906
|
-
return { compiled, definition: raw };
|
|
907
|
-
}
|
|
908
|
-
if (compiled.protocol === "cli") {
|
|
909
|
-
if (compiled.source.path.startsWith("dynamic://")) {
|
|
910
|
-
throw new Error(`Dynamic CLI connection ${compiled.name} is not allowed.`);
|
|
911
|
-
}
|
|
912
|
-
if (raw.transport !== "sandbox" || typeof raw.command !== "string" || !raw.command.trim()) {
|
|
913
|
-
throw new Error(`Sandbox CLI connection ${compiled.name} must export transport "sandbox" and command.`);
|
|
914
|
-
}
|
|
915
|
-
if (typeof raw.description !== "string" || !Array.isArray(raw.tools)) {
|
|
916
|
-
throw new Error(`Sandbox CLI connection ${compiled.name} must export description and tools.`);
|
|
917
|
-
}
|
|
918
|
-
return { compiled, definition: raw };
|
|
919
|
-
}
|
|
920
|
-
if (compiled.protocol === "credential") {
|
|
921
|
-
if (typeof raw.description !== "string" || !raw.auth || !raw.materialize || typeof raw.materialize !== "object") {
|
|
922
|
-
throw new Error(`Credential connection ${compiled.name} must export description, auth, and materialize.`);
|
|
923
|
-
}
|
|
924
|
-
return { compiled, definition: raw };
|
|
925
|
-
}
|
|
926
|
-
throw new Error(`Connection ${compiled.name} is declaration-only.`);
|
|
927
|
-
}
|
|
928
354
|
targetConnections(name) {
|
|
929
|
-
return this.options.connections.filter((connection) =>
|
|
930
|
-
if (!isLiveConnection(connection))
|
|
931
|
-
return false;
|
|
932
|
-
return !name || connection.name === name;
|
|
933
|
-
});
|
|
355
|
+
return this.options.connections.filter((connection) => isLiveConnection(connection) && (!name || connection.name === name));
|
|
934
356
|
}
|
|
935
|
-
/**
|
|
936
|
-
* The single audience rule: personal (user-subject) connections exist only
|
|
937
|
-
* on private surfaces. Everything else — discovery, execution, pairing,
|
|
938
|
-
* and credential materialization — derives from this one predicate.
|
|
939
|
-
*/
|
|
940
357
|
connectionAccessible(connection) {
|
|
941
|
-
|
|
942
|
-
return audienceAllowsConnectionAccess(audience, connection.subject);
|
|
358
|
+
return audienceAllowsConnectionAccess(this.options.audience ?? { private: true }, connection.subject);
|
|
943
359
|
}
|
|
944
360
|
toolAllowed(connection) {
|
|
945
361
|
return isLiveConnection(connection) && this.connectionAccessible(connection);
|
|
946
362
|
}
|
|
947
|
-
connectionFailure(connection, error) {
|
|
948
|
-
if (isConnectionAuthorizationRequiredError(error)) {
|
|
949
|
-
return {
|
|
950
|
-
connection: connection.name,
|
|
951
|
-
description: connection.description ?? `${connection.name} connection`,
|
|
952
|
-
needsAuthorization: true,
|
|
953
|
-
error: error.reason
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
if (isConnectionAuthorizationFailedError(error)) {
|
|
957
|
-
return {
|
|
958
|
-
connection: connection.name,
|
|
959
|
-
description: connection.description ?? `${connection.name} connection`,
|
|
960
|
-
error: `${error.reason}: ${error.message}`
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
return {
|
|
964
|
-
connection: connection.name,
|
|
965
|
-
description: connection.description ?? `${connection.name} connection`,
|
|
966
|
-
error: error instanceof Error ? error.message : String(error)
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
async resolveToken(connection, auth, context, resolvedUrl) {
|
|
970
|
-
const authorization = await normalizeAuthorization(auth, context);
|
|
971
|
-
const principal = connectionPrincipalFor(connection, authorization, context);
|
|
972
|
-
const principalKey = connectionPrincipalKey(principal);
|
|
973
|
-
const cacheKey = tokenCacheKey(connection.name, principal);
|
|
974
|
-
const cached = this.tokenCache.get(cacheKey);
|
|
975
|
-
if (cached && !isTokenExpired(cached.token))
|
|
976
|
-
return cached.token;
|
|
977
|
-
const connectionContext = { url: resolvedUrl ?? connection.url ?? "" };
|
|
978
|
-
if (authorization.kind === "oauth" || authorization.kind === "interactive") {
|
|
979
|
-
const stored = await this.grantStore.getGrant(connection.name, principalKey);
|
|
980
|
-
if (stored && !storedGrantExpired(stored)) {
|
|
981
|
-
const token = grantToTokenResult(stored);
|
|
982
|
-
this.tokenCache.set(cacheKey, { token, authorization, principal, connection: connectionContext });
|
|
983
|
-
return token;
|
|
984
|
-
}
|
|
985
|
-
if (stored?.refreshToken && authorization.kind === "oauth" && authorization.oauth) {
|
|
986
|
-
const refreshed = await this.refreshOAuthGrant(authorization.oauth, connection.name, principal, principalKey, stored.refreshToken);
|
|
987
|
-
// saveGrantFromToken preserved the credential bag on the grant; mirror
|
|
988
|
-
// that on the returned token so template rendering sees it too.
|
|
989
|
-
if (!refreshed.credential && stored.credential)
|
|
990
|
-
refreshed.credential = stored.credential;
|
|
991
|
-
this.tokenCache.set(cacheKey, { token: refreshed, authorization, principal, connection: connectionContext });
|
|
992
|
-
return refreshed;
|
|
993
|
-
}
|
|
994
|
-
if (authorization.kind === "oauth") {
|
|
995
|
-
throw await this.createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
try {
|
|
999
|
-
const token = await authorization.getToken({ principal, connection: connectionContext });
|
|
1000
|
-
this.tokenCache.set(cacheKey, { token, authorization, principal, connection: connectionContext });
|
|
1001
|
-
return token;
|
|
1002
|
-
}
|
|
1003
|
-
catch (error) {
|
|
1004
|
-
if (isConnectionAuthorizationRequiredError(error) && authorization.kind === "interactive") {
|
|
1005
|
-
if (this.authorizationIntent.getStore() === "discover") {
|
|
1006
|
-
throw this.authorizationRequiredWithoutSession(connection, error);
|
|
1007
|
-
}
|
|
1008
|
-
throw await this.createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext, error);
|
|
1009
|
-
}
|
|
1010
|
-
if (isConnectionAuthorizationRequiredError(error) || isConnectionAuthorizationFailedError(error))
|
|
1011
|
-
throw error;
|
|
1012
|
-
throw error;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
resolverContext(connection, session = this.options.session) {
|
|
1016
|
-
const factory = this.factoryContext(connection, session);
|
|
1017
|
-
return {
|
|
1018
|
-
config: factory.config,
|
|
1019
|
-
credentials: factory.credentials,
|
|
1020
|
-
fetch: this.options.fetch,
|
|
1021
|
-
session
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
factoryContext(connection, session = this.options.session) {
|
|
1025
|
-
const principal = this.connectionPrincipal(connection, session);
|
|
1026
|
-
const audience = this.options.audience ?? { private: true };
|
|
1027
|
-
const identity = () => {
|
|
1028
|
-
const runId = this.executionContext.getStore()?.runId ?? this.options.runId;
|
|
1029
|
-
return { principal, audience, ...(runId ? { runId } : {}) };
|
|
1030
|
-
};
|
|
1031
|
-
const configNames = [...(connection.requiredConfig ?? []), ...(connection.optionalConfig ?? [])];
|
|
1032
|
-
return {
|
|
1033
|
-
connectionName: connection.name,
|
|
1034
|
-
agentId: this.options.agentId ?? "",
|
|
1035
|
-
principal,
|
|
1036
|
-
audience,
|
|
1037
|
-
config: Object.freeze(Object.fromEntries(configNames.map((name) => [name, this.options.config?.[name]]))),
|
|
1038
|
-
credentials: this.credentialBroker.scoped(connection, identity),
|
|
1039
|
-
...(this.options.blob && (this.executionContext.getStore()?.runId ?? this.options.runId) ? {
|
|
1040
|
-
assets: createConnectionAssetApi({
|
|
1041
|
-
blob: this.options.blob,
|
|
1042
|
-
runId: (this.executionContext.getStore()?.runId ?? this.options.runId),
|
|
1043
|
-
sourceConnection: connection.name
|
|
1044
|
-
})
|
|
1045
|
-
} : {}),
|
|
1046
|
-
fetch: this.options.fetch,
|
|
1047
|
-
session
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
connectionPrincipal(connection, session = this.options.session) {
|
|
1051
|
-
if (connection.subject !== "user")
|
|
1052
|
-
return { type: "app" };
|
|
1053
|
-
if (session.principal?.type === "user")
|
|
1054
|
-
return session.principal;
|
|
1055
|
-
if (session.userId) {
|
|
1056
|
-
return {
|
|
1057
|
-
type: "user",
|
|
1058
|
-
id: session.userId,
|
|
1059
|
-
issuer: session.channel ?? "assembly-line"
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
throw new ConnectionConfigurationError(`Connection ${connection.name} requires an authenticated user principal.`);
|
|
1063
|
-
}
|
|
1064
|
-
async withExecutionContext(execution, fn) {
|
|
1065
|
-
return execution ? this.executionContext.run(execution, fn) : fn();
|
|
1066
|
-
}
|
|
1067
|
-
async createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext, cause) {
|
|
1068
|
-
if (this.authorizationIntent.getStore() === "discover") {
|
|
1069
|
-
return this.authorizationRequiredWithoutSession(connection, cause);
|
|
1070
|
-
}
|
|
1071
|
-
const key = `${connection.name}\0${principalKey}`;
|
|
1072
|
-
const pending = this.pendingAuthorizationCreations.get(key);
|
|
1073
|
-
if (pending)
|
|
1074
|
-
return pending;
|
|
1075
|
-
const creation = this.createAuthorizationRequiredErrorWithContext(connection, authorization, principal, principalKey, context, connectionContext, cause);
|
|
1076
|
-
this.pendingAuthorizationCreations.set(key, creation);
|
|
1077
|
-
try {
|
|
1078
|
-
return await creation;
|
|
1079
|
-
}
|
|
1080
|
-
finally {
|
|
1081
|
-
if (this.pendingAuthorizationCreations.get(key) === creation) {
|
|
1082
|
-
this.pendingAuthorizationCreations.delete(key);
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
authorizationRequiredWithoutSession(connection, cause) {
|
|
1087
|
-
const options = {
|
|
1088
|
-
reason: cause?.reason ?? "authorization_required"
|
|
1089
|
-
};
|
|
1090
|
-
if (cause?.message)
|
|
1091
|
-
options.message = cause.message;
|
|
1092
|
-
if (cause?.challenge)
|
|
1093
|
-
options.challenge = cause.challenge;
|
|
1094
|
-
return new ConnectionAuthorizationRequiredError(connection.name, options);
|
|
1095
|
-
}
|
|
1096
|
-
async createAuthorizationRequiredErrorWithContext(connection, authorization, principal, principalKey, context, connectionContext, cause) {
|
|
1097
|
-
await this.authorizationSessionStore.deleteExpiredSessions?.(Date.now());
|
|
1098
|
-
const existing = await this.authorizationSessionStore.findPendingSession(connection.name, principalKey);
|
|
1099
|
-
if (existing) {
|
|
1100
|
-
const options = {
|
|
1101
|
-
reason: cause?.reason ?? "authorization_required",
|
|
1102
|
-
challenge: existing.challenge
|
|
1103
|
-
};
|
|
1104
|
-
if (cause?.message)
|
|
1105
|
-
options.message = cause.message;
|
|
1106
|
-
return new ConnectionAuthorizationRequiredError(connection.name, {
|
|
1107
|
-
...options
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
const callbackUrl = this.callbackUrl();
|
|
1111
|
-
const id = randomUUID();
|
|
1112
|
-
const state = randomUrlToken();
|
|
1113
|
-
const expiresAt = Date.now() + 10 * 60_000;
|
|
1114
|
-
let challenge;
|
|
1115
|
-
let resume;
|
|
1116
|
-
let codeVerifier;
|
|
1117
|
-
if (authorization.kind === "oauth" && authorization.oauth) {
|
|
1118
|
-
codeVerifier = randomUrlToken(48);
|
|
1119
|
-
const oauthChallengeOptions = {
|
|
1120
|
-
state,
|
|
1121
|
-
callbackUrl,
|
|
1122
|
-
codeVerifier
|
|
1123
|
-
};
|
|
1124
|
-
if (authorization.displayName)
|
|
1125
|
-
oauthChallengeOptions.displayName = authorization.displayName;
|
|
1126
|
-
challenge = buildOAuthChallenge(authorization.oauth, oauthChallengeOptions);
|
|
1127
|
-
}
|
|
1128
|
-
else if (authorization.startAuthorization) {
|
|
1129
|
-
const started = await authorization.startAuthorization({ principal, connection: connectionContext, callbackUrl, state });
|
|
1130
|
-
challenge = started.challenge;
|
|
1131
|
-
resume = started.resume;
|
|
1132
|
-
if (!challenge.url && cause?.challenge?.url)
|
|
1133
|
-
challenge.url = cause.challenge.url;
|
|
1134
|
-
if (!challenge.instructions && cause?.challenge?.instructions)
|
|
1135
|
-
challenge.instructions = cause.challenge.instructions;
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
challenge = cause?.challenge ?? { instructions: `Authorize connection ${connection.name} and retry.` };
|
|
1139
|
-
if (authorization.displayName)
|
|
1140
|
-
challenge.displayName = authorization.displayName;
|
|
1141
|
-
}
|
|
1142
|
-
challenge.sessionId = id;
|
|
1143
|
-
challenge.state = state;
|
|
1144
|
-
challenge.expiresAt = expiresAt;
|
|
1145
|
-
const session = {
|
|
1146
|
-
id,
|
|
1147
|
-
state,
|
|
1148
|
-
connectionName: connection.name,
|
|
1149
|
-
principal,
|
|
1150
|
-
principalKey,
|
|
1151
|
-
session: context.session,
|
|
1152
|
-
callbackUrl,
|
|
1153
|
-
challenge,
|
|
1154
|
-
status: "pending",
|
|
1155
|
-
expiresAt,
|
|
1156
|
-
createdAt: timestamp(),
|
|
1157
|
-
updatedAt: timestamp()
|
|
1158
|
-
};
|
|
1159
|
-
if (codeVerifier)
|
|
1160
|
-
session.codeVerifier = codeVerifier;
|
|
1161
|
-
if (resume !== undefined)
|
|
1162
|
-
session.resume = resume;
|
|
1163
|
-
const execution = this.executionContext.getStore();
|
|
1164
|
-
const runId = execution?.runId ?? this.options.runId;
|
|
1165
|
-
if (runId)
|
|
1166
|
-
session.runId = runId;
|
|
1167
|
-
if (execution?.toolCallId)
|
|
1168
|
-
session.toolCallId = execution.toolCallId;
|
|
1169
|
-
if (execution?.toolName)
|
|
1170
|
-
session.toolName = execution.toolName;
|
|
1171
|
-
await this.authorizationSessionStore.createSession(session);
|
|
1172
|
-
const errorOptions = {
|
|
1173
|
-
reason: cause?.reason ?? "authorization_required",
|
|
1174
|
-
challenge
|
|
1175
|
-
};
|
|
1176
|
-
if (cause?.message)
|
|
1177
|
-
errorOptions.message = cause.message;
|
|
1178
|
-
return new ConnectionAuthorizationRequiredError(connection.name, errorOptions);
|
|
1179
|
-
}
|
|
1180
|
-
callbackUrl() {
|
|
1181
|
-
const base = (this.options.callbackBaseUrl ?? this.options.config?.ASSEMBLY_LINE_PUBLIC_URL ?? "http://localhost").replace(/\/+$/u, "");
|
|
1182
|
-
return `${base}/assembly-line/connections/callback`;
|
|
1183
|
-
}
|
|
1184
|
-
async exchangeOAuthAuthorizationCode(oauth, session, callback) {
|
|
1185
|
-
const code = callback.params.code;
|
|
1186
|
-
if (!code) {
|
|
1187
|
-
throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "missing_code", retryable: true });
|
|
1188
|
-
}
|
|
1189
|
-
return this.requestOAuthToken(oauth, {
|
|
1190
|
-
grant_type: "authorization_code",
|
|
1191
|
-
code,
|
|
1192
|
-
redirect_uri: oauth.redirectUri ?? session.callbackUrl,
|
|
1193
|
-
code_verifier: session.codeVerifier
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
|
-
async refreshOAuthGrant(oauth, connectionName, principal, principalKey, refreshToken) {
|
|
1197
|
-
const token = await this.requestOAuthToken(oauth, {
|
|
1198
|
-
grant_type: "refresh_token",
|
|
1199
|
-
refresh_token: refreshToken
|
|
1200
|
-
});
|
|
1201
|
-
if (!token.refreshToken)
|
|
1202
|
-
token.refreshToken = refreshToken;
|
|
1203
|
-
await this.saveGrantFromToken(connectionName, principal, principalKey, token);
|
|
1204
|
-
return token;
|
|
1205
|
-
}
|
|
1206
|
-
async requestOAuthToken(oauth, fields) {
|
|
1207
|
-
const body = new URLSearchParams();
|
|
1208
|
-
const headers = {
|
|
1209
|
-
accept: "application/json",
|
|
1210
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
1211
|
-
};
|
|
1212
|
-
if (oauth.tokenEndpointAuthMethod === "client_secret_basic") {
|
|
1213
|
-
if (!oauth.clientSecret) {
|
|
1214
|
-
throw new ConnectionAuthorizationFailedError("oauth", { reason: "missing_client_secret" });
|
|
1215
|
-
}
|
|
1216
|
-
headers.authorization = `Basic ${Buffer.from(`${oauth.clientId}:${oauth.clientSecret}`).toString("base64")}`;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
body.set("client_id", oauth.clientId);
|
|
1220
|
-
if (oauth.clientSecret)
|
|
1221
|
-
body.set("client_secret", oauth.clientSecret);
|
|
1222
|
-
}
|
|
1223
|
-
for (const [key, value] of Object.entries(oauth.tokenParams ?? {}))
|
|
1224
|
-
body.set(key, value);
|
|
1225
|
-
for (const [key, value] of Object.entries(fields)) {
|
|
1226
|
-
if (value !== undefined)
|
|
1227
|
-
body.set(key, value);
|
|
1228
|
-
}
|
|
1229
|
-
const response = await this.request(oauth.tokenUrl, {
|
|
1230
|
-
method: "POST",
|
|
1231
|
-
headers,
|
|
1232
|
-
body
|
|
1233
|
-
});
|
|
1234
|
-
const payload = await readResponseBody(response);
|
|
1235
|
-
if (!response.ok || !isRecord(payload)) {
|
|
1236
|
-
throw new ConnectionAuthorizationFailedError("oauth", {
|
|
1237
|
-
reason: "token_exchange_failed",
|
|
1238
|
-
message: `OAuth token exchange failed with HTTP ${response.status}.`
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
const accessToken = stringValue(payload.access_token);
|
|
1242
|
-
if (!accessToken) {
|
|
1243
|
-
throw new ConnectionAuthorizationFailedError("oauth", { reason: "missing_access_token" });
|
|
1244
|
-
}
|
|
1245
|
-
const token = { token: accessToken };
|
|
1246
|
-
const refreshToken = stringValue(payload.refresh_token);
|
|
1247
|
-
const tokenType = oauth.tokenType?.trim() || stringValue(payload.token_type);
|
|
1248
|
-
const scope = stringValue(payload.scope);
|
|
1249
|
-
const expiresIn = typeof payload.expires_in === "number" ? payload.expires_in : Number(payload.expires_in);
|
|
1250
|
-
if (refreshToken)
|
|
1251
|
-
token.refreshToken = refreshToken;
|
|
1252
|
-
if (tokenType)
|
|
1253
|
-
token.tokenType = tokenType;
|
|
1254
|
-
if (scope)
|
|
1255
|
-
token.scopes = scope.split(/\s+/u).filter(Boolean);
|
|
1256
|
-
if (Number.isFinite(expiresIn) && expiresIn > 0)
|
|
1257
|
-
token.expiresAt = Date.now() + expiresIn * 1000;
|
|
1258
|
-
// Non-standard response fields (provider identity ids and the like) ride
|
|
1259
|
-
// along as the grant's credential bag; RFC 6749 §5.1 permits extras.
|
|
1260
|
-
const credential = {};
|
|
1261
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
1262
|
-
if (OAUTH_STANDARD_TOKEN_FIELDS.has(key))
|
|
1263
|
-
continue;
|
|
1264
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
1265
|
-
credential[key] = value;
|
|
1266
|
-
}
|
|
1267
|
-
if (Object.keys(credential).length > 0)
|
|
1268
|
-
token.credential = credential;
|
|
1269
|
-
return token;
|
|
1270
|
-
}
|
|
1271
|
-
async saveGrantFromToken(connectionName, principal, principalKey, token) {
|
|
1272
|
-
const now = timestamp();
|
|
1273
|
-
const existing = await this.grantStore.getGrant(connectionName, principalKey);
|
|
1274
|
-
const grant = {
|
|
1275
|
-
connectionName,
|
|
1276
|
-
principalKey,
|
|
1277
|
-
principal,
|
|
1278
|
-
token: token.token,
|
|
1279
|
-
createdAt: existing?.createdAt ?? now,
|
|
1280
|
-
updatedAt: now
|
|
1281
|
-
};
|
|
1282
|
-
if (token.refreshToken)
|
|
1283
|
-
grant.refreshToken = token.refreshToken;
|
|
1284
|
-
else if (existing?.refreshToken)
|
|
1285
|
-
grant.refreshToken = existing.refreshToken;
|
|
1286
|
-
if (token.tokenType)
|
|
1287
|
-
grant.tokenType = token.tokenType;
|
|
1288
|
-
if (token.scopes)
|
|
1289
|
-
grant.scopes = token.scopes;
|
|
1290
|
-
if (token.expiresAt)
|
|
1291
|
-
grant.expiresAt = token.expiresAt;
|
|
1292
|
-
// A response that omits extras must not erase identity captured earlier.
|
|
1293
|
-
if (token.credential)
|
|
1294
|
-
grant.credential = token.credential;
|
|
1295
|
-
else if (existing?.credential)
|
|
1296
|
-
grant.credential = existing.credential;
|
|
1297
|
-
await this.grantStore.saveGrant(grant);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
function sandboxCredentialWasRequested(run, connectionName) {
|
|
1301
|
-
const requests = run?.input.sandboxCredentials;
|
|
1302
|
-
return Boolean(requests
|
|
1303
|
-
&& typeof requests === "object"
|
|
1304
|
-
&& !Array.isArray(requests)
|
|
1305
|
-
&& Object.prototype.hasOwnProperty.call(requests, connectionName));
|
|
1306
|
-
}
|
|
1307
|
-
function isConnectionFactory(value) {
|
|
1308
|
-
return Boolean(value && typeof value === "object" && typeof value.create === "function");
|
|
1309
363
|
}
|
|
1310
364
|
//# sourceMappingURL=connections.js.map
|