diff --git a/.github/workflows/SECRETS.md b/.github/workflows/SECRETS.md index 23c6c9aa..8898542b 100644 --- a/.github/workflows/SECRETS.md +++ b/.github/workflows/SECRETS.md @@ -34,10 +34,13 @@ This document lists all secrets required to deploy MCPs via GitHub Actions. - **`PINECONE_INDEX`**: Pinecone index name (if required) ### MCP: `meta-ads` -- **`META_APP_ID`**: Facebook App ID for Meta Ads - - Obtain at: https://developers.facebook.com/apps/ -- **`META_APP_SECRET`**: Facebook App Secret for Meta Ads - - Obtain at: https://developers.facebook.com/apps/ (Settings > Basic) +- **`META_ACCESS_TOKEN`**: Facebook Access Token for Meta Ads API + - Obtain at: https://developers.facebook.com/tools/explorer/ + - Select your app and generate token with required permissions: + - `ads_read` - Read ad information + - `ads_management` - Manage ads + - `pages_read_engagement` - Read associated pages + - `business_management` - Access business accounts ## How to Add Secrets on GitHub diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 9b77583a..1992c9a6 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -80,8 +80,7 @@ jobs: APIFY_API_TOKEN: ${{ secrets.APIFY_API_TOKEN }} REPLICATE_API_TOKEN: ${{ secrets.REPLICATE_API_TOKEN }} PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} - META_APP_ID: ${{ secrets.META_APP_ID }} - META_APP_SECRET: ${{ secrets.META_APP_SECRET }} + META_ACCESS_TOKEN: ${{ secrets.META_ACCESS_TOKEN }} - name: Save deployment info if: steps.deploy.outputs.preview_url diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f6993845..0c0d64cd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -74,8 +74,7 @@ jobs: APIFY_API_TOKEN: ${{ secrets.APIFY_API_TOKEN }} REPLICATE_API_TOKEN: ${{ secrets.REPLICATE_API_TOKEN }} PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} - META_APP_ID: ${{ secrets.META_APP_ID }} - META_APP_SECRET: ${{ secrets.META_APP_SECRET }} + META_ACCESS_TOKEN: ${{ secrets.META_ACCESS_TOKEN }} - name: Notify success if: success() diff --git a/bun.lock b/bun.lock index b4268c17..62acd0f4 100644 --- a/bun.lock +++ b/bun.lock @@ -98,9 +98,11 @@ "name": "mcp-studio", "version": "1.0.0", "dependencies": { - "@decocms/bindings": "1.0.1-alpha.27", - "@decocms/mcps-shared": "1.0.0", - "@decocms/runtime": "1.0.0-alpha.42", + "@ai-sdk/mcp": "^1.0.1", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", + "@jitl/quickjs-wasmfile-release-sync": "^0.31.0", + "@modelcontextprotocol/sdk": "^1.25.1", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-slot": "^1.2.3", @@ -108,36 +110,34 @@ "@tanstack/react-query": "^5.66.5", "@tanstack/react-router": "^1.121.2", "@tanstack/react-router-devtools": "^1.121.2", + "@types/prettier": "^3.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.554.0", "next-themes": "^0.4.6", + "prettier": "^3.7.3", + "quickjs-emscripten": "^0.31.0", + "quickjs-emscripten-core": "^0.31.0", "react": "^19.0.0", "react-dom": "^19.0.0", "sonner": "^2.0.7", + "sucrase": "^3.35.0", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.6", "tailwindcss-animate": "^1.0.7", "zod": "^3.24.3", }, "devDependencies": { - "@decocms/vite-plugin": "1.0.0-alpha.1", - "@modelcontextprotocol/sdk": "1.20.2", - "@types/mime-db": "^1.43.6", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^5.1.1", - "concurrently": "^9.2.0", + "@decocms/mcps-shared": "1.0.0", "deco-cli": "^0.28.0", "typescript": "^5.7.2", - "vite": "7.2.0", }, }, "meta-ads": { "name": "meta-ads", "version": "1.0.0", "dependencies": { - "@decocms/runtime": "1.0.0-alpha.41", + "@decocms/runtime": "^1.0.3", "zod": "^3.24.3", }, "devDependencies": { @@ -216,8 +216,8 @@ "dependencies": { "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.1", - "@decocms/bindings": "1.0.1-alpha.23", - "@decocms/runtime": "1.0.0-alpha.41", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", "@openrouter/ai-sdk-provider": "^1.2.0", "@openrouter/sdk": "^0.1.11", "ai": "^6.0.3", @@ -302,11 +302,12 @@ "name": "registry", "version": "1.0.0", "dependencies": { - "@decocms/bindings": "1.0.1-alpha.11", - "@decocms/runtime": "1.0.0-alpha.22", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", "zod": "^3.24.3", }, "devDependencies": { + "@decocms/mcps-shared": "workspace:*", "@decocms/vite-plugin": "1.0.0-alpha.1", "@modelcontextprotocol/sdk": "1.20.2", "@types/mime-db": "^1.43.6", @@ -474,10 +475,12 @@ "@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-egqr9PHqqX2Am5mn/Xs1C3+1/wphVKiAjpsVpW85eLc2WpW7AgiAg52DCBr4By9bw3UVVuMeR4uEO1X0dKDUDA=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.0", "@ai-sdk/provider-utils": "4.0.1", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-giJEg9ob45htbu3iautK+2kvplY2JnTj7ir4wZzYSQWvqGatWfBBfDuNCU5wSJt9BCGjymM5ZS9ziD42JGCZBw=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.4", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OlccjNYZ5+4FaNyvs0kb3N5H6U/QCKlKPTGsgUo8IZkqfMQu8ALI1XD6l/BCuTKto+OO9xUPObT/W7JhbqJ5nA=="], "@ai-sdk/google-v5": ["@ai-sdk/google@2.0.40", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-E7MTVE6vhWXQJzXQDvojwA9t5xlhWpxttCH3R/kUyiE6y0tT8Ay2dmZLO+bLpFBQ5qrvBMrjKWpDVQMoo6TJZg=="], + "@ai-sdk/mcp": ["@ai-sdk/mcp@1.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-1ukAZDVyRA968d/qPa8RA+LiXf2QFbEbZrlW/1/D8y1a1i8dw4JbsfeiSk3FsvPYkjojhCCouaz3dkiWE8hlaQ=="], + "@ai-sdk/mistral-v5": ["@ai-sdk/mistral@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.16" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-np2bTlL5ZDi7iAOPCF5SZ5xKqls059iOvsigbgd9VNUCIrWSf6GYOaPvoWEgJ650TUOZitTfMo9MiEhLgutPfA=="], "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Q+lwBIeMprc/iM+vg1yGjvzRrp74l316wDpqWdbmd4VXXlllblzGsUgBLTeKvcEapFTgqk0FRETvSb58Y6dsfA=="], @@ -486,9 +489,9 @@ "@ai-sdk/openai-v5": ["@ai-sdk/openai@2.0.53", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.12" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-GIkR3+Fyif516ftXv+YPSPstnAHhcZxNoR2s8uSHhQ1yBT7I7aQYTVwpjAuYoT3GR+TeP50q7onj2/nDRbT2FQ=="], - "@ai-sdk/provider": ["@ai-sdk/provider@3.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ=="], + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-2lR4w7mr9XrydzxBSjir4N6YMGdXD+Np1Sh0RXABh7tWdNFFwIeRI1Q+SaYZMbfL8Pg8RRLcrxQm51yxTLhokg=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.1", "", { "dependencies": { "@ai-sdk/provider": "3.0.0", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-de2v8gH9zj47tRI38oSxhQIewmNc+OZjYIOOaMoVWKL65ERSav2PYYZHPSPCrfOeLMkv+Dyh8Y0QGwkO29wMWQ=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.2", "", { "dependencies": { "@ai-sdk/provider": "3.0.1", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-KaykkuRBdF/ffpI5bwpL4aSCmO/99p8/ci+VeHwJO8tmvXtiVAb99QeyvvvXmL61e9Zrvv4GBGoajW19xdjkVQ=="], "@ai-sdk/provider-utils-v5": ["@ai-sdk/provider-utils@3.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg=="], @@ -642,7 +645,7 @@ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251210.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Uaz6/9XE+D6E7pCY4OvkCuJHu7HcSDzeGcCGY1HLhojXhHd7yL52c3yfiyJdS8hPatiAa0nn5qSI/42+aTdDSw=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251230.0", "", {}, "sha512-mTpeOLyC088fqC0hDMFFErq0C/4tLFTDgYgkBhpbM7YeoASVErhnR5irvnHFarvJ5NWXa8jY08bSaRIG8V8PAA=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251231.0", "", {}, "sha512-XOP7h2y9Nu3ECuZM9S7w3g4GSliTgj6SEEkYj6G6d3TEQtOiV/cHXuI/fKiLj8Z9+qJK/RLLcKkX14NxajrXCw=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], @@ -650,7 +653,7 @@ "@deco/mcp": ["@jsr/deco__mcp@0.5.5", "https://npm.jsr.io/~/11/@jsr/deco__mcp/0.5.5.tgz", { "dependencies": { "@jsr/deco__deco": "^1.112.1", "@jsr/hono__hono": "^4.5.4", "@modelcontextprotocol/sdk": "^1.11.4", "fetch-to-node": "^2.1.0", "zod": "^3.24.2" } }, "sha512-46TaWGu7lbsPleHjCVrG6afhQjv3muBTNRFBkIhLrSzlQ+9d21UeukpYs19z0AGpOlmjSSK9qIRFTf8SlH2B6Q=="], - "@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.27", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5" } }, "sha512-0usZaDC5wKMT+aoLV/H8jDLJkuT0lVHWrXGR7AoAJ0A6eEZCCuXu3G4/Yoo8xR5uYjv2MjEw2Xbf4GnVS2Rttg=="], + "@decocms/bindings": ["@decocms/bindings@1.0.3", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.1", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5" } }, "sha512-0qGrAcH74Td9Ruhx7SI31o9mvKlMeQGtiRf5BzDcSgG0cvgJhaMMSvz72tvbUVl77GLu93v02NlKupui8yeiMw=="], "@decocms/mcps-shared": ["@decocms/mcps-shared@workspace:shared"], @@ -724,6 +727,8 @@ "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="], + "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], @@ -770,6 +775,16 @@ "@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="], + "@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.31.0", "", {}, "sha512-1yrgvXlmXH2oNj3eFTrkwacGJbmM0crwipA3ohCrjv52gBeDaD7PsTvFYinlAnqU8iPME3LGP437yk05a2oejw=="], + + "@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-YkdzQdr1uaftFhgEnTRjTTZHk2SFZdpWO7XhOmRVbi6CEVsH9g5oNF8Ta1q3OuSJHRwwT8YsuR1YzEiEIJEk6w=="], + + "@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-8XvloaaWBONqcHXYs5tWOjdhQVxzULilIfB2hvZfS6S+fI4m2+lFiwQy7xeP8ExHmiZ7D8gZGChNkdLgjGfknw=="], + + "@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-uz0BbQYTxNsFkvkurd7vk2dOg57ElTBLCuvNtRl4rgrtbC++NIndD5qv2+AXb6yXDD3Uy1O2PCwmoaH0eXgEOg=="], + + "@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-hYduecOByj9AsAfsJhZh5nA6exokmuFC8cls39+lYmTCGY51bgjJJJwReEu7Ff7vBWaQCL6TeDdVlnp2WYz0jw=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -790,7 +805,7 @@ "@jsr/deco__codemod-toolkit": ["@jsr/deco__codemod-toolkit@0.3.4", "https://npm.jsr.io/~/11/@jsr/deco__codemod-toolkit/0.3.4.tgz", { "dependencies": { "@jsr/std__flags": "^0.224.0", "@jsr/std__fmt": "^1.0.0", "@jsr/std__fs": "^1.0.1", "@jsr/std__path": "^1.0.2", "@jsr/std__semver": "^1.0.1", "diff": "5.1.0", "ts-morph": "^21.0" } }, "sha512-ykI472we3cPyP1bDJ9TCfAqFu2CYMghLNx+UVVuByEvkRikMGfffQpRl18yqQnQ0elVYJtyr7InJVzlzuw1sRA=="], - "@jsr/deco__deco": ["@jsr/deco__deco@1.133.1", "https://npm.jsr.io/~/11/@jsr/deco__deco/1.133.1.tgz", { "dependencies": { "@jsr/core__asyncutil": "^1.0.2", "@jsr/deco__codemod-toolkit": "^0.3.4", "@jsr/deco__deno-ast-wasm": "^0.5.5", "@jsr/deco__durable": "^0.5.3", "@jsr/deco__inspect-vscode": "0.2.1", "@jsr/deco__warp": "^0.3.8", "@jsr/deno__cache-dir": "0.10.1", "@jsr/hono__hono": "^4.5.4", "@jsr/std__assert": "^1.0.2", "@jsr/std__async": "^0.224.1", "@jsr/std__cli": "^1.0.3", "@jsr/std__crypto": "1.0.0-rc.1", "@jsr/std__encoding": "^1.0.0-rc.1", "@jsr/std__flags": "^0.224.0", "@jsr/std__fmt": "^0.225.3", "@jsr/std__fs": "^0.229.1", "@jsr/std__http": "^1.0.0", "@jsr/std__io": "^0.224.4", "@jsr/std__log": "^0.224.5", "@jsr/std__media-types": "^1.0.0-rc.1", "@jsr/std__path": "^0.225.2", "@jsr/std__semver": "^0.224.3", "@jsr/zaubrik__djwt": "^3.0.2", "@opentelemetry/api": "1.9.0", "@opentelemetry/api-logs": "0.52.1", "@opentelemetry/exporter-logs-otlp-http": "0.52.1", "@opentelemetry/exporter-metrics-otlp-http": "0.52.1", "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", "@opentelemetry/instrumentation": "0.52.1", "@opentelemetry/instrumentation-fetch": "0.52.1", "@opentelemetry/otlp-exporter-base": "0.52.1", "@opentelemetry/resources": "1.25.1", "@opentelemetry/sdk-logs": "0.52.1", "@opentelemetry/sdk-metrics": "1.25.1", "@opentelemetry/sdk-trace-base": "1.25.1", "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", "@redis/client": "^1.6.0", "@types/json-schema": "7.0.11", "brotli": "1.3.3", "fast-json-patch": "^3.1.1", "lru-cache": "10.2.0", "preact": "10.23.1", "preact-render-to-string": "6.4.0", "simple-git": "^3.25.0", "terser": "5.34.0", "ua-parser-js": "2.0.0-beta.2", "unique-names-generator": "4.7.1", "utility-types": "3.10.0", "weak-lru-cache": "1.0.0" } }, "sha512-aLQk/sYlkPlUYrGCHEjJPfG8AmON2QahqRCw4Pc4gOFZA/vHOH+RYs/cOJsJGwZittUC/GcEssQqdgDvaaFB/A=="], + "@jsr/deco__deco": ["@jsr/deco__deco@1.133.2", "https://npm.jsr.io/~/11/@jsr/deco__deco/1.133.2.tgz", { "dependencies": { "@jsr/core__asyncutil": "^1.0.2", "@jsr/deco__codemod-toolkit": "^0.3.4", "@jsr/deco__deno-ast-wasm": "^0.5.5", "@jsr/deco__durable": "^0.5.3", "@jsr/deco__inspect-vscode": "0.2.1", "@jsr/deco__warp": "^0.3.8", "@jsr/deno__cache-dir": "0.10.1", "@jsr/hono__hono": "^4.5.4", "@jsr/std__assert": "^1.0.2", "@jsr/std__async": "^0.224.1", "@jsr/std__cli": "^1.0.3", "@jsr/std__crypto": "1.0.0-rc.1", "@jsr/std__encoding": "^1.0.0-rc.1", "@jsr/std__flags": "^0.224.0", "@jsr/std__fmt": "^0.225.3", "@jsr/std__fs": "^0.229.1", "@jsr/std__http": "^1.0.0", "@jsr/std__io": "^0.224.4", "@jsr/std__log": "^0.224.5", "@jsr/std__media-types": "^1.0.0-rc.1", "@jsr/std__path": "^0.225.2", "@jsr/std__semver": "^0.224.3", "@jsr/zaubrik__djwt": "^3.0.2", "@opentelemetry/api": "1.9.0", "@opentelemetry/api-logs": "0.52.1", "@opentelemetry/exporter-logs-otlp-http": "0.52.1", "@opentelemetry/exporter-metrics-otlp-http": "0.52.1", "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", "@opentelemetry/instrumentation": "0.52.1", "@opentelemetry/instrumentation-fetch": "0.52.1", "@opentelemetry/otlp-exporter-base": "0.52.1", "@opentelemetry/resources": "1.25.1", "@opentelemetry/sdk-logs": "0.52.1", "@opentelemetry/sdk-metrics": "1.25.1", "@opentelemetry/sdk-trace-base": "1.25.1", "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", "@redis/client": "^1.6.0", "@types/json-schema": "7.0.11", "brotli": "1.3.3", "fast-json-patch": "^3.1.1", "lru-cache": "10.2.0", "preact": "10.23.1", "preact-render-to-string": "6.4.0", "simple-git": "^3.25.0", "terser": "5.34.0", "ua-parser-js": "2.0.0-beta.2", "unique-names-generator": "4.7.1", "utility-types": "3.10.0", "weak-lru-cache": "1.0.0" } }, "sha512-qoudkjNvEAsPIgdgB9RKp8WD29ZU6+1m8w4QA6ku0v3QnUVOGlSkNiNEHLKqTpg1d5ByKIC3ePFKPVrXOqES/w=="], "@jsr/deco__deno-ast-wasm": ["@jsr/deco__deno-ast-wasm@0.5.5", "https://npm.jsr.io/~/11/@jsr/deco__deno-ast-wasm/0.5.5.tgz", {}, "sha512-weeOVf6cddt6hGDUNlMYbCAxV2nCnj3fm7Pb7pdqvKus9Wqo9NmcWKyZqu5P5Q0ai9xOFURFa+GGEZP0pRfIwg=="], @@ -1138,7 +1153,7 @@ "@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.1", "", {}, "sha512-J1dev372wtJqmqn9U/qbpbZxbJSQrogNN2+Qv1lKlpATpe/WQ9aCZfl/xSb9d2Rgh1IyLSvNxZAXPZxruO6Xig=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng=="], @@ -1344,9 +1359,9 @@ "@tanstack/history": ["@tanstack/history@1.141.0", "", {}, "sha512-LS54XNyxyTs5m/pl1lkwlg7uZM3lvsv2FIIV1rsJgnfwVCnI+n4ZGZ2CcjNT13BPu/3hPP+iHmliBSscJxW5FQ=="], - "@tanstack/query-core": ["@tanstack/query-core@5.90.15", "", {}, "sha512-mInIZNUZftbERE+/Hbtswfse49uUQwch46p+27gP9DWJL927UjnaWEF2t3RMOqBcXbfMdcNkPe06VyUIAZTV1g=="], + "@tanstack/query-core": ["@tanstack/query-core@5.90.16", "", {}, "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww=="], - "@tanstack/react-query": ["@tanstack/react-query@5.90.15", "", { "dependencies": { "@tanstack/query-core": "5.90.15" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-uQvnDDcTOgJouNtAyrgRej+Azf0U5WDov3PXmHFUBc+t1INnAYhIlpZtCGNBLwCN41b43yO7dPNZu8xWkUFBwQ=="], + "@tanstack/react-query": ["@tanstack/react-query@5.90.16", "", { "dependencies": { "@tanstack/query-core": "5.90.16" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ=="], "@tanstack/react-router": ["@tanstack/react-router@1.144.0", "", { "dependencies": { "@tanstack/history": "1.141.0", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.144.0", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-GmRyIGmHtGj3VLTHXepIwXAxTcHyL5W7Vw7O1CnVEtFxQQWKMVOnWgI7tPY6FhlNwMKVb3n0mPFWz9KMYyd2GA=="], @@ -1416,6 +1431,8 @@ "@types/phoenix": ["@types/phoenix@1.6.7", "", {}, "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q=="], + "@types/prettier": ["@types/prettier@3.0.0", "", { "dependencies": { "prettier": "*" } }, "sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA=="], + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], @@ -1436,13 +1453,13 @@ "@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], @@ -1452,18 +1469,22 @@ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], - "ai": ["ai@6.0.3", "", { "dependencies": { "@ai-sdk/gateway": "3.0.2", "@ai-sdk/provider": "3.0.0", "@ai-sdk/provider-utils": "4.0.1", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OOo+/C+sEyscoLnbY3w42vjQDICioVNyS+F+ogwq6O5RJL/vgWGuiLzFwuP7oHTeni/MkmX8tIge48GTdaV7QQ=="], + "ai": ["ai@6.0.5", "", { "dependencies": { "@ai-sdk/gateway": "3.0.4", "@ai-sdk/provider": "3.0.1", "@ai-sdk/provider-utils": "4.0.2", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CKL3dDHedWskC6EY67LrULonZBU9vL+Bwa+xQEcprBhJfxpogntG3utjiAkYuy5ZQatyWk+SmWG8HLvcnhvbRg=="], "ai-v5": ["ai@5.0.97", "", { "dependencies": { "@ai-sdk/gateway": "2.0.12", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "apify": ["apify@workspace:apify"], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -1678,6 +1699,8 @@ "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], @@ -1838,6 +1861,8 @@ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-schema-walker": ["json-schema-walker@2.0.0", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", "clone": "^2.1.2" } }, "sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw=="], "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -1870,6 +1895,8 @@ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], @@ -1930,6 +1957,8 @@ "mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + "nanobanana": ["nanobanana@workspace:nanobanana"], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -2014,6 +2043,8 @@ "pino-std-serializers": ["pino-std-serializers@7.0.0", "", {}, "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="], + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], @@ -2054,6 +2085,10 @@ "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], + "quickjs-emscripten": ["quickjs-emscripten@0.31.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-debug-sync": "0.31.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-release-sync": "0.31.0", "quickjs-emscripten-core": "0.31.0" } }, "sha512-K7Yt78aRPLjPcqv3fIuLW1jW3pvwO21B9pmFOolsjM/57ZhdVXBr51GqJpalgBlkPu9foAvhEAuuQPnvIGvLvQ=="], + + "quickjs-emscripten-core": ["quickjs-emscripten-core@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ=="], + "radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -2064,7 +2099,7 @@ "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], - "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], @@ -2084,6 +2119,8 @@ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "require-in-the-middle": ["require-in-the-middle@7.5.2", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ=="], "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], @@ -2190,6 +2227,8 @@ "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], + "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -2206,6 +2245,10 @@ "terser": ["terser@5.34.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-y5NUX+U9HhVsK/zihZwoq4r9dICLyV2jXGOriDAVOeKhq3LKVjgJbGO90FisozXLlJfvjHqgckGmJFBb9KYoWQ=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="], "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], @@ -2228,6 +2271,8 @@ "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + "ts-morph": ["ts-morph@21.0.1", "", { "dependencies": { "@ts-morph/common": "~0.22.0", "code-block-writer": "^12.0.0" } }, "sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -2380,8 +2425,14 @@ "@deco-cx/warp-node/undici": ["undici@6.22.0", "", {}, "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw=="], + "@deco/mcp/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + + "@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], + "@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], @@ -2586,7 +2637,7 @@ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], @@ -2598,6 +2649,8 @@ "ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "apify/@decocms/runtime": ["@decocms/runtime@0.24.0", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.5.5", "@mastra/cloudflare-d1": "^0.13.4", "@mastra/core": "^0.20.2", "@modelcontextprotocol/sdk": "^1.19.1", "bidc": "0.0.3", "drizzle-orm": "^0.44.5", "jose": "^6.0.11", "mime-db": "1.52.0", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5", "zod-to-json-schema": "^3.24.4" } }, "sha512-ZWa9z6I0dl4LtVnv3NUDvxuVYU0Aka1gpUEkpJP0tW2ETCGQkmDx50MdFqEksXiL1RHoNZuv45Fz8u9FkdTKJg=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -2636,27 +2689,25 @@ "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "mcp-studio/@decocms/runtime": ["@decocms/runtime@1.0.0-alpha.42", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.7.8", "@decocms/bindings": "1.0.1-alpha.23", "@modelcontextprotocol/sdk": "1.20.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-Z70UbSoERgIZAEhWX9B64xjIOcjmBhuAKsH59dSbBF5AWkIVdIC8KvcT5shJLGBwnFK0CtTvaxUx+8jpyG61Pg=="], + "mcp-studio/@decocms/runtime": ["@decocms/runtime@1.0.3", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "1.0.3", "@modelcontextprotocol/sdk": "1.25.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-uAM3TLsJh7oxyT1CUQckxZbPiKUqqM3zER31EZ3n8azyShsiCKukGLz46bbJSgjajPf8TysaplH9ARR17s7a1Q=="], - "mcp-template-with-view/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "mcp-studio/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], "mcp-template-with-view/lucide-react": ["lucide-react@0.476.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-x6cLTk8gahdUPje0hSgLN1/MgiJH+Xl90Xoxy9bkPAsMPOUiyRSKR4JCDPGVCEpyqnZXH3exFWNItcvra9WzUQ=="], - "meta-ads/@decocms/runtime": ["@decocms/runtime@1.0.0-alpha.41", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.7.8", "@decocms/bindings": "1.0.1-alpha.23", "@modelcontextprotocol/sdk": "1.20.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-zpGy4bMoly/uQV8jNOeaAsIbXs5rS+ksXGqvIU9EqS3cH/aP2Dab158sy12OH+ixvlAZab5bd7yBij0viy+T6g=="], + "meta-ads/@decocms/runtime": ["@decocms/runtime@1.0.3", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "1.0.3", "@modelcontextprotocol/sdk": "1.25.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-uAM3TLsJh7oxyT1CUQckxZbPiKUqqM3zER31EZ3n8azyShsiCKukGLz46bbJSgjajPf8TysaplH9ARR17s7a1Q=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], - "object-storage/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], "object-storage/lucide-react": ["lucide-react@0.476.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-x6cLTk8gahdUPje0hSgLN1/MgiJH+Xl90Xoxy9bkPAsMPOUiyRSKR4JCDPGVCEpyqnZXH3exFWNItcvra9WzUQ=="], - "openrouter/@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.23", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5" } }, "sha512-CVvfLeQPzD0RwkIznl4CPg8yLcdz5O2bgMmaL+lyHe+azO2qkGpQkTp1pX5fDhdr7jvWtjTcThUNoiCno58cSw=="], - - "openrouter/@decocms/runtime": ["@decocms/runtime@1.0.0-alpha.41", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.7.8", "@decocms/bindings": "1.0.1-alpha.23", "@modelcontextprotocol/sdk": "1.20.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-zpGy4bMoly/uQV8jNOeaAsIbXs5rS+ksXGqvIU9EqS3cH/aP2Dab158sy12OH+ixvlAZab5bd7yBij0viy+T6g=="], + "openrouter/@decocms/runtime": ["@decocms/runtime@1.0.3", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "1.0.3", "@modelcontextprotocol/sdk": "1.25.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-uAM3TLsJh7oxyT1CUQckxZbPiKUqqM3zER31EZ3n8azyShsiCKukGLz46bbJSgjajPf8TysaplH9ARR17s7a1Q=="], "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -2668,9 +2719,7 @@ "pino-pretty/secure-json-parse": ["secure-json-parse@4.1.0", "", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="], - "registry/@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.11", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5", "zod-to-json-schema": "3.25.0" } }, "sha512-KcWUSCRphxwEJIzt1zPANf/DNUkg1EvzeEteP/st4zsFEAjDXTTtXt8vrNTTpHQuY01GH1R62oX3PsRnVOuBrw=="], - - "registry/@decocms/runtime": ["@decocms/runtime@1.0.0-alpha.22", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.5.5", "@decocms/bindings": "1.0.1-alpha.12", "@modelcontextprotocol/sdk": "1.20.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5", "zod-to-json-schema": "3.25.0" } }, "sha512-6/rQWV629MEIdNxcE0xHQeFReBgx+0m9ppfqDGJRUlSfFdNp3AP02P8eWxAna5IfpknqbRsm0mpX+SjXCoBvbw=="], + "registry/@decocms/runtime": ["@decocms/runtime@1.0.3", "", { "dependencies": { "@ai-sdk/provider": "^2.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "1.0.3", "@modelcontextprotocol/sdk": "1.25.1", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^3.25.76", "zod-to-json-schema": "3.25.0" } }, "sha512-uAM3TLsJh7oxyT1CUQckxZbPiKUqqM3zER31EZ3n8azyShsiCKukGLz46bbJSgjajPf8TysaplH9ARR17s7a1Q=="], "registry/@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="], @@ -2684,6 +2733,8 @@ "solid-js/seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "whisper/@decocms/runtime": ["@decocms/runtime@0.24.0", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250617.0", "@deco/mcp": "npm:@jsr/deco__mcp@0.5.5", "@mastra/cloudflare-d1": "^0.13.4", "@mastra/core": "^0.20.2", "@modelcontextprotocol/sdk": "^1.19.1", "bidc": "0.0.3", "drizzle-orm": "^0.44.5", "jose": "^6.0.11", "mime-db": "1.52.0", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5", "zod-to-json-schema": "^3.24.4" } }, "sha512-ZWa9z6I0dl4LtVnv3NUDvxuVYU0Aka1gpUEkpJP0tW2ETCGQkmDx50MdFqEksXiL1RHoNZuv45Fz8u9FkdTKJg=="], @@ -2726,6 +2777,10 @@ "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@deco/mcp/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "@decocms/bindings/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -2750,6 +2805,8 @@ "@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], + "@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -2808,7 +2865,7 @@ "@mastra/schema-compat/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -2828,16 +2885,24 @@ "@opentelemetry/sdk-trace-web/@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@1.25.1", "", { "dependencies": { "@opentelemetry/core": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "apify/@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], + "apify/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "concurrently/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "data-for-seo/@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], + "data-for-seo/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "gemini-pro-vision/@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], + "gemini-pro-vision/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "inquirer-search-checkbox/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], "inquirer-search-checkbox/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], @@ -2880,33 +2945,21 @@ "log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "mcp-studio/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], - - "mcp-studio/@decocms/runtime/@deco/mcp": ["@jsr/deco__mcp@0.7.8", "https://npm.jsr.io/~/11/@jsr/deco__mcp/0.7.8.tgz", { "dependencies": { "@jsr/deco__deco": "^1.112.1", "@jsr/hono__hono": "^4.5.4", "@modelcontextprotocol/sdk": "^1.11.4", "fetch-to-node": "^2.1.0", "zod": "^3.24.2" } }, "sha512-NcDGuKv2ZxId8ZHCD3zGZhHNLzrExn+DL11yDo60mZ44zbbRwuLRRmAYTwrdtZ6A45fVWYmEIloPY93tKrtimw=="], - - "mcp-studio/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.23", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5" } }, "sha512-CVvfLeQPzD0RwkIznl4CPg8yLcdz5O2bgMmaL+lyHe+azO2qkGpQkTp1pX5fDhdr7jvWtjTcThUNoiCno58cSw=="], + "mcp-studio/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], "mcp-studio/@decocms/runtime/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], - "mcp-template-with-view/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - - "mcp-template-with-view/@vitejs/plugin-react/react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "meta-ads/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + "mcp-studio/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], - "meta-ads/@decocms/runtime/@deco/mcp": ["@jsr/deco__mcp@0.7.8", "https://npm.jsr.io/~/11/@jsr/deco__mcp/0.7.8.tgz", { "dependencies": { "@jsr/deco__deco": "^1.112.1", "@jsr/hono__hono": "^4.5.4", "@modelcontextprotocol/sdk": "^1.11.4", "fetch-to-node": "^2.1.0", "zod": "^3.24.2" } }, "sha512-NcDGuKv2ZxId8ZHCD3zGZhHNLzrExn+DL11yDo60mZ44zbbRwuLRRmAYTwrdtZ6A45fVWYmEIloPY93tKrtimw=="], + "meta-ads/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], - "meta-ads/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.23", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5" } }, "sha512-CVvfLeQPzD0RwkIznl4CPg8yLcdz5O2bgMmaL+lyHe+azO2qkGpQkTp1pX5fDhdr7jvWtjTcThUNoiCno58cSw=="], + "meta-ads/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], "meta-ads/@decocms/runtime/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], - "object-storage/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - - "object-storage/@vitejs/plugin-react/react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "openrouter/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + "openrouter/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], - "openrouter/@decocms/runtime/@deco/mcp": ["@jsr/deco__mcp@0.7.8", "https://npm.jsr.io/~/11/@jsr/deco__mcp/0.7.8.tgz", { "dependencies": { "@jsr/deco__deco": "^1.112.1", "@jsr/hono__hono": "^4.5.4", "@modelcontextprotocol/sdk": "^1.11.4", "fetch-to-node": "^2.1.0", "zod": "^3.24.2" } }, "sha512-NcDGuKv2ZxId8ZHCD3zGZhHNLzrExn+DL11yDo60mZ44zbbRwuLRRmAYTwrdtZ6A45fVWYmEIloPY93tKrtimw=="], + "openrouter/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], "openrouter/@decocms/runtime/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], @@ -2914,11 +2967,11 @@ "perplexity/@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], - "registry/@decocms/bindings/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + "perplexity/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], - "registry/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + "registry/@decocms/runtime/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], - "registry/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.0.1-alpha.12", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.20.2", "zod": "^3.25.76", "zod-from-json-schema": "^0.0.5", "zod-to-json-schema": "3.25.0" } }, "sha512-nqPft0iiZ82QPG0ACi+33bQDWZABGf8bhG6QGLi9BFpt/SOGTSCwYZykItw1phFP9t6/aqx8JG/JSy6XXvF6Tg=="], + "registry/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], "registry/@decocms/runtime/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], @@ -2926,6 +2979,8 @@ "whisper/@decocms/runtime/@mastra/core": ["@mastra/core@0.20.2", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/anthropic-v5": "npm:@ai-sdk/anthropic@2.0.23", "@ai-sdk/google-v5": "npm:@ai-sdk/google@2.0.17", "@ai-sdk/openai-compatible-v5": "npm:@ai-sdk/openai-compatible@1.0.19", "@ai-sdk/openai-v5": "npm:@ai-sdk/openai@2.0.42", "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.10", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/ui-utils": "^1.2.11", "@ai-sdk/xai-v5": "npm:@ai-sdk/xai@2.0.23", "@isaacs/ttlcache": "^1.4.1", "@mastra/schema-compat": "0.11.4", "@openrouter/ai-sdk-provider-v5": "npm:@openrouter/ai-sdk-provider@1.2.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/auto-instrumentations-node": "^0.62.1", "@opentelemetry/core": "^2.0.1", "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0", "@opentelemetry/exporter-trace-otlp-http": "^0.203.0", "@opentelemetry/otlp-exporter-base": "^0.203.0", "@opentelemetry/otlp-transformer": "^0.203.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.36.0", "@sindresorhus/slugify": "^2.2.1", "ai": "^4.3.19", "ai-v5": "npm:ai@5.0.60", "date-fns": "^3.6.0", "dotenv": "^16.6.1", "hono": "^4.9.7", "hono-openapi": "^0.4.8", "js-tiktoken": "^1.0.20", "json-schema": "^0.4.0", "json-schema-to-zod": "^2.6.1", "p-map": "^7.0.3", "pino": "^9.7.0", "pino-pretty": "^13.0.0", "radash": "^12.1.1", "sift": "^17.1.3", "xstate": "^5.20.1", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-RbwuLwOVrcLbbjLFEBSlGTBA3mzGAy4bXp4JeXg2miJWDR/7WbXtxKIU+sTZGw5LpzlvvEFtj7JtHI1l+gKMVg=="], + "whisper/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.1", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="], "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="], @@ -3000,6 +3055,10 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@deco/mcp/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@decocms/bindings/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], @@ -3034,6 +3093,8 @@ "@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], + "@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "apify/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "apify/@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -3058,6 +3119,8 @@ "apify/@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], + "apify/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "data-for-seo/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "data-for-seo/@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -3082,6 +3145,8 @@ "data-for-seo/@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], + "data-for-seo/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "gemini-pro-vision/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "gemini-pro-vision/@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -3106,6 +3171,8 @@ "gemini-pro-vision/@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], + "gemini-pro-vision/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "inquirer-search-checkbox/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "inquirer-search-checkbox/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], @@ -3126,6 +3193,16 @@ "inquirer-search-list/inquirer/strip-ansi/ansi-regex": ["ansi-regex@3.0.1", "", {}, "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="], + "mcp-studio/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "meta-ads/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "meta-ads/@decocms/runtime/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + + "openrouter/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "openrouter/@decocms/runtime/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + "perplexity/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "perplexity/@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -3150,6 +3227,12 @@ "perplexity/@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], + "perplexity/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "registry/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "registry/@decocms/runtime/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + "whisper/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5": ["@ai-sdk/anthropic@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw=="], "whisper/@decocms/runtime/@mastra/core/@ai-sdk/google-v5": ["@ai-sdk/google@2.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6LyuUrCZuiULg0rUV+kT4T2jG19oUntudorI4ttv1ARkSbwl8A39ue3rA487aDDy6fUScdbGFiV5Yv/o4gidVA=="], @@ -3174,7 +3257,9 @@ "whisper/@decocms/runtime/@mastra/core/ai-v5": ["ai@5.0.60", "", { "dependencies": { "@ai-sdk/gateway": "1.0.33", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-80U/3kmdBW6g+JkLXpz/P2EwkyEaWlPlYtuLUpx/JYK9F7WZh9NnkYoh1KvUi1Sbpo0NyurBTvX0a2AG9mmbDA=="], - "@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "whisper/@decocms/runtime/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -3216,6 +3301,8 @@ "apify/@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], + "apify/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "data-for-seo/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "data-for-seo/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], @@ -3250,6 +3337,8 @@ "data-for-seo/@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], + "data-for-seo/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "gemini-pro-vision/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "gemini-pro-vision/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], @@ -3284,6 +3373,8 @@ "gemini-pro-vision/@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], + "gemini-pro-vision/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "inquirer-search-checkbox/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "inquirer-search-checkbox/inquirer/cli-cursor/restore-cursor/onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ=="], @@ -3296,6 +3387,10 @@ "inquirer-search-list/inquirer/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "meta-ads/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "openrouter/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "perplexity/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "perplexity/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], @@ -3330,6 +3425,10 @@ "perplexity/@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], + "perplexity/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "registry/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "whisper/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "whisper/@decocms/runtime/@mastra/core/@ai-sdk/anthropic-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], @@ -3364,7 +3463,9 @@ "whisper/@decocms/runtime/@mastra/core/ai-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="], - "apify/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "whisper/@decocms/runtime/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "apify/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "apify/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -3372,7 +3473,7 @@ "apify/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "data-for-seo/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "data-for-seo/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "data-for-seo/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -3380,7 +3481,7 @@ "data-for-seo/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "gemini-pro-vision/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "gemini-pro-vision/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "gemini-pro-vision/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -3392,7 +3493,7 @@ "inquirer-search-list/inquirer/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="], - "perplexity/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "perplexity/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "perplexity/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], @@ -3400,7 +3501,7 @@ "perplexity/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "whisper/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "whisper/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="], "whisper/@decocms/runtime/@mastra/core/@openrouter/ai-sdk-provider-v5/ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], diff --git a/connection-binding/app.json b/connection-binding/app.json new file mode 100644 index 00000000..60be69e4 --- /dev/null +++ b/connection-binding/app.json @@ -0,0 +1,617 @@ +{ + "scopeName": "deco", + "name": "connection", + "connection": { + "type": "BINDING" + }, + "description": "Connection Binding for MCPs.", + "icon": "https://assets.decocache.com/mcp/390f7756-ec01-47e4-bb31-9e7b18f6f56f/database.png", + "unlisted": true, + "tools": [ + { + "name": "COLLECTION_CONNECTIONS_UPDATE", + "description": "Update an existing MCP connection in the organization", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the connection" + }, + "data": { + "type": "object", + "properties": { + "updated_at": { + "type": "string", + "description": "When the connection was last updated" + }, + "updated_by": { + "type": "string", + "description": "User ID who last updated the connection" + }, + "configuration_state": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "null" + } + ], + "description": "Configuration state (decrypted)" + }, + "configuration_scopes": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Configuration scopes" + } + }, + "additionalProperties": true + } + }, + "required": [ + "id", + "data" + ], + "additionalProperties": false + }, + "outputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the connection" + }, + "title": { + "type": "string", + "description": "Human-readable name for the connection" + }, + "created_at": { + "type": "string", + "description": "When the connection was created" + }, + "updated_at": { + "type": "string", + "description": "When the connection was last updated" + }, + "created_by": { + "type": "string", + "description": "User ID who created the connection" + }, + "updated_by": { + "type": "string", + "description": "User ID who last updated the connection" + }, + "organization_id": { + "type": "string", + "description": "Organization ID this connection belongs to" + }, + "description": { + "type": [ + "string", + "null" + ], + "description": "Description of the connection" + }, + "icon": { + "type": [ + "string", + "null" + ], + "description": "Icon URL for the connection" + }, + "app_name": { + "type": [ + "string", + "null" + ], + "description": "Associated app name" + }, + "app_id": { + "type": [ + "string", + "null" + ], + "description": "Associated app ID" + }, + "connection_type": { + "type": "string", + "enum": [ + "HTTP", + "SSE", + "Websocket" + ], + "description": "Type of connection" + }, + "connection_url": { + "type": "string", + "description": "URL for the connection" + }, + "connection_token": { + "type": [ + "string", + "null" + ], + "description": "Authentication token" + }, + "connection_headers": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Custom headers" + }, + "oauth_config": { + "anyOf": [ + { + "type": "object", + "properties": { + "authorizationEndpoint": { + "type": "string", + "format": "uri" + }, + "tokenEndpoint": { + "type": "string", + "format": "uri" + }, + "introspectionEndpoint": { + "type": "string", + "format": "uri" + }, + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "grantType": { + "type": "string", + "enum": [ + "authorization_code", + "client_credentials" + ] + } + }, + "required": [ + "authorizationEndpoint", + "tokenEndpoint", + "clientId", + "scopes", + "grantType" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ], + "description": "OAuth configuration" + }, + "configuration_state": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "null" + } + ], + "description": "Configuration state (decrypted)" + }, + "configuration_scopes": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Configuration scopes" + }, + "metadata": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "null" + } + ], + "description": "Additional metadata (includes repository info)" + }, + "tools": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "inputSchema": { + "type": "object", + "additionalProperties": {} + }, + "outputSchema": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name", + "inputSchema" + ], + "additionalProperties": false + } + }, + { + "type": "null" + } + ], + "description": "Discovered tools from MCP" + }, + "bindings": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Detected bindings" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive", + "error" + ], + "description": "Current status" + } + }, + "required": [ + "id", + "title", + "created_at", + "updated_at", + "created_by", + "organization_id", + "description", + "icon", + "app_name", + "app_id", + "connection_type", + "connection_url", + "connection_token", + "connection_headers", + "oauth_config", + "configuration_state", + "metadata", + "tools", + "bindings", + "status" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + { + "name": "COLLECTION_CONNECTIONS_GET", + "description": "Get an existing MCP connection in the organization", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the connection" + } + } + }, + "outputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the connection" + }, + "title": { + "type": "string", + "description": "Human-readable name for the connection" + }, + "created_at": { + "type": "string", + "description": "When the connection was created" + }, + "updated_at": { + "type": "string", + "description": "When the connection was last updated" + }, + "created_by": { + "type": "string", + "description": "User ID who created the connection" + }, + "updated_by": { + "type": "string", + "description": "User ID who last updated the connection" + }, + "organization_id": { + "type": "string", + "description": "Organization ID this connection belongs to" + }, + "description": { + "type": [ + "string", + "null" + ], + "description": "Description of the connection" + }, + "icon": { + "type": [ + "string", + "null" + ], + "description": "Icon URL for the connection" + }, + "app_name": { + "type": [ + "string", + "null" + ], + "description": "Associated app name" + }, + "app_id": { + "type": [ + "string", + "null" + ], + "description": "Associated app ID" + }, + "connection_type": { + "type": "string", + "enum": [ + "HTTP", + "SSE", + "Websocket" + ], + "description": "Type of connection" + }, + "connection_url": { + "type": "string", + "description": "URL for the connection" + }, + "connection_token": { + "type": [ + "string", + "null" + ], + "description": "Authentication token" + }, + "connection_headers": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Custom headers" + }, + "oauth_config": { + "anyOf": [ + { + "type": "object", + "properties": { + "authorizationEndpoint": { + "type": "string", + "format": "uri" + }, + "tokenEndpoint": { + "type": "string", + "format": "uri" + }, + "introspectionEndpoint": { + "type": "string", + "format": "uri" + }, + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "grantType": { + "type": "string", + "enum": [ + "authorization_code", + "client_credentials" + ] + } + }, + "required": [ + "authorizationEndpoint", + "tokenEndpoint", + "clientId", + "scopes", + "grantType" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ], + "description": "OAuth configuration" + }, + "configuration_state": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "null" + } + ], + "description": "Configuration state (decrypted)" + }, + "configuration_scopes": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Configuration scopes" + }, + "metadata": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "null" + } + ], + "description": "Additional metadata (includes repository info)" + }, + "tools": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "inputSchema": { + "type": "object", + "additionalProperties": {} + }, + "outputSchema": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name", + "inputSchema" + ], + "additionalProperties": false + } + }, + { + "type": "null" + } + ], + "description": "Discovered tools from MCP" + }, + "bindings": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Detected bindings" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive", + "error" + ], + "description": "Current status" + } + }, + "required": [ + "id", + "title", + "created_at", + "updated_at", + "created_by", + "organization_id", + "description", + "icon", + "app_name", + "app_id", + "connection_type", + "connection_url", + "connection_token", + "connection_headers", + "oauth_config", + "configuration_state", + "metadata", + "tools", + "bindings", + "status" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + } + ] +} diff --git a/mcp-studio/package.json b/mcp-studio/package.json index 356bf16a..5607d721 100644 --- a/mcp-studio/package.json +++ b/mcp-studio/package.json @@ -14,9 +14,11 @@ "publish": "cat app.json | deco registry publish -w /shared/deco -y" }, "dependencies": { - "@decocms/bindings": "1.0.1-alpha.27", - "@decocms/runtime": "1.0.0-alpha.42", - "@decocms/mcps-shared": "1.0.0", + "@ai-sdk/mcp": "^1.0.1", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", + "@jitl/quickjs-wasmfile-release-sync": "^0.31.0", + "@modelcontextprotocol/sdk": "^1.25.1", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-slot": "^1.2.3", @@ -24,31 +26,29 @@ "@tanstack/react-query": "^5.66.5", "@tanstack/react-router": "^1.121.2", "@tanstack/react-router-devtools": "^1.121.2", + "@types/prettier": "^3.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.554.0", "next-themes": "^0.4.6", + "prettier": "^3.7.3", + "quickjs-emscripten": "^0.31.0", + "quickjs-emscripten-core": "^0.31.0", "react": "^19.0.0", "react-dom": "^19.0.0", "sonner": "^2.0.7", + "sucrase": "^3.35.0", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.6", "tailwindcss-animate": "^1.0.7", "zod": "^3.24.3" }, "devDependencies": { - "@decocms/vite-plugin": "1.0.0-alpha.1", - "@modelcontextprotocol/sdk": "1.20.2", - "@types/mime-db": "^1.43.6", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^5.1.1", - "concurrently": "^9.2.0", "deco-cli": "^0.28.0", "typescript": "^5.7.2", - "vite": "7.2.0" + "@decocms/mcps-shared": "1.0.0" }, "engines": { "node": ">=22.0.0" } -} \ No newline at end of file +} diff --git a/mcp-studio/server/db/index.ts b/mcp-studio/server/db/index.ts new file mode 100644 index 00000000..496c0b6f --- /dev/null +++ b/mcp-studio/server/db/index.ts @@ -0,0 +1,60 @@ +import type { Env } from "../types/env.ts"; +import { runSQL } from "./postgres.ts"; +import { postgresQueries } from "./schemas/workflow.ts"; + +/** + * Collection queries for database table creation. + * + * Note: Only 'agents' and 'workflows' are true collections with MCP tools. + * 'workflow_execution', 'workflow_execution_step_result', and 'workflow_event' + * are internal engine tables managed by direct database functions. + */ +const collectionsQueries = { + workflow_collections: { + idempotent: postgresQueries.workflowCollectionTableIdempotentQuery, + indexes: postgresQueries.workflowCollectionTableIndexesQuery, + }, + workflows: { + idempotent: postgresQueries.workflowTableIdempotentQuery, + indexes: postgresQueries.workflowTableIndexesQuery, + }, + // Internal engine tables (not collections, no MCP tools) + workflow_executions: { + idempotent: postgresQueries.workflowExecutionTableIdempotentQuery, + indexes: postgresQueries.workflowExecutionTableIndexesQuery, + }, + execution_step_results: { + idempotent: postgresQueries.executionStepResultsTableIdempotentQuery, + indexes: postgresQueries.executionStepResultsTableIndexesQuery, + }, + workflow_events: { + idempotent: postgresQueries.workflowEventsTableIdempotentQuery, + indexes: postgresQueries.workflowEventsTableIndexesQuery, + }, +}; + +async function ensureCollections(env: Env) { + for (const collection of Object.values(collectionsQueries)) { + try { + await runSQL(env, collection.idempotent); + } catch (error) { + console.error( + `Error ensuring collection ${collection.idempotent}`, + error, + ); + throw error; + } + } +} + +async function ensureIndexes(env: Env) { + for (const collection of Object.values(collectionsQueries)) { + try { + await runSQL(env, collection.indexes); + } catch (error) { + console.error(`Error ensuring indexes ${collection.indexes}`, error); + } + } +} + +export { collectionsQueries, ensureCollections, ensureIndexes }; diff --git a/mcp-studio/server/db/postgres.ts b/mcp-studio/server/db/postgres.ts new file mode 100644 index 00000000..1a578851 --- /dev/null +++ b/mcp-studio/server/db/postgres.ts @@ -0,0 +1,27 @@ +/** + * PostgreSQL Database Module + * + * Generic SQL runner using the DATABASE binding. + */ + +import type { Env } from "../types/env.ts"; + +/** + * Run a SQL query using the DATABASE binding + * @param env - The environment containing the DATABASE binding + * @param sql - SQL query with ? placeholders + * @param params - Parameters to substitute for ? placeholders + * @returns The query results as an array of rows + */ +export async function runSQL( + env: Env, + sql: string, + params: unknown[] = [], +): Promise { + const response = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql, + params, + }); + return (response.result[0]?.results ?? []) as T[]; +} diff --git a/mcp-studio/server/db/queries/executions.ts b/mcp-studio/server/db/queries/executions.ts new file mode 100644 index 00000000..b89ced20 --- /dev/null +++ b/mcp-studio/server/db/queries/executions.ts @@ -0,0 +1,558 @@ +/** + * Workflow Execution Database Operations + * + * Internal engine functions for workflow execution persistence. + * Uses DBOS patterns: checkpoint-based results, cancellation support. + * + * @see https://github.com/dbos-inc/dbos-transact-ts + */ + +import type { + Step, + WorkflowExecution, + WorkflowExecutionStatus, +} from "@decocms/bindings/workflow"; +import type { Env } from "../../types/env.ts"; +import { runSQL } from "../postgres.ts"; +import { + transformDbRowToExecution, + transformDbRowToStepResult, + type WorkflowExecutionStepResult, +} from "../transformers.ts"; + +/** + * Execution with workflow data joined + */ +export interface ExecutionWithWorkflow extends WorkflowExecution { + workflow_id: string; + steps: Step[]; + gateway_id: string; +} + +export async function claimExecution( + env: Env, + executionId: string, +): Promise { + const now = Date.now(); + + // Atomic claim: only succeeds if status is 'enqueued' + // Join with workflow table to get steps and gateway_id + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: ` + UPDATE workflow_execution + SET + status = 'running', + updated_at = ? + WHERE id = ? AND (status = 'enqueued') + RETURNING * + `, + params: [now, executionId], + }); + + const executionRow = result.result[0]?.results?.[0] as + | Record + | undefined; + + if (!executionRow) { + return null; + } + + // Get the workflow data + const workflowId = executionRow.workflow_id as string; + console.log( + `[EXECUTION] Claimed execution ${executionId}, fetching workflow ${workflowId}`, + ); + const workflow = await getWorkflow(env, workflowId); + + if (!workflow) { + throw new Error( + `Workflow ${workflowId} not found for execution ${executionId}`, + ); + } + + console.log(`[EXECUTION] Workflow data:`, { + id: workflow.id, + gateway_id: workflow.gateway_id, + stepsCount: workflow.steps?.length ?? 0, + steps: workflow.steps?.map((s: Step) => s.name) ?? [], + }); + + const execution = transformDbRowToExecution(executionRow); + + return { + ...execution, + workflow_id: workflowId, + steps: workflow.steps, + gateway_id: workflow.gateway_id, + }; +} + +/** + * Get a workflow execution by ID + */ +export async function getExecution( + env: Env, + id: string, +): Promise { + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: "SELECT * FROM workflow_execution WHERE id = ? LIMIT 1", + params: [id], + }); + + const row = result.result[0]?.results?.[0] as + | Record + | undefined; + return row ? transformDbRowToExecution(row) : null; +} + +/** + * Create an immutable workflow record (snapshot of the workflow definition) + */ +export async function createWorkflow( + env: Env, + data: { + workflow_collection_id?: string | null; + gateway_id: string; + input?: Record | null; + steps: Step[]; + }, +): Promise<{ id: string }> { + const user = env.MESH_REQUEST_CONTEXT?.ensureAuthenticated(); + const now = Date.now(); + const id = crypto.randomUUID(); + + const stepsJson = JSON.stringify(data.steps); + const result = await runSQL<{ id: string }>( + env, + `INSERT INTO workflow (id, workflow_collection_id, steps, input, gateway_id, created_at_epoch_ms, created_by) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id`, + [ + id, + data.workflow_collection_id ?? null, + stepsJson, + JSON.stringify(data.input || {}), + data.gateway_id, + now, + user?.id || null, + ], + ); + + if (!result.length) { + throw new Error(`Failed to create workflow`); + } + + return { id: result[0].id }; +} + +/** + * Get a workflow by ID + */ +export async function getWorkflow( + env: Env, + id: string, +): Promise<{ + id: string; + workflow_collection_id: string | null; + steps: Step[]; + input: Record | null; + gateway_id: string; + created_at_epoch_ms: number; + created_by: string | null; +} | null> { + const result = await runSQL>( + env, + "SELECT * FROM workflow WHERE id = ? LIMIT 1", + [id], + ); + + const row = result[0]; + if (!row) return null; + + return { + id: row.id as string, + workflow_collection_id: row.workflow_collection_id as string | null, + steps: + typeof row.steps === "string" + ? JSON.parse(row.steps) + : (row.steps as Step[]), + input: + typeof row.input === "string" + ? JSON.parse(row.input) + : (row.input as Record | null), + gateway_id: row.gateway_id as string, + created_at_epoch_ms: Number(row.created_at_epoch_ms), + created_by: row.created_by as string | null, + }; +} + +/** + * Create a new workflow execution + * First creates an immutable workflow record, then creates the execution referencing it + */ +export async function createExecution( + env: Env, + data: { + gateway_id: string; + input?: Record | null; + steps: Step[]; + timeout_ms?: number | null; + start_at_epoch_ms?: number | null; + workflow_collection_id?: string | null; + }, +): Promise<{ id: string; workflow_id: string }> { + console.log(`[EXECUTION] Creating execution with data:`, { + gateway_id: data.gateway_id, + hasInput: !!data.input, + stepsCount: data.steps?.length ?? 0, + steps: data.steps?.map((s) => s.name) ?? [], + workflow_collection_id: data.workflow_collection_id, + }); + + const user = env.MESH_REQUEST_CONTEXT?.ensureAuthenticated(); + const now = Date.now(); + + // First, create an immutable workflow record + const { id: workflowId } = await createWorkflow(env, { + workflow_collection_id: data.workflow_collection_id, + gateway_id: data.gateway_id, + input: data.input, + steps: data.steps, + }); + + // Then create the execution referencing the workflow + const executionId = crypto.randomUUID(); + const startAtEpochMs = data.start_at_epoch_ms ?? now; + const timeoutMs = data.timeout_ms; + const deadlineAtEpochMs = timeoutMs ? startAtEpochMs + timeoutMs : undefined; + + const result = await runSQL<{ id: string }>( + env, + `INSERT INTO workflow_execution (id, workflow_id, status, created_at, updated_at, created_by, input, timeout_ms, start_at_epoch_ms, deadline_at_epoch_ms) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id`, + [ + executionId, + workflowId, + "enqueued", + now, + now, + user?.id || null, + JSON.stringify(data.input || {}), + timeoutMs ?? null, + startAtEpochMs, + deadlineAtEpochMs ?? null, + ], + ); + + if (!result.length) { + throw new Error(`Failed to create workflow execution`); + } + + return { id: result[0].id, workflow_id: workflowId }; +} + +/** + * Update a workflow execution + */ +export async function updateExecution( + env: Env, + id: string, + data: Partial<{ + status: WorkflowExecutionStatus; + output: unknown; + error: string; + completed_at_epoch_ms: number; + }>, +): Promise<{ + id: string; + status: WorkflowExecutionStatus; + output: unknown; + error: string; + completed_at_epoch_ms: number; +}> { + const now = Date.now(); + + const setClauses: string[] = []; + const params: unknown[] = []; + + // Always update these fields + setClauses.push(`updated_at = ?`); + params.push(now); + + // Conditionally update other fields + if (data.status !== undefined) { + setClauses.push(`status = ?`); + params.push(data.status); + } + if (data.output !== undefined) { + setClauses.push(`output = ?::jsonb`); + params.push(JSON.stringify(data.output)); + } + if (data.error !== undefined) { + setClauses.push(`error = ?::jsonb`); + params.push(JSON.stringify(data.error)); + } + if (data.completed_at_epoch_ms !== undefined) { + setClauses.push(`completed_at_epoch_ms = ?`); + params.push(data.completed_at_epoch_ms); + } + + params.push(id); + + const result = await runSQL<{ + id: string; + status: WorkflowExecutionStatus; + output: unknown; + error: string; + completed_at_epoch_ms: number; + }>( + env, + `UPDATE workflow_execution SET ${setClauses.join(", ")} WHERE id = ? RETURNING id, status, output, error, completed_at_epoch_ms`, + params, + ); + return result[0]; +} + +/** + * Cancel a workflow execution. + * + * Sets status to 'cancelled' and prevents further execution unless the execution is resumed. + * Does NOT stop currently running steps - they will complete but + * subsequent steps won't start. The executor checks cancellation + * at step boundaries via checkIfCancelled(). + * + * @param env - Environment with database access + * @param executionId - The execution ID to cancel + * @returns The updated execution or null if not found + */ +export async function cancelExecution( + env: Env, + executionId: string, +): Promise { + const now = Date.now(); + + // Only cancel if currently enqueued or running + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: ` + UPDATE workflow_execution + SET + status = 'cancelled', + updated_at = ? + WHERE id = ? AND status IN ('enqueued', 'running') + RETURNING id + `, + // error column is JSONB, so serialize it + params: [now, executionId], + }); + + const id = result.result[0]?.results?.[0] as string | undefined; + + if (!id) { + // Check if execution exists but wasn't in cancellable state + return null; + } + + return id; +} + +/** + * Resume a cancelled execution. + * + * Sets status back to 'pending' so it can be re-queued. + * + * @param env - Environment with database access + * @param scheduler - Scheduler for re-queuing + * @param executionId - The execution ID to resume + * @param options - Optional settings + * @returns The updated execution or null if not found/resumable + */ +export async function resumeExecution( + env: Env, + executionId: string, +): Promise { + const now = Date.now(); + + const result = await runSQL( + env, + `UPDATE workflow_execution SET status = 'enqueued', updated_at = ?, completed_at_epoch_ms = NULL WHERE id = ? AND status = 'cancelled' RETURNING *`, + [now, executionId], + ); + + return result[0] ?? null; +} + +/** + * List workflow executions with filtering + */ +export async function listExecutions( + env: Env, + options: { + status?: string; + limit?: number; + offset?: number; + } = {}, +): Promise<{ + items: WorkflowExecution[]; + totalCount: number; + hasMore: boolean; +}> { + const { status, limit = 50, offset = 0 } = options; + const conditions: string[] = []; + const params: unknown[] = []; + if (status) { + conditions.push(`status = ?`); + params.push(status); + } + + const whereClause = conditions.length + ? `WHERE ${conditions.join(" AND ")}` + : ""; + + const result = await runSQL[]>( + env, + ` + SELECT * FROM workflow_execution + ${whereClause} + ORDER BY created_at DESC + LIMIT ? OFFSET ? + `, + [...params, limit, offset], + ); + + const totalCount = result.length; + + return { + items: result.map((row) => + transformDbRowToExecution(row as unknown as Record), + ), + totalCount, + hasMore: offset + result.length < totalCount, + }; +} + +// ============================================================================ +// Step Results +// ============================================================================ + +export async function getStepResults( + env: Env, + executionId: string, +): Promise { + const result = await runSQL( + env, + `SELECT * FROM workflow_execution_step_result WHERE execution_id = ?`, + [executionId], + ); + + return ( + result.map((row) => + transformDbRowToStepResult(row as unknown as Record), + ) ?? [] + ); +} + +/** + * Get a specific step result + */ +export async function getStepResult( + env: Env, + executionId: string, + stepId: string, +): Promise { + const result = await runSQL( + env, + `SELECT * FROM workflow_execution_step_result WHERE execution_id = ? AND step_id = ?`, + [executionId, stepId], + ); + + return result[0] ?? null; +} + +/** + * Create a new step result, or detect if another worker already created it. + * + * Uses UNIQUE constraint to detect race conditions: + * - If we create the record, we won the race → execute the step + * - If UNIQUE conflict, we lost the race → DON'T execute, use existing result + * + * @returns CreateStepResultOutcome with created=false if we lost the race + */ +export async function createStepResult( + env: Env, + data: { + execution_id: string; + step_id: string; + output?: unknown; + error?: string; + completed_at_epoch_ms?: number; + }, +): Promise { + // Try to INSERT - if UNIQUE conflict, RETURNING gives nothing + const result = await runSQL( + env, + `INSERT INTO workflow_execution_step_result (execution_id, step_id, started_at_epoch_ms, completed_at_epoch_ms, output, error) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (execution_id, step_id) DO NOTHING RETURNING *`, + [ + data.execution_id, + data.step_id, + Date.now(), + data.completed_at_epoch_ms ?? null, + JSON.stringify(data.output), + JSON.stringify(data.error), + ], + ); + + return result[0]; +} + +/** + * Update a step result + */ +export async function updateStepResult( + env: Env, + executionId: string, + stepId: string, + data: Partial<{ + output: unknown; + error: string; + started_at_epoch_ms: number; + completed_at_epoch_ms: number; + }>, +): Promise { + const setClauses: string[] = []; + const params: unknown[] = []; + + if (data.error !== undefined) { + setClauses.push(`error = ?::jsonb`); + params.push(JSON.stringify(data.error)); + } + if (data.started_at_epoch_ms !== undefined) { + setClauses.push(`started_at_epoch_ms = ?`); + params.push(data.started_at_epoch_ms); + } + if (data.completed_at_epoch_ms !== undefined) { + setClauses.push(`completed_at_epoch_ms = ?`); + params.push(data.completed_at_epoch_ms); + } + if (data.output !== undefined) { + setClauses.push(`output = ?::jsonb`); + params.push(JSON.stringify(data.output)); + } + + if (setClauses.length === 0) { + throw new Error("No fields to update"); + } + + params.push(executionId, stepId); + + const sql = ` + UPDATE workflow_execution_step_result + SET ${setClauses.join(", ")} + WHERE execution_id = ?::text AND step_id = ?::text + RETURNING * + `; + + // Don't overwrite a completed step + const result = await runSQL(env, sql, params); + + return result[0]; +} diff --git a/mcp-studio/server/lib/postgres.ts b/mcp-studio/server/db/schemas/agents.ts similarity index 94% rename from mcp-studio/server/lib/postgres.ts rename to mcp-studio/server/db/schemas/agents.ts index 9a9177aa..21293b48 100644 --- a/mcp-studio/server/lib/postgres.ts +++ b/mcp-studio/server/db/schemas/agents.ts @@ -1,11 +1,4 @@ -/** - * PostgreSQL Database Module - * - * This module provides PostgreSQL connectivity using the DATABASE binding - * and handles automatic table creation for the assistants collection. - */ - -import type { Env } from "../main.ts"; +import type { Env } from "../../types/env.ts"; /** * Run a SQL query using the DATABASE binding diff --git a/mcp-studio/server/lib/query-builder.ts b/mcp-studio/server/db/schemas/query-builder.ts similarity index 100% rename from mcp-studio/server/lib/query-builder.ts rename to mcp-studio/server/db/schemas/query-builder.ts diff --git a/mcp-studio/server/db/schemas/workflow.ts b/mcp-studio/server/db/schemas/workflow.ts new file mode 100644 index 00000000..da08bc7e --- /dev/null +++ b/mcp-studio/server/db/schemas/workflow.ts @@ -0,0 +1,165 @@ +// ============================================================================ +// PostgreSQL Dialect +// ============================================================================ + +import type { WorkflowQueries } from "../transformers.ts"; + +const postgresWorkflowCollectionTableIdempotentQuery = ` + CREATE TABLE IF NOT EXISTS workflow_collection ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + input JSONB, + gateway_id TEXT NOT NULL, + description TEXT, + steps JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + created_by TEXT, + updated_by TEXT + ) +`; + +const postgresWorkflowCollectionTableIndexesQuery = ` + CREATE INDEX IF NOT EXISTS idx_workflow_collection_created_at ON workflow_collection(created_at DESC); + CREATE INDEX IF NOT EXISTS idx_workflow_collection_updated_at ON workflow_collection(updated_at DESC); + CREATE INDEX IF NOT EXISTS idx_workflow_collection_title ON workflow_collection(title); +`; + +const postgresWorkflowTableIdempotentQuery = ` + CREATE TABLE IF NOT EXISTS workflow ( + id TEXT PRIMARY KEY, + workflow_collection_id TEXT, + steps JSONB NOT NULL DEFAULT '{}', + input JSONB, + gateway_id TEXT NOT NULL, + created_at_epoch_ms BIGINT NOT NULL, + created_by TEXT + ) +`; + +const postgresWorkflowTableIndexesQuery = ` + CREATE INDEX IF NOT EXISTS idx_workflow_created_at_epoch ON workflow(created_at_epoch_ms DESC); + CREATE INDEX IF NOT EXISTS idx_workflow_collection_id ON workflow(workflow_collection_id); + CREATE INDEX IF NOT EXISTS idx_workflow_gateway_id ON workflow(gateway_id); +`; + +const postgresWorkflowExecutionTableIdempotentQuery = ` +CREATE TABLE IF NOT EXISTS workflow_execution ( + id TEXT PRIMARY KEY, + workflow_id TEXT NOT NULL, + status TEXT NOT NULL CHECK(status IN ('enqueued', 'cancelled', 'success', 'error', 'running')), + input JSONB, + output JSONB, + + created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM now())*1000)::bigint, + updated_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM now())*1000)::bigint, + start_at_epoch_ms BIGINT, + started_at_epoch_ms BIGINT, + completed_at_epoch_ms BIGINT, + + timeout_ms BIGINT, + deadline_at_epoch_ms BIGINT, + error JSONB, + + created_by TEXT +) +`; + +const postgresWorkflowExecutionTableIndexesQuery = ` + CREATE INDEX IF NOT EXISTS idx_workflow_execution_status ON workflow_execution(status); + CREATE INDEX IF NOT EXISTS idx_workflow_execution_created_at ON workflow_execution(created_at DESC); + CREATE INDEX IF NOT EXISTS idx_workflow_execution_start_at ON workflow_execution(start_at_epoch_ms); +`; + +const postgresExecutionStepResultsTableIdempotentQuery = ` +CREATE TABLE IF NOT EXISTS workflow_execution_step_result ( + execution_id TEXT NOT NULL, + step_id TEXT NOT NULL, + started_at_epoch_ms BIGINT, + completed_at_epoch_ms BIGINT, + output JSONB, + error JSONB, + PRIMARY KEY (execution_id, step_id), + FOREIGN KEY (execution_id) REFERENCES workflow_execution(id) +) +`; + +const postgresExecutionStepResultsTableIndexesQuery = ` + CREATE INDEX IF NOT EXISTS idx_workflow_execution_step_result_execution ON workflow_execution_step_result(execution_id); + CREATE INDEX IF NOT EXISTS idx_workflow_execution_step_result_started ON workflow_execution_step_result(started_at_epoch_ms DESC); + CREATE INDEX IF NOT EXISTS idx_workflow_execution_step_result_completed ON workflow_execution_step_result(completed_at_epoch_ms DESC); +`; + +const postgresWorkflowEventsTableIdempotentQuery = ` +CREATE TABLE IF NOT EXISTS workflow_event ( + id TEXT PRIMARY KEY, + execution_id TEXT NOT NULL, + + type TEXT NOT NULL CHECK(type IN ( + 'signal', + 'timer', + 'message', + 'output', + 'step_started', + 'step_completed', + 'workflow_started', + 'workflow_completed' + )), + + name TEXT, + payload JSONB, + + created_at BIGINT NOT NULL, + visible_at BIGINT, + consumed_at BIGINT, + + source_execution_id TEXT, + + FOREIGN KEY (execution_id) REFERENCES workflow_execution(id) +) +`; + +const postgresWorkflowEventsTableIndexesQuery = ` + CREATE INDEX IF NOT EXISTS idx_workflow_event_pending + ON workflow_event(execution_id, type, consumed_at, visible_at) + WHERE consumed_at IS NULL; + + CREATE INDEX IF NOT EXISTS idx_workflow_event_by_name + ON workflow_event(execution_id, type, name) + WHERE consumed_at IS NULL; + + CREATE INDEX IF NOT EXISTS idx_workflow_event_output + ON workflow_event(execution_id, name) + WHERE type = 'output'; + + CREATE UNIQUE INDEX IF NOT EXISTS idx_workflow_event_output_unique + ON workflow_event(execution_id, type, name) + WHERE type = 'output'; + + CREATE INDEX IF NOT EXISTS idx_workflow_event_source + ON workflow_event(source_execution_id) + WHERE source_execution_id IS NOT NULL; + + CREATE INDEX IF NOT EXISTS idx_workflow_event_created + ON workflow_event(execution_id, created_at); + `; + +export const postgresQueries: WorkflowQueries = { + workflowCollectionTableIdempotentQuery: + postgresWorkflowCollectionTableIdempotentQuery, + workflowCollectionTableIndexesQuery: + postgresWorkflowCollectionTableIndexesQuery, + workflowTableIdempotentQuery: postgresWorkflowTableIdempotentQuery, + workflowTableIndexesQuery: postgresWorkflowTableIndexesQuery, + workflowExecutionTableIdempotentQuery: + postgresWorkflowExecutionTableIdempotentQuery, + workflowExecutionTableIndexesQuery: + postgresWorkflowExecutionTableIndexesQuery, + executionStepResultsTableIdempotentQuery: + postgresExecutionStepResultsTableIdempotentQuery, + executionStepResultsTableIndexesQuery: + postgresExecutionStepResultsTableIndexesQuery, + workflowEventsTableIdempotentQuery: + postgresWorkflowEventsTableIdempotentQuery, + workflowEventsTableIndexesQuery: postgresWorkflowEventsTableIndexesQuery, +}; diff --git a/mcp-studio/server/db/transformers.ts b/mcp-studio/server/db/transformers.ts new file mode 100644 index 00000000..60534486 --- /dev/null +++ b/mcp-studio/server/db/transformers.ts @@ -0,0 +1,142 @@ +/** + * Workflow DB Transformers + * + * Single source of truth for database row transformations. + * All workflow-related DB operations should import from here. + */ + +import { + type WorkflowEvent, + WorkflowEventSchema, + type WorkflowExecution, + WorkflowExecutionSchema, +} from "@decocms/bindings/workflow"; + +// ============================================================================ +// Utility Helpers +// ============================================================================ + +/** Convert epoch ms to ISO string. Handles number, bigint, string. */ +export const epochMsToIsoString = (epochMs: unknown): string => { + if (epochMs === null || epochMs === undefined) + return new Date().toISOString(); + const num = + typeof epochMs === "string" ? parseInt(epochMs, 10) : Number(epochMs); + return Number.isNaN(num) + ? new Date().toISOString() + : new Date(num).toISOString(); +}; + +/** Convert bigint/string to number or null */ +export const toNumberOrNull = (value: unknown): number | null => { + if (value === null || value === undefined) return null; + const num = typeof value === "string" ? parseInt(value, 10) : Number(value); + return Number.isNaN(num) ? null : num; +}; + +/** Safely parse JSON - handles JSONB already-parsed values */ +export const safeJsonParse = (value: unknown): unknown => { + if (value === null || value === undefined) return undefined; + if (typeof value === "object") return value; + if (typeof value === "string") { + const trimmed = value.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + return JSON.parse(value); + } catch { + return value; + } + } + return value; + } + return value; +}; + +// ============================================================================ +// Row Transformers +// ============================================================================ + +/** Transform DB row to WorkflowExecution */ +export function transformDbRowToExecution( + row: Record = {}, +): WorkflowExecution { + const transformed = { + ...row, + // Execution now references workflow via workflow_id + // These fields are now on the workflow table, provide empty placeholders for schema + // The actual values will be joined from the workflow table when needed + workflow_id: row.workflow_id as string, + title: row.title ?? "", + steps: row.steps ?? [], + gateway_id: row.gateway_id ?? "", + start_at_epoch_ms: toNumberOrNull(row.start_at_epoch_ms), + started_at_epoch_ms: toNumberOrNull(row.started_at_epoch_ms), + timeout_ms: toNumberOrNull(row.timeout_ms), + deadline_at_epoch_ms: toNumberOrNull(row.deadline_at_epoch_ms), + completed_at_epoch_ms: toNumberOrNull(row.completed_at_epoch_ms), + created_at: epochMsToIsoString(row.created_at), + updated_at: epochMsToIsoString(row.updated_at), + input: safeJsonParse(row.input), + output: safeJsonParse(row.output), + error: safeJsonParse(row.error), + }; + const parsed = WorkflowExecutionSchema.parse(transformed); + return { ...parsed }; +} + +export interface WorkflowExecutionStepResult { + started_at_epoch_ms: number; + completed_at_epoch_ms?: number; + output?: unknown; + error?: unknown; + step_id: string; + execution_id: string; +} +/** Transform DB row to WorkflowExecutionStepResult */ +export function transformDbRowToStepResult( + row: Record = {}, +): WorkflowExecutionStepResult { + return { + started_at_epoch_ms: toNumberOrNull(row.started_at_epoch_ms) ?? Date.now(), + completed_at_epoch_ms: + toNumberOrNull(row.completed_at_epoch_ms) ?? undefined, + output: safeJsonParse(row.output), + error: safeJsonParse(row.error), + step_id: row.step_id as string, + execution_id: row.execution_id as string, + }; +} + +/** Transform DB row to WorkflowEvent */ +export function transformDbRowToEvent( + row: Record, +): WorkflowEvent { + return WorkflowEventSchema.parse({ + ...row, + created_at: epochMsToIsoString(Number(row.created_at)), + visible_at: Number(row.visible_at), + consumed_at: row.consumed_at ? Number(row.consumed_at) : undefined, + payload: row.payload + ? typeof row.payload === "string" + ? JSON.parse(row.payload) + : row.payload + : undefined, + }); +} + +// ============================================================================ +// SQL Query Types (for dialect-specific implementations) +// ============================================================================ + +export interface WorkflowQueries { + workflowCollectionTableIdempotentQuery: string; + workflowCollectionTableIndexesQuery: string; + workflowTableIdempotentQuery: string; + workflowTableIndexesQuery: string; + workflowExecutionTableIdempotentQuery: string; + workflowExecutionTableIndexesQuery: string; + executionStepResultsTableIdempotentQuery: string; + executionStepResultsTableIndexesQuery: string; + workflowEventsTableIdempotentQuery: string; + workflowEventsTableIndexesQuery: string; +} diff --git a/mcp-studio/server/engine/context.ts b/mcp-studio/server/engine/context.ts new file mode 100644 index 00000000..0ce226c5 --- /dev/null +++ b/mcp-studio/server/engine/context.ts @@ -0,0 +1,73 @@ +/** + * Workflow Execution Context + * + * Encapsulates env and executionId to avoid prop drilling. + * Provides convenient access to common operations. + */ + +import { + createStepResult, + getExecution, + getStepResults, + updateStepResult, +} from "../db/queries/executions.ts"; +import type { Env } from "../types/env.ts"; +import { WorkflowCancelledError } from "../utils/errors.ts"; + +export class ExecutionContext { + constructor( + readonly env: Env, + readonly executionId: string, + readonly gatewayId: string, + ) {} + + get token(): string { + return this.env.MESH_REQUEST_CONTEXT?.token || ""; + } + + get meshUrl(): string { + return this.env.MESH_REQUEST_CONTEXT?.meshUrl ?? ""; + } + + async checkCancelled(): Promise { + const execution = await getExecution(this.env, this.executionId); + if (execution?.status === "cancelled") { + throw new WorkflowCancelledError(this.executionId); + } + } + + async getStepResults() { + return getStepResults(this.env, this.executionId); + } + + async claimStep(stepId: string): Promise { + await createStepResult(this.env, { + execution_id: this.executionId, + step_id: stepId, + }); + } + + async completeStep( + stepId: string, + output?: unknown, + error?: string, + ): Promise { + await updateStepResult(this.env, this.executionId, stepId, { + output, + error, + completed_at_epoch_ms: Date.now(), + }); + } + + async updateStep( + stepId: string, + data: { + output?: unknown; + error?: string; + started_at_epoch_ms?: number; + completed_at_epoch_ms?: number; + }, + ) { + return updateStepResult(this.env, this.executionId, stepId, data); + } +} diff --git a/mcp-studio/server/engine/error-handler.ts b/mcp-studio/server/engine/error-handler.ts new file mode 100644 index 00000000..f5ae4296 --- /dev/null +++ b/mcp-studio/server/engine/error-handler.ts @@ -0,0 +1,83 @@ +/** + * Workflow Error Handler + * + * Centralized error handling for workflow execution. + */ + +import { updateExecution } from "../db/queries/executions.ts"; +import type { Env } from "../types/env.ts"; +import { + ExecutionNotFoundError, + StepExecutionError, + StepTimeoutError, + WaitingForSignalError, + WorkflowCancelledError, +} from "../utils/errors.ts"; + +export type ExecuteWorkflowResult = + | { status: "success"; output: unknown } + | { status: "error"; error: string } + | { status: "cancelled"; error?: string } + | { status: "waiting_for_signal"; message: string } + | { status: "skipped"; reason: string } + | { status: "stuck"; message: string }; + +export async function handleExecutionError( + env: Env, + executionId: string, + err: unknown, +): Promise { + if (err instanceof ExecutionNotFoundError) { + return { status: "skipped", reason: "Execution busy or not found" }; + } + + if (err instanceof StepTimeoutError) { + await updateExecution(env, executionId, { + status: "error", + error: + typeof err.message === "string" + ? err.message + : JSON.stringify(err.message), + completed_at_epoch_ms: Date.now(), + }); + return { status: "error", error: err.message }; + } + + if (err instanceof StepExecutionError) { + await updateExecution(env, executionId, { + status: "error", + error: err.message, + completed_at_epoch_ms: Date.now(), + }); + return { status: "error", error: err.message }; + } + + if (err instanceof WaitingForSignalError) { + await updateExecution(env, executionId, { + status: "enqueued", + }); + return { status: "waiting_for_signal", message: err.message }; + } + if (err instanceof WorkflowCancelledError) { + await updateExecution(env, executionId, { + status: "cancelled", + error: err.message, + }); + return { status: "cancelled", error: err.message }; + } + + const errorMsg = err instanceof Error ? err.message : String(err); + console.error(`[WORKFLOW] Unknown error executing workflow: ${errorMsg}`); + + try { + await updateExecution(env, executionId, { + status: "error", + error: errorMsg, + completed_at_epoch_ms: Date.now(), + }); + } catch (err) { + console.error(`[WORKFLOW] Error updating execution: ${err}`); + } + + return { status: "error", error: errorMsg }; +} diff --git a/mcp-studio/server/engine/events.ts b/mcp-studio/server/engine/events.ts new file mode 100644 index 00000000..f3fcb786 --- /dev/null +++ b/mcp-studio/server/engine/events.ts @@ -0,0 +1,56 @@ +/** + * Workflow Events & Signals API + * + * Unified events system for signals, timers, and durable execution. + * Inspired by DBOS send/recv patterns and deco-cx/durable visible_at. + */ + +import type { EventType, WorkflowEvent } from "@decocms/bindings/workflow"; +import { transformDbRowToEvent } from "../db/transformers.ts"; +import type { Env } from "../types/env.ts"; + +export async function getPendingEvents( + env: Env, + executionId: string, + type?: EventType, +): Promise { + const now = Date.now(); + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: `SELECT * FROM workflow_event WHERE execution_id = ? AND consumed_at IS NULL + AND (visible_at IS NULL OR visible_at <= ?) ${type ? "AND type = ?" : ""} + ORDER BY visible_at ASC NULLS FIRST, created_at ASC`, + params: type ? [executionId, now, type] : [executionId, now], + }); + return ((result.result[0]?.results || []) as Record[]).map( + transformDbRowToEvent, + ); +} + +// ============================================================================ +// Signals (Human-in-the-loop: approvals, webhooks, manual data entry) +// ============================================================================ + +export type WorkflowSignal = WorkflowEvent & { signal_name?: string }; + +export async function getSignals( + env: Env, + executionId: string, +): Promise { + const events = await getPendingEvents(env, executionId, "signal"); + return events.map( + (e): WorkflowSignal => ({ ...e, signal_name: e.name ?? undefined }), + ); +} + +export async function consumeSignal( + env: Env, + signalId: string, +): Promise { + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: `UPDATE workflow_event SET consumed_at = ? WHERE id = ? AND consumed_at IS NULL RETURNING id`, + params: [Date.now(), signalId], + }); + return (result.result[0]?.results?.length ?? 0) > 0; +} diff --git a/mcp-studio/server/engine/executor.ts b/mcp-studio/server/engine/executor.ts new file mode 100644 index 00000000..ff35e119 --- /dev/null +++ b/mcp-studio/server/engine/executor.ts @@ -0,0 +1,219 @@ +/** + * Workflow Executor + * + * Orchestrates parallel step execution based on DAG analysis. + */ + +import { + groupStepsByLevel, + validateNoCycles, +} from "@decocms/bindings/workflow"; +import { + claimExecution, + getStepResults, + updateExecution, + updateStepResult, +} from "../db/queries/executions.ts"; +import type { Env } from "../types/env.ts"; +import type { Step, StepResult } from "../types/step.ts"; +import { ExecutionNotFoundError, StepExecutionError } from "../utils/errors.ts"; +import type { RefContext } from "../utils/ref-resolver.ts"; +import { resolveAllRefs } from "../utils/ref-resolver.ts"; +import { + type ExecuteWorkflowResult, + handleExecutionError, +} from "./error-handler.ts"; +import { StepExecutor } from "./steps/step-executor.ts"; + +export type { ExecuteWorkflowResult }; + +export async function executeWorkflow( + env: Env, + executionId: string, +): Promise { + try { + console.log(`[WORKFLOW] Starting execution: ${executionId}`); + const execution = await claimExecution(env, executionId); + if (!execution) throw new ExecutionNotFoundError(executionId); + + console.log(`[WORKFLOW] Claimed execution:`, { + id: execution.id, + workflow_id: execution.workflow_id, + gateway_id: execution.gateway_id, + status: execution.status, + stepsCount: execution.steps?.length ?? 0, + hasInput: !!execution.input, + }); + + // Steps and gateway_id are now on the joined workflow data + const steps = execution.steps as Step[]; + if (!steps || steps.length === 0) { + console.error(`[WORKFLOW] No steps found in execution:`, execution); + throw new Error( + `Workflow has no steps. Execution data: ${JSON.stringify(execution)}`, + ); + } + + const workflowInput = parseInput(execution.input); + console.log(`[WORKFLOW] Parsed input:`, workflowInput); + const ctx = await buildContext(env, executionId, workflowInput); + + const validation = validateNoCycles(steps); + if (!validation.isValid) throw new Error(validation.error); + + const completedSteps: string[] = []; + + const stepExecutor = new StepExecutor( + env, + executionId, + execution.gateway_id, // Now comes from joined workflow data + ); + + for (const levelSteps of groupStepsByLevel(steps)) { + const { completed, pending } = partitionSteps( + levelSteps, + ctx.stepOutputs, + ); + completedSteps.push(...completed.map((s) => s.name)); + + if (!pending.length) continue; + + const results = await executeLevelSteps({ + executionId, + steps: pending, + ctx, + stepExecutor, + env, + }); + + processResults(results, ctx.stepOutputs, completedSteps, executionId); + } + + const lastStepResult = await stepExecutor.getLastStepResult(); + + const output = buildOutput(completedSteps, lastStepResult?.output); + await updateExecution(env, executionId, { + status: "success", + output, + completed_at_epoch_ms: Date.now(), + }); + + return { status: "success", output }; + } catch (err) { + console.error(`[WORKFLOW] Error executing workflow ${executionId}:`, err); + if (err instanceof Error) { + console.error(`[WORKFLOW] Error stack:`, err.stack); + } + return await handleExecutionError(env, executionId, err); + } +} + +function parseInput(input: unknown): Record { + if (typeof input === "string") { + return JSON.parse(input); + } + return (input as Record) || {}; +} + +async function buildContext( + env: Env, + executionId: string, + workflowInput: Record, +): Promise }> { + const stepOutputs = new Map(); + const allStepResults = await getStepResults(env, executionId); + for (const sr of allStepResults) { + if (sr.completed_at_epoch_ms && sr.output) { + stepOutputs.set(sr.step_id, sr.output); + } + } + + return { stepOutputs, workflowInput }; +} + +function partitionSteps( + steps: Step[], + completedOutputs: Map, +): { completed: Step[]; pending: Step[] } { + const completed = steps.filter((s) => completedOutputs.has(s.name)); + const pending = steps.filter((s) => !completedOutputs.has(s.name)); + return { completed, pending }; +} + +interface StepExecutionResult { + step: Step; + result?: StepResult; +} + +async function executeLevelSteps({ + executionId, + steps, + ctx, + stepExecutor, + env, +}: { + executionId: string; + steps: Step[]; + ctx: RefContext; + stepExecutor: StepExecutor; + env: Env; +}): Promise { + return Promise.all( + steps.map(async (step) => { + const input = resolveAllRefs(step.input, ctx).resolved as Record< + string, + unknown + >; + const existingResult = ctx.stepOutputs.get(step.name) as + | { startedAt: number } + | undefined; + const startedAt = existingResult + ? new Date(existingResult.startedAt).getTime() + : Date.now(); + + try { + const result = await stepExecutor.executeStep(step, input, { + started_at_epoch_ms: startedAt, + }); + console.log("Step executed", step.name, result); + return { step, result }; + } catch (err) { + console.error("Step failed", step.name, err); + updateStepResult(env, executionId, step.name, { + error: err instanceof Error ? err.message : String(err), + completed_at_epoch_ms: Date.now(), + }); + throw err; + } + }), + ); +} + +function processResults( + results: StepExecutionResult[], + stepOutputs: Map, + completedSteps: string[], + executionId: string, +): void { + // Check for any step failures + const failedStep = results.find((r) => r.result?.error); + if (failedStep && failedStep.result?.error) { + throw new StepExecutionError( + executionId, + failedStep.step.name, + failedStep.result.error, + ); + } + + for (const r of results) { + stepOutputs.set(r.step.name, r.result?.output ?? undefined); + completedSteps.push(r.step.name); + } +} + +function buildOutput(completedSteps: string[], output: unknown) { + return { + completedSteps, + output, + }; +} diff --git a/mcp-studio/server/engine/steps/code-step.ts b/mcp-studio/server/engine/steps/code-step.ts new file mode 100644 index 00000000..d114e1af --- /dev/null +++ b/mcp-studio/server/engine/steps/code-step.ts @@ -0,0 +1,296 @@ +/** + * Transform Step Executor + * + * Executes pure TypeScript transformation steps in a QuickJS sandbox. + * + * Features: + * - TypeScript transpilation to JavaScript (using sucrase for edge compatibility) + * - Deterministic sandbox execution (no Date, Math.random, etc.) + * - Input/Output interface extraction for validation + * + * @see docs/WORKFLOW_SCHEMA_DESIGN.md + */ + +import { transform } from "sucrase"; +import { + callFunction, + createSandboxRuntime, + installConsole, + type QuickJSHandle, + type SandboxContext, +} from "../../sandbox/index.ts"; +import type { StepResult } from "../../types/step.ts"; + +export function transpileTypeScript(code: string): string { + const result = transform(code, { + transforms: ["typescript"], + disableESTransforms: true, // Keep modern JS syntax for QuickJS + }); + + return result.code; +} + +export function extractSchemas(code: string): { + input: Record; + output: Record; +} { + const inputMatch = code.match(/interface\s+Input\s*\{([^}]*)\}/); + const outputMatch = code.match(/interface\s+Output\s*\{([^}]*)\}/); + + const parseInterfaceBody = (body: string): Record => { + if (!body.trim()) return { type: "object" }; + + const properties: Record = {}; + const required: string[] = []; + + // Parse simple property declarations + const propRegex = /(\w+)(\?)?\s*:\s*([^;]+);/g; + let match: RegExpExecArray | null = propRegex.exec(body); + + while (match !== null) { + const [, name, optional, typeStr] = match; + const type = typeStr.trim(); + + if (!optional) { + required.push(name); + } + + properties[name] = parseTypeToSchema(type); + match = propRegex.exec(body); + } + + return { + type: "object", + properties, + ...(required.length > 0 ? { required } : {}), + }; + }; + + const parseTypeToSchema = (type: string): Record => { + type = type.trim(); + + // Array types + if (type.endsWith("[]")) { + return { + type: "array", + items: parseTypeToSchema(type.slice(0, -2)), + }; + } + if (type.startsWith("Array<") && type.endsWith(">")) { + return { + type: "array", + items: parseTypeToSchema(type.slice(6, -1)), + }; + } + + // Object type with inline properties + if (type.startsWith("{") && type.endsWith("}")) { + return parseInterfaceBody(type.slice(1, -1)); + } + + // Primitive types + switch (type) { + case "string": + return { type: "string" }; + case "number": + return { type: "number" }; + case "boolean": + return { type: "boolean" }; + case "null": + return { type: "null" }; + case "unknown": + case "any": + return {}; + default: + // For complex types, just return object + return { type: "object" }; + } + }; + + return { + input: inputMatch ? parseInterfaceBody(inputMatch[1]) : { type: "object" }, + output: outputMatch + ? parseInterfaceBody(outputMatch[1]) + : { type: "object" }, + }; +} + +/** + * Summarize input for error messages (truncated to avoid huge error messages) + */ +function summarizeInput(input: unknown, maxLength = 500): string { + try { + const json = JSON.stringify(input, null, 2); + if (json.length <= maxLength) return json; + return json.substring(0, maxLength) + "... (truncated)"; + } catch { + return String(input); + } +} + +/** + * Detect common issues with input and provide helpful warnings + */ +function validateTransformInput(input: unknown): string[] { + const warnings: string[] = []; + + if (input === undefined) { + warnings.push( + "Input is undefined - the transform function will receive undefined as its argument", + ); + } else if (input === null) { + warnings.push( + "Input is null - make sure your transform handles null values", + ); + } else if (typeof input === "object" && input !== null) { + // Check for common undefined nested properties + const obj = input as Record; + for (const [key, value] of Object.entries(obj)) { + if (value === undefined) { + warnings.push(`Input.${key} is undefined`); + } + } + } + + return warnings; +} + +export async function executeCode( + code: string, + input: unknown, + stepName: string, +): Promise { + let ctx: SandboxContext | undefined; + const startedAt = Date.now(); + + // Validate input and collect warnings + const inputWarnings = validateTransformInput(input); + if (inputWarnings.length > 0) { + console.warn(`[TRANSFORM] Warnings for step "${stepName}":`, inputWarnings); + } + + try { + const jsCode = transpileTypeScript(code); + const runtime = await createSandboxRuntime( + `transform-${stepName}-${Date.now()}`, + { + memoryLimitBytes: 64 * 1024 * 1024, + stackSizeBytes: 1 << 20, + }, + ); + + ctx = runtime.newContext({ interruptAfterMs: 10000 }); + installConsole(ctx); + + const result = ctx.evalCode(jsCode, "transform.js", { + strict: true, + strip: true, + type: "module", + }); + + let exportsHandle: QuickJSHandle; + if (ctx.runtime.hasPendingJob()) { + const promise = ctx.resolvePromise(ctx.unwrapResult(result)); + ctx.runtime.executePendingJobs(); + exportsHandle = ctx.unwrapResult(await promise); + } else { + exportsHandle = ctx.unwrapResult(result); + } + + const defaultHandle = ctx.getProp(exportsHandle, "default"); + if (ctx.typeof(defaultHandle) !== "function") { + return { + error: "Transform must export a default function", + startedAt, + completedAt: Date.now(), + stepId: stepName, + }; + } + + const callHandle = await callFunction(ctx, defaultHandle, undefined, input); + return { + completedAt: Date.now(), + output: ctx.dump(ctx.unwrapResult(callHandle)), + startedAt, + stepId: stepName, + }; + } catch (err) { + const baseError = err instanceof Error ? err.message : String(err); + + // Enhance error message with input context for common runtime errors + let enhancedError = baseError; + if ( + baseError.includes("cannot read property") || + baseError.includes("undefined") + ) { + enhancedError = `${baseError}\n\nInput received by transform:\n${summarizeInput(input)}`; + if (inputWarnings.length > 0) { + enhancedError += `\n\nWarnings:\n- ${inputWarnings.join("\n- ")}`; + } + } + + return { + completedAt: Date.now(), + startedAt, + error: enhancedError, + stepId: stepName, + }; + } finally { + ctx?.dispose(); + } +} + +export async function validateCode( + code: string, + stepName: string, +): Promise<{ + valid: boolean; + error?: string; + schemas?: ReturnType; +}> { + let ctx: SandboxContext | undefined; + + try { + const schemas = extractSchemas(code); + const jsCode = transpileTypeScript(code); + const runtime = await createSandboxRuntime( + `validate-${stepName}-${Date.now()}`, + { + memoryLimitBytes: 32 * 1024 * 1024, + stackSizeBytes: 512 * 1024, + }, + ); + + ctx = runtime.newContext({ interruptAfterMs: 5000 }); + const result = ctx.evalCode(jsCode, "validate.js", { + strict: true, + strip: true, + type: "module", + }); + + let exportsHandle: QuickJSHandle; + if (ctx.runtime.hasPendingJob()) { + const promise = ctx.resolvePromise(ctx.unwrapResult(result)); + ctx.runtime.executePendingJobs(); + exportsHandle = ctx.unwrapResult(await promise); + } else { + exportsHandle = ctx.unwrapResult(result); + } + + const defaultHandle = ctx.getProp(exportsHandle, "default"); + if (ctx.typeof(defaultHandle) !== "function") { + return { + valid: false, + error: "Transform must export a default function", + }; + } + return { valid: true, schemas }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : String(error), + }; + } finally { + ctx?.dispose(); + } +} diff --git a/mcp-studio/server/engine/steps/signal-step.ts b/mcp-studio/server/engine/steps/signal-step.ts new file mode 100644 index 00000000..b5761c79 --- /dev/null +++ b/mcp-studio/server/engine/steps/signal-step.ts @@ -0,0 +1,66 @@ +/** + * Signal Step Executor + * + * Executes waitForSignal steps - blocks until signal received or timeout. + */ + +import { WaitForSignalActionSchema } from "@decocms/bindings/workflow"; +import { createStepResult } from "../../db/queries/executions.ts"; +import type { ExistingStepResult, Step, StepResult } from "../../types/step.ts"; +import { WaitingForSignalError } from "../../utils/errors.ts"; +import type { ExecutionContext } from "../context.ts"; +import { consumeSignal, getSignals } from "../events.ts"; + +export async function executeSignalStep( + ctx: ExecutionContext, + step: Step, + existingStepResult?: ExistingStepResult, +): Promise { + const parsed = WaitForSignalActionSchema.safeParse(step.action); + if (!parsed.success) { + throw new Error("waitForSignal step missing configuration"); + } + + const { signalName } = parsed.data; + const timeoutMs = step.config?.timeoutMs; + const waitStartedAt = existingStepResult?.started_at_epoch_ms || Date.now(); + + if (timeoutMs && Date.now() - waitStartedAt > timeoutMs) { + throw new Error(`Signal '${signalName}' timed out after ${timeoutMs}ms`); + } + + const signals = await getSignals(ctx.env, ctx.executionId); + const matching = signals.find((s) => s.signal_name === signalName); + + if (matching?.signal_name) { + const consumed = await consumeSignal(ctx.env, matching.id); + if (!consumed) { + throw new Error(`Signal '${signalName}' not consumed`); + } + + await createStepResult(ctx.env, { + execution_id: ctx.executionId, + step_id: step.name, + }); + + return { + output: { + signalName: matching.signal_name, + payload: matching.payload, + receivedAt: matching.created_at, + waitDurationMs: Date.now() - waitStartedAt, + }, + stepId: step.name, + startedAt: waitStartedAt, + completedAt: Date.now(), + }; + } + + throw new WaitingForSignalError( + ctx.executionId, + step.name, + signalName, + timeoutMs, + waitStartedAt, + ); +} diff --git a/mcp-studio/server/engine/steps/step-executor.ts b/mcp-studio/server/engine/steps/step-executor.ts new file mode 100644 index 00000000..9a718427 --- /dev/null +++ b/mcp-studio/server/engine/steps/step-executor.ts @@ -0,0 +1,103 @@ +/** + * Step Executor + * + * Orchestrates step execution with retry logic and timeout handling. + */ + +import { createStepResult } from "../../db/queries/executions.ts"; +import type { Env } from "../../types/env.ts"; +import type { ExistingStepResult, Step, StepResult } from "../../types/step.ts"; +import { getStepType } from "../../types/step.ts"; +import { ExecutionContext } from "../context.ts"; +import { executeCode } from "./code-step.ts"; +import { executeSignalStep } from "./signal-step.ts"; +import { executeToolStep } from "./tool-step.ts"; + +export class StepExecutor { + private ctx: ExecutionContext; + + constructor(env: Env, executionId: string, gatewayId: string) { + this.ctx = new ExecutionContext(env, executionId, gatewayId); + } + + /** + * Execute a step with retry and timeout handling. + */ + async executeStep( + step: Step, + resolvedInput: Record, + existingStepResult: ExistingStepResult, + ): Promise { + await this.ctx.checkCancelled(); + const stepType = getStepType(step); + + // Signal steps don't need to claim (handled differently) + let result: StepResult | undefined; + if (stepType === "tool") { + await this.ctx.claimStep(step.name); + result = await executeToolStep(this.ctx, step, resolvedInput); + await this.ctx.completeStep(step.name, result.output, result.error); + } + + if (stepType === "code" && "code" in step.action) { + result = await executeCode(step.action.code, resolvedInput, step.name); + await createStepResult(this.ctx.env, { + execution_id: this.ctx.executionId, + step_id: step.name, + output: result.output, + error: result.error, + completed_at_epoch_ms: Date.now(), + }); + } + + if (stepType === "signal" && "signalName" in step.action) { + result = await executeSignalStep(this.ctx, step, existingStepResult); + await createStepResult(this.ctx.env, { + execution_id: this.ctx.executionId, + step_id: step.name, + output: result.output, + error: result.error, + completed_at_epoch_ms: Date.now(), + }); + } + + if (!result) { + // Return error result instead of throwing + return { + stepId: step.name, + startedAt: existingStepResult?.started_at_epoch_ms ?? Date.now(), + completedAt: Date.now(), + output: undefined, + error: `Step '${step.name}' failed: no result returned`, + }; + } + + if (result.error) { + // Save error to database but don't throw - let executor decide + await this.ctx.updateStep(step.name, { + error: result.error, + started_at_epoch_ms: + existingStepResult?.started_at_epoch_ms ?? Date.now(), + }); + } + + return result; + } + + async getLastStepResult(): Promise { + const results = await this.ctx.getStepResults(); + const lastResult = results.sort( + (a, b) => b.started_at_epoch_ms - a.started_at_epoch_ms, + )[0]; + if (!lastResult) { + return null; + } + return { + stepId: lastResult.step_id, + startedAt: lastResult.started_at_epoch_ms, + completedAt: lastResult.completed_at_epoch_ms, + output: lastResult.output, + error: lastResult.error as string | undefined, + }; + } +} diff --git a/mcp-studio/server/engine/steps/tool-step.ts b/mcp-studio/server/engine/steps/tool-step.ts new file mode 100644 index 00000000..d1cba667 --- /dev/null +++ b/mcp-studio/server/engine/steps/tool-step.ts @@ -0,0 +1,258 @@ +/** + * Tool Step Executor + * + * Executes MCP tool calls via connection proxy. + */ + +import { ToolCallActionSchema } from "@decocms/bindings/workflow"; +import { Client } from "@modelcontextprotocol/sdk/client"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import type { Env } from "../../types/env.ts"; +import type { Step, StepResult } from "../../types/step.ts"; +import type { ExecutionContext } from "../context.ts"; +import { executeCode } from "./code-step.ts"; + +type JSONSchema = { + type?: string | string[]; + properties?: Record; + items?: JSONSchema; + oneOf?: JSONSchema[]; + anyOf?: JSONSchema[]; + allOf?: JSONSchema[]; +}; + +/** + * Coerce a value to match the expected type from a JSON Schema. + * Handles common cases like string "5" -> number 5. + */ +function coerceValue(value: unknown, schema: JSONSchema | undefined): unknown { + if (value === undefined || value === null || !schema) return value; + + const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type; + + // Handle union types (oneOf/anyOf) - try to find a matching type + if (schema.oneOf || schema.anyOf) { + const variants = schema.oneOf || schema.anyOf; + for (const variant of variants ?? []) { + const coerced = coerceValue(value, variant); + if (coerced !== value) return coerced; + } + return value; + } + + // String to number coercion + if (schemaType === "number" || schemaType === "integer") { + if (typeof value === "string") { + const num = Number(value); + if (!Number.isNaN(num)) return num; + } + return value; + } + + // String to boolean coercion + if (schemaType === "boolean") { + if (typeof value === "string") { + if (value === "true") return true; + if (value === "false") return false; + } + return value; + } + + // Array coercion + if (schemaType === "array" && Array.isArray(value) && schema.items) { + return value.map((item) => coerceValue(item, schema.items)); + } + + // Object coercion - recursively coerce properties + if ( + schemaType === "object" && + typeof value === "object" && + !Array.isArray(value) && + schema.properties + ) { + const coerced: Record = {}; + for (const [k, v] of Object.entries(value as Record)) { + coerced[k] = coerceValue(v, schema.properties[k]); + } + return coerced; + } + + return value; +} + +/** + * Clean up input to prevent common validation errors. + * Removes empty objects that would fail schema validation. + * Optionally coerces types based on the tool's input schema. + */ +function sanitizeInput( + input: Record, + inputSchema?: JSONSchema, +): Record { + const sanitized: Record = {}; + + for (const [key, value] of Object.entries(input)) { + // Skip undefined values + if (value === undefined) continue; + + // Handle 'where' clause - if it's an empty object or missing required fields, skip it + if (key === "where" && typeof value === "object" && value !== null) { + const whereObj = value as Record; + // Empty where object - skip entirely + if (Object.keys(whereObj).length === 0) continue; + // Where object without operator - skip (would fail validation) + if (!("operator" in whereObj)) { + console.warn( + `[TOOL_STEP] Skipping invalid 'where' clause: missing 'operator'. Use { field: [...], operator: "eq"|"gt"|..., value: ... } for simple conditions or { operator: "and"|"or"|"not", conditions: [...] } for logical conditions.`, + ); + continue; + } + } + + // Get the property schema for type coercion + const propSchema = inputSchema?.properties?.[key]; + + // Recursively clean nested objects + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + const cleaned = sanitizeInput( + value as Record, + propSchema, + ); + if (Object.keys(cleaned).length > 0) { + sanitized[key] = cleaned; + } + } else { + // Coerce the value based on schema + sanitized[key] = coerceValue(value, propSchema); + } + } + + return sanitized; +} + +const fixProtocol = (url: URL) => { + const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1"; + if (!isLocal) { + // force http if not local + url.protocol = "https:"; + } + return url; +}; + +function createGatewayTransport( + gatewayId: string, + env: Env, +): StreamableHTTPClientTransport { + // Build base URL for gateway + const url = fixProtocol( + new URL(`${env.MESH_REQUEST_CONTEXT?.meshUrl}/mcp/gateway/${gatewayId}`), + ); + + const headers = new Headers(); + headers.set( + "Authorization", + `Bearer ${env.MESH_REQUEST_CONTEXT?.token || ""}`, + ); + + return new StreamableHTTPClientTransport(url, { requestInit: { headers } }); +} + +export async function executeToolStep( + ctx: ExecutionContext, + step: Step, + input: Record, +): Promise { + const startedAt = Date.now(); + const parsed = ToolCallActionSchema.safeParse(step.action); + if (!parsed.success) { + throw new Error("Tool step missing tool configuration"); + } + + const { toolName, transformCode } = parsed.data; + const gatewayId = ctx.gatewayId; + + const transport = createGatewayTransport(gatewayId, ctx.env); + const client = new Client({ + title: "MCP Studio", + version: "1.0.0", + name: "MCP Studio", + websiteUrl: "https://mcp-studio.com", + description: "MCP Studio", + icons: [ + { + src: "https://mcp-studio.com/icon.png", + mimeType: "image/png", + }, + ], + }); + await client.connect(transport); + + // Fetch tool schema for type coercion + let inputSchema: JSONSchema | undefined; + try { + const { tools } = await client.listTools(); + const tool = tools.find((t) => t.name === toolName); + inputSchema = tool?.inputSchema as JSONSchema | undefined; + } catch { + // If we can't get the schema, proceed without type coercion + } + + // Sanitize input and coerce types based on tool schema + const sanitizedInput = sanitizeInput(input, inputSchema); + + const timeoutMs = step.config?.timeoutMs ?? 30000; + + const { content, structuredContent, isError } = await client.callTool( + { + name: toolName, + arguments: sanitizedInput, + }, + undefined, + { + timeout: timeoutMs, + }, + ); + + const result = structuredContent ?? content; + + // If there's transform code, run it on the raw tool result + if (transformCode) { + const transformResult = await executeCode( + transformCode, + result as Record, + step.name, + ); + return transformResult; + } + + // If there's an output schema but no transform, filter the result + if (step.outputSchema) { + const outputSchemaProperties = step.outputSchema.properties as Record< + string, + unknown + >; + const output = outputSchemaProperties + ? Object.fromEntries( + Object.entries(result as Record).filter( + ([key]) => key in outputSchemaProperties, + ), + ) + : (result as Record); + + return { + output, + startedAt, + error: isError ? JSON.stringify(result) : undefined, + completedAt: Date.now(), + stepId: step.name, + }; + } + + return { + output: result, + startedAt, + error: isError ? JSON.stringify(result) : undefined, + completedAt: Date.now(), + stepId: step.name, + }; +} diff --git a/mcp-studio/server/events/handler.ts b/mcp-studio/server/events/handler.ts new file mode 100644 index 00000000..3bb61e56 --- /dev/null +++ b/mcp-studio/server/events/handler.ts @@ -0,0 +1,34 @@ +/** + * Event Handler + * + * Handles workflow-related events from the event bus. + */ + +import { executeWorkflow } from "../engine/executor.ts"; +import type { Env } from "../types/env.ts"; + +interface WorkflowEvent { + type: string; + data?: unknown; + subject?: string; + id: string; +} + +export const WORKFLOW_EVENTS = ["workflow.execution.created"] as const; + +/** + * Handle a batch of workflow events. + */ +export function handleWorkflowEvents(events: WorkflowEvent[], env: Env): void { + for (const event of events) { + if (!event.subject) continue; + + switch (event.type) { + case "workflow.execution.created": + executeWorkflow(env, event.subject).catch((error: Error) => { + console.error(`[EXECUTE_WORKFLOW] Error: ${error}`); + }); + break; + } + } +} diff --git a/mcp-studio/server/main.ts b/mcp-studio/server/main.ts index 333cf94c..060dfdb1 100644 --- a/mcp-studio/server/main.ts +++ b/mcp-studio/server/main.ts @@ -6,75 +6,42 @@ */ import { serve } from "@decocms/mcps-shared/serve"; +import { withRuntime } from "@decocms/runtime"; +import { ensureCollections, ensureIndexes } from "./db/index.ts"; import { - type BindingRegistry, - type DefaultEnv, - withRuntime, -} from "@decocms/runtime"; -import type { z } from "zod"; -import { - type Env as DecoEnv, - Scopes, - StateSchema, -} from "../shared/deco.gen.ts"; -import { ensureAssistantsTable, ensurePromptsTable } from "./lib/postgres.ts"; + ensureAssistantsTable, + ensurePromptsTable, +} from "./db/schemas/agents.ts"; +import { handleWorkflowEvents, WORKFLOW_EVENTS } from "./events/handler.ts"; import { createPrompts } from "./prompts.ts"; import { tools } from "./tools/index.ts"; +import { type Env, type Registry, StateSchema } from "./types/env.ts"; -interface Registry extends BindingRegistry { - "@deco/postgres": [ - { - name: "DATABASES_RUN_SQL"; - description: "Run a SQL query against the database"; - inputSchema: z.ZodType< - Parameters[0] - >; - outputSchema: z.ZodType< - Awaited> - >; - }, - ]; -} -/** - * This Env type is the main context object that is passed to - * all of your Application. - * - * It includes all of the generated types from your - * Deco bindings, along with the default ones. - */ -export type Env = DefaultEnv & DecoEnv; +export { StateSchema }; const runtime = withRuntime({ + events: { + handlers: { + events: [...WORKFLOW_EVENTS] as string[], + handler: async ({ events }, env) => { + try { + handleWorkflowEvents(events, env); + return { success: true }; + } catch (error) { + console.error(`[MAIN] Error handling events: ${error}`); + return { success: false }; + } + }, + }, + }, configuration: { onChange: async (env) => { + await ensureIndexes(env); + await ensureCollections(env); await ensureAssistantsTable(env); await ensurePromptsTable(env); }, - /** - * These scopes define the asking permissions of your - * app when a user is installing it. When a user - * authorizes your app for using AI_GENERATE, you will - * now be able to use `env.AI_GATEWAY.AI_GENERATE` - * and utilize the user's own AI Gateway, without having to - * deploy your own, setup any API keys, etc. - */ - scopes: [Scopes.DATABASE.DATABASES_RUN_SQL], - /** - * The state schema of your Application defines what - * your installed App state will look like. When a user - * is installing your App, they will have to fill in - * a form with the fields defined in the state schema. - * - * This is powerful for building multi-tenant apps, - * where you can have multiple users and projects - * sharing different configurations on the same app. - * - * When you define a binding dependency on another app, - * it will automatically be linked to your StateSchema on - * type generation. You can also `.extend` it to add more - * fields to the state schema, like asking for an API Key - * for connecting to a third-party service. - */ + scopes: ["DATABASE::DATABASES_RUN_SQL", "EVENT_BUS::*", "*"], state: StateSchema, }, tools, diff --git a/mcp-studio/server/prompts.ts b/mcp-studio/server/prompts.ts index f3fb545f..1428e9c6 100644 --- a/mcp-studio/server/prompts.ts +++ b/mcp-studio/server/prompts.ts @@ -7,8 +7,8 @@ import { createPrompt, type GetPromptResult } from "@decocms/runtime"; import { z } from "zod"; -import { runSQL } from "./lib/postgres.ts"; -import type { Env } from "./main.ts"; +import { runSQL } from "./db/postgres.ts"; +import type { Env } from "./types/env.ts"; interface StoredPromptArgument { name: string; diff --git a/mcp-studio/server/sandbox/builtins/console.ts b/mcp-studio/server/sandbox/builtins/console.ts new file mode 100644 index 00000000..d7ce92ca --- /dev/null +++ b/mcp-studio/server/sandbox/builtins/console.ts @@ -0,0 +1,52 @@ +import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten"; +import type { Log } from "../types.ts"; + +export interface ConsoleBuiltin { + readonly logs: Log[]; + [Symbol.dispose]: () => void; +} + +export function installConsole(ctx: QuickJSContext): ConsoleBuiltin { + const logs: Log[] = []; + const handles: QuickJSHandle[] = []; + + const makeLog = (level: string) => { + const logFn = ctx.newFunction(level, (...args: QuickJSHandle[]) => { + try { + const parts = args.map((h) => ctx.dump(h)); + logs.push({ + type: (level as "log" | "warn" | "error") ?? "log", + content: parts.map(String).join(" "), + }); + } finally { + args.forEach((h) => { + h.dispose(); + }); + } + return ctx.undefined; + }); + handles.push(logFn); + return logFn; + }; + + const consoleObj = ctx.newObject(); + handles.push(consoleObj); + + const log = makeLog("log"); + const warn = makeLog("warn"); + const error = makeLog("error"); + + ctx.setProp(consoleObj, "log", log); + ctx.setProp(consoleObj, "warn", warn); + ctx.setProp(consoleObj, "error", error); + ctx.setProp(ctx.global, "console", consoleObj); + + return { + logs, + [Symbol.dispose]() { + handles.forEach((handle) => { + handle.dispose(); + }); + }, + }; +} diff --git a/mcp-studio/server/sandbox/builtins/fetch.ts b/mcp-studio/server/sandbox/builtins/fetch.ts new file mode 100644 index 00000000..22cf8b97 --- /dev/null +++ b/mcp-studio/server/sandbox/builtins/fetch.ts @@ -0,0 +1,183 @@ +import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten"; +import { toQuickJS } from "../utils/to-quickjs.ts"; + +export interface FetchBuiltin { + [Symbol.dispose]: () => void; +} + +export function installFetch(ctx: QuickJSContext): FetchBuiltin { + const handles: QuickJSHandle[] = []; + + // Create Response class + const ResponseClass = ctx.newFunction( + "Response", + (body?: QuickJSHandle, init?: QuickJSHandle) => { + try { + const responseObj = ctx.newObject(); + handles.push(responseObj); + + // Parse body + let bodyText = ""; + if (body && body !== ctx.null && body !== ctx.undefined) { + bodyText = String(ctx.dump(body)); + } + + // Parse init options + let status = 200; + let statusText = "OK"; + + if (init && init !== ctx.null && init !== ctx.undefined) { + const initObj = ctx.dump(init) as Record; + if (typeof initObj.status === "number") { + status = initObj.status; + } + if (typeof initObj.statusText === "string") { + statusText = initObj.statusText; + } + } + + // Set response properties + ctx.setProp(responseObj, "status", ctx.newNumber(status)); + ctx.setProp(responseObj, "statusText", ctx.newString(statusText)); + ctx.setProp( + responseObj, + "ok", + status >= 200 && status < 300 ? ctx.true : ctx.false, + ); + ctx.setProp(responseObj, "body", toQuickJS(ctx, bodyText)); + + // Add text() method + const textMethod = ctx.newFunction("text", () => { + const deferredPromise = ctx.newPromise(); + deferredPromise.resolve(toQuickJS(ctx, bodyText)); + return deferredPromise.handle; + }); + handles.push(textMethod); + ctx.setProp(responseObj, "text", textMethod); + + // Add json() method + const jsonMethod = ctx.newFunction("json", () => { + const deferredPromise = ctx.newPromise(); + try { + const parsed = JSON.parse(bodyText); + deferredPromise.resolve(toQuickJS(ctx, parsed)); + } catch (error) { + deferredPromise.reject(toQuickJS(ctx, String(error))); + } + return deferredPromise.handle; + }); + handles.push(jsonMethod); + ctx.setProp(responseObj, "json", jsonMethod); + + return responseObj; + } finally { + if (body) body.dispose(); + if (init) init.dispose(); + } + }, + ); + handles.push(ResponseClass); + + // Create fetch function + const fetchFn = ctx.newFunction( + "fetch", + (url: QuickJSHandle, options?: QuickJSHandle) => { + const deferredPromise = ctx.newPromise(); + + // Start async operation + (async () => { + try { + const urlString = String(ctx.dump(url)); + const fetchOptions: RequestInit = {}; + + if (options && options !== ctx.null && options !== ctx.undefined) { + const optionsObj = ctx.dump(options) as Record; + + if (optionsObj.method && typeof optionsObj.method === "string") { + fetchOptions.method = optionsObj.method; + } + + if (optionsObj.headers && typeof optionsObj.headers === "object") { + fetchOptions.headers = optionsObj.headers as Record< + string, + string + >; + } + + if (optionsObj.body) { + if (typeof optionsObj.body === "string") { + fetchOptions.body = optionsObj.body; + } else { + fetchOptions.body = JSON.stringify(optionsObj.body); + } + } + } + + // Perform the actual fetch + const response = await fetch(urlString, fetchOptions); + const responseText = await response.text(); + + // Create Response object + const responseObj = ctx.newObject(); + handles.push(responseObj); + + ctx.setProp(responseObj, "status", ctx.newNumber(response.status)); + ctx.setProp( + responseObj, + "statusText", + ctx.newString(response.statusText), + ); + ctx.setProp(responseObj, "ok", response.ok ? ctx.true : ctx.false); + ctx.setProp(responseObj, "body", toQuickJS(ctx, responseText)); + + // Add text() method + const textMethod = ctx.newFunction("text", () => { + const textDeferredPromise = ctx.newPromise(); + textDeferredPromise.resolve(toQuickJS(ctx, responseText)); + return textDeferredPromise.handle; + }); + handles.push(textMethod); + ctx.setProp(responseObj, "text", textMethod); + + // Add json() method + const jsonMethod = ctx.newFunction("json", () => { + const jsonDeferredPromise = ctx.newPromise(); + try { + const parsed = JSON.parse(responseText); + jsonDeferredPromise.resolve(toQuickJS(ctx, parsed)); + } catch (error) { + jsonDeferredPromise.reject(toQuickJS(ctx, String(error))); + } + return jsonDeferredPromise.handle; + }); + handles.push(jsonMethod); + ctx.setProp(responseObj, "json", jsonMethod); + + deferredPromise.resolve(responseObj); + } catch (error) { + deferredPromise.reject(toQuickJS(ctx, String(error))); + } finally { + if (url) url.dispose(); + if (options) options.dispose(); + // Execute pending jobs to propagate the promise resolution + ctx.runtime.executePendingJobs(); + } + })(); + + return deferredPromise.handle; + }, + ); + handles.push(fetchFn); + + // Install fetch and Response in global scope + ctx.setProp(ctx.global, "fetch", fetchFn); + ctx.setProp(ctx.global, "Response", ResponseClass); + + return { + [Symbol.dispose]() { + handles.forEach((handle) => { + handle.dispose(); + }); + }, + }; +} diff --git a/mcp-studio/server/sandbox/builtins/index.ts b/mcp-studio/server/sandbox/builtins/index.ts new file mode 100644 index 00000000..988f306e --- /dev/null +++ b/mcp-studio/server/sandbox/builtins/index.ts @@ -0,0 +1,2 @@ +export { type ConsoleBuiltin, installConsole } from "./console.ts"; +export { type FetchBuiltin, installFetch } from "./fetch.ts"; diff --git a/mcp-studio/server/sandbox/index.ts b/mcp-studio/server/sandbox/index.ts new file mode 100644 index 00000000..c868bd44 --- /dev/null +++ b/mcp-studio/server/sandbox/index.ts @@ -0,0 +1,142 @@ +import { + DefaultIntrinsics, + type Intrinsics, + type QuickJSContext, + type QuickJSRuntime, +} from "quickjs-emscripten"; +import { getQuickJS } from "./quickjs.ts"; + +// Export QuickJS types +export type { + DefaultIntrinsics, + Intrinsics, + JSValue, + JSValueConst, + JSValueConstPointer, + JSValuePointer, + QuickJSContext, + QuickJSHandle, + QuickJSRuntime, + QuickJSWASMModule, +} from "quickjs-emscripten"; +export { Scope } from "quickjs-emscripten"; +// Export built-ins +export { installConsole, installFetch } from "./builtins/index.ts"; +// Export types +export type { EvaluationResult, Log } from "./types.ts"; +// Export utilities +export { callFunction } from "./utils/call-function.ts"; +export { inspect } from "./utils/error-handling.ts"; +export { toQuickJS } from "./utils/to-quickjs.ts"; + +/** + * Creates a timing function that measures execution time and logs the result. + * @param msg - The message to include in the log output + * @returns A function that when called, logs the elapsed time + */ +function timings(msg: string): () => void { + const start = performance.now(); + return () => { + const elapsed = Math.round(performance.now() - start); + console.log(`[${elapsed}ms] ${msg}`); + }; +} + +export interface SandboxRuntimeOptions { + /** + * The memory limit for the tenant sandbox in bytes. + */ + memoryLimitBytes?: number; + /** + * The stack size for the tenant sandbox in bytes. + */ + stackSizeBytes?: number; +} + +export interface SandboxContextOptions extends Intrinsics { + interruptAfterMs?: number; +} + +// EvaluationResult is now imported from types.ts + +export interface SandboxRuntime { + runtimeId: string; + newContext: (options?: SandboxContextOptions) => SandboxContext; + [Symbol.dispose]: () => void; +} + +export type SandboxContext = QuickJSContext; + +const runtimes = new Map>(); + +function getOrCreateRuntime(runtimeId: string, options: SandboxRuntimeOptions) { + let promise = runtimes.get(runtimeId); + if (!promise) { + promise = (async () => { + const QuickJS = await getQuickJS(); + const runtime = QuickJS.newRuntime({ + maxStackSizeBytes: options.stackSizeBytes, + memoryLimitBytes: options.memoryLimitBytes, + }); + + return runtime; + })(); + runtimes.set(runtimeId, promise); + } + return promise; +} + +export async function createSandboxRuntime( + runtimeId: string, + options: SandboxRuntimeOptions = {}, +): Promise { + const endSandboxCreation = timings( + `Creating sandbox runtime for ${runtimeId}`, + ); + const runtime = await getOrCreateRuntime(runtimeId, options); + + const createContext = ({ + interruptAfterMs, + ...intrinsics + }: SandboxContextOptions = {}): SandboxContext => { + const ctx = runtime.newContext({ + intrinsics: { ...DefaultIntrinsics, ...intrinsics }, + }); + + // Interrupt control (per-execution deadline) + let deadline = 0; + const setDeadline = (ms?: number) => { + deadline = ms ? Date.now() + ms : 0; + }; + + // Set up interrupt handler for this context + runtime.setInterruptHandler(() => { + const shouldInterrupt = deadline > 0 && Date.now() > deadline; + if (shouldInterrupt) { + console.warn( + `[cf-sandbox] Execution interrupted due to deadline for runtimeId: ${runtimeId}`, + ); + } + return shouldInterrupt; + }); + + // Set initial deadline if provided + if (interruptAfterMs) { + setDeadline(interruptAfterMs); + } + + return ctx; + }; + + const dispose = () => { + runtime.dispose(); + runtimes.delete(runtimeId); + }; + + endSandboxCreation(); + return { + runtimeId, + newContext: createContext, + [Symbol.dispose]: dispose, + }; +} diff --git a/mcp-studio/server/sandbox/quickjs.ts b/mcp-studio/server/sandbox/quickjs.ts new file mode 100644 index 00000000..c180f108 --- /dev/null +++ b/mcp-studio/server/sandbox/quickjs.ts @@ -0,0 +1,11 @@ +import { + newQuickJSWASMModule, + type QuickJSWASMModule, +} from "quickjs-emscripten"; + +let quickJSSingleton: Promise | undefined; + +export function getQuickJS() { + quickJSSingleton ??= newQuickJSWASMModule(); + return quickJSSingleton; +} diff --git a/mcp-studio/server/sandbox/types.d.ts b/mcp-studio/server/sandbox/types.d.ts new file mode 100644 index 00000000..0956b08a --- /dev/null +++ b/mcp-studio/server/sandbox/types.d.ts @@ -0,0 +1,4 @@ +declare module "@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.wasm" { + const wasmModule: ArrayBuffer; + export default wasmModule; +} diff --git a/mcp-studio/server/sandbox/types.ts b/mcp-studio/server/sandbox/types.ts new file mode 100644 index 00000000..42806b8e --- /dev/null +++ b/mcp-studio/server/sandbox/types.ts @@ -0,0 +1,14 @@ +export interface EvaluationResult { + value?: T; + error?: unknown; + logs: Array<{ type: "log" | "warn" | "error"; content: string }>; +} + +export interface Log { + type: "log" | "warn" | "error"; + content: string; +} + +export interface Builtin { + [Symbol.dispose]: () => void; +} diff --git a/mcp-studio/server/sandbox/utils/call-function.ts b/mcp-studio/server/sandbox/utils/call-function.ts new file mode 100644 index 00000000..5e3ebf03 --- /dev/null +++ b/mcp-studio/server/sandbox/utils/call-function.ts @@ -0,0 +1,33 @@ +import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten"; +import { toQuickJS } from "./to-quickjs.ts"; + +export function callFunction( + ctx: QuickJSContext, + fn: QuickJSHandle, + thisArg?: unknown, + ...args: unknown[] +) { + // Convert thisArg and args to QuickJS handles + const thisArgHandle = thisArg ? toQuickJS(ctx, thisArg) : ctx.undefined; + const argHandles = args.map((arg) => toQuickJS(ctx, arg)); + + // Call the function + const result = ctx.unwrapResult( + ctx.callFunction(fn, thisArgHandle, ...argHandles), + ); + + // Wrap function result in a promise + const resultPromise = ctx.newPromise(); + resultPromise.resolve(result); + + // Resolve the promise + const toAwait = ctx.resolvePromise(resultPromise.handle); + + // Execute pending jobs to avoid deadlocks + if (ctx.runtime.hasPendingJob()) { + ctx.runtime.executePendingJobs(); + } + + // Return the promise + return toAwait; +} diff --git a/mcp-studio/server/sandbox/utils/error-handling.ts b/mcp-studio/server/sandbox/utils/error-handling.ts new file mode 100644 index 00000000..a6f85393 --- /dev/null +++ b/mcp-studio/server/sandbox/utils/error-handling.ts @@ -0,0 +1,123 @@ +/** + * Inspects and serializes any value to a meaningful string representation. + * Similar to Node.js's util.inspect, but optimized for error handling and debugging. + * Handles Error objects, objects with message properties, and other types. + * Includes stack traces when available for better debugging. + * @param value - The value to inspect + * @returns A meaningful string representation with stack trace if available + */ +export function inspect(value: unknown): string { + if (value === null) { + return "null"; + } + + if (value === undefined) { + return "undefined"; + } + + // Handle Error instances + if (value instanceof Error) { + const message = value.message || value.name || "Error"; + // Include stack trace if available and not too long + if (value.stack && value.stack.length < 2000) { + return `${message}\n${value.stack}`; + } + return message; + } + + // Handle objects + if (typeof value === "object" && value !== null) { + const obj = value as Record; + + // Try common error message properties first + let message = ""; + if (typeof obj.message === "string" && obj.message) { + message = obj.message; + } else if (typeof obj.error === "string" && obj.error) { + message = obj.error; + } else if (typeof obj.description === "string" && obj.description) { + message = obj.description; + } else if (typeof obj.reason === "string" && obj.reason) { + message = obj.reason; + } + + // If we found a message, check for stack trace + if (message) { + if ( + typeof obj.stack === "string" && + obj.stack && + obj.stack.length < 2000 + ) { + return `${message}\n${obj.stack}`; + } + return message; + } + + // Try to stringify the object if it has meaningful properties + try { + const stringified = JSON.stringify(obj, null, 2); + // Only use JSON if it's not just "{}" and has reasonable length + if (stringified !== "{}" && stringified.length < 1000) { + // If there's a stack trace, append it after the JSON + if ( + typeof obj.stack === "string" && + obj.stack && + obj.stack.length < 2000 + ) { + return `${stringified}\n\nStack trace:\n${obj.stack}`; + } + return stringified; + } + } catch { + // JSON.stringify failed, continue to other methods + } + + // Try toString method + if (typeof obj.toString === "function") { + try { + const stringified = obj.toString(); + if (stringified !== "[object Object]") { + return stringified; + } + } catch { + // toString failed, continue + } + } + + // Show object keys if available + const keys = Object.keys(obj); + if (keys.length > 0) { + return `Object with keys: ${keys.join(", ")}`; + } + + return "[object Object]"; + } + + // Handle primitive types + if (typeof value === "string") { + return value; + } + + if (typeof value === "number" || typeof value === "boolean") { + return String(value); + } + + if (typeof value === "function") { + return `[Function: ${value.name || "anonymous"}]`; + } + + if (typeof value === "symbol") { + return value.toString(); + } + + if (typeof value === "bigint") { + return value.toString(); + } + + // Last resort + try { + return String(value); + } catch { + return "Unknown value (could not convert to string)"; + } +} diff --git a/mcp-studio/server/sandbox/utils/to-quickjs.ts b/mcp-studio/server/sandbox/utils/to-quickjs.ts new file mode 100644 index 00000000..f1d651d5 --- /dev/null +++ b/mcp-studio/server/sandbox/utils/to-quickjs.ts @@ -0,0 +1,126 @@ +import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten"; +import { inspect } from "./error-handling.ts"; + +export function toQuickJS(ctx: QuickJSContext, value: unknown): QuickJSHandle { + switch (typeof value) { + case "string": { + return ctx.newString(value); + } + case "number": { + return ctx.newNumber(value); + } + case "boolean": { + return value ? ctx.true : ctx.false; + } + case "undefined": { + return ctx.undefined; + } + case "object": { + if (value === null) return ctx.null; + if (Array.isArray(value)) { + const arr = ctx.newArray(); + value.forEach((v, i) => { + const hv = toQuickJS(ctx, v); + ctx.setProp(arr, String(i), hv); + }); + return arr; + } + + // plain object + const obj = ctx.newObject(); + for (const [k, v] of Object.entries(value as Record)) { + const hv = toQuickJS(ctx, v); + ctx.setProp(obj, k, hv); + } + return obj; + } + case "function": { + // Create a host function bridge that can be called from guest context + const functionId = `__hostFn_${Date.now()}_${Math.random() + .toString(36) + .substr(2, 9)}`; + + // Store the function in a way that can be accessed from guest context + // We'll create a proxy function that calls the original function + const proxyFn = ctx.newFunction( + functionId, + (...args: QuickJSHandle[]) => { + try { + // Convert QuickJS arguments back to JavaScript values + const jsArgs = args.map((h) => { + const dumped = ctx.dump(h); + return dumped; + }); + + // Call the original function + const result = value(...jsArgs); + + // Handle promises returned by host functions + if (result && typeof result.then === "function") { + // The function returned a promise, we need to handle it asynchronously + // Create a deferred promise that will be resolved in the guest context + const deferredPromise = ctx.newPromise(); + + // Start the async operation + result + .then((resolvedValue: unknown) => { + try { + const quickJSValue = toQuickJS(ctx, resolvedValue); + deferredPromise.resolve(quickJSValue); + quickJSValue.dispose(); + // Execute pending jobs to propagate the promise resolution + ctx.runtime.executePendingJobs(); + } catch (e) { + const errorMsg = inspect(e); + const errorHandle = ctx.newString( + `Promise resolution error: ${errorMsg}`, + ); + deferredPromise.reject(errorHandle); + errorHandle.dispose(); + // Execute pending jobs to propagate the promise rejection + ctx.runtime.executePendingJobs(); + } + }) + .catch((error: unknown) => { + const errorMsg = inspect(error); + const errorHandle = ctx.newString( + `Promise rejection: ${errorMsg}`, + ); + deferredPromise.reject(errorHandle); + errorHandle.dispose(); + // Execute pending jobs to propagate the promise rejection + ctx.runtime.executePendingJobs(); + }); + + return deferredPromise.handle; + } else { + // The function returned a synchronous value + return toQuickJS(ctx, result); + } + } catch (e) { + const msg = inspect(e); + return ctx.newString(`HostFunctionError: ${msg}`); + } + }, + ); + + return proxyFn; + } + case "bigint": { + // Convert BigInt to string for serialization + return ctx.newString(value.toString()); + } + case "symbol": { + // Convert Symbol to string description + return ctx.newString(value.toString()); + } + default: { + // For any other type, try to convert to string + try { + return ctx.newString(String(value)); + } catch { + return ctx.undefined; + } + } + } +} diff --git a/mcp-studio/server/tools/_helpers.ts b/mcp-studio/server/tools/_helpers.ts new file mode 100644 index 00000000..68afb7cd --- /dev/null +++ b/mcp-studio/server/tools/_helpers.ts @@ -0,0 +1,157 @@ +/** + * SQL Query Builder Helpers + * + * Shared utilities for building SQL WHERE and ORDER BY clauses. + */ + +export type WhereExpression = { + field?: string[]; + operator?: string; + value?: unknown; + conditions?: WhereExpression[]; +}; + +export type OrderByExpression = Array<{ + field: string[]; + direction: string; + nulls?: string; +}>; + +/** + * Check if a where expression is empty (no meaningful filters) + */ +function isEmptyWhereExpression(whereExpr: WhereExpression): boolean { + // Empty object + if (Object.keys(whereExpr).length === 0) return true; + + // Has operator but no field (for simple conditions) and no conditions (for logical) + if (!whereExpr.field && !whereExpr.conditions) return true; + + // Logical condition with empty conditions array + if (whereExpr.conditions && whereExpr.conditions.length === 0) return true; + + return false; +} + +/** + * Build SQL WHERE clause from filter expression using ? placeholders + * + * Supported simple operators: eq, gt, gte, lt, lte, in, like, contains + * Supported logical operators: and, or, not + * + * @example Simple condition + * { field: ["status"], operator: "eq", value: "active" } + * + * @example Logical AND + * { operator: "and", conditions: [ + * { field: ["status"], operator: "eq", value: "active" }, + * { field: ["type"], operator: "in", value: ["A", "B"] } + * ]} + */ +export function buildWhereClause( + whereExpr: WhereExpression | undefined, + params: unknown[] = [], +): { clause: string; params: unknown[] } { + if (!whereExpr) { + return { clause: "", params }; + } + + // Handle empty where expressions gracefully + if (isEmptyWhereExpression(whereExpr)) { + return { clause: "", params }; + } + + // Simple condition + if ( + "field" in whereExpr && + "operator" in whereExpr && + !("conditions" in whereExpr) + ) { + const fieldPath = whereExpr.field; + if (!fieldPath) return { clause: "", params }; + const fieldName = fieldPath[fieldPath.length - 1]; + + switch (whereExpr.operator) { + case "eq": + params.push(whereExpr.value); + return { clause: `${fieldName} = ?`, params }; + case "gt": + params.push(whereExpr.value); + return { clause: `${fieldName} > ?`, params }; + case "gte": + params.push(whereExpr.value); + return { clause: `${fieldName} >= ?`, params }; + case "lt": + params.push(whereExpr.value); + return { clause: `${fieldName} < ?`, params }; + case "lte": + params.push(whereExpr.value); + return { clause: `${fieldName} <= ?`, params }; + case "in": { + const values = Array.isArray(whereExpr.value) + ? whereExpr.value + : [whereExpr.value]; + // Create placeholders for each value in the IN clause + const placeholders = values.map(() => "?").join(", "); + params.push(...values); + return { clause: `${fieldName} IN (${placeholders})`, params }; + } + case "like": + case "contains": + params.push(`%${whereExpr.value}%`); + return { clause: `${fieldName} LIKE ?`, params }; + default: + throw new Error(`Unsupported operator: ${whereExpr.operator}`); + } + } + + // Logical condition (and, or, not) + if ("operator" in whereExpr && "conditions" in whereExpr) { + const conditions = (whereExpr.conditions || []) + .map((cond) => { + const result = buildWhereClause(cond, params); + params = result.params; + return result.clause; + }) + .filter(Boolean); // Filter out empty clauses + + // If all conditions resolved to empty, return empty + if (conditions.length === 0) { + return { clause: "", params }; + } + + switch (whereExpr.operator) { + case "and": + return { clause: `(${conditions.join(" AND ")})`, params }; + case "or": + return { clause: `(${conditions.join(" OR ")})`, params }; + case "not": + return { clause: `NOT (${conditions[0]})`, params }; + default: + throw new Error(`Unsupported logical operator: ${whereExpr.operator}`); + } + } + + return { clause: "", params }; +} + +/** + * Build SQL ORDER BY clause from sort expression + */ +export function buildOrderByClause( + orderByExpr: OrderByExpression | undefined, +): string { + if (!orderByExpr || orderByExpr.length === 0) { + return "ORDER BY created_at DESC"; + } + + const orderClauses = orderByExpr.map((order) => { + const fieldPath = order.field; + const fieldName = fieldPath[fieldPath.length - 1]; + const direction = order.direction.toUpperCase(); + const nulls = order.nulls ? ` NULLS ${order.nulls.toUpperCase()}` : ""; + return `${fieldName} ${direction}${nulls}`; + }); + + return `ORDER BY ${orderClauses.join(", ")}`; +} diff --git a/mcp-studio/server/tools/assistant.ts b/mcp-studio/server/tools/assistant.ts index 977a60b3..7ee704c3 100644 --- a/mcp-studio/server/tools/assistant.ts +++ b/mcp-studio/server/tools/assistant.ts @@ -25,14 +25,14 @@ import { } from "@decocms/bindings/collections"; import { createPrivateTool } from "@decocms/runtime/tools"; import type { z } from "zod"; -import { runSQL } from "../lib/postgres.ts"; +import { runSQL } from "../db/postgres.ts"; +import type { Env } from "../types/env.ts"; import { buildOrderByClause, buildWhereClause, type OrderByExpression, type WhereExpression, -} from "../lib/query-builder.ts"; -import type { Env } from "../main.ts"; +} from "../db/schemas/query-builder.ts"; // Extract binding schemas const LIST_BINDING = ASSISTANTS_BINDING.find( @@ -403,20 +403,17 @@ export const createDeleteTool = (env: Env) => const { id } = context; - // Get the assistant before deleting - const existing = await runSQL>( + const result = await runSQL>( env, - `SELECT * FROM assistants WHERE id = ? LIMIT 1`, + `DELETE FROM assistants WHERE id = ? RETURNING *`, [id], ); - const assistant = existing[0]; + const assistant = result[0]; if (!assistant) { throw new Error(`Assistant with id ${id} not found`); } - await runSQL(env, `DELETE FROM assistants WHERE id = ?`, [id]); - return { item: mapDbRowToAssistant(assistant), }; diff --git a/mcp-studio/server/tools/execution.ts b/mcp-studio/server/tools/execution.ts new file mode 100644 index 00000000..b2cdd831 --- /dev/null +++ b/mcp-studio/server/tools/execution.ts @@ -0,0 +1,205 @@ +import { createPrivateTool } from "@decocms/runtime/tools"; +import type { Env } from "../types/env.ts"; +import { z } from "zod"; +import { StepSchema, WORKFLOW_BINDING } from "@decocms/bindings/workflow"; +import { + getStepResults, + getExecution, + listExecutions, + createExecution, + cancelExecution, + resumeExecution, +} from "../db/queries/executions.ts"; + +const LIST_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_EXECUTION_LIST", +); + +if (!LIST_BINDING?.inputSchema || !LIST_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_EXECUTION_LIST binding not found or missing schemas", + ); +} + +const GET_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_EXECUTION_GET", +); + +if (!GET_BINDING?.inputSchema || !GET_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_EXECUTION_GET binding not found or missing schemas", + ); +} + +const CREATE_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_EXECUTION_CREATE", +); + +if (!CREATE_BINDING?.inputSchema || !CREATE_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_EXECUTION_GET binding not found or missing schemas", + ); +} + +export const cancelExecutionTool = (env: Env) => + createPrivateTool({ + id: "CANCEL_EXECUTION", + description: + "Cancel a running or pending workflow execution. Currently executing steps will complete, but no new steps will start. The execution can be resumed later using RESUME_EXECUTION.", + inputSchema: z.object({ + executionId: z.string().describe("The execution ID to cancel"), + }), + outputSchema: z.object({ + success: z.boolean(), + }), + execute: async ({ context }) => { + const { executionId } = context; + + const result = await cancelExecution(env, executionId); + + if (!result) { + return { + success: false, + }; + } + + return { + success: true, + }; + }, + }); + +export const resumeExecutionTool = (env: Env) => + createPrivateTool({ + id: "RESUME_EXECUTION", + description: + "Resume a cancelled workflow execution. The execution will be set back to pending and can be re-queued for processing.", + inputSchema: z.object({ + executionId: z.string().describe("The execution ID to resume"), + }), + outputSchema: z.object({ + success: z.boolean(), + }), + execute: async ({ context }) => { + const { executionId } = context; + + const result = await resumeExecution(env, executionId); + + if (!result) { + return { + success: false, + }; + } + + await env.EVENT_BUS.EVENT_PUBLISH({ + type: "workflow.execution.created", + subject: executionId, + }); + + return { + success: true, + }; + }, + }); + +export const createCreateTool = (env: Env) => + createPrivateTool({ + id: CREATE_BINDING?.name, + description: "Create a workflow execution and return the execution ID", + inputSchema: z.object({ + input: z.record(z.unknown()), + steps: z.array(StepSchema), + gateway_id: z.string(), + start_at_epoch_ms: z.number().optional(), + timeout_ms: z.number().optional(), + workflow_collection_id: z.string().optional(), + }), + outputSchema: z.object({ + id: z.string(), + workflow_id: z.string(), + }), + execute: async ({ context }) => { + try { + const { id: executionId, workflow_id } = await createExecution(env, { + input: context.input, + gateway_id: context.gateway_id, + start_at_epoch_ms: context.start_at_epoch_ms, + timeout_ms: context.timeout_ms, + steps: context.steps, + workflow_collection_id: context.workflow_collection_id, + }); + await env.EVENT_BUS.EVENT_PUBLISH({ + type: "workflow.execution.created", + subject: executionId, + }); + return { + id: executionId, + workflow_id, + }; + } catch (error) { + console.error("🚀 ~ Error creating and queueing execution:", error); + throw error; + } + }, + }); + +export const createGetTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_EXECUTION_GET", + description: "Get a single workflow execution by ID with step results", + inputSchema: GET_BINDING.inputSchema, + execute: async ({ + context, + }: { + context: z.infer; + }) => { + const { id } = context; + + const execution = await getExecution(env, id); + + if (!execution) { + throw new Error("Execution not found"); + } + + const stepResults = await getStepResults(env, id); + return { + item: execution, + step_results: stepResults, + }; + }, + }); + +export const createListTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_EXECUTION_LIST", + description: + "List workflow executions with filtering, sorting, and pagination", + inputSchema: LIST_BINDING.inputSchema, + outputSchema: LIST_BINDING.outputSchema, + execute: async ({ + context, + }: { + context: z.infer; + }) => { + const { limit = 50, offset = 0 } = context; + + const itemsResult = await listExecutions(env, { + limit, + offset, + }); + + return { + items: itemsResult.items, + totalCount: itemsResult.totalCount, + hasMore: itemsResult.hasMore, + }; + }, + }); + +export const workflowExecutionCollectionTools = [ + createListTool, + createGetTool, + createCreateTool, +]; + +export const workflowTools = [cancelExecutionTool, resumeExecutionTool]; diff --git a/mcp-studio/server/tools/index.ts b/mcp-studio/server/tools/index.ts index f0b5d178..0b28186a 100644 --- a/mcp-studio/server/tools/index.ts +++ b/mcp-studio/server/tools/index.ts @@ -1,12 +1,15 @@ -/** - * Central export point for all tools organized by domain. - * - * This file aggregates all tools from different domains into a single - * export, making it easy to import all tools in main.ts while keeping - * the domain separation. - */ import { assistantTools } from "./assistant.ts"; +import { + workflowExecutionCollectionTools, + workflowTools, +} from "./execution.ts"; import { promptTools } from "./prompt.ts"; +import { workflowCollectionTools } from "./workflow.ts"; -// Export all tools from all domains -export const tools = [...assistantTools, ...promptTools]; +export const tools = [ + ...assistantTools, + ...promptTools, + ...workflowTools, + ...workflowCollectionTools, + ...workflowExecutionCollectionTools, +]; diff --git a/mcp-studio/server/tools/prompt.ts b/mcp-studio/server/tools/prompt.ts index b0a6a81d..3a3e33be 100644 --- a/mcp-studio/server/tools/prompt.ts +++ b/mcp-studio/server/tools/prompt.ts @@ -18,15 +18,15 @@ import { PromptSchema, } from "@decocms/bindings/prompt"; import { createPrivateTool } from "@decocms/runtime/tools"; -import { z, type ZodType } from "zod"; -import { runSQL } from "../lib/postgres.ts"; +import type { ZodType, z } from "zod"; +import { runSQL } from "../db/postgres.ts"; import { buildOrderByClause, buildWhereClause, type OrderByExpression, type WhereExpression, -} from "../lib/query-builder.ts"; -import type { Env } from "../main.ts"; +} from "../db/schemas/query-builder.ts"; +import type { Env } from "../types/env.ts"; // ============================================================================ // Schemas (following MCP Prompts specification) diff --git a/mcp-studio/server/tools/workflow.ts b/mcp-studio/server/tools/workflow.ts new file mode 100644 index 00000000..2faf1467 --- /dev/null +++ b/mcp-studio/server/tools/workflow.ts @@ -0,0 +1,384 @@ +/** biome-ignore-all lint/suspicious/noExplicitAny: complicated types */ +import { createCollectionListOutputSchema } from "@decocms/bindings/collections"; +import { + createDefaultWorkflow, + WORKFLOW_BINDING, + type Workflow, + WorkflowSchema, +} from "@decocms/bindings/workflow"; +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import { runSQL } from "../db/postgres.ts"; +import type { Env } from "../types/env.ts"; +import { validateWorkflow } from "../utils/validator.ts"; +import { buildOrderByClause, buildWhereClause } from "./_helpers.ts"; + +const LIST_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_LIST", +); +const GET_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_GET", +); +const CREATE_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_CREATE", +); +const UPDATE_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_UPDATE", +); +const DELETE_BINDING = WORKFLOW_BINDING.find( + (b) => b.name === "COLLECTION_WORKFLOW_DELETE", +); + +if (!LIST_BINDING?.inputSchema || !LIST_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_LIST binding not found or missing schemas", + ); +} +if (!GET_BINDING?.inputSchema || !GET_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_GET binding not found or missing schemas", + ); +} +if (!CREATE_BINDING?.inputSchema || !CREATE_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_CREATE binding not found or missing schemas", + ); +} + +if (!UPDATE_BINDING?.inputSchema || !UPDATE_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_UPDATE binding not found or missing schemas", + ); +} +if (!DELETE_BINDING?.inputSchema || !DELETE_BINDING?.outputSchema) { + throw new Error( + "COLLECTION_WORKFLOW_DELETE binding not found or missing schemas", + ); +} + +function transformDbRowToWorkflow(row: unknown): Workflow { + const r = row as Record; + + // Parse steps - handle both old { phases: [...] } format and new direct array format + let steps: unknown = []; + if (r.steps) { + const parsed = typeof r.steps === "string" ? JSON.parse(r.steps) : r.steps; + // Handle legacy { phases: [...] } format + if (parsed && typeof parsed === "object" && "phases" in parsed) { + steps = (parsed as { phases: unknown }).phases; + } else { + steps = parsed; + } + } + + return { + id: r.id as string, + title: r.title as string, + description: r.description as string | undefined, + steps: steps as Workflow["steps"], + created_at: r.created_at as string, + updated_at: r.updated_at as string, + created_by: r.created_by as string | undefined, + updated_by: r.updated_by as string | undefined, + }; +} + +export const createListTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_LIST", + description: "List workflows with filtering, sorting, and pagination", + inputSchema: LIST_BINDING.inputSchema, + outputSchema: createCollectionListOutputSchema(WorkflowSchema), + execute: async ({ + context, + }: { + context: z.infer; + }) => { + const { where, orderBy, limit = 50, offset = 0 } = context; + + let whereClause = ""; + let params: any[] = []; + if (where) { + const result = buildWhereClause(where, params); + whereClause = result.clause ? `WHERE ${result.clause}` : ""; + params = result.params; + } + + const orderByClause = buildOrderByClause(orderBy); + + const sql = ` + SELECT * FROM workflow_collection + ${whereClause} + ${orderByClause} + LIMIT ? OFFSET ? + `; + + const itemsResult: any = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql, + params: [...params, limit, offset], + }); + + const countQuery = `SELECT COUNT(*) as count FROM workflow_collection ${whereClause}`; + const countResult = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: countQuery, + params, + }); + const totalCount = parseInt( + (countResult.result[0]?.results?.[0] as { count: string })?.count || + "0", + 10, + ); + + return { + items: itemsResult.result[0]?.results?.map( + (item: Record) => transformDbRowToWorkflow(item), + ), + totalCount, + hasMore: + offset + (itemsResult.result[0]?.results?.length || 0) < totalCount, + }; + }, + }); + +export async function getWorkflowCollection( + env: Env, + id: string, +): Promise { + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql: "SELECT * FROM workflow_collection WHERE id = ? LIMIT 1", + params: [id], + }); + const item = result.result[0]?.results?.[0] || null; + return item + ? transformDbRowToWorkflow(item as Record) + : null; +} + +export const createGetTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_GET", + description: "Get a single workflow by ID", + inputSchema: GET_BINDING.inputSchema, + outputSchema: GET_BINDING.outputSchema, + execute: async ({ + context, + }: { + context: z.infer; + }) => { + const { id } = context; + + const workflow = await getWorkflowCollection(env, id); + return { + item: workflow, + }; + }, + }); + +export async function insertWorkflowCollection(env: Env, data?: Workflow) { + try { + const user = env.MESH_REQUEST_CONTEXT?.ensureAuthenticated(); + const now = new Date().toISOString(); + + const workflow: Workflow = { + ...createDefaultWorkflow(), + ...data, + }; + await validateWorkflow(workflow, env); + + const stepsJson = JSON.stringify( + workflow.steps.map((s) => ({ + ...s, + name: s.name.trim().replaceAll(/\s+/g, "_"), + })) || [], + ); + + // Note: gateway_id should come from workflow data, not hard-coded + const gatewayId = (workflow as any).gateway_id ?? ""; + + const result = await runSQL>( + env, + `INSERT INTO workflow_collection (id, title, input, gateway_id, description, steps, created_at, updated_at, created_by, updated_by) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`, + [ + workflow.id, + workflow.title, + JSON.stringify((workflow as any).input ?? null), + gatewayId, + workflow.description || null, + stepsJson, + now, + now, + user?.id || null, + user?.id || null, + ], + ); + + if (!result.length) { + throw new Error("Failed to create workflow collection"); + } + + return { + item: workflow, + }; + } catch (error) { + console.error("Error creating workflow:", error); + throw error; + } +} + +export const createInsertTool = (env: Env) => + createPrivateTool({ + id: CREATE_BINDING.name, + description: `Create a workflow: a sequence of steps that execute automatically with data flowing between them. + +Key concepts: +- Steps run in parallel unless they reference each other's outputs via @ref +- Use @ref syntax to wire data: @input.field, @stepName.field, @item (in loops) +- Execution order is auto-determined from @ref dependencies + +Example workflow with 2 parallel steps: +{ "title": "Fetch users and orders", "steps": [ + { "name": "fetch_users", "action": { "toolName": "GET_USERS" } }, + { "name": "fetch_orders", "action": { "toolName": "GET_ORDERS" } }, +]} + +Example workflow with a step that references the output of another step: +{ "title": "Get first user and then fetch orders", "steps": [ + true }, "transformCode": "export default async (i) => i[0]" }, + { "name": "fetch_orders", "action": { "toolName": "GET_ORDERS" }, "input": { "user": "@fetch_users.user" } }, +]} +`, + inputSchema: CREATE_BINDING.inputSchema, + outputSchema: z + .object({}) + .catchall(z.unknown()) + .describe("The ID of the created workflow"), + execute: async ({ + context, + }: { + context: z.infer; + }) => { + const { data } = context; + const workflow = { + ...createDefaultWorkflow(), + ...data, + }; + return await insertWorkflowCollection(env, workflow); + }, + }); + +async function updateWorkflowCollection( + env: Env, + context: { id: string; data: Workflow }, +) { + const user = env.MESH_REQUEST_CONTEXT?.ensureAuthenticated(); + const now = new Date().toISOString(); + + const { id, data } = context; + + await validateWorkflow(data, env); + + const setClauses: string[] = []; + const params: unknown[] = []; + + setClauses.push(`updated_at = ?`); + params.push(now); + + setClauses.push(`updated_by = ?`); + params.push(user?.id || null); + + if (data.title !== undefined) { + setClauses.push(`title = ?`); + params.push(data.title); + } + if (data.description !== undefined) { + setClauses.push(`description = ?`); + params.push(data.description); + } + if (data.steps !== undefined) { + setClauses.push(`steps = ?`); + params.push(JSON.stringify(data.steps || [])); + } + + params.push(id); + + const sql = ` +y UPDATE workflow_collection + SET ${setClauses.join(", ")} + WHERE id = ? + RETURNING * + `; + + const result = + await env.MESH_REQUEST_CONTEXT.state.DATABASE.DATABASES_RUN_SQL({ + sql, + params, + }); + + if (result.result[0]?.results?.length === 0) { + throw new Error(`Workflow collection with id ${id} not found`); + } + + return { + item: transformDbRowToWorkflow( + result.result[0]?.results?.[0] as Record, + ), + }; +} + +export const createUpdateTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_UPDATE", + description: "Update an existing workflow", + inputSchema: UPDATE_BINDING.inputSchema, + outputSchema: UPDATE_BINDING.outputSchema, + execute: async ({ context }) => { + try { + return await updateWorkflowCollection(env, { + id: context.id as string, + data: context.data as Workflow, + }); + } catch (error) { + console.error("Error updating workflow:", error); + throw new Error( + error instanceof Error ? error.message : "Unknown error", + ); + } + }, + }); + +export const createDeleteTool = (env: Env) => + createPrivateTool({ + id: "COLLECTION_WORKFLOW_DELETE", + description: "Delete a workflow by ID", + inputSchema: DELETE_BINDING.inputSchema, + outputSchema: DELETE_BINDING.outputSchema, + execute: async ({ context }) => { + const { id } = context; + + const result = await runSQL>( + env, + "DELETE FROM workflow_collection WHERE id = ? RETURNING *", + [id], + ); + + const item = result[0]; + if (!item) { + throw new Error(`Workflow collection with id ${id} not found`); + } + return { + item: transformDbRowToWorkflow(item), + }; + }, + }); + +export const workflowCollectionTools = [ + createListTool, + createGetTool, + createInsertTool, + createUpdateTool, + createDeleteTool, +]; diff --git a/mcp-studio/server/types/env.ts b/mcp-studio/server/types/env.ts new file mode 100644 index 00000000..8a2972ea --- /dev/null +++ b/mcp-studio/server/types/env.ts @@ -0,0 +1,92 @@ +/** + * Environment Type Definitions + * + * Central definition for the Env type used throughout the workflow system. + */ + +import type { EVENT_BUS_BINDING } from "@decocms/bindings"; +import type { createCollectionBindings } from "@decocms/bindings/collections"; +import { + BindingOf, + type BindingRegistry, + type DefaultEnv, +} from "@decocms/runtime"; +import z from "zod"; + +export const StateSchema = z.object({ + DATABASE: BindingOf("@deco/postgres"), + EVENT_BUS: BindingOf("@deco/event-bus"), + CONNECTION: BindingOf("@deco/connection"), +}); + +export type ConnectionBinding = { + COLLECTION_CONNECTIONS_UPDATE: (params: { + id: string; + data: { + configuration_state: object; + configuration_scopes: string[]; + }; + }) => Promise; + COLLECTION_CONNECTIONS_GET: (params: { id: string }) => Promise<{ + item: { + configuration_state: object; + configuration_scopes: string[]; + tools: { + name: string; + description: string; + inputSchema: object; + outputSchema: object; + }[]; + }; + }>; + // Accepts an (empty) object because MCP tool validation rejects `undefined` inputs. + COLLECTION_CONNECTIONS_LIST: (params?: Record) => Promise<{ + items: { + id: string; + title: string; + tools: { + name: string; + description: string; + inputSchema: object; + outputSchema: object; + }[]; + }[]; + }>; +}; +const ConnectionSchema = z.object({ + configuration_state: z.object({}), + configuration_scopes: z.array(z.string()), + tools: z.array( + z.object({ + name: z.string(), + description: z.string(), + inputSchema: z.object({}), + outputSchema: z.object({}), + }), + ), +}); + +export interface Registry extends BindingRegistry { + "@deco/event-bus": typeof EVENT_BUS_BINDING; + "@deco/connection": ReturnType< + typeof createCollectionBindings + >; + "@deco/postgres": [ + { + name: "DATABASES_RUN_SQL"; + description: "Run a SQL query against the database"; + inputSchema: z.ZodType<{ + sql: string; + params?: unknown[]; + }>; + outputSchema: z.ZodType<{ + result: { + results?: unknown[]; + success?: boolean; + }[]; + }>; + }, + ]; +} + +export type Env = DefaultEnv; diff --git a/mcp-studio/server/types/step.ts b/mcp-studio/server/types/step.ts new file mode 100644 index 00000000..0a9a2d51 --- /dev/null +++ b/mcp-studio/server/types/step.ts @@ -0,0 +1,50 @@ +/** + * Step Type Definitions + * + * Types for step execution results and extended step definitions. + */ + +import type { Step as BaseStep, CodeAction } from "@decocms/bindings/workflow"; + +export type Step = BaseStep; + +/** + * Result of executing a single step. + */ +export interface StepResult { + stepId: string; + startedAt: number; + completedAt?: number; + output?: unknown; + error?: string; +} + +/** + * Existing step result from database (for resuming executions). + */ +export interface ExistingStepResult { + started_at_epoch_ms?: number | null; + output?: unknown; +} + +/** + * Step type discriminator. + */ +export type StepType = "tool" | "code" | "signal"; + +/** + * Determine the type of a step based on its action. + */ +export function getStepType(step: Step): StepType { + if ("toolName" in step.action) return "tool"; + if ("code" in step.action) return "code"; + if ("signalName" in step.action) return "signal"; + throw new Error(`Unknown step type for step: ${step.name}`); +} + +/** + * Type guard for code action. + */ +export function isCodeAction(action: Step["action"]): action is CodeAction { + return "code" in action; +} diff --git a/mcp-studio/server/utils/errors.ts b/mcp-studio/server/utils/errors.ts new file mode 100644 index 00000000..3d6742c5 --- /dev/null +++ b/mcp-studio/server/utils/errors.ts @@ -0,0 +1,110 @@ +export class WorkflowCancelledError extends Error { + readonly code = "WORKFLOW_CANCELLED"; + + constructor( + public readonly executionId: string, + message?: string, + ) { + super(message || `Workflow execution ${executionId} was cancelled`); + this.name = "WorkflowCancelledError"; + Object.setPrototypeOf(this, WorkflowCancelledError.prototype); + } +} + +/** + * Error thrown when a workflow execution is not found. + */ +export class ExecutionNotFoundError extends Error { + readonly code = "EXECUTION_NOT_FOUND"; + + constructor( + public readonly executionId: string, + message?: string, + ) { + super(message || `Workflow execution ${executionId} not found`); + this.name = "ExecutionNotFoundError"; + Object.setPrototypeOf(this, ExecutionNotFoundError.prototype); + } +} + +/** + * Error thrown when max retry attempts have been exceeded. + */ +export class MaxRetriesExceededError extends Error { + readonly code = "MAX_RETRIES_EXCEEDED"; + + constructor( + public readonly executionId: string, + public readonly retryCount: number, + public readonly maxRetries: number, + message?: string, + ) { + super( + message || + `Execution ${executionId} exceeded max retries (${retryCount}/${maxRetries})`, + ); + this.name = "MaxRetriesExceededError"; + Object.setPrototypeOf(this, MaxRetriesExceededError.prototype); + } +} + +export class StepTimeoutError extends Error { + readonly code = "STEP_TIMEOUT"; + constructor( + public readonly executionId: string, + public readonly stepName: string, + public readonly timeoutMs: number, + ) { + super(`Step '${stepName}' timed out after ${timeoutMs}ms`); + this.name = "StepTimeoutError"; + Object.setPrototypeOf(this, StepTimeoutError.prototype); + } +} + +/** + * Error thrown when a step fails execution. + */ +export class StepExecutionError extends Error { + readonly code = "STEP_EXECUTION_FAILED"; + + constructor( + public readonly executionId: string, + public readonly stepName: string, + public readonly stepError: string, + ) { + super(`Step '${stepName}' failed: ${stepError}`); + this.name = "StepExecutionError"; + Object.setPrototypeOf(this, StepExecutionError.prototype); + } +} + +/** + * Error thrown when a step is waiting for a signal. + * + * This is a special "pause" error - the workflow should: + * 1. Keep the step as "running" + * 2. Release the execution lock + * 3. NOT retry automatically + * + * The workflow will be resumed when a matching signal is sent. + */ +export class WaitingForSignalError extends Error { + readonly code = "WAITING_FOR_SIGNAL"; + + constructor( + public readonly executionId: string, + public readonly stepName: string, + public readonly signalName: string, + public readonly timeoutMs?: number, + public readonly waitStartedAt: number = Date.now(), + ) { + super(`Step '${stepName}' is waiting for signal '${signalName}'`); + this.name = "WaitingForSignalError"; + Object.setPrototypeOf(this, WaitingForSignalError.prototype); + } + + get isTimedOut(): boolean { + if (!this.timeoutMs) return false; + return Date.now() - this.waitStartedAt > this.timeoutMs; + } +} diff --git a/mcp-studio/server/utils/ref-resolver.ts b/mcp-studio/server/utils/ref-resolver.ts new file mode 100644 index 00000000..12c35fda --- /dev/null +++ b/mcp-studio/server/utils/ref-resolver.ts @@ -0,0 +1,299 @@ +/** + * @ref Resolution for Workflow v3 + * + * Resolves references in step inputs: + * - @stepName.path - Output from previous step + * - @input.path - Workflow input + * - @item - Current item in forEach loop + * - @index - Current index in forEach loop + * + * @see docs/WORKFLOW_SCHEMA_DESIGN.md + */ + +/** + * Context for @ref resolution + */ +export interface RefContext { + /** Outputs from completed steps: Map */ + stepOutputs: Map; + /** Workflow input data */ + workflowInput: Record; + /** Current item in forEach loop (if applicable) */ + item?: unknown; + /** Current index in forEach loop (if applicable) */ + index?: number; +} + +/** + * Resolution result for a single @ref + */ +export interface RefResolution { + value: unknown; + error?: string; +} + +/** + * Check if a value is an @ref string + */ +export function isAtRef(value: unknown): value is `@${string}` { + return typeof value === "string" && value.startsWith("@"); +} + +/** + * Parse an @ref string into its components + */ +export function parseAtRef(ref: `@${string}`): { + type: "step" | "input" | "item"; + stepName?: string; + groupId?: string; + path?: string; +} { + const refStr = ref.substring(1); // Remove @ prefix + + // ForEach item reference: @item or @item.path + if (refStr === "item" || refStr.startsWith("item.")) { + const path = refStr.length > 4 ? refStr.substring(5) : ""; // Remove 'item.' or 'item' + return { type: "item", path }; + } + + // Input reference: @input.path.to.value + if (refStr.startsWith("input")) { + const path = refStr.length > 5 ? refStr.substring(6) : ""; // Remove 'input.' or 'input' + return { type: "input", path }; + } + + // Step output reference: @stepName.path + const parts = refStr.split("."); + const stepName = parts[0]; + const path = parts.slice(1).join("."); + + return { + type: "step", + stepName, + path, + }; +} + +/** + * Get a value from an object by path + */ +export function getValueByPath(obj: unknown, path: string): unknown { + if (!path) return obj; + + const keys = path.split("."); + let current = obj; + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + + if (current === null || current === undefined) { + // Better error context: show which part of the path failed + const traversedPath = keys.slice(0, i).join("."); + console.warn( + `[REF] Null/undefined at path "${traversedPath}" while accessing "${key}"`, + ); + return undefined; + } + + if (typeof current === "object" && !Array.isArray(current)) { + current = (current as Record)[key]; + } else if (Array.isArray(current)) { + const index = parseInt(key, 10); + current = Number.isNaN(index) ? undefined : current[index]; + } else { + // Trying to access property on primitive + const traversedPath = keys.slice(0, i).join("."); + console.warn( + `[REF] Cannot access "${key}" on primitive value at "${traversedPath}"`, + ); + return undefined; + } + } + + return current; +} + +/** + * Resolve a single @ref + */ +export function resolveRef(ref: `@${string}`, ctx: RefContext): RefResolution { + try { + const parsed = parseAtRef(ref); + + switch (parsed.type) { + case "input": { + const value = getValueByPath(ctx.workflowInput, parsed.path || ""); + if (value === undefined) { + return { + value: undefined, + error: `Input path not found: @input.${parsed.path}`, + }; + } + return { value }; + } + + case "step": { + const stepOutput = ctx.stepOutputs.get(parsed.stepName || ""); + if (stepOutput === undefined) { + return { + value: undefined, + error: `Step not found or not completed: ${parsed.stepName}`, + }; + } + const value = getValueByPath(stepOutput, parsed.path || ""); + if (value === undefined) { + return { + value: undefined, + error: `Path not found in step output: @${parsed.stepName}.${parsed.path}`, + }; + } + return { value }; + } + + case "item": { + if (ctx.item === undefined) { + return { + value: undefined, + error: `@item used outside of forEach context`, + }; + } + const value = getValueByPath(ctx.item, parsed.path || ""); + return { value }; + } + + default: + return { value: undefined, error: `Unknown reference type: ${ref}` }; + } + } catch (error) { + return { + value: undefined, + error: `Failed to resolve ${ref}: ${String(error)}`, + }; + } +} + +/** + * Resolution result with errors + */ +export interface ResolveResult { + resolved: unknown; + errors?: Array<{ ref: string; error: string }>; +} + +/** + * Regex to match @refs in strings for interpolation + */ +const AT_REF_PATTERN = + /@([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)/g; + +/** + * Regex to match a COMPLETE @ref (entire string is one reference) + */ +const SINGLE_AT_REF_PATTERN = + /^@([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)$/; + +/** + * Check if a value is a COMPLETE @ref string (the entire value is one reference) + */ +function isSingleAtRef(value: unknown): value is `@${string}` { + return typeof value === "string" && SINGLE_AT_REF_PATTERN.test(value); +} + +/** + * Resolve all @refs in an input object + * + * Handles: + * - Direct @ref values (entire value is a reference) + * - Interpolated @refs in strings + * - Nested objects and arrays + */ +export function resolveAllRefs(input: unknown, ctx: RefContext): ResolveResult { + const errors: Array<{ ref: string; error: string }> = []; + function resolveValue(value: unknown): unknown { + // If it's a string that IS an @ref (entire value is ONE reference) + if (isSingleAtRef(value)) { + const result = resolveRef(value, ctx); + if (result.error) { + errors.push({ ref: value, error: result.error }); + } + return result.value; + } + + // If it's a string that CONTAINS @refs, interpolate them + if (typeof value === "string" && value.includes("@")) { + const interpolated = value.replace(AT_REF_PATTERN, (match) => { + if (isAtRef(match as `@${string}`)) { + const result = resolveRef(match as `@${string}`, ctx); + if (result.error) { + errors.push({ ref: match, error: result.error }); + return match; // Keep original if resolution fails + } + // Fix: JSON.stringify objects instead of String() + const val = result.value; + if (val === null || val === undefined) return ""; + if (typeof val === "object") return JSON.stringify(val); + return String(val); + } + return match; + }); + return interpolated; + } + + // If it's an array, resolve each element + if (Array.isArray(value)) { + return value.map((v) => { + const resolved = resolveValue(v); + return resolved; + }); + } + + // If it's an object, resolve each property + if (value !== null && typeof value === "object") { + const resolvedObj: Record = {}; + for (const [key, val] of Object.entries(value)) { + resolvedObj[key] = resolveValue(val); + } + return resolvedObj; + } + + // Primitive value, return as-is + return value; + } + + const resolved = resolveValue(input); + return { resolved, errors: errors.length > 0 ? errors : undefined }; +} + +/** + * Get all @refs used in an input object + */ +export function extractRefs(input: unknown): string[] { + const refs: string[] = []; + + function extract(value: unknown): void { + if (isAtRef(value)) { + refs.push(value); + return; + } + + if (typeof value === "string" && value.includes("@")) { + const matches = value.match(AT_REF_PATTERN); + if (matches) { + refs.push(...matches); + } + return; + } + + if (Array.isArray(value)) { + value.forEach(extract); + return; + } + + if (value !== null && typeof value === "object") { + Object.values(value).forEach(extract); + } + } + + extract(input); + return refs; +} diff --git a/mcp-studio/server/utils/validator.ts b/mcp-studio/server/utils/validator.ts new file mode 100644 index 00000000..8cf149af --- /dev/null +++ b/mcp-studio/server/utils/validator.ts @@ -0,0 +1,230 @@ +/** + * Workflow Validation + * + * Validates workflow definitions at creation time: + * - Step type validation (exactly one of tool/transform/sleep) + * - @ref validation (references point to valid steps/paths) + * - Schema extraction from transform steps + * - Type compatibility between step outputs and inputs + * - Permission token management for tool steps + * + * @see docs/WORKFLOW_SCHEMA_DESIGN.md + */ + +import { + CodeActionSchema, + type Step, + type ToolCallAction, + type Workflow, +} from "@decocms/bindings/workflow"; +import z from "zod"; +import { validateCode } from "../engine/steps/code-step.ts"; +import type { Env } from "../types/env.ts"; +import { getStepType } from "../types/step.ts"; +import { extractRefs, parseAtRef } from "./ref-resolver.ts"; + +export const ValidationErrorSchema = z.object({ + type: z.enum([ + "missing_ref", + "type_mismatch", + "missing_schema", + "invalid_typescript", + ]), + step: z.string(), + field: z.string(), + ref: z.string().optional(), + expected: z.record(z.unknown()).optional(), + actual: z.record(z.unknown()).optional(), + message: z.string(), +}); + +export type ValidationError = z.infer; + +/** + * Validation result + */ +export interface ValidationResult { + valid: boolean; + errors: unknown[]; + schemas?: Record< + string, + { + input: Record; + output: Record; + } + >; +} + +/** + * Validate @refs in a step's input + */ +function validateStepRefs( + step: Step, + availableSteps: Map, +): ValidationError[] { + const errors: ValidationError[] = []; + + // Get all @refs used in this step + const refs = extractRefs(step.input || {}); + + for (const ref of refs) { + const parsed = parseAtRef(ref as `@${string}`); + + switch (parsed.type) { + case "step": { + const stepName = parsed.stepName; + if (!stepName) { + errors.push({ + type: "missing_ref", + step: step.name, + field: "input", + ref, + message: `Invalid step reference: ${ref}`, + }); + continue; + } + + // Check if step exists in previous steps + const stepIndex = availableSteps.get(stepName); + if (stepIndex === undefined) { + errors.push({ + type: "missing_ref", + step: step.name, + field: "input", + ref, + message: `Step '${stepName}' not found in previous steps. Available: ${ + Array.from(availableSteps.keys()).join(", ") || "none" + }`, + }); + } + break; + } + + case "input": + // Input refs are always valid at this stage (validated at execution time) + break; + } + } + + return errors; +} + +/** + * Validate a transform step's TypeScript code + */ +async function validateCodeStep(step: Step): Promise<{ + error: ValidationError | null; + schema?: { input: Record; output: Record }; +}> { + const parsed = CodeActionSchema.safeParse(step.action); + const isCodeAction = parsed.success; + if (!isCodeAction) { + return { error: null }; + } + + const codeAction = parsed.data; + const result = await validateCode(codeAction.code, step.name); + + if (!result.valid) { + return { + error: { + type: "invalid_typescript", + step: step.name, + field: "code", + message: result.error || "Invalid TypeScript code", + }, + }; + } + + return { + error: null, + schema: result.schemas, + }; +} + +export async function validateWorkflow( + workflow: Workflow, + env: Env, +): Promise { + const errors: ValidationError[] = []; + const schemas: Record< + string, + { input: Record; output: Record } + > = {}; + const stepNames = new Set(); + const duplicateNames = new Set(); + // Some MCP clients send `undefined` when a tool has no arguments. + // The Connection binding expects an object input for LIST, so always pass `{}`. + const currentTools = ( + await env.MESH_REQUEST_CONTEXT.state.CONNECTION.COLLECTION_CONNECTIONS_LIST( + {}, + ) + ).items.flatMap((connection) => connection.tools); + + const availableSteps = new Map(); + + const steps = workflow.steps || []; + + for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) { + const step = steps[stepIndex]; + + const toolName = "toolName" in step.action ? step.action.toolName : null; + if (toolName) { + const tool = currentTools.find((tool) => tool.name === toolName); + if (!tool) { + errors.push({ + type: "missing_ref", + step: step.name, + field: "action.toolName", + ref: toolName, + message: `Tool '${toolName}' not found in connections. Available: ${currentTools + .map((tool) => tool.name) + .join(", ")}`, + }); + } + // biome-ignore lint/suspicious/noExplicitAny: hard typings + step.outputSchema = tool?.outputSchema as any; + } + + if (stepNames.has(step.name)) { + duplicateNames.add(step.name); + } + stepNames.add(step.name); + + const refErrors = validateStepRefs(step, availableSteps); + errors.push(...refErrors); + const stepType = getStepType(step); + + if (stepType === "tool") { + const tool = currentTools.find( + (tool) => tool.name === (step.action as ToolCallAction).toolName, + ); + + const transformCode = (step.action as ToolCallAction).transformCode; + // biome-ignore lint/suspicious/noExplicitAny: hard for typing + if (!transformCode) step.outputSchema = (tool?.outputSchema as any) ?? {}; // hacky, but works for now + } + + if (stepType === "code") { + const { error, schema } = await validateCodeStep(step); + if (error) errors.push(error); + if (schema) schemas[step.name] = schema; + } + + // Make this step available for subsequent steps to reference + availableSteps.set(step.name, stepIndex); + } + + for (const name of duplicateNames) { + errors.push({ + type: "invalid_typescript", + step: name, + field: "name", + message: `Duplicate step name: ${name}`, + }); + } + + if (errors.length > 0) { + throw new Error(JSON.stringify(errors, null, 2)); + } +} diff --git a/mcp-studio/shared/deco.gen.ts b/mcp-studio/shared/deco.gen.ts deleted file mode 100644 index cdedd436..00000000 --- a/mcp-studio/shared/deco.gen.ts +++ /dev/null @@ -1,230 +0,0 @@ -// Generated types - do not edit manually - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type DATABASES_GET_METAInput = {}; - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type Number = number; - -export interface DATABASES_GET_METAOutput { - bytes?: Number; -} - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -/** - * The date to recover to - */ -export type String = string; - -export interface DATABASES_RECOVERYInput { - date: String; -} - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type Boolean = boolean; - -export interface DATABASES_RECOVERYOutput { - success: Boolean; -} - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -/** - * The SQL query to run - */ -export type String_1 = string; -/** - * The parameters to pass to the SQL query - */ -export type Array = unknown[]; - -export interface DATABASES_RUN_SQLInput { - sql: String_1; - params?: Array; -} - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type Boolean_1 = boolean; -export type Number_1 = number; -export type Number_2 = number; -export type Number_3 = number; -export type Number_4 = number; -export type Number_5 = number; -export type Boolean_2 = boolean; -export type String_2 = "WNAM" | "ENAM" | "WEUR" | "EEUR" | "APAC" | "OC"; -export type Number_6 = number; -export type Number_7 = number; -export type Array_2 = unknown[]; -export type Boolean_3 = boolean; -export type Array_1 = Object[]; - -export interface DATABASES_RUN_SQLOutput { - result: Array_1; -} -export interface Object { - meta?: Object_1; - results?: Array_2; - success?: Boolean_3; -} -export interface Object_1 { - changed_db?: Boolean_1; - changes?: Number_1; - duration?: Number_2; - last_row_id?: Number_3; - rows_read?: Number_4; - rows_written?: Number_5; - served_by_primary?: Boolean_2; - served_by_region?: String_2; - size_after?: Number_6; - timings?: Object_2; -} -export interface Object_2 { - sql_duration_ms?: Number_7; -} - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type DECO_CHAT_VIEWS_LISTInput = {}; - -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ - -export type String_3 = string; -export type String_4 = string; -export type String_5 = string; -export type String_6 = string; -export type String_7 = string; -export type String_8 = string; -export type String_9 = string; -export type String_10 = string; -export type String_11 = string; -export type Array_4 = String_11[]; -export type String_12 = string; -export type String_13 = "none" | "open" | "autoPin"; -export type Array_3 = Object_3[]; - -export interface DECO_CHAT_VIEWS_LISTOutput { - views: Array_3; -} -export interface Object_3 { - id?: String_3; - name?: String_4; - title: String_5; - description?: String_6; - icon: String_7; - url?: String_8; - mimeTypePattern?: String_9; - resourceName?: String_10; - tools?: Array_4; - prompt?: String_12; - installBehavior?: String_13; -} - -import { z } from "zod"; - -export type Mcp Promise>> = { - [K in keyof T]: (( - input: Parameters[0], - ) => Promise>>) & { - asTool: () => Promise<{ - inputSchema: z.ZodType[0]>; - outputSchema?: z.ZodType>>; - description: string; - id: string; - execute: ( - input: Parameters[0], - ) => Promise>>; - }>; - }; -}; - -export const StateSchema = z.object({ - DATABASE: z.object({ - value: z.string(), - __type: z.literal("@deco/postgres").default("@deco/postgres"), - }), -}); - -export interface Env { - DECO_CHAT_WORKSPACE: string; - DECO_CHAT_API_JWT_PUBLIC_KEY: string; - DATABASE: Mcp<{ - /** - * Run a SQL query against the workspace database - */ - DATABASES_GET_META: ( - input: DATABASES_GET_METAInput, - ) => Promise; - /** - * Run a SQL query against the workspace database - */ - DATABASES_RECOVERY: ( - input: DATABASES_RECOVERYInput, - ) => Promise; - /** - * Run a SQL query against the workspace database - */ - DATABASES_RUN_SQL: ( - input: DATABASES_RUN_SQLInput, - ) => Promise; - /** - * List views exposed by this MCP - */ - DECO_CHAT_VIEWS_LIST: ( - input: DECO_CHAT_VIEWS_LISTInput, - ) => Promise; - }>; -} - -export const Scopes = { - DATABASE: { - DATABASES_GET_META: "DATABASE::DATABASES_GET_META", - DATABASES_RECOVERY: "DATABASE::DATABASES_RECOVERY", - DATABASES_RUN_SQL: "DATABASE::DATABASES_RUN_SQL", - DECO_CHAT_VIEWS_LIST: "DATABASE::DECO_CHAT_VIEWS_LIST", - }, -}; diff --git a/mcp-studio/tsconfig.json b/mcp-studio/tsconfig.json index 2d35ad9f..65a71cd7 100644 --- a/mcp-studio/tsconfig.json +++ b/mcp-studio/tsconfig.json @@ -4,8 +4,6 @@ "useDefineForClassFields": true, "lib": [ "ES2023", - "DOM", - "DOM.Iterable" ], "module": "ESNext", "skipLibCheck": true, @@ -16,7 +14,6 @@ "verbatimModuleSyntax": false, "moduleDetection": "force", "noEmit": true, - "jsx": "react-jsx", "allowJs": true, /* Linting */ "strict": true, @@ -27,29 +24,16 @@ /* Path Aliases */ "baseUrl": ".", "paths": { - "@/*": [ - "./view/src/*" - ], - "shared/*": [ - "./shared/*" - ], "server/*": [ "./server/*" ], - "worker/*": [ - "./worker/*" - ], }, /* Types */ "types": [ "@types/node", - "vite/client", ] }, "include": [ - "view", "server", - "shared", - "vite.config.ts" ] } \ No newline at end of file diff --git a/meta-ads/package.json b/meta-ads/package.json index 3bcf3a54..afe59847 100644 --- a/meta-ads/package.json +++ b/meta-ads/package.json @@ -13,16 +13,16 @@ "dev:tunnel": "deco link -p 3003 -- PORT=3003 bun run dev" }, "dependencies": { - "@decocms/runtime": "1.0.0-alpha.41", + "@decocms/runtime": "^1.0.3", "zod": "^3.24.3" }, "devDependencies": { "@decocms/mcps-shared": "workspace:*", - "@modelcontextprotocol/sdk": "1.20.2", + "@modelcontextprotocol/sdk": "1.25.1", "deco-cli": "^0.28.0", "typescript": "^5.7.2" }, "engines": { "node": ">=22.0.0" } -} +} \ No newline at end of file diff --git a/meta-ads/types.d.ts b/meta-ads/types.d.ts new file mode 100644 index 00000000..9073c615 --- /dev/null +++ b/meta-ads/types.d.ts @@ -0,0 +1,8 @@ +// Type declarations to fix runtime dependency issues +declare module "packages/bindings/src/well-known/collections.ts" { + export const collections: any; + export class CollectionBinding { + constructor(...args: any[]); + } + export type CollectionBindingType = CollectionBinding; +} diff --git a/openrouter/package.json b/openrouter/package.json index c568a356..1a891844 100644 --- a/openrouter/package.json +++ b/openrouter/package.json @@ -14,8 +14,8 @@ "dependencies": { "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.1", - "@decocms/bindings": "1.0.1-alpha.23", - "@decocms/runtime": "1.0.0-alpha.41", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", "@openrouter/ai-sdk-provider": "^1.2.0", "@openrouter/sdk": "^0.1.11", "ai": "^6.0.3", @@ -26,7 +26,7 @@ "@cloudflare/workers-types": "^4.20251014.0", "@decocms/mcps-shared": "1.0.0", "@mastra/core": "^0.24.0", - "@modelcontextprotocol/sdk": "1.20.2", + "@modelcontextprotocol/sdk": "1.25.1", "@types/mime-db": "^1.43.6", "deco-cli": "^0.28.0", "typescript": "^5.7.2", diff --git a/registry/package.json b/registry/package.json index 62864657..f448ae47 100644 --- a/registry/package.json +++ b/registry/package.json @@ -16,14 +16,14 @@ "publish": "cat app.json | deco registry publish -w /shared/deco -y" }, "dependencies": { - "@decocms/bindings": "1.0.1-alpha.11", - "@decocms/runtime": "1.0.0-alpha.22", + "@decocms/bindings": "^1.0.3", + "@decocms/runtime": "^1.0.3", "zod": "^3.24.3" }, "devDependencies": { "@decocms/mcps-shared": "workspace:*", "@decocms/vite-plugin": "1.0.0-alpha.1", - "@modelcontextprotocol/sdk": "1.20.2", + "@modelcontextprotocol/sdk": "1.25.1", "@types/mime-db": "^1.43.6", "@types/node": "^22.0.0", "deco-cli": "^0.28.0", @@ -33,4 +33,4 @@ "engines": { "node": ">=22.0.0" } -} +} \ No newline at end of file diff --git a/registry/server/tools/registry-binding.ts b/registry/server/tools/registry-binding.ts index 3d5408cc..3d963e10 100644 --- a/registry/server/tools/registry-binding.ts +++ b/registry/server/tools/registry-binding.ts @@ -163,6 +163,7 @@ const ListInputSchema = z .max(100) .default(30) .describe("Number of items per page (default: 30)"), + where: WhereSchema.optional().describe( "Standard WhereExpression filter (converted to simple search internally)", ),