skybridge 0.0.0-dev.c884459 → 0.0.0-dev.c88e592
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 +155 -0
- package/dist/cli/build-helpers.d.ts +11 -0
- package/dist/cli/build-helpers.js +107 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.js +132 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +18 -0
- package/dist/cli/detect-port.js +49 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/header.js +1 -1
- package/dist/cli/header.js.map +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/resolve-skybridge-config.js +18 -0
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-command.js.map +1 -1
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +90 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/telemetry.js +2 -0
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/cli/tunnel-control-server.d.ts +9 -0
- package/dist/cli/tunnel-control-server.js +31 -0
- package/dist/cli/tunnel-control-server.js.map +1 -0
- package/dist/cli/tunnel-control-server.test.js +39 -0
- package/dist/cli/tunnel-control-server.test.js.map +1 -0
- package/dist/cli/tunnel-handler.d.ts +3 -0
- package/dist/cli/tunnel-handler.js +48 -0
- package/dist/cli/tunnel-handler.js.map +1 -0
- package/dist/cli/tunnel-handler.test.js +105 -0
- package/dist/cli/tunnel-handler.test.js.map +1 -0
- package/dist/cli/tunnel.d.ts +57 -0
- package/dist/cli/tunnel.js +154 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/tunnel.test.js +190 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +3 -2
- package/dist/cli/use-execute-steps.js +6 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +26 -0
- package/dist/cli/use-nodemon.js +106 -0
- package/dist/cli/use-nodemon.js.map +1 -0
- package/dist/cli/use-open-browser.d.ts +1 -0
- package/dist/cli/use-open-browser.js +44 -0
- package/dist/cli/use-open-browser.js.map +1 -0
- package/dist/cli/use-tunnel.d.ts +14 -0
- package/dist/cli/use-tunnel.js +131 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +15 -0
- package/dist/cli/use-typescript-check.js +97 -0
- package/dist/cli/use-typescript-check.js.map +1 -0
- package/dist/commands/build.d.ts +0 -3
- package/dist/commands/build.js +3 -16
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +5 -1
- package/dist/commands/dev.js +110 -13
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +37 -15
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/telemetry/disable.js.map +1 -1
- package/dist/commands/telemetry/enable.js.map +1 -1
- package/dist/commands/telemetry/status.js.map +1 -1
- package/dist/context-warnings.d.ts +5 -0
- package/dist/context-warnings.js +31 -0
- package/dist/context-warnings.js.map +1 -0
- package/dist/context-warnings.test.d.ts +1 -0
- package/dist/context-warnings.test.js +28 -0
- package/dist/context-warnings.test.js.map +1 -0
- package/dist/server/app.d.ts +220 -0
- package/dist/server/app.js +295 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/app.test.d.ts +1 -0
- package/dist/server/app.test.js +97 -0
- package/dist/server/app.test.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
- package/dist/server/asset-base-url-transform-plugin.js +48 -0
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.js +134 -0
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
- package/dist/server/auth/discovery.d.ts +33 -0
- package/dist/server/auth/discovery.js +56 -0
- package/dist/server/auth/discovery.js.map +1 -0
- package/dist/server/auth/discovery.test.d.ts +1 -0
- package/dist/server/auth/discovery.test.js +93 -0
- package/dist/server/auth/discovery.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +48 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +40 -0
- package/dist/server/auth/providers/auth0.js +36 -0
- package/dist/server/auth/providers/auth0.js.map +1 -0
- package/dist/server/auth/providers/auth0.test.d.ts +1 -0
- package/dist/server/auth/providers/auth0.test.js +50 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/authplane.d.ts +49 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.d.ts +1 -0
- package/dist/server/auth/providers/authplane.test.js +143 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +51 -0
- package/dist/server/auth/providers/clerk.js +19 -0
- package/dist/server/auth/providers/clerk.js.map +1 -0
- package/dist/server/auth/providers/clerk.test.d.ts +1 -0
- package/dist/server/auth/providers/clerk.test.js +30 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +42 -0
- package/dist/server/auth/providers/custom.js +59 -0
- package/dist/server/auth/providers/custom.js.map +1 -0
- package/dist/server/auth/providers/custom.test.d.ts +1 -0
- package/dist/server/auth/providers/custom.test.js +161 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +44 -0
- package/dist/server/auth/providers/descope.js +44 -0
- package/dist/server/auth/providers/descope.js.map +1 -0
- package/dist/server/auth/providers/descope.test.d.ts +1 -0
- package/dist/server/auth/providers/descope.test.js +62 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/shared.d.ts +2 -0
- package/dist/server/auth/providers/shared.js +6 -0
- package/dist/server/auth/providers/shared.js.map +1 -0
- package/dist/server/auth/providers/shared.test.d.ts +1 -0
- package/dist/server/auth/providers/shared.test.js +10 -0
- package/dist/server/auth/providers/shared.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +25 -0
- package/dist/server/auth/providers/stytch.js +16 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/verify-spy.d.ts +12 -0
- package/dist/server/auth/providers/verify-spy.js +21 -0
- package/dist/server/auth/providers/verify-spy.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +34 -0
- package/dist/server/auth/providers/workos.js +16 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +6 -0
- package/dist/server/auth/setup.js +91 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +514 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +48 -0
- package/dist/server/auth/verify.js +62 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +165 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth-extra.test-d.d.ts +1 -0
- package/dist/server/auth-extra.test-d.js +130 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +57 -0
- package/dist/server/auth.js +27 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +28 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +67 -0
- package/dist/server/content-helpers.js +79 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/content-helpers.test.d.ts +1 -0
- package/dist/server/content-helpers.test.js +70 -0
- package/dist/server/content-helpers.test.js.map +1 -0
- package/dist/server/events.d.ts +123 -0
- package/dist/server/events.js +374 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/events.test.d.ts +1 -0
- package/dist/server/events.test.js +154 -0
- package/dist/server/events.test.js.map +1 -0
- package/dist/server/express.d.ts +31 -0
- package/dist/server/express.js +107 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +608 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +22 -3
- package/dist/server/index.js +17 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/inferUtilityTypes.d.ts +6 -6
- package/dist/server/inferUtilityTypes.js.map +1 -1
- package/dist/server/mcp-errors.test.d.ts +1 -0
- package/dist/server/mcp-errors.test.js +41 -0
- package/dist/server/mcp-errors.test.js.map +1 -0
- package/dist/server/metric.d.ts +14 -0
- package/dist/server/metric.js +62 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +162 -0
- package/dist/server/middleware.js +126 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +601 -0
- package/dist/server/middleware.test.js.map +1 -0
- package/dist/server/register-tool.test.d.ts +1 -0
- package/dist/server/register-tool.test.js +110 -0
- package/dist/server/register-tool.test.js.map +1 -0
- package/dist/server/requestOrigin.d.ts +7 -0
- package/dist/server/requestOrigin.js +25 -0
- package/dist/server/requestOrigin.js.map +1 -0
- package/dist/server/server.d.ts +534 -64
- package/dist/server/server.js +632 -109
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +86 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +27 -0
- package/dist/server/skills.js +188 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +243 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/templateHelper.d.ts +2 -7
- package/dist/server/templateHelper.js +3 -22
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.d.ts +4 -0
- package/dist/server/templates.generated.js +43 -0
- package/dist/server/templates.generated.js.map +1 -0
- package/dist/server/tool-extra.test.d.ts +1 -0
- package/dist/server/tool-extra.test.js +88 -0
- package/dist/server/tool-extra.test.js.map +1 -0
- package/dist/server/tunnel-proxy-router.d.ts +7 -0
- package/dist/server/tunnel-proxy-router.js +110 -0
- package/dist/server/tunnel-proxy-router.js.map +1 -0
- package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
- package/dist/server/tunnel-proxy-router.test.js +229 -0
- package/dist/server/tunnel-proxy-router.test.js.map +1 -0
- package/dist/server/view-name.test-d.d.ts +1 -0
- package/dist/server/view-name.test-d.js +8 -0
- package/dist/server/view-name.test-d.js.map +1 -0
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +176 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/server/viewsDevServer.d.ts +14 -0
- package/dist/server/viewsDevServer.js +48 -0
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +21 -31
- package/dist/test/utils.js +222 -198
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/view.test.js +540 -0
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/views/index.d.ts +10 -0
- package/dist/views/index.js +3 -0
- package/dist/views/index.js.map +1 -0
- package/dist/views/scan-views.d.ts +16 -0
- package/dist/views/scan-views.js +88 -0
- package/dist/views/scan-views.js.map +1 -0
- package/dist/views/scan-views.test.d.ts +1 -0
- package/dist/views/scan-views.test.js +99 -0
- package/dist/views/scan-views.test.js.map +1 -0
- package/dist/views/validate-view.d.ts +1 -0
- package/dist/views/validate-view.js +9 -0
- package/dist/views/validate-view.js.map +1 -0
- package/dist/views/validate-view.test.d.ts +1 -0
- package/dist/views/validate-view.test.js +24 -0
- package/dist/views/validate-view.test.js.map +1 -0
- package/dist/web/bridges/adaptor.d.ts +56 -0
- package/dist/web/bridges/adaptor.js +397 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +264 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +7 -2
- package/dist/web/bridges/apps-sdk/bridge.js +15 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +1 -2
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +32 -14
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +9 -7
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +32 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +27 -31
- package/dist/web/bridges/mcp-app/bridge.js +118 -198
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +19 -2
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +17 -3
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +14 -2
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +2 -42
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +162 -15
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +1 -1
- package/dist/web/components/modal-provider.js +5 -6
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +43 -3
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +33 -27
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +35 -2
- package/dist/web/data-llm.js +34 -5
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +59 -37
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +40 -32
- package/dist/web/generate-helpers.js +38 -30
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +33 -31
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js +2 -2
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/helpers/state.d.ts +2 -2
- package/dist/web/helpers/state.js +11 -11
- package/dist/web/helpers/state.js.map +1 -1
- package/dist/web/helpers/state.test.js +9 -9
- package/dist/web/helpers/state.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +11 -3
- package/dist/web/hooks/index.js +10 -2
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
- package/dist/web/hooks/openai-extensions.test.js +107 -0
- package/dist/web/hooks/openai-extensions.test.js.map +1 -0
- package/dist/web/hooks/test/utils.d.ts +7 -2
- package/dist/web/hooks/test/utils.js +18 -3
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +47 -2
- package/dist/web/hooks/use-call-tool.js +43 -10
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +75 -29
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-deep-link.d.ts +22 -0
- package/dist/web/hooks/use-deep-link.js +34 -0
- package/dist/web/hooks/use-deep-link.js.map +1 -0
- package/dist/web/hooks/use-display-mode.d.ts +23 -3
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- package/dist/web/hooks/use-display-mode.test.js +57 -21
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +3 -0
- package/dist/web/hooks/use-download.js +7 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +103 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +34 -1
- package/dist/web/hooks/use-files.js +33 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +36 -4
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host.d.ts +44 -0
- package/dist/web/hooks/use-host.js +45 -0
- package/dist/web/hooks/use-host.js.map +1 -0
- package/dist/web/hooks/use-host.test.d.ts +1 -0
- package/dist/web/hooks/use-host.test.js +59 -0
- package/dist/web/hooks/use-host.test.js.map +1 -0
- package/dist/web/hooks/use-model-context.d.ts +44 -0
- package/dist/web/hooks/use-model-context.js +63 -0
- package/dist/web/hooks/use-model-context.js.map +1 -0
- package/dist/web/hooks/use-open-external.d.ts +20 -1
- package/dist/web/hooks/use-open-external.js +17 -1
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +40 -15
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-open-file.d.ts +20 -0
- package/dist/web/hooks/use-open-file.js +25 -0
- package/dist/web/hooks/use-open-file.js.map +1 -0
- package/dist/web/hooks/use-register-view-tool.d.ts +37 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +47 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +19 -4
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +16 -2
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +47 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +25 -1
- package/dist/web/hooks/use-send-follow-up-message.js +25 -2
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +29 -21
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +46 -14
- package/dist/web/hooks/use-tool-info.js +29 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -34
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +43 -38
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +5 -2
- package/dist/web/hooks/use-user.js +23 -4
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +105 -29
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +25 -0
- package/dist/web/hooks/use-view-state.js +42 -0
- package/dist/web/hooks/use-view-state.js.map +1 -0
- package/dist/web/hooks/use-view-state.test.d.ts +1 -0
- package/dist/web/hooks/use-view-state.test.js +212 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/hooks/use-viewport.d.ts +20 -0
- package/dist/web/hooks/use-viewport.js +21 -0
- package/dist/web/hooks/use-viewport.js.map +1 -0
- package/dist/web/hooks/use-viewport.test.d.ts +1 -0
- package/dist/web/hooks/use-viewport.test.js +109 -0
- package/dist/web/hooks/use-viewport.test.js.map +1 -0
- package/dist/web/index.d.ts +1 -2
- package/dist/web/index.js +1 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/mount-view.d.ts +21 -0
- package/dist/web/mount-view.js +43 -0
- package/dist/web/mount-view.js.map +1 -0
- package/dist/web/proxy.js +0 -1
- package/dist/web/proxy.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +59 -32
- package/tsconfig.base.json +33 -0
- package/dist/server/templates/development.hbs +0 -66
- package/dist/server/templates/production.hbs +0 -7
- package/dist/server/widgetsDevServer.d.ts +0 -12
- package/dist/server/widgetsDevServer.js +0 -47
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/test/widget.test.js +0 -255
- package/dist/test/widget.test.js.map +0 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -22
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -64
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -36
- package/dist/web/bridges/mcp-app/adaptor.js +0 -185
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
- package/dist/web/hooks/use-layout.d.ts +0 -22
- package/dist/web/hooks/use-layout.js +0 -23
- package/dist/web/hooks/use-layout.js.map +0 -1
- package/dist/web/hooks/use-layout.test.js +0 -96
- package/dist/web/hooks/use-layout.test.js.map +0 -1
- package/dist/web/hooks/use-widget-state.d.ts +0 -4
- package/dist/web/hooks/use-widget-state.js +0 -32
- package/dist/web/hooks/use-widget-state.js.map +0 -1
- package/dist/web/hooks/use-widget-state.test.js +0 -61
- package/dist/web/hooks/use-widget-state.test.js.map +0 -1
- package/dist/web/mount-widget.d.ts +0 -1
- package/dist/web/mount-widget.js +0 -27
- package/dist/web/mount-widget.js.map +0 -1
- package/dist/web/plugin/data-llm.test.js +0 -81
- package/dist/web/plugin/data-llm.test.js.map +0 -1
- package/dist/web/plugin/plugin.d.ts +0 -2
- package/dist/web/plugin/plugin.js +0 -39
- package/dist/web/plugin/plugin.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.d.ts +0 -12
- package/dist/web/plugin/transform-data-llm.js +0 -96
- package/dist/web/plugin/transform-data-llm.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.test.js +0 -81
- package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
- /package/dist/{test/widget.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
- /package/dist/{web/plugin/data-llm.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
- /package/dist/{web/plugin/transform-data-llm.test.d.ts → cli/tunnel.test.d.ts} +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a base64-encoded string.
|
|
3
|
+
* - `Uint8Array` input is encoded via `Buffer.toString("base64")`.
|
|
4
|
+
* - `string` input is assumed to be **already base64-encoded** and is returned
|
|
5
|
+
* as-is. Passing raw/unencoded string bytes will produce invalid MCP content.
|
|
6
|
+
*/
|
|
7
|
+
function toBase64(data) {
|
|
8
|
+
if (typeof data === "string") {
|
|
9
|
+
return data;
|
|
10
|
+
}
|
|
11
|
+
return Buffer.from(data).toString("base64");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build an MCP text content block.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* return { content: [text("Found 3 results.")] };
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function text(value, annotations) {
|
|
22
|
+
return { type: "text", text: value, ...(annotations && { annotations }) };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build an MCP image content block.
|
|
26
|
+
*
|
|
27
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
28
|
+
* **already base64-encoded**. Passing a raw byte-string will produce invalid
|
|
29
|
+
* content.
|
|
30
|
+
*/
|
|
31
|
+
export function image(data, mimeType, annotations) {
|
|
32
|
+
return {
|
|
33
|
+
type: "image",
|
|
34
|
+
data: toBase64(data),
|
|
35
|
+
mimeType,
|
|
36
|
+
...(annotations && { annotations }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an MCP audio content block.
|
|
41
|
+
*
|
|
42
|
+
* `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is
|
|
43
|
+
* **already base64-encoded**.
|
|
44
|
+
*/
|
|
45
|
+
export function audio(data, mimeType, annotations) {
|
|
46
|
+
return {
|
|
47
|
+
type: "audio",
|
|
48
|
+
data: toBase64(data),
|
|
49
|
+
mimeType,
|
|
50
|
+
...(annotations && { annotations }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build an MCP embedded resource — the full content travels inline. Use this
|
|
55
|
+
* when the client needs the bytes themselves rather than a link.
|
|
56
|
+
*
|
|
57
|
+
* Pass either `text` (UTF-8 string) or `blob` (base64-encoded bytes).
|
|
58
|
+
*/
|
|
59
|
+
export function embeddedResource(resource, annotations) {
|
|
60
|
+
return {
|
|
61
|
+
type: "resource",
|
|
62
|
+
resource,
|
|
63
|
+
...(annotations && { annotations }),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build an MCP resource link — a `type: "resource_link"` block carrying a URI
|
|
68
|
+
* the client can fetch (or subscribe to) on demand. Use a link when the
|
|
69
|
+
* client should retrieve the bytes itself; use {@link embeddedResource} when
|
|
70
|
+
* the content must travel inline with the response.
|
|
71
|
+
*/
|
|
72
|
+
export function resourceLink(link, annotations) {
|
|
73
|
+
return {
|
|
74
|
+
type: "resource_link",
|
|
75
|
+
...link,
|
|
76
|
+
...(annotations && { annotations }),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=content-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-helpers.js","sourceRoot":"","sources":["../../src/server/content-helpers.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,IAAyB;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAClB,KAAa,EACb,WAAgC;IAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAEoD,EACpD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,IAOC,EACD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,GAAG,IAAI;QACP,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n AudioContent,\n EmbeddedResource,\n ImageContent,\n ResourceLink,\n TextContent,\n} from \"@modelcontextprotocol/server\";\n\n/**\n * MCP content annotations applied to any returned block.\n *\n * - `audience` — who is meant to see the content (`\"user\"`, `\"assistant\"`, or both).\n * - `priority` — relative importance hint for the host.\n * - `lastModified` — ISO timestamp for when the content was produced.\n */\ntype ContentAnnotations = {\n audience?: (\"user\" | \"assistant\")[];\n priority?: number;\n lastModified?: string;\n};\n\n/**\n * Returns a base64-encoded string.\n * - `Uint8Array` input is encoded via `Buffer.toString(\"base64\")`.\n * - `string` input is assumed to be **already base64-encoded** and is returned\n * as-is. Passing raw/unencoded string bytes will produce invalid MCP content.\n */\nfunction toBase64(data: string | Uint8Array): string {\n if (typeof data === \"string\") {\n return data;\n }\n return Buffer.from(data).toString(\"base64\");\n}\n\n/**\n * Build an MCP text content block.\n *\n * @example\n * ```ts\n * return { content: [text(\"Found 3 results.\")] };\n * ```\n */\nexport function text(\n value: string,\n annotations?: ContentAnnotations,\n): TextContent {\n return { type: \"text\", text: value, ...(annotations && { annotations }) };\n}\n\n/**\n * Build an MCP image content block.\n *\n * `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is\n * **already base64-encoded**. Passing a raw byte-string will produce invalid\n * content.\n */\nexport function image(\n data: string | Uint8Array,\n mimeType: string,\n annotations?: ContentAnnotations,\n): ImageContent {\n return {\n type: \"image\",\n data: toBase64(data),\n mimeType,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP audio content block.\n *\n * `data` may be a `Uint8Array` (encoded to base64 for you) or a string that is\n * **already base64-encoded**.\n */\nexport function audio(\n data: string | Uint8Array,\n mimeType: string,\n annotations?: ContentAnnotations,\n): AudioContent {\n return {\n type: \"audio\",\n data: toBase64(data),\n mimeType,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP embedded resource — the full content travels inline. Use this\n * when the client needs the bytes themselves rather than a link.\n *\n * Pass either `text` (UTF-8 string) or `blob` (base64-encoded bytes).\n */\nexport function embeddedResource(\n resource:\n | { uri: string; mimeType?: string; text: string }\n | { uri: string; mimeType?: string; blob: string },\n annotations?: ContentAnnotations,\n): EmbeddedResource {\n return {\n type: \"resource\",\n resource,\n ...(annotations && { annotations }),\n };\n}\n\n/**\n * Build an MCP resource link — a `type: \"resource_link\"` block carrying a URI\n * the client can fetch (or subscribe to) on demand. Use a link when the\n * client should retrieve the bytes itself; use {@link embeddedResource} when\n * the content must travel inline with the response.\n */\nexport function resourceLink(\n link: {\n uri: string;\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n size?: number;\n },\n annotations?: ContentAnnotations,\n): ResourceLink {\n return {\n type: \"resource_link\",\n ...link,\n ...(annotations && { annotations }),\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { audio, embeddedResource, image, resourceLink, text, } from "./content-helpers.js";
|
|
3
|
+
describe("text", () => {
|
|
4
|
+
it("returns a TextContent without annotations when none given", () => {
|
|
5
|
+
expect(text("hello")).toEqual({ type: "text", text: "hello" });
|
|
6
|
+
});
|
|
7
|
+
it("includes annotations when provided", () => {
|
|
8
|
+
expect(text("hi", { priority: 1 })).toEqual({
|
|
9
|
+
type: "text",
|
|
10
|
+
text: "hi",
|
|
11
|
+
annotations: { priority: 1 },
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe("image", () => {
|
|
16
|
+
it("base64-encodes Uint8Array data", () => {
|
|
17
|
+
const bytes = new Uint8Array([104, 105]);
|
|
18
|
+
expect(image(bytes, "image/png")).toEqual({
|
|
19
|
+
type: "image",
|
|
20
|
+
data: "aGk=",
|
|
21
|
+
mimeType: "image/png",
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("passes string data through unchanged (caller is responsible for base64)", () => {
|
|
25
|
+
expect(image("YWxyZWFkeS1iNjQ=", "image/png")).toEqual({
|
|
26
|
+
type: "image",
|
|
27
|
+
data: "YWxyZWFkeS1iNjQ=",
|
|
28
|
+
mimeType: "image/png",
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe("audio", () => {
|
|
33
|
+
it("base64-encodes Uint8Array data", () => {
|
|
34
|
+
const bytes = new Uint8Array([104, 105]);
|
|
35
|
+
expect(audio(bytes, "audio/mpeg")).toMatchObject({
|
|
36
|
+
type: "audio",
|
|
37
|
+
data: "aGk=",
|
|
38
|
+
mimeType: "audio/mpeg",
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("embeddedResource", () => {
|
|
43
|
+
it("wraps a text resource with a type tag", () => {
|
|
44
|
+
expect(embeddedResource({
|
|
45
|
+
uri: "file:///a.txt",
|
|
46
|
+
mimeType: "text/plain",
|
|
47
|
+
text: "x",
|
|
48
|
+
})).toEqual({
|
|
49
|
+
type: "resource",
|
|
50
|
+
resource: { uri: "file:///a.txt", mimeType: "text/plain", text: "x" },
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("wraps a blob resource with a type tag", () => {
|
|
54
|
+
expect(embeddedResource({ uri: "file:///a.bin", blob: "YmFy" })).toEqual({
|
|
55
|
+
type: "resource",
|
|
56
|
+
resource: { uri: "file:///a.bin", blob: "YmFy" },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("resourceLink", () => {
|
|
61
|
+
it("spreads link fields alongside the type tag", () => {
|
|
62
|
+
expect(resourceLink({ uri: "file:///a", name: "a", title: "A" })).toEqual({
|
|
63
|
+
type: "resource_link",
|
|
64
|
+
uri: "file:///a",
|
|
65
|
+
name: "a",
|
|
66
|
+
title: "A",
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=content-helpers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-helpers.test.js","sourceRoot":"","sources":["../../src/server/content-helpers.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,IAAI,GACL,MAAM,sBAAsB,CAAC;AAE9B,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,WAAW;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,kBAAkB;YACxB,QAAQ,EAAE,WAAW;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;YAC/C,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,YAAY;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CACJ,gBAAgB,CAAC;YACf,GAAG,EAAE,eAAe;YACpB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,GAAG;SACV,CAAC,CACH,CAAC,OAAO,CAAC;YACR,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE;SACtE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACvE,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE;SACjD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACxE,IAAI,EAAE,eAAe;YACrB,GAAG,EAAE,WAAW;YAChB,IAAI,EAAE,GAAG;YACT,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport {\n audio,\n embeddedResource,\n image,\n resourceLink,\n text,\n} from \"./content-helpers.js\";\n\ndescribe(\"text\", () => {\n it(\"returns a TextContent without annotations when none given\", () => {\n expect(text(\"hello\")).toEqual({ type: \"text\", text: \"hello\" });\n });\n\n it(\"includes annotations when provided\", () => {\n expect(text(\"hi\", { priority: 1 })).toEqual({\n type: \"text\",\n text: \"hi\",\n annotations: { priority: 1 },\n });\n });\n});\n\ndescribe(\"image\", () => {\n it(\"base64-encodes Uint8Array data\", () => {\n const bytes = new Uint8Array([104, 105]);\n expect(image(bytes, \"image/png\")).toEqual({\n type: \"image\",\n data: \"aGk=\",\n mimeType: \"image/png\",\n });\n });\n\n it(\"passes string data through unchanged (caller is responsible for base64)\", () => {\n expect(image(\"YWxyZWFkeS1iNjQ=\", \"image/png\")).toEqual({\n type: \"image\",\n data: \"YWxyZWFkeS1iNjQ=\",\n mimeType: \"image/png\",\n });\n });\n});\n\ndescribe(\"audio\", () => {\n it(\"base64-encodes Uint8Array data\", () => {\n const bytes = new Uint8Array([104, 105]);\n expect(audio(bytes, \"audio/mpeg\")).toMatchObject({\n type: \"audio\",\n data: \"aGk=\",\n mimeType: \"audio/mpeg\",\n });\n });\n});\n\ndescribe(\"embeddedResource\", () => {\n it(\"wraps a text resource with a type tag\", () => {\n expect(\n embeddedResource({\n uri: \"file:///a.txt\",\n mimeType: \"text/plain\",\n text: \"x\",\n }),\n ).toEqual({\n type: \"resource\",\n resource: { uri: \"file:///a.txt\", mimeType: \"text/plain\", text: \"x\" },\n });\n });\n\n it(\"wraps a blob resource with a type tag\", () => {\n expect(embeddedResource({ uri: \"file:///a.bin\", blob: \"YmFy\" })).toEqual({\n type: \"resource\",\n resource: { uri: \"file:///a.bin\", blob: \"YmFy\" },\n });\n });\n});\n\ndescribe(\"resourceLink\", () => {\n it(\"spreads link fields alongside the type tag\", () => {\n expect(resourceLink({ uri: \"file:///a\", name: \"a\", title: \"A\" })).toEqual({\n type: \"resource_link\",\n uri: \"file:///a\",\n name: \"a\",\n title: \"A\",\n });\n });\n});\n"]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type McpServer as SdkMcpServer, type StandardSchemaV1, type StandardSchemaWithJSON } from "@modelcontextprotocol/server";
|
|
2
|
+
/**
|
|
3
|
+
* A webhook subscription as held by an {@link EventStore}. Skybridge creates,
|
|
4
|
+
* refreshes and deletes these; a store only persists them.
|
|
5
|
+
*/
|
|
6
|
+
export interface EventSubscription {
|
|
7
|
+
/** Deterministic over `(principal, url, name, arguments)`. */
|
|
8
|
+
id: string;
|
|
9
|
+
/** The authenticated subject that subscribed. */
|
|
10
|
+
principal: string;
|
|
11
|
+
/** Event type name. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Subscription arguments, validated against the event's `inputSchema`. */
|
|
14
|
+
arguments: Record<string, unknown>;
|
|
15
|
+
/** Callback URL deliveries are POSTed to. */
|
|
16
|
+
url: string;
|
|
17
|
+
/** Standard Webhooks signing secret (`whsec_…`). */
|
|
18
|
+
secret: string;
|
|
19
|
+
/** Secret being rotated out, still used to dual-sign until `until`. */
|
|
20
|
+
previousSecret?: {
|
|
21
|
+
secret: string;
|
|
22
|
+
until: number;
|
|
23
|
+
};
|
|
24
|
+
/** Expiry, in epoch milliseconds. */
|
|
25
|
+
expiresAt: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Where webhook subscriptions live between the `events/subscribe` request
|
|
29
|
+
* that creates them and the {@link Skybridge.emit} call that delivers to them.
|
|
30
|
+
* Skybridge ignores subscriptions past `expiresAt`, so a store only needs to
|
|
31
|
+
* persist them; dropping expired entries is optional housekeeping.
|
|
32
|
+
*
|
|
33
|
+
* The default in-memory store is enough for a single long-lived process. Pass
|
|
34
|
+
* your own when the app runs on several instances or restarts often.
|
|
35
|
+
*/
|
|
36
|
+
export interface EventStore {
|
|
37
|
+
get(id: string): Promise<EventSubscription | undefined>;
|
|
38
|
+
set(subscription: EventSubscription): Promise<void>;
|
|
39
|
+
delete(id: string): Promise<void>;
|
|
40
|
+
/** Every stored subscription to the event named `name`. */
|
|
41
|
+
listByEvent(name: string): Promise<EventSubscription[]>;
|
|
42
|
+
}
|
|
43
|
+
/** Options for MCP Events, passed as `events` to {@link Skybridge}. */
|
|
44
|
+
export interface EventsOptions {
|
|
45
|
+
/** Where subscriptions are kept. Defaults to an in-memory store. */
|
|
46
|
+
store?: EventStore;
|
|
47
|
+
}
|
|
48
|
+
/** An event occurrence as passed to {@link Skybridge.emit}. */
|
|
49
|
+
export interface EmittedEvent<TData> {
|
|
50
|
+
/** Stable identifier used for deduplication, ideally the upstream's. Generated when omitted. */
|
|
51
|
+
id?: string;
|
|
52
|
+
/** When the event happened. Defaults to now. */
|
|
53
|
+
timestamp?: Date;
|
|
54
|
+
/** Payload, validated against the event's `payloadSchema`. */
|
|
55
|
+
data: TData;
|
|
56
|
+
}
|
|
57
|
+
/** Type marker for a registered event, produced by `registerEvent`. */
|
|
58
|
+
export type EventDef<TArguments = unknown, TPayload = unknown> = {
|
|
59
|
+
arguments: TArguments;
|
|
60
|
+
payload: TPayload;
|
|
61
|
+
};
|
|
62
|
+
type EventSchema = Record<string, StandardSchemaWithJSON> | StandardSchemaWithJSON;
|
|
63
|
+
export type InferEventSchema<T extends EventSchema> = T extends StandardSchemaWithJSON ? StandardSchemaV1.InferOutput<T> : T extends Record<string, StandardSchemaWithJSON> ? {
|
|
64
|
+
[K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]> ? never : K]: StandardSchemaV1.InferOutput<T[K]>;
|
|
65
|
+
} & {
|
|
66
|
+
[K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]> ? K : never]?: StandardSchemaV1.InferOutput<T[K]>;
|
|
67
|
+
} : never;
|
|
68
|
+
/** The `registerEvent` config: how the event type is described in `events/list`. */
|
|
69
|
+
export interface EventConfig<TName extends string, TInput extends EventSchema, TPayload extends EventSchema> {
|
|
70
|
+
/** Stable, specific name such as `comment.created`. */
|
|
71
|
+
name: TName;
|
|
72
|
+
/** What the event is and when it fires. */
|
|
73
|
+
description?: string;
|
|
74
|
+
/** Subscription arguments (filters): a Zod shape or a Standard Schema object. */
|
|
75
|
+
inputSchema?: TInput;
|
|
76
|
+
/** Shape of the delivered `data`: a Zod shape or a Standard Schema object. */
|
|
77
|
+
payloadSchema: TPayload;
|
|
78
|
+
_meta?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
/** Optional per-event behavior passed as the second argument of `registerEvent`. */
|
|
81
|
+
export interface EventHooks<TArguments, TPayload, TExtra> {
|
|
82
|
+
/**
|
|
83
|
+
* Decide whether an emitted event goes to a given subscription. Without it,
|
|
84
|
+
* every subscription to the event receives every emit.
|
|
85
|
+
*/
|
|
86
|
+
match?(event: {
|
|
87
|
+
id: string;
|
|
88
|
+
data: TPayload;
|
|
89
|
+
}, subscription: {
|
|
90
|
+
arguments: TArguments;
|
|
91
|
+
principal: string;
|
|
92
|
+
}): boolean | Promise<boolean>;
|
|
93
|
+
/**
|
|
94
|
+
* Runs on every `events/subscribe`, including refreshes, once the callback
|
|
95
|
+
* URL is verified and before the subscription is stored. Throw (e.g. a
|
|
96
|
+
* `ProtocolError`) to reject it, for instance when the caller may not access
|
|
97
|
+
* what the arguments point to.
|
|
98
|
+
*/
|
|
99
|
+
onSubscribe?(args: TArguments, extra: TExtra): void | Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* App-wide MCP Events state: the registered event types, the subscription
|
|
103
|
+
* store and the callback verification cache. One per {@link Skybridge} app,
|
|
104
|
+
* shared by every per-request server.
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
export declare class EventsRuntime {
|
|
109
|
+
private readonly store;
|
|
110
|
+
private readonly events;
|
|
111
|
+
private readonly verifiedCallbacks;
|
|
112
|
+
constructor(options?: EventsOptions);
|
|
113
|
+
register(config: EventConfig<string, EventSchema, EventSchema>, hooks?: EventHooks<never, never, never>): void;
|
|
114
|
+
/** Install the `events/*` handlers on a per-request server. */
|
|
115
|
+
install(server: Pick<SdkMcpServer["server"], "setRequestHandler">): void;
|
|
116
|
+
/** Deliver one event to every live, matching subscription. */
|
|
117
|
+
emit(name: string, emitted: EmittedEvent<unknown>): Promise<void>;
|
|
118
|
+
private resolve;
|
|
119
|
+
private deliver;
|
|
120
|
+
private liveSubscription;
|
|
121
|
+
private verifyCallback;
|
|
122
|
+
}
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import https from "node:https";
|
|
4
|
+
import { INVALID_PARAMS, ProtocolError, } from "@modelcontextprotocol/server";
|
|
5
|
+
import { globalHttpAgent, globalHttpsAgent } from "request-filtering-agent";
|
|
6
|
+
import { Webhook } from "standardwebhooks";
|
|
7
|
+
import { z } from "zod/v4";
|
|
8
|
+
const EVENTS_ERROR = {
|
|
9
|
+
NotFound: -32011,
|
|
10
|
+
Forbidden: -32012,
|
|
11
|
+
Unsupported: -32014,
|
|
12
|
+
CallbackEndpointError: -32015,
|
|
13
|
+
};
|
|
14
|
+
const MAX_PAYLOAD_BYTES = 256 * 1024;
|
|
15
|
+
const DELIVERY_TIMEOUT_MS = 10_000;
|
|
16
|
+
const RETRY_DELAYS_MS = [1_000, 5_000];
|
|
17
|
+
const VERIFICATION_CACHE_MS = 60 * 60_000;
|
|
18
|
+
const SECRET_ROTATION_GRACE_MS = 5 * 60_000;
|
|
19
|
+
const MIN_TTL_MS = 60_000;
|
|
20
|
+
const DEFAULT_TTL_MS = 30 * 60_000;
|
|
21
|
+
const MAX_TTL_MS = 24 * 60 * 60_000;
|
|
22
|
+
function toStandardSchema(schema) {
|
|
23
|
+
return "~standard" in schema
|
|
24
|
+
? schema
|
|
25
|
+
: z.object(schema);
|
|
26
|
+
}
|
|
27
|
+
async function validate(schema, value, what) {
|
|
28
|
+
const result = await schema["~standard"].validate(value);
|
|
29
|
+
if (result.issues) {
|
|
30
|
+
throw new ProtocolError(INVALID_PARAMS, `Invalid ${what}: ${result.issues.map((issue) => issue.message).join(", ")}`);
|
|
31
|
+
}
|
|
32
|
+
return result.value;
|
|
33
|
+
}
|
|
34
|
+
function canonicalJson(value) {
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
37
|
+
}
|
|
38
|
+
if (value && typeof value === "object") {
|
|
39
|
+
return `{${Object.keys(value)
|
|
40
|
+
.sort()
|
|
41
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
|
|
42
|
+
.join(",")}}`;
|
|
43
|
+
}
|
|
44
|
+
return JSON.stringify(value);
|
|
45
|
+
}
|
|
46
|
+
function principalOf(ctx) {
|
|
47
|
+
const authInfo = ctx.http?.authInfo;
|
|
48
|
+
const subject = authInfo?.extra?.subject;
|
|
49
|
+
const principal = typeof subject === "string" ? subject : authInfo?.clientId;
|
|
50
|
+
if (!principal) {
|
|
51
|
+
throw new ProtocolError(EVENTS_ERROR.Forbidden, "Webhook subscriptions require an authenticated caller");
|
|
52
|
+
}
|
|
53
|
+
return principal;
|
|
54
|
+
}
|
|
55
|
+
function isValidSecret(secret) {
|
|
56
|
+
const encoded = secret.startsWith("whsec_") ? secret.slice(6) : "";
|
|
57
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const length = Buffer.from(encoded, "base64").length;
|
|
61
|
+
return length >= 24 && length <= 64;
|
|
62
|
+
}
|
|
63
|
+
const insecureCallbacksAllowed = () => process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test";
|
|
64
|
+
function checkCallbackUrl(raw) {
|
|
65
|
+
let url;
|
|
66
|
+
try {
|
|
67
|
+
url = new URL(raw);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new ProtocolError(INVALID_PARAMS, "delivery.url is not a valid URL");
|
|
71
|
+
}
|
|
72
|
+
const allowed = url.protocol === "https:" ||
|
|
73
|
+
(url.protocol === "http:" && insecureCallbacksAllowed());
|
|
74
|
+
if (!allowed) {
|
|
75
|
+
throw new ProtocolError(INVALID_PARAMS, "delivery.url must use https");
|
|
76
|
+
}
|
|
77
|
+
return url.href;
|
|
78
|
+
}
|
|
79
|
+
const isRetryable = (status = 0) => status !== 410 && status !== 413 && (status < 300 || status >= 400);
|
|
80
|
+
function post(url, headers, body) {
|
|
81
|
+
const signal = AbortSignal.timeout(DELIVERY_TIMEOUT_MS);
|
|
82
|
+
const failure = (error) => ({
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: signal.aborted
|
|
85
|
+
? "timeout"
|
|
86
|
+
: error?.code?.startsWith("ERR_TLS") || error?.code?.includes("CERT")
|
|
87
|
+
? "tls_error"
|
|
88
|
+
: "connection_refused",
|
|
89
|
+
});
|
|
90
|
+
const [client, agent] = url.startsWith("https:")
|
|
91
|
+
? [https, globalHttpsAgent]
|
|
92
|
+
: [http, globalHttpAgent];
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const request = client.request(url, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { ...headers, "content-length": Buffer.byteLength(body) },
|
|
97
|
+
signal,
|
|
98
|
+
...(!insecureCallbacksAllowed() && { agent }),
|
|
99
|
+
}, (response) => {
|
|
100
|
+
const status = response.statusCode ?? 0;
|
|
101
|
+
const chunks = [];
|
|
102
|
+
let size = 0;
|
|
103
|
+
response.on("data", (chunk) => {
|
|
104
|
+
size += chunk.length;
|
|
105
|
+
if (size <= 64 * 1024) {
|
|
106
|
+
chunks.push(chunk);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
response.on("end", () => {
|
|
110
|
+
if (status >= 200 && status < 300) {
|
|
111
|
+
resolve({
|
|
112
|
+
ok: true,
|
|
113
|
+
status,
|
|
114
|
+
body: Buffer.concat(chunks).toString(),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
resolve({
|
|
119
|
+
ok: false,
|
|
120
|
+
status,
|
|
121
|
+
reason: status >= 500 ? "http_5xx" : "http_4xx",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
response.on("error", (error) => resolve(failure(error)));
|
|
126
|
+
response.on("close", () => resolve(failure()));
|
|
127
|
+
});
|
|
128
|
+
request.on("error", (error) => resolve(failure(error)));
|
|
129
|
+
request.end(body);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function signedPost(subscription, messageId, body) {
|
|
133
|
+
const signedAt = new Date();
|
|
134
|
+
const secrets = [subscription.secret];
|
|
135
|
+
if (subscription.previousSecret &&
|
|
136
|
+
subscription.previousSecret.until > signedAt.getTime()) {
|
|
137
|
+
secrets.push(subscription.previousSecret.secret);
|
|
138
|
+
}
|
|
139
|
+
return post(subscription.url, {
|
|
140
|
+
"content-type": "application/json",
|
|
141
|
+
"webhook-id": messageId,
|
|
142
|
+
"webhook-timestamp": String(Math.floor(signedAt.getTime() / 1000)),
|
|
143
|
+
"webhook-signature": secrets
|
|
144
|
+
.map((secret) => new Webhook(secret).sign(messageId, signedAt, body))
|
|
145
|
+
.join(" "),
|
|
146
|
+
"x-mcp-subscription-id": subscription.id,
|
|
147
|
+
}, body);
|
|
148
|
+
}
|
|
149
|
+
function memoryEventStore() {
|
|
150
|
+
const subscriptions = new Map();
|
|
151
|
+
return {
|
|
152
|
+
get: async (id) => subscriptions.get(id),
|
|
153
|
+
set: async (subscription) => {
|
|
154
|
+
subscriptions.set(subscription.id, subscription);
|
|
155
|
+
},
|
|
156
|
+
delete: async (id) => {
|
|
157
|
+
subscriptions.delete(id);
|
|
158
|
+
},
|
|
159
|
+
listByEvent: async (name) => {
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
for (const [id, subscription] of subscriptions) {
|
|
162
|
+
if (subscription.expiresAt <= now) {
|
|
163
|
+
subscriptions.delete(id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return [...subscriptions.values()].filter((sub) => sub.name === name);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const SubscriptionKeySchema = z.object({
|
|
171
|
+
name: z.string(),
|
|
172
|
+
arguments: z.record(z.string(), z.unknown()).optional(),
|
|
173
|
+
delivery: z.object({ url: z.string() }),
|
|
174
|
+
});
|
|
175
|
+
const SubscribeParamsSchema = SubscriptionKeySchema.extend({
|
|
176
|
+
delivery: z.object({
|
|
177
|
+
mode: z.string().optional(),
|
|
178
|
+
url: z.string(),
|
|
179
|
+
secret: z.string(),
|
|
180
|
+
}),
|
|
181
|
+
cursor: z.string().nullable().optional(),
|
|
182
|
+
ttlMs: z.number().nullable().optional(),
|
|
183
|
+
});
|
|
184
|
+
/**
|
|
185
|
+
* App-wide MCP Events state: the registered event types, the subscription
|
|
186
|
+
* store and the callback verification cache. One per {@link Skybridge} app,
|
|
187
|
+
* shared by every per-request server.
|
|
188
|
+
*
|
|
189
|
+
* @internal
|
|
190
|
+
*/
|
|
191
|
+
export class EventsRuntime {
|
|
192
|
+
store;
|
|
193
|
+
events = new Map();
|
|
194
|
+
verifiedCallbacks = new Map();
|
|
195
|
+
constructor(options = {}) {
|
|
196
|
+
this.store = options.store ?? memoryEventStore();
|
|
197
|
+
}
|
|
198
|
+
register(config, hooks = {}) {
|
|
199
|
+
this.events.set(config.name, {
|
|
200
|
+
name: config.name,
|
|
201
|
+
description: config.description,
|
|
202
|
+
inputSchema: toStandardSchema(config.inputSchema ?? {}),
|
|
203
|
+
payloadSchema: toStandardSchema(config.payloadSchema),
|
|
204
|
+
_meta: config._meta,
|
|
205
|
+
hooks,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** Install the `events/*` handlers on a per-request server. */
|
|
209
|
+
install(server) {
|
|
210
|
+
server.setRequestHandler("events/list", { params: z.object({ cursor: z.string().optional() }).optional() }, () => ({
|
|
211
|
+
events: [...this.events.values()].map((event) => ({
|
|
212
|
+
name: event.name,
|
|
213
|
+
...(event.description && { description: event.description }),
|
|
214
|
+
delivery: ["webhook"],
|
|
215
|
+
inputSchema: event.inputSchema["~standard"].jsonSchema.input({
|
|
216
|
+
target: "draft-2020-12",
|
|
217
|
+
}),
|
|
218
|
+
payloadSchema: event.payloadSchema["~standard"].jsonSchema.output({
|
|
219
|
+
target: "draft-2020-12",
|
|
220
|
+
}),
|
|
221
|
+
...(event._meta && { _meta: event._meta }),
|
|
222
|
+
})),
|
|
223
|
+
}));
|
|
224
|
+
server.setRequestHandler("events/subscribe", { params: SubscribeParamsSchema }, async (params, ctx) => {
|
|
225
|
+
if (params.delivery.mode && params.delivery.mode !== "webhook") {
|
|
226
|
+
throw new ProtocolError(EVENTS_ERROR.Unsupported, `Unsupported delivery mode: ${params.delivery.mode}`, { feature: "deliveryMode", value: params.delivery.mode });
|
|
227
|
+
}
|
|
228
|
+
if (!isValidSecret(params.delivery.secret)) {
|
|
229
|
+
throw new ProtocolError(INVALID_PARAMS, "delivery.secret must be whsec_ followed by base64 of 24 to 64 bytes");
|
|
230
|
+
}
|
|
231
|
+
const { event, subscription } = await this.resolve(params, ctx);
|
|
232
|
+
const ttl = params.ttlMs === null ? MAX_TTL_MS : (params.ttlMs ?? DEFAULT_TTL_MS);
|
|
233
|
+
const candidate = {
|
|
234
|
+
...subscription,
|
|
235
|
+
secret: params.delivery.secret,
|
|
236
|
+
expiresAt: Date.now() + Math.min(Math.max(ttl, MIN_TTL_MS), MAX_TTL_MS),
|
|
237
|
+
};
|
|
238
|
+
await this.verifyCallback(candidate);
|
|
239
|
+
await event.hooks.onSubscribe?.(subscription.arguments, ctx);
|
|
240
|
+
const existing = await this.liveSubscription(subscription.id);
|
|
241
|
+
const previousSecret = existing && existing.secret !== candidate.secret
|
|
242
|
+
? {
|
|
243
|
+
secret: existing.secret,
|
|
244
|
+
until: Date.now() + SECRET_ROTATION_GRACE_MS,
|
|
245
|
+
}
|
|
246
|
+
: existing?.previousSecret;
|
|
247
|
+
await this.store.set({
|
|
248
|
+
...candidate,
|
|
249
|
+
...(previousSecret && { previousSecret }),
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
id: candidate.id,
|
|
253
|
+
refreshBefore: new Date(candidate.expiresAt).toISOString(),
|
|
254
|
+
cursor: null,
|
|
255
|
+
truncated: false,
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
server.setRequestHandler("events/unsubscribe", { params: SubscriptionKeySchema }, async (params, ctx) => {
|
|
259
|
+
const { subscription } = await this.resolve(params, ctx);
|
|
260
|
+
await this.store.delete(subscription.id);
|
|
261
|
+
return {};
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/** Deliver one event to every live, matching subscription. */
|
|
265
|
+
async emit(name, emitted) {
|
|
266
|
+
const event = this.events.get(name);
|
|
267
|
+
if (!event) {
|
|
268
|
+
throw new Error(`Event "${name}" is not registered.`);
|
|
269
|
+
}
|
|
270
|
+
const data = await validate(event.payloadSchema, emitted.data, "payload");
|
|
271
|
+
const id = emitted.id ?? `evt_${randomBytes(12).toString("hex")}`;
|
|
272
|
+
const body = JSON.stringify({
|
|
273
|
+
eventId: id,
|
|
274
|
+
name,
|
|
275
|
+
timestamp: (emitted.timestamp ?? new Date()).toISOString(),
|
|
276
|
+
data,
|
|
277
|
+
cursor: null,
|
|
278
|
+
});
|
|
279
|
+
if (Buffer.byteLength(body) > MAX_PAYLOAD_BYTES) {
|
|
280
|
+
throw new Error(`Event "${name}" payload exceeds ${MAX_PAYLOAD_BYTES} bytes; send a summary and expose a tool to fetch the rest.`);
|
|
281
|
+
}
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
const subscriptions = (await this.store.listByEvent(name)).filter((sub) => sub.expiresAt > now);
|
|
284
|
+
const results = await Promise.allSettled(subscriptions.map(async (subscription) => {
|
|
285
|
+
const matches = (await event.hooks.match?.({ id, data }, {
|
|
286
|
+
arguments: subscription.arguments,
|
|
287
|
+
principal: subscription.principal,
|
|
288
|
+
})) ?? true;
|
|
289
|
+
if (matches) {
|
|
290
|
+
await this.deliver(subscription, id, body, 0);
|
|
291
|
+
}
|
|
292
|
+
}));
|
|
293
|
+
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
294
|
+
if (failures.length > 0) {
|
|
295
|
+
throw new AggregateError(failures, `Event ${id} was not delivered to ${failures.length} subscription(s) whose match hook threw.`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async resolve(params, ctx) {
|
|
299
|
+
const event = this.events.get(params.name);
|
|
300
|
+
if (!event) {
|
|
301
|
+
throw new ProtocolError(EVENTS_ERROR.NotFound, `Unknown event: ${params.name}`, { kind: "event" });
|
|
302
|
+
}
|
|
303
|
+
const url = checkCallbackUrl(params.delivery.url);
|
|
304
|
+
const principal = principalOf(ctx);
|
|
305
|
+
const rawArguments = params.arguments ?? {};
|
|
306
|
+
const args = (await validate(event.inputSchema, rawArguments, "arguments"));
|
|
307
|
+
const hash = createHash("sha256")
|
|
308
|
+
.update(canonicalJson([principal, url, event.name, rawArguments]))
|
|
309
|
+
.digest("hex");
|
|
310
|
+
return {
|
|
311
|
+
event,
|
|
312
|
+
subscription: {
|
|
313
|
+
id: `sub_${hash.slice(0, 32)}`,
|
|
314
|
+
principal,
|
|
315
|
+
name: event.name,
|
|
316
|
+
arguments: args,
|
|
317
|
+
url,
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
async deliver(subscription, eventId, body, attempt) {
|
|
322
|
+
const outcome = await signedPost(subscription, eventId, body);
|
|
323
|
+
if (outcome.ok) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const delay = RETRY_DELAYS_MS[attempt];
|
|
327
|
+
if (!isRetryable(outcome.status) || delay === undefined) {
|
|
328
|
+
console.warn(`skybridge: dropped event ${eventId} for subscription ${subscription.id} (${outcome.reason})`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
this.liveSubscription(subscription.id)
|
|
333
|
+
.then((live) => live && this.deliver(live, eventId, body, attempt + 1))
|
|
334
|
+
.catch(() => { });
|
|
335
|
+
}, delay).unref();
|
|
336
|
+
}
|
|
337
|
+
async liveSubscription(id) {
|
|
338
|
+
const subscription = await this.store.get(id);
|
|
339
|
+
if (subscription && subscription.expiresAt <= Date.now()) {
|
|
340
|
+
await this.store.delete(id);
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
return subscription;
|
|
344
|
+
}
|
|
345
|
+
async verifyCallback(subscription) {
|
|
346
|
+
const key = `${subscription.principal}\n${subscription.url}`;
|
|
347
|
+
const now = Date.now();
|
|
348
|
+
if ((this.verifiedCallbacks.get(key) ?? 0) > now) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const challenge = randomBytes(24).toString("base64url");
|
|
352
|
+
const outcome = await signedPost(subscription, `msg_verification_${randomBytes(12).toString("hex")}`, JSON.stringify({ type: "verification", challenge }));
|
|
353
|
+
let echoed;
|
|
354
|
+
if (outcome.ok) {
|
|
355
|
+
try {
|
|
356
|
+
echoed = JSON.parse(outcome.body)?.challenge;
|
|
357
|
+
}
|
|
358
|
+
catch { }
|
|
359
|
+
}
|
|
360
|
+
const matched = typeof echoed === "string" &&
|
|
361
|
+
echoed.length === challenge.length &&
|
|
362
|
+
timingSafeEqual(Buffer.from(echoed), Buffer.from(challenge));
|
|
363
|
+
if (!matched) {
|
|
364
|
+
throw new ProtocolError(EVENTS_ERROR.CallbackEndpointError, "Callback endpoint verification failed", { reason: outcome.ok ? "challenge_failed" : outcome.reason });
|
|
365
|
+
}
|
|
366
|
+
for (const [cached, until] of this.verifiedCallbacks) {
|
|
367
|
+
if (until <= now) {
|
|
368
|
+
this.verifiedCallbacks.delete(cached);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
this.verifiedCallbacks.set(key, now + VERIFICATION_CACHE_MS);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=events.js.map
|