diff --git a/bun.lock b/bun.lock index 6c195032..0a55c3d0 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "@decocms/mcps", @@ -715,6 +714,20 @@ "typescript": "^5.7.2", }, }, + "task-runner": { + "name": "mcp-task-runner", + "version": "1.0.0", + "dependencies": { + "@decocms/runtime": "1.2.5", + "zod": "^4.0.0", + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2", + }, + }, "template-minimal": { "name": "mcp-template-minimal", "version": "1.0.0", @@ -875,9 +888,9 @@ "@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.39", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-SeCZBAdDNbWpVUXiYgOAqis22p5MEYfrjRw0hiBa5hM+7sDGYQpMinUjkM8kbPXMkY+AhKLrHleBl+SuqpzlgA=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.46", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zH1UbNRjG5woOXXFOrVCZraqZuFTtmPvLardMGcgLkzpxKV0U3tAGoyWKSZ862H+eBJfI/Hf2yj/zzGJcCkycg=="], - "@ai-sdk/google": ["@ai-sdk/google@2.0.52", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g=="], + "@ai-sdk/google": ["@ai-sdk/google@2.0.53", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ccCxr5mrd3AC2CjLq4e1ST7+UiN5T2Pdmgi0XdWM3QohmNBwUQ/RBG7BvL+cB/ex/j6y64tkMmpYz9zBw/SEFQ=="], "@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=="], @@ -893,7 +906,7 @@ "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.14", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7bzKd9lgiDeXM7O4U4nQ8iTxguAOkg8LZGD9AfDVZYjO5cKYRwBPwVjboFcVrxncRHu0tYxZtXZtiLKpG4pEng=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.15", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w=="], "@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=="], @@ -921,35 +934,35 @@ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], - "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.986.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.7", "@aws-sdk/credential-provider-node": "^3.972.6", "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", "@aws-sdk/middleware-expect-continue": "^3.972.3", "@aws-sdk/middleware-flexible-checksums": "^3.972.5", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-location-constraint": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-sdk-s3": "^3.972.7", "@aws-sdk/middleware-ssec": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.7", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/signature-v4-multi-region": "3.986.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.986.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.5", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.1", "@smithy/eventstream-serde-browser": "^4.2.8", "@smithy/eventstream-serde-config-resolver": "^4.3.8", "@smithy/eventstream-serde-node": "^4.2.8", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-blob-browser": "^4.2.9", "@smithy/hash-node": "^4.2.8", "@smithy/hash-stream-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/md5-js": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.13", "@smithy/middleware-retry": "^4.4.30", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.9", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.29", "@smithy/util-defaults-mode-node": "^4.2.32", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-stream": "^4.5.11", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-IcDJ8shVVvbxgMe8+dLWcv6uhSwmX65PHTVGX81BhWAElPnp3CL8w/5uzOPRo4n4/bqIk9eskGVEIicw2o+SrA=="], + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.990.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.10", "@aws-sdk/credential-provider-node": "^3.972.9", "@aws-sdk/middleware-bucket-endpoint": "^3.972.3", "@aws-sdk/middleware-expect-continue": "^3.972.3", "@aws-sdk/middleware-flexible-checksums": "^3.972.8", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-location-constraint": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-sdk-s3": "^3.972.10", "@aws-sdk/middleware-ssec": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.10", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/signature-v4-multi-region": "3.990.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.990.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.8", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.0", "@smithy/eventstream-serde-browser": "^4.2.8", "@smithy/eventstream-serde-config-resolver": "^4.3.8", "@smithy/eventstream-serde-node": "^4.2.8", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-blob-browser": "^4.2.9", "@smithy/hash-node": "^4.2.8", "@smithy/hash-stream-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/md5-js": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.14", "@smithy/middleware-retry": "^4.4.31", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.30", "@smithy/util-defaults-mode-node": "^4.2.33", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-stream": "^4.5.12", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-XnsM8RgB35Atn2+aYSocitCybDG82x9yYf/s2D23ytpyHCupmuZN3LzK2a0WxmKO6Zf7EtEIYy0mHGY4tLp9YA=="], - "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.985.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.7", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.7", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.985.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.5", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.13", "@smithy/middleware-retry": "^4.4.30", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.9", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.29", "@smithy/util-defaults-mode-node": "^4.2.32", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-81J8iE8MuXhdbMfIz4sWFj64Pe41bFi/uqqmqOC5SlGv+kwoyLsyKS/rH2tW2t5buih4vTUxskRjxlqikTD4oQ=="], + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.990.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.10", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.990.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.8", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.14", "@smithy/middleware-retry": "^4.4.31", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.30", "@smithy/util-defaults-mode-node": "^4.2.33", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xTEaPjZwOqVjGbLOP7qzwbdOWJOo1ne2mUhTZwEBBkPvNk4aXB/vcYwWwrjoSWUqtit4+GDbO75ePc/S6TUJYQ=="], - "@aws-sdk/core": ["@aws-sdk/core@3.973.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.4", "@smithy/core": "^3.22.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wNZZQQNlJ+hzD49cKdo+PY6rsTDElO8yDImnrI69p2PLBa7QomeUKAJWYp9xnaR38nlHqWhMHZuYLCQ3oSX+xg=="], + "@aws-sdk/core": ["@aws-sdk/core@3.973.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.4", "@smithy/core": "^3.23.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-4u/FbyyT3JqzfsESI70iFg6e2yp87MB5kS2qcxIA66m52VSTN1fvuvbCY1h/LKq1LvuxIrlJ1ItcyjvcKoaPLg=="], "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw=="], - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-LxJ9PEO4gKPXzkufvIESUysykPIdrV7+Ocb9yAhbhJLE4TiAYqbCVUE+VuKP1leGR1bBfjWjYgSV5MxprlX3mQ=="], + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.8", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-r91OOPAcHnLCSxaeu/lzZAVRCZ/CtTNuwmJkUwpwSDshUrP7bkX1OmFn2nUMWd9kN53Q4cEo8b7226G4olt2Mg=="], - "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.7", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.9", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.11", "tslib": "^2.6.2" } }, "sha512-L2uOGtvp2x3bTcxFTpSM+GkwFIPd8pHfGWO1764icMbo7e5xJh0nfhx1UwkXLnwvocTNEf8A7jISZLYjUSNaTg=="], + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.10", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/types": "^3.973.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.10", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" } }, "sha512-DTtuyXSWB+KetzLcWaSahLJCtTUe/3SXtlGp4ik9PCe9xD6swHEkG8n8/BNsQ9dsihb9nhFvuUB4DpdBGDcvVg=="], - "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/credential-provider-env": "^3.972.5", "@aws-sdk/credential-provider-http": "^3.972.7", "@aws-sdk/credential-provider-login": "^3.972.5", "@aws-sdk/credential-provider-process": "^3.972.5", "@aws-sdk/credential-provider-sso": "^3.972.5", "@aws-sdk/credential-provider-web-identity": "^3.972.5", "@aws-sdk/nested-clients": "3.985.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-SdDTYE6jkARzOeL7+kudMIM4DaFnP5dZVeatzw849k4bSXDdErDS188bgeNzc/RA2WGrlEpsqHUKP6G7sVXhZg=="], + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.8", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/credential-provider-env": "^3.972.8", "@aws-sdk/credential-provider-http": "^3.972.10", "@aws-sdk/credential-provider-login": "^3.972.8", "@aws-sdk/credential-provider-process": "^3.972.8", "@aws-sdk/credential-provider-sso": "^3.972.8", "@aws-sdk/credential-provider-web-identity": "^3.972.8", "@aws-sdk/nested-clients": "3.990.0", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-n2dMn21gvbBIEh00E8Nb+j01U/9rSqFIamWRdGm/mE5e+vHQ9g0cBNdrYFlM6AAiryKVHZmShWT9D1JAWJ3ISw=="], - "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/nested-clients": "3.985.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-uYq1ILyTSI6ZDCMY5+vUsRM0SOCVI7kaW4wBrehVVkhAxC6y+e9rvGtnoZqCOWL1gKjTMouvsf4Ilhc5NCg1Aw=="], + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.8", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/nested-clients": "3.990.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-rMFuVids8ICge/X9DF5pRdGMIvkVhDV9IQFQ8aTYk6iF0rl9jOUa1C3kjepxiXUlpgJQT++sLZkT9n0TMLHhQw=="], - "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.6", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.5", "@aws-sdk/credential-provider-http": "^3.972.7", "@aws-sdk/credential-provider-ini": "^3.972.5", "@aws-sdk/credential-provider-process": "^3.972.5", "@aws-sdk/credential-provider-sso": "^3.972.5", "@aws-sdk/credential-provider-web-identity": "^3.972.5", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DZ3CnAAtSVtVz+G+ogqecaErMLgzph4JH5nYbHoBMgBkwTUV+SUcjsjOJwdBJTHu3Dm6l5LBYekZoU2nDqQk2A=="], + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.9", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.8", "@aws-sdk/credential-provider-http": "^3.972.10", "@aws-sdk/credential-provider-ini": "^3.972.8", "@aws-sdk/credential-provider-process": "^3.972.8", "@aws-sdk/credential-provider-sso": "^3.972.8", "@aws-sdk/credential-provider-web-identity": "^3.972.8", "@aws-sdk/types": "^3.973.1", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-LfJfO0ClRAq2WsSnA9JuUsNyIicD2eyputxSlSL0EiMrtxOxELLRG6ZVYDf/a1HCepaYPXeakH4y8D5OLCauag=="], - "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-HDKF3mVbLnuqGg6dMnzBf1VUOywE12/N286msI9YaK9mEIzdsGCtLTvrDhe3Up0R9/hGFbB+9l21/TwF5L1C6g=="], + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.8", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-6cg26ffFltxM51OOS8NH7oE41EccaYiNlbd5VgUYwhiGCySLfHoGuGrLm2rMB4zhy+IO5nWIIG0HiodX8zdvHA=="], - "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.5", "", { "dependencies": { "@aws-sdk/client-sso": "3.985.0", "@aws-sdk/core": "^3.973.7", "@aws-sdk/token-providers": "3.985.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8urj3AoeNeQisjMmMBhFeiY2gxt6/7wQQbEGun0YV/OaOOiXrIudTIEYF8ZfD+NQI6X1FY5AkRsx6O/CaGiybA=="], + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.8", "", { "dependencies": { "@aws-sdk/client-sso": "3.990.0", "@aws-sdk/core": "^3.973.10", "@aws-sdk/token-providers": "3.990.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-35kqmFOVU1n26SNv+U37sM8b2TzG8LyqAcd6iM9gprqxyHEh/8IM3gzN4Jzufs3qM6IrH8e43ryZWYdvfVzzKQ=="], - "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.5", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/nested-clients": "3.985.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-OK3cULuJl6c+RcDZfPpaK5o3deTOnKZbxm7pzhFNGA3fI2hF9yDih17fGRazJzGGWaDVlR9ejZrpDef4DJCEsw=="], + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.8", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/nested-clients": "3.990.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-CZhN1bOc1J3ubQPqbmr5b4KaMJBgdDvYsmEIZuX++wFlzmZsKj1bwkaiTEb5U2V7kXuzLlpF5HJSOM9eY/6nGA=="], "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-arn-parser": "^3.972.2", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg=="], "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg=="], - "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.972.5", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.7", "@aws-sdk/crc64-nvme": "3.972.0", "@aws-sdk/types": "^3.973.1", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.11", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-SF/1MYWx67OyCrLA4icIpWUfCkdlOi8Y1KecQ9xYxkL10GMjVdPTGPnYhAg0dw5U43Y9PVUWhAV2ezOaG+0BLg=="], + "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.972.8", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.10", "@aws-sdk/crc64-nvme": "3.972.0", "@aws-sdk/types": "^3.973.1", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.12", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Hn6gumcN/3/8Fzo9z7N1pA2PRfE8S+qAqdb4g3MqzXjIOIe+VxD7edO/DKAJ1YH11639EGQIHBz0wdOb5btjtw=="], "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA=="], @@ -959,27 +972,27 @@ "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q=="], - "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.7", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-arn-parser": "^3.972.2", "@smithy/core": "^3.22.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.11", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-VtZ7tMIw18VzjG+I6D6rh2eLkJfTtByiFoCIauGDtTTPBEUMQUiGaJ/zZrPlCY6BsvLLeFKz3+E5mntgiOWmIg=="], + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.10", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-arn-parser": "^3.972.2", "@smithy/core": "^3.23.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.12", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wLkB4bshbBtsAiC2WwlHzOWXu1fx3ftL63fQl0DxEda48Q6B8bcHydZppE3KjEIpPyiNOllByfSnb07cYpIgmw=="], "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.7", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.985.0", "@smithy/core": "^3.22.1", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-HUD+geASjXSCyL/DHPQc/Ua7JhldTcIglVAoCV8kiVm99IaFSlAbTvEnyhZwdE6bdFyTL+uIaWLaCFSRsglZBQ=="], + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.10", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.990.0", "@smithy/core": "^3.23.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-bBEL8CAqPQkI91ZM5a9xnFAzedpzH6NYCOtNyLarRAzTUTFN2DKqaC60ugBa7pnU1jSi4mA7WAXBsrod7nJltg=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.985.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.7", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.7", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.985.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.5", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.22.1", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.13", "@smithy/middleware-retry": "^4.4.30", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.9", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.29", "@smithy/util-defaults-mode-node": "^4.2.32", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-TsWwKzb/2WHafAY0CE7uXgLj0FmnkBTgfioG9HO+7z/zCPcl1+YU+i7dW4o0y+aFxFgxTMG+ExBQpqT/k2ao8g=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.990.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.10", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.990.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.8", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.14", "@smithy/middleware-retry": "^4.4.31", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.30", "@smithy/util-defaults-mode-node": "^4.2.33", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-3NA0s66vsy8g7hPh36ZsUgO4SiMyrhwcYvuuNK1PezO52vX3hXDW4pQrC6OQLGKGJV0o6tbEyQtXb/mPs8zg8w=="], "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow=="], - "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.986.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.986.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-format-url": "^3.972.3", "@smithy/middleware-endpoint": "^4.4.13", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-+yopxtoXwRXZ2Ai9H4GzkN+T2D07sGrURYcm7Eh2OQe3p+Ys/3VrR6UrzILssaJGYtR2vQqVKnGJBHVYqaM1EQ=="], + "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.990.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.990.0", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-format-url": "^3.972.3", "@smithy/middleware-endpoint": "^4.4.14", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-cex+QPepH6a2gPBERvOnbPxxLeAtp1P1smnCVzxZ236YBy5XeIBMGySknrG7WZoCu9CqiXv1sfI/+sJPcM7QdQ=="], - "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.986.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.7", "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Upw+rw7wCH93E6QWxqpAqJLrUmJYVUAWrk4tCOBnkeuwzGERZvJFL5UQ6TAJFj9T18Ih+vNFaACh8J5aP4oTBw=="], + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.990.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.10", "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-O55s1eFmKi+2Ko5T1hbdxL6tFVONGscSVe9VRxS4m91Tlbo9iG2Q2HvKWq1DuKQAuUWSUfMmjrRt07JNzizr2A=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.985.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.7", "@aws-sdk/nested-clients": "3.985.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-+hwpHZyEq8k+9JL2PkE60V93v2kNhUIv7STFt+EAez1UJsJOQDhc5LpzEX66pNjclI5OTwBROs/DhJjC/BtMjQ=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.990.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.10", "@aws-sdk/nested-clients": "3.990.0", "@aws-sdk/types": "^3.973.1", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-L3BtUb2v9XmYgQdfGBzbBtKMXaP5fV973y3Qdxeevs6oUTVXFmi/mV1+LnScA/1wVPJC9/hlK+1o5vbt7cG7EQ=="], "@aws-sdk/types": ["@aws-sdk/types@3.973.1", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg=="], "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.986.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-Mqi79L38qi1gCG3adlVdbNrSxvcm1IPDLiJPA3OBypY5ewxUyWbaA3DD4goG+EwET6LSFgZJcRSIh6KBNpP5pA=="], + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.990.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-kVwtDc9LNI3tQZHEMNbkLIOpeDK8sRSTuT8eMnzGY+O+JImPisfSTjdh+jw9OTznu+MYZjQsv0258sazVKunYg=="], "@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-n7F2ycckcKFXa01vAsT/SJdjFHfKH9s96QHcs5gn8AaaigASICeME8WdUL9uBp8XV/OVwEt8+6gzn6KFUgQa8g=="], @@ -987,7 +1000,7 @@ "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.3", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw=="], - "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.5", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.7", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-GsUDF+rXyxDZkkJxUsDxnA67FG+kc5W1dnloCFLl6fWzceevsCYzJpASBzT+BPjwUgREE6FngfJYYYMQUY5fZQ=="], + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.8", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.10", "@aws-sdk/types": "^3.973.1", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-XJZuT0LWsFCW1C8dEpPAXSa7h6Pb3krr2y//1X0Zidpcl0vmgY5nL/X0JuBZlntpBzaN3+U4hvKjuijyiiR8zw=="], "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.4", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.3.4", "tslib": "^2.6.2" } }, "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q=="], @@ -997,21 +1010,21 @@ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.12.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260115.0" }, "optionalPeers": ["workerd"] }, "sha512-NK4vN+2Z/GbfGS4BamtbbVk1rcu5RmqaYGiyHJQrA09AoxdZPHDF3W/EhgI0YSK8p3vRo/VNCtbSJFPON7FWMQ=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.12.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260115.0" }, "optionalPeers": ["workerd"] }, "sha512-tP/Wi+40aBJovonSNJSsS7aFJY0xjuckKplmzDs2Xat06BJ68B6iG7YDUWXJL8gNn0gqW7YC5WhlYhO3QbugQA=="], - "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.23.1", "", { "dependencies": { "@cloudflare/unenv-preset": "2.12.0", "miniflare": "4.20260205.0", "unenv": "2.0.0-rc.24", "wrangler": "4.63.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-TnE2+U0xM8QWQBC5SlthtIPyit9j6RD7YB0I61jRj28fU4beBH3zYoNXcmHjnhSVU6Y//gIg2xrGV4jXIvdwXw=="], + "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.25.0", "", { "dependencies": { "@cloudflare/unenv-preset": "2.12.1", "miniflare": "4.20260212.0", "unenv": "2.0.0-rc.24", "wrangler": "4.65.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-IZV17IekBxc7dcu8TZw5I8DTQ+F6CT8f/rUwLYrQZdsPnfaqbPoc8t9/2V9ci+XNfgGRBNJxd/4YGuyJg+h1pQ=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260205.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ToOItqcirmWPwR+PtT+Q4bdjTn/63ZxhJKEfW4FNn7FxMTS1Tw5dml0T0mieOZbCpcvY8BdvPKFCSlJuI8IVHQ=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260212.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-kLxuYutk88Wlo7edp8mlkN68TgZZ9237SUnuX9kNaD5jcOdblUqiBctMRZeRcPsuoX/3g2t0vS4ga02NBEVRNg=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260205.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-402ZqLz+LrG0NDXp7Hn7IZbI0DyhjNfjAlVenb0K3yod9KCuux0u3NksNBvqJx0mIGHvVR4K05h+jfT5BTHqGA=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260212.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fqoqQWMA1D0ZzDOD8sp0allREM2M8GHdpxMXQ8EdZpZ70z5bJbJ9Vr4qe35++FNIZJspsDHfTw3Xm/M4ELm/dQ=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260205.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rz9jBzazIA18RHY+osa19hvsPfr0LZI1AJzIjC6UqkKKphcTpHBEQ25Xt8cIA34ivMIqeENpYnnmpDFesLkfcQ=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260212.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bCSQoZzDzV5MSh4ueWo1DgmOn4Hf3QBu4Yo3eQFXA2llYFIu/sZgRtkEehw1X2/SY5Sn6O0EMCqxJYRf82Wdeg=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260205.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jr6cKpMM/DBEbL+ATJ9rYue758CKp0SfA/nXt5vR32iINVJrb396ye9iat2y9Moa/PgPKnTrFgmT6urUmG3IUg=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260212.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GPvp1iiKQodtbUDi6OmR5I0vD75lawB54tdYGtmypuHC7ZOI2WhBmhb3wCxgnQNOG1z7mhCQrzRCoqrKwYbVWQ=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260205.0", "", { "os": "win32", "cpu": "x64" }, "sha512-SMPW5jCZYOG7XFIglSlsgN8ivcl0pCrSAYxCwxtWvZ88whhcDB/aISNtiQiDZujPH8tIo2hE5dEkxW7tGEwc3A=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260212.0", "", { "os": "win32", "cpu": "x64" }, "sha512-wHRI218Xn4ndgWJCUHH4Zx0YlU5q/o6OmcxXkcw95tJOsQn4lDrhppioPh4eScxJZALf2X+ODeZcyQTCq5exGw=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260207.0", "", {}, "sha512-PSxgnAOK0EtTytlY7/+gJcsQJYg0Qo7KlOMSC/wiBE+pBqKjuKdd1ZgM+NvpPNqZAjWV5jqAMTTNYEmgk27gYw=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260214.0", "", {}, "sha512-qb8rgbAdJR4BAPXolXhFL/wuGtecHLh1veOyZ1mK6QqWuCdI3vK1biKC0i3lzmzdLR/DZvsN3mNtpUE8zpWGEg=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], @@ -1165,10 +1178,6 @@ "@ioredis/commands": ["@ioredis/commands@1.5.0", "", {}, "sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow=="], - "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], - - "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.1", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ=="], - "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="], @@ -1203,7 +1212,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.137.1", "https://npm.jsr.io/~/11/@jsr/deco__deco/1.137.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-/euR/jwlSbycVpHP7W1Zp8urWmTxz2GU1/Mput+rzTYpK62Z7EBINkg4GDzOBcZk2y+KzCNweNLvawHWZ2Am+w=="], + "@jsr/deco__deco": ["@jsr/deco__deco@1.139.0", "https://npm.jsr.io/~/11/@jsr/deco__deco/1.139.0.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-9Baf5UY0EPZ1dzlxgjZX4ZySVG5F2uMX1kfN9YBFERY1KEwTqsQ+HEDFx7plcKDuYiPrcZEq0d0XeevYOjT1zA=="], "@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=="], @@ -1211,7 +1220,7 @@ "@jsr/deco__inspect-vscode": ["@jsr/deco__inspect-vscode@0.2.1", "https://npm.jsr.io/~/11/@jsr/deco__inspect-vscode/0.2.1.tgz", { "dependencies": { "@jsr/std__path": "^1.0.2", "fuse.js": "7.0.0" } }, "sha512-cl5GguNRHVkHAlvBHP2LKWU3kX+WtSG5vKymLDU7w72whdTi+0LyDPecshneLmYG6nvQSeYxL+OeYg4Ln1LE+A=="], - "@jsr/deco__warp": ["@jsr/deco__warp@0.3.10", "https://npm.jsr.io/~/11/@jsr/deco__warp/0.3.10.tgz", {}, "sha512-nNgKFI+Wyflsuc4cBOxMgz2vEhJifRgKUojpkK3B375Jrkc9j00omWeUoUI10YwEzZKh5ZJBlKU8sa4q4xVTEQ=="], + "@jsr/deco__warp": ["@jsr/deco__warp@0.3.12", "https://npm.jsr.io/~/11/@jsr/deco__warp/0.3.12.tgz", {}, "sha512-Q5Z8pw0y5dSPsvg4iC2U6ndMVyBMTHeDfawu9WeOBBUpwdCKMQ4+07LcoUiZ90XEtkK4GxZpNbBLVZY1mV8TRg=="], "@jsr/deno__cache-dir": ["@jsr/deno__cache-dir@0.10.1", "https://npm.jsr.io/~/11/@jsr/deno__cache-dir/0.10.1.tgz", { "dependencies": { "@jsr/deno__graph": "^0.73.1", "@jsr/std__fmt": "^0.223", "@jsr/std__fs": "^0.223", "@jsr/std__io": "^0.223", "@jsr/std__path": "^0.223" } }, "sha512-RBWPI0b+Mc/pYiqIdojn9O4VvGu6qecelYslNerlJSm4heQORATX+EAE/41jaMwg3s1tWLp+gNiWvQgoy/4TRQ=="], @@ -1339,9 +1348,9 @@ "@opentelemetry/auto-instrumentations-node": ["@opentelemetry/auto-instrumentations-node@0.62.2", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/instrumentation-amqplib": "^0.50.0", "@opentelemetry/instrumentation-aws-lambda": "^0.54.1", "@opentelemetry/instrumentation-aws-sdk": "^0.58.0", "@opentelemetry/instrumentation-bunyan": "^0.49.0", "@opentelemetry/instrumentation-cassandra-driver": "^0.49.0", "@opentelemetry/instrumentation-connect": "^0.47.0", "@opentelemetry/instrumentation-cucumber": "^0.19.0", "@opentelemetry/instrumentation-dataloader": "^0.21.1", "@opentelemetry/instrumentation-dns": "^0.47.0", "@opentelemetry/instrumentation-express": "^0.52.0", "@opentelemetry/instrumentation-fastify": "^0.48.0", "@opentelemetry/instrumentation-fs": "^0.23.0", "@opentelemetry/instrumentation-generic-pool": "^0.47.0", "@opentelemetry/instrumentation-graphql": "^0.51.0", "@opentelemetry/instrumentation-grpc": "^0.203.0", "@opentelemetry/instrumentation-hapi": "^0.50.0", "@opentelemetry/instrumentation-http": "^0.203.0", "@opentelemetry/instrumentation-ioredis": "^0.51.0", "@opentelemetry/instrumentation-kafkajs": "^0.13.0", "@opentelemetry/instrumentation-knex": "^0.48.0", "@opentelemetry/instrumentation-koa": "^0.51.0", "@opentelemetry/instrumentation-lru-memoizer": "^0.48.0", "@opentelemetry/instrumentation-memcached": "^0.47.0", "@opentelemetry/instrumentation-mongodb": "^0.56.0", "@opentelemetry/instrumentation-mongoose": "^0.50.0", "@opentelemetry/instrumentation-mysql": "^0.49.0", "@opentelemetry/instrumentation-mysql2": "^0.50.0", "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", "@opentelemetry/instrumentation-net": "^0.47.0", "@opentelemetry/instrumentation-oracledb": "^0.29.0", "@opentelemetry/instrumentation-pg": "^0.56.1", "@opentelemetry/instrumentation-pino": "^0.50.1", "@opentelemetry/instrumentation-redis": "^0.52.0", "@opentelemetry/instrumentation-restify": "^0.49.0", "@opentelemetry/instrumentation-router": "^0.48.0", "@opentelemetry/instrumentation-runtime-node": "^0.17.1", "@opentelemetry/instrumentation-socket.io": "^0.50.0", "@opentelemetry/instrumentation-tedious": "^0.22.0", "@opentelemetry/instrumentation-undici": "^0.14.0", "@opentelemetry/instrumentation-winston": "^0.48.1", "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.3", "@opentelemetry/resource-detector-aws": "^2.3.0", "@opentelemetry/resource-detector-azure": "^0.10.0", "@opentelemetry/resource-detector-container": "^0.7.3", "@opentelemetry/resource-detector-gcp": "^0.37.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-node": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.4.1", "@opentelemetry/core": "^2.0.0" } }, "sha512-Ipe6X7ddrCiRsuewyTU83IvKiSFT4piqmv9z8Ovg1E7v98pdTj1pUE6sDrHV50zl7/ypd+cONBgt+EYSZu4u9Q=="], - "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw=="], + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw=="], - "@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="], + "@opentelemetry/core": ["@opentelemetry/core@2.5.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA=="], "@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.203.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/sdk-logs": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw=="], @@ -1471,17 +1480,17 @@ "@opentelemetry/resource-detector-gcp": ["@opentelemetry/resource-detector-gcp@0.37.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0", "gcp-metadata": "^6.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-LGpJBECIMsVKhiulb4nxUw++m1oF4EiDDPmFGW2aqYaAF0oUvJNv8Z/55CAzcZ7SxvlTgUwzewXDBsuCup7iqw=="], - "@opentelemetry/resources": ["@opentelemetry/resources@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g=="], + "@opentelemetry/resources": ["@opentelemetry/resources@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ=="], "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw=="], - "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA=="], + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-RKMn3QKi8nE71ULUo0g/MBvq1N4icEBo7cQSKnL3URZT16/YH3nSVgWegOjwx7FRBTrjOIkMJkCUn/ZFIEfn4A=="], "@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0", "@opentelemetry/exporter-logs-otlp-http": "0.203.0", "@opentelemetry/exporter-logs-otlp-proto": "0.203.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0", "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0", "@opentelemetry/exporter-prometheus": "0.203.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0", "@opentelemetry/exporter-trace-otlp-http": "0.203.0", "@opentelemetry/exporter-trace-otlp-proto": "0.203.0", "@opentelemetry/exporter-zipkin": "2.0.1", "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/propagator-b3": "2.0.1", "@opentelemetry/propagator-jaeger": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ=="], - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ=="], + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw=="], - "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.5.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.5.0", "@opentelemetry/core": "2.5.0", "@opentelemetry/sdk-trace-base": "2.5.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-O6N/ejzburFm2C84aKNrwJVPpt6HSTSq8T0ZUMq3xT2XmqT4cwxUItcL5UWGThYuq8RTcbH8u1sfj6dmRci0Ow=="], + "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.5.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.5.1", "@opentelemetry/core": "2.5.1", "@opentelemetry/sdk-trace-base": "2.5.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-9lopQ6ZoElETOEN0csgmtEV5/9C7BMfA7VtF4Jape3i954b6sTY2k3Xw3CxUTKreDck/vpAuJM+EDo4zheUw+A=="], "@opentelemetry/sdk-trace-web": ["@opentelemetry/sdk-trace-web@1.25.1", "", { "dependencies": { "@opentelemetry/core": "1.25.1", "@opentelemetry/sdk-trace-base": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SS6JaSkHngcBCNdWGthzcvaKGRnDw2AeP57HyTEileLToJ7WLMeV+064iRlVyoT4+e77MRp2T2dDSrmaUyxoNg=="], @@ -1505,21 +1514,43 @@ "@oxfmt/win32-x64": ["@oxfmt/win32-x64@0.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-77OiFJ9lpc7ICmHMSN+belxHPDMOu9U7N/LEp40YuC219QWClt6E5Ved6GwNV5bsDCTxTrpH1/3LhxBNKC66Xg=="], - "@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.43.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C/GhObv/pQZg34NOzB6Mk8x0wc9AKj8fXzJF8ZRKTsBPyHusC6AZ6bba0QG0TUufw1KWuD0j++oebQfWeiFXNw=="], + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.47.0", "", { "os": "android", "cpu": "arm" }, "sha512-UHqo3te9K/fh29brCuQdHjN+kfpIi9cnTPABuD5S9wb9ykXYRGTOOMVuSV/CK43sOhU4wwb2nT1RVjcbrrQjFw=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.47.0", "", { "os": "android", "cpu": "arm64" }, "sha512-xh02lsTF1TAkR+SZrRMYHR/xCx8Wg2MAHxJNdHVpAKELh9/yE9h4LJeqAOBbIb3YYn8o/D97U9VmkvkfJfrHfw=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.47.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OSOfNJqabOYbkyQDGT5pdoL+05qgyrmlQrvtCO58M4iKGEQ/xf3XkkKj7ws+hO+k8Y4VF4zGlBsJlwqy7qBcHA=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.47.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-hP2bOI4IWNS+F6pVXWtRshSTuJ1qCRZgDgVUg6EBUqsRy+ExkEPJkx+YmIuxgdCduYK1LKptLNFuQLJP8voPbQ=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.47.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-F55jIEH5xmGu7S661Uho8vGiLFk0bY3A/g4J8CTKiLJnYu/PSMZ2WxFoy5Hji6qvFuujrrM9Q8XXbMO0fKOYPg=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.47.0", "", { "os": "linux", "cpu": "arm" }, "sha512-wxmOn/wns/WKPXUC1fo5mu9pMZPVOu8hsynaVDrgmmXMdHKS7on6bA5cPauFFN9tJXNdsjW26AK9lpfu3IfHBQ=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.47.0", "", { "os": "linux", "cpu": "arm" }, "sha512-KJTmVIA/GqRlM2K+ZROH30VMdydEU7bDTY35fNg3tOPzQRIs2deLZlY/9JWwdWo1F/9mIYmpbdCmPqtKhWNOPg=="], - "@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.43.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-4NjfUtEEH8ewRQ2KlZGmm6DyrvypMdHwBnQT92vD0dLScNOQzr0V9O8Ua4IWXdeCNl/XMVhAV3h4/3YEYern5A=="], + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.47.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-PF7ELcFg1GVlS0X0ZB6aWiXobjLrAKer3T8YEkwIoO8RwWiAMkL3n3gbleg895BuZkHVlJ2kPRUwfrhHrVkD1A=="], - "@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-75tf1HvwdZ3ebk83yMbSB+moAEWK98mYqpXiaFAi6Zshie7r+Cx5PLXZFUEqkscenoZ+fcNXakHxfn94V6nf1g=="], + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.47.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4BezLRO5cu0asf0Jp1gkrnn2OHiXrPPPEfBTxq1k5/yJ2zdGGTmZxHD2KF2voR23wb8Elyu3iQawXo7wvIZq0Q=="], - "@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BHV4fb36T2p/7bpA9fiJ5ayt7oJbiYX10nklW5arYp4l9/9yG/FQC5J4G1evzbJ/YbipF9UH0vYBAm5xbqGrvw=="], + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.47.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-aI5ds9jq2CPDOvjeapiIj48T/vlWp+f4prkxs+FVzrmVN9BWIj0eqeJ/hV8WgXg79HVMIz9PU6deI2ki09bR1w=="], - "@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1l3nvnzWWse1YHibzZ4HQXdF/ibfbKZhp9IguElni3bBqEyPEyurzZ0ikWynDxKGXqZa+UNXTFuU1NRVX1RJ3g=="], + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.47.0", "", { "os": "linux", "cpu": "none" }, "sha512-mO7ycp9Elvgt5EdGkQHCwJA6878xvo9tk+vlMfT1qg++UjvOMB8INsOCQIOH2IKErF/8/P21LULkdIrocMw9xA=="], - "@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+jNYgLGRFTJxJuaSOZJBwlYo5M0TWRw0+3y5MHOL4ArrIdHyCthg6r4RbVWrsR1qUfUE1VSSHQ2bfbC99RXqMg=="], + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.47.0", "", { "os": "linux", "cpu": "none" }, "sha512-24D0wsYT/7hDFn3Ow32m3/+QT/1ZwrUhShx4/wRDAmz11GQHOZ1k+/HBuK/MflebdnalmXWITcPEy4BWTi7TCA=="], - "@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.43.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-dvs1C/HCjCyGTURMagiHprsOvVTT3omDiSzi5Qw0D4QFJ1pEaNlfBhVnOUYgUfS6O7Mcmj4+G+sidRsQcWQ/kA=="], + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.47.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-8tPzPne882mtML/uy3mApvdCyuVOpthJ7xUv3b67gVfz63hOOM/bwO0cysSkPyYYFDFRn6/FnUb7Jhmsesntvg=="], - "@oxlint/win32-x64": ["@oxlint/win32-x64@1.43.0", "", { "os": "win32", "cpu": "x64" }, "sha512-bSuItSU8mTSDsvmmLTepTdCL2FkJI6dwt9tot/k0EmiYF+ArRzmsl4lXVLssJNRV5lJEc5IViyTrh7oiwrjUqA=="], + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.47.0", "", { "os": "linux", "cpu": "x64" }, "sha512-q58pIyGIzeffEBhEgbRxLFHmHfV9m7g1RnkLiahQuEvyjKNiJcvdHOwKH2BdgZxdzc99Cs6hF5xTa86X40WzPw=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.47.0", "", { "os": "linux", "cpu": "x64" }, "sha512-e7DiLZtETZUCwTa4EEHg9G+7g3pY+afCWXvSeMG7m0TQ29UHHxMARPaEQUE4mfKgSqIWnJaUk2iZzRPMRdga5g=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.47.0", "", { "os": "none", "cpu": "arm64" }, "sha512-3AFPfQ0WKMleT/bKd7zsks3xoawtZA6E/wKf0DjwysH7wUiMMJkNKXOzYq1R/00G98JFgSU1AkrlOQrSdNNhlg=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.47.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-cLMVVM6TBxp+N7FldQJ2GQnkcLYEPGgiuEaXdvhgvSgODBk9ov3jed+khIXSAWtnFOW0wOnG3RjwqPh0rCuheA=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.47.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-VpFOSzvTnld77/Edje3ZdHgZWnlTb5nVWXyTgjD3/DKF/6t5bRRbwn3z77zOdnGy44xAMvbyAwDNOSeOdVUmRA=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.47.0", "", { "os": "win32", "cpu": "x64" }, "sha512-+q8IWptxXx2HMTM6JluR67284t0h8X/oHJgqpxH1siowxPMqZeIpAcWCUq+tY+Rv2iQK8TUugjZnSBQAVV5CmA=="], "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], @@ -1617,9 +1648,9 @@ "@slack/logger": ["@slack/logger@4.0.0", "", { "dependencies": { "@types/node": ">=18.0.0" } }, "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA=="], - "@slack/types": ["@slack/types@2.19.0", "", {}, "sha512-7+QZ38HGcNh/b/7MpvPG6jnw7mliV6UmrquJLqgdxkzJgQEYUcEztvFWRU49z0x4vthF0ixL5lTK601AXrS8IA=="], + "@slack/types": ["@slack/types@2.20.0", "", {}, "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA=="], - "@slack/web-api": ["@slack/web-api@7.13.0", "", { "dependencies": { "@slack/logger": "^4.0.0", "@slack/types": "^2.18.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", "axios": "^1.11.0", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-ERcExbWrnkDN8ovoWWe6Wgt/usanj1dWUd18dJLpctUI4mlPS0nKt81Joh8VI+OPbNnY1lIilVt9gdMBD9U2ig=="], + "@slack/web-api": ["@slack/web-api@7.14.1", "", { "dependencies": { "@slack/logger": "^4.0.0", "@slack/types": "^2.20.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", "axios": "^1.13.5", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng=="], "@smithy/abort-controller": ["@smithy/abort-controller@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw=="], @@ -1629,7 +1660,7 @@ "@smithy/config-resolver": ["@smithy/config-resolver@4.4.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ=="], - "@smithy/core": ["@smithy/core@3.22.1", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.11", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g=="], + "@smithy/core": ["@smithy/core@3.23.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.12", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Yq4UPVoQICM9zHnByLmG8632t2M0+yap4T7ANVw482J0W7HW0pOuxwVmeOwzJqX2Q89fkXz0Vybz55Wj2Xzrsg=="], "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw=="], @@ -1659,9 +1690,9 @@ "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.13", "", { "dependencies": { "@smithy/core": "^3.22.1", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.14", "", { "dependencies": { "@smithy/core": "^3.23.0", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-FUFNE5KVeaY6U/GL0nzAAHkaCHzXLZcY1EhtQnsAqhD8Du13oPKtMB9/0WK4/LK6a/T5OZ24wPoSShff5iI6Ag=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.30", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.31", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-RXBzLpMkIrxBPe4C8OmEOHvS8aH9RUuCOH++Acb5jZDEblxDjyg6un72X9IcbrGTJoiUwmI7hLypNfuDACypbg=="], "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.9", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="], @@ -1669,7 +1700,7 @@ "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.8", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.9", "", { "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.10", "", { "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA=="], "@smithy/property-provider": ["@smithy/property-provider@4.2.8", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w=="], @@ -1685,7 +1716,7 @@ "@smithy/signature-v4": ["@smithy/signature-v4@5.3.8", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.11.2", "", { "dependencies": { "@smithy/core": "^3.22.1", "@smithy/middleware-endpoint": "^4.4.13", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.11", "tslib": "^2.6.2" } }, "sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.11.3", "", { "dependencies": { "@smithy/core": "^3.23.0", "@smithy/middleware-endpoint": "^4.4.14", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.12", "tslib": "^2.6.2" } }, "sha512-Q7kY5sDau8OoE6Y9zJoRGgje8P4/UY0WzH8R2ok0PDh+iJ+ZnEKowhjEqYafVcubkbYxQVaqwm3iufktzhprGg=="], "@smithy/types": ["@smithy/types@4.12.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="], @@ -1701,9 +1732,9 @@ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.29", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.30", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-cMni0uVU27zxOiU8TuC8pQLC1pYeZ/xEMxvchSK/ILwleRd1ugobOcIRr5vXtcRqKd4aBLWlpeBoDPJJ91LQng=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.32", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.2", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.33", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-LEb2aq5F4oZUSzWBG7S53d4UytZSkOEJPXcBq/xbG2/TmK9EW5naUZ8lKu1BEyWMzdHIzEVN16M3k8oxDq+DJA=="], "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw=="], @@ -1713,7 +1744,7 @@ "@smithy/util-retry": ["@smithy/util-retry@4.2.8", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg=="], - "@smithy/util-stream": ["@smithy/util-stream@4.5.11", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.9", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA=="], + "@smithy/util-stream": ["@smithy/util-stream@4.5.12", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.10", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg=="], "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], @@ -1759,7 +1790,7 @@ "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], - "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], + "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], "@types/bunyan": ["@types/bunyan@1.8.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ=="], @@ -1791,7 +1822,7 @@ "@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="], - "@types/node": ["@types/node@24.10.12", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-68e+T28EbdmLSTkPgs3+UacC6rzmqrcWFPQs1C8mwJhI/r5Uxr0yEuQotczNRROd1gq30NGxee+fo0rSIxpyAw=="], + "@types/node": ["@types/node@24.10.13", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg=="], "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], @@ -1811,7 +1842,7 @@ "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], - "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], + "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], @@ -1843,11 +1874,11 @@ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], - "ai": ["ai@6.0.78", "", { "dependencies": { "@ai-sdk/gateway": "3.0.39", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.14", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-eriIX/NLWfWNDeE/OJy8wmIp9fyaH7gnxTOCPT5bp0MNkvORstp1TwRUql9au8XjXzH7o2WApqbwgxJDDV0Rbw=="], + "ai": ["ai@6.0.86", "", { "dependencies": { "@ai-sdk/gateway": "3.0.46", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.15", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-U2W2LBCHA/pr0Ui7vmmsjBiLEzBbZF3yVHNy7Rbzn7IX+SvoQPFM5rN74hhfVzZoE8zBuGD4nLLk+j0elGacvQ=="], "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@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=="], + "ajv": ["ajv@8.18.0", "", { "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-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -2019,7 +2050,7 @@ "dingbat-to-unicode": ["dingbat-to-unicode@1.0.1", "", {}, "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w=="], - "discord-api-types": ["discord-api-types@0.38.38", "", {}, "sha512-7qcM5IeZrfb+LXW07HvoI5L+j4PQeMZXEkSm1htHAHh4Y9JSMXBWjy/r7zmUCOj4F7zNjMcm7IMWr131MT2h0Q=="], + "discord-api-types": ["discord-api-types@0.38.39", "", {}, "sha512-XRdDQvZvID1XvcFftjSmd4dcmMi/RL/jSy5sduBDAvCGFcNFHThdIQXCEBDZFe52lCNEzuIL0QJoKYAmRmxLUA=="], "discord-read": ["discord-read@workspace:discord-read"], @@ -2237,7 +2268,9 @@ "inquirer-search-list": ["inquirer-search-list@1.2.6", "", { "dependencies": { "chalk": "^2.3.0", "figures": "^2.0.0", "fuzzy": "^0.1.3", "inquirer": "^3.3.0" } }, "sha512-C4pKSW7FOYnkAloH8rB4FiM91H1v08QFZZJh6KRt//bMfdDBIhgdX8wjHvrVH2bu5oIo6wYqGpzSBxkeClPxew=="], - "ioredis": ["ioredis@5.9.2", "", { "dependencies": { "@ioredis/commands": "1.5.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ=="], + "ioredis": ["ioredis@5.9.3", "", { "dependencies": { "@ioredis/commands": "1.5.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA=="], + + "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -2265,7 +2298,7 @@ "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "isbot": ["isbot@5.1.34", "", {}, "sha512-aCMIBSKd/XPRYdiCQTLC8QHH4YT8B3JUADu+7COgYIZPvkeoMcUHMRjZLM9/7V8fCj+l7FSREc1lOPNjzogo/A=="], + "isbot": ["isbot@5.1.35", "", {}, "sha512-waFfC72ZNfwLLuJ2iLaoVaqcNo+CAaLR7xCpAn0Y5WfGzkNHv7ZN39Vbi1y+kb+Zs46XHOX3tZNExroFUPX+Kg=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -2329,7 +2362,7 @@ "lop": ["lop@0.4.2", "", { "dependencies": { "duck": "^0.1.12", "option": "~0.2.1", "underscore": "^1.13.1" } }, "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw=="], - "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], + "lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], "magic-bytes.js": ["magic-bytes.js@1.13.0", "", {}, "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg=="], @@ -2339,6 +2372,8 @@ "mcp-studio": ["mcp-studio@workspace:mcp-studio"], + "mcp-task-runner": ["mcp-task-runner@workspace:task-runner"], + "mcp-template-minimal": ["mcp-template-minimal@workspace:template-minimal"], "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], @@ -2361,7 +2396,7 @@ "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "miniflare": ["miniflare@4.20260205.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260205.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-jG1TknEDeFqcq/z5gsOm1rKeg4cNG7ruWxEuiPxl3pnQumavxo8kFpeQC6XKVpAhh2PI9ODGyIYlgd77sTHl5g=="], + "miniflare": ["miniflare@4.20260212.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260212.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Lgxq83EuR2q/0/DAVOSGXhXS1V7GDB04HVggoPsenQng8sqEDR3hO4FigIw5ZI2Sv2X7kIc30NCzGHJlCFIYWg=="], "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -2429,7 +2464,7 @@ "oxfmt": ["oxfmt@0.9.0", "", { "optionalDependencies": { "@oxfmt/darwin-arm64": "0.9.0", "@oxfmt/darwin-x64": "0.9.0", "@oxfmt/linux-arm64-gnu": "0.9.0", "@oxfmt/linux-arm64-musl": "0.9.0", "@oxfmt/linux-x64-gnu": "0.9.0", "@oxfmt/linux-x64-musl": "0.9.0", "@oxfmt/win32-arm64": "0.9.0", "@oxfmt/win32-x64": "0.9.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-RVMw8kqZjCDCFxBZyDK4VW8DHxmSHV0pRky7LoLq9JL3ge4kelT0UB8GS0nVTZIteqOJ9rfwPxSZRUVXSX/n0w=="], - "oxlint": ["oxlint@1.43.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.43.0", "@oxlint/darwin-x64": "1.43.0", "@oxlint/linux-arm64-gnu": "1.43.0", "@oxlint/linux-arm64-musl": "1.43.0", "@oxlint/linux-x64-gnu": "1.43.0", "@oxlint/linux-x64-musl": "1.43.0", "@oxlint/win32-arm64": "1.43.0", "@oxlint/win32-x64": "1.43.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.11.2" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-xiqTCsKZch+R61DPCjyqUVP2MhkQlRRYxLRBeBDi+dtQJ90MOgdcjIktvDCgXz0bgtx94EQzHEndsizZjMX2OA=="], + "oxlint": ["oxlint@1.47.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.47.0", "@oxlint/binding-android-arm64": "1.47.0", "@oxlint/binding-darwin-arm64": "1.47.0", "@oxlint/binding-darwin-x64": "1.47.0", "@oxlint/binding-freebsd-x64": "1.47.0", "@oxlint/binding-linux-arm-gnueabihf": "1.47.0", "@oxlint/binding-linux-arm-musleabihf": "1.47.0", "@oxlint/binding-linux-arm64-gnu": "1.47.0", "@oxlint/binding-linux-arm64-musl": "1.47.0", "@oxlint/binding-linux-ppc64-gnu": "1.47.0", "@oxlint/binding-linux-riscv64-gnu": "1.47.0", "@oxlint/binding-linux-riscv64-musl": "1.47.0", "@oxlint/binding-linux-s390x-gnu": "1.47.0", "@oxlint/binding-linux-x64-gnu": "1.47.0", "@oxlint/binding-linux-x64-musl": "1.47.0", "@oxlint/binding-openharmony-arm64": "1.47.0", "@oxlint/binding-win32-arm64-msvc": "1.47.0", "@oxlint/binding-win32-ia32-msvc": "1.47.0", "@oxlint/binding-win32-x64-msvc": "1.47.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.11.2" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-v7xkK1iv1qdvTxJGclM97QzN8hHs5816AneFAQ0NGji1BMUquhiDAhXpMwp8+ls16uRVJtzVHxP9pAAXblDeGA=="], "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], @@ -2543,7 +2578,7 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="], + "qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -2655,7 +2690,7 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "simple-git": ["simple-git@3.30.0", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" } }, "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg=="], + "simple-git": ["simple-git@3.31.1", "", { "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" } }, "sha512-oiWP4Q9+kO8q9hHqkX35uuHmxiEbZNTrZ5IPxgMGrJwN76pzjm/jabkZO0ItEcqxAincqGAzL3QHSaHt4+knBg=="], "simple-wcswidth": ["simple-wcswidth@1.1.2", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="], @@ -2663,7 +2698,7 @@ "smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="], - "sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="], + "sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="], "sora": ["sora@workspace:sora"], @@ -2819,9 +2854,9 @@ "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], - "workerd": ["workerd@1.20260205.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260205.0", "@cloudflare/workerd-darwin-arm64": "1.20260205.0", "@cloudflare/workerd-linux-64": "1.20260205.0", "@cloudflare/workerd-linux-arm64": "1.20260205.0", "@cloudflare/workerd-windows-64": "1.20260205.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-CcMH5clHwrH8VlY7yWS9C/G/C8g9czIz1yU3akMSP9Z3CkEMFSoC3GGdj5G7Alw/PHEeez1+1IrlYger4pwu+w=="], + "workerd": ["workerd@1.20260212.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260212.0", "@cloudflare/workerd-darwin-arm64": "1.20260212.0", "@cloudflare/workerd-linux-64": "1.20260212.0", "@cloudflare/workerd-linux-arm64": "1.20260212.0", "@cloudflare/workerd-windows-64": "1.20260212.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-4B9BoZUzKSRv3pVZGEPh7OX+Q817hpUqAUtz5O0TxJVqo4OsYJAUA/sY177Q5ha/twjT9KaJt2DtQzE+oyCOzw=="], - "wrangler": ["wrangler@4.63.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.12.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.0", "miniflare": "4.20260205.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260205.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260205.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-+R04jF7Eb8K3KRMSgoXpcIdLb8GC62eoSGusYh1pyrSMm/10E0hbKkd7phMJO4HxXc6R7mOHC5SSoX9eof30Uw=="], + "wrangler": ["wrangler@4.65.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.12.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260212.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260212.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260212.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-R+n3o3tlGzLK9I4fGocPReOuvcnjhtOL2aCVKkHMeuEwt9pPbOO4FxJtx/ec5cIUG/otRyJnfQGCAr9DplBVng=="], "wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], @@ -2833,7 +2868,7 @@ "xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="], - "xstate": ["xstate@5.26.0", "", {}, "sha512-Fvi9VBoqHgsGYLU2NTag8xDTWtKqUC0+ue7EAhBNBb06wf620QEy05upBaEI1VLMzIn63zugLV8nHb69ZUWYAA=="], + "xstate": ["xstate@5.28.0", "", {}, "sha512-Iaqq6ZrUzqeUtA3hC5LQKZfR8ZLzEFTImMHJM3jWEdVvXWdKvvVLXZEiNQWm3SCA9ZbEou/n5rcsna1wb9t28A=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], @@ -2869,7 +2904,7 @@ "@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], - "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="], + "@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-veuMwTLxsgh31Jjn0SnBABnM1f7ebHhRWcV2ZuY3hP3iJDCZ8VXBaYqcHXoOQDqUXTCas08sKQcHyWK+zl882Q=="], "@ai-sdk/google-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], @@ -2915,12 +2950,6 @@ "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - "@aws-sdk/client-sso/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.985.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA=="], - - "@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.985.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA=="], - - "@aws-sdk/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.985.0", "", { "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-vth7UfGSUR3ljvaq8V4Rc62FsM7GUTH/myxPWkaEgOrprz1/Pc72EgTXxj+cPPPDAfHFIpjhkB7T7Td0RJx+BA=="], - "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], "@deco-cx/warp-node/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], @@ -2931,11 +2960,11 @@ "@decocms/mcps-shared/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@decocms/openrouter/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "@decocms/openrouter/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "@decocms/openrouter/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "@decocms/runtime/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3167,11 +3196,7 @@ "@supabase/ssr/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "@ts-morph/common/minimatch": ["minimatch@10.1.2", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.1" } }, "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw=="], - - "@types/bun/bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], - - "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + "@ts-morph/common/minimatch": ["minimatch@10.2.0", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w=="], "ai-v5/@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=="], @@ -3211,7 +3236,7 @@ "deco-cli/@supabase/supabase-js": ["@supabase/supabase-js@2.50.0", "", { "dependencies": { "@supabase/auth-js": "2.70.0", "@supabase/functions-js": "2.4.4", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "1.19.4", "@supabase/realtime-js": "2.11.10", "@supabase/storage-js": "2.7.1" } }, "sha512-M1Gd5tPaaghYZ9OjeO1iORRqbTWFEz/cF3pPubRnMPzA+A8SiUsXXWDP+DWsASZcjEcVEcVQIAF38i5wrijYOg=="], - "deco-llm/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "deco-llm/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "deco-llm/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3221,7 +3246,7 @@ "defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], - "discord-read/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "discord-read/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "discord-read/@decocms/runtime": ["@decocms/runtime@1.2.6", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.25.3", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-Eyp9pWkhlFLU3iQl/dQEULcUnirwcggKo3TEH0l5fLpkYoIDY48lW360T9iNm2Cd2ic1jRO13+rpo5eUTYU39w=="], @@ -3243,14 +3268,12 @@ "gemini-pro-vision/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "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", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="], - "github/@types/node": ["@types/node@22.19.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw=="], + "github/@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], "github/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "github-repo-reports/@decocms/runtime": ["@decocms/runtime@1.2.5", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.1.1", "@modelcontextprotocol/sdk": "1.25.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-0s02lfj/O7nTAc7FTmFsA+lZpUDnapjQHnRYrQXItLKrbJvjSnfoq5V8HA1Npv5HelBvsVk7QQHaW8pSN/l37w=="], - "github-repo-reports/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], - "github-repo-reports/@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="], "github-repo-reports/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3279,7 +3302,7 @@ "google-forms/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "google-gemini/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-gemini/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-gemini/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3309,7 +3332,7 @@ "grain/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "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", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="], - "hyperdx/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "hyperdx/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "inquirer-search-checkbox/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], @@ -3329,12 +3352,16 @@ "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/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "mcp-studio/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "mcp-studio/@decocms/runtime": ["@decocms/runtime@1.2.8", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.25.3", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-AG0nqc7ofe7oGeqQpqoqsmSo2t9bZ+xEVnhHDXKQqwmGgXHwRhfVTzROqUq0rasTGnrnTKKSRVLgsHWUgfCaxA=="], "mcp-studio/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "mcp-task-runner/@decocms/runtime": ["@decocms/runtime@1.2.5", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.1.1", "@modelcontextprotocol/sdk": "1.25.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-0s02lfj/O7nTAc7FTmFsA+lZpUDnapjQHnRYrQXItLKrbJvjSnfoq5V8HA1Npv5HelBvsVk7QQHaW8pSN/l37w=="], + + "mcp-task-runner/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "mcp-template-minimal/@decocms/runtime": ["@decocms/runtime@1.2.5", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.1.1", "@modelcontextprotocol/sdk": "1.25.2", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-0s02lfj/O7nTAc7FTmFsA+lZpUDnapjQHnRYrQXItLKrbJvjSnfoq5V8HA1Npv5HelBvsVk7QQHaW8pSN/l37w=="], "mcp-template-minimal/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3381,9 +3408,9 @@ "reddit/deco-cli": ["deco-cli@0.26.0", "", { "dependencies": { "@deco-cx/warp-node": "0.3.16", "@modelcontextprotocol/sdk": "^1.19.1", "@supabase/ssr": "0.6.1", "@supabase/supabase-js": "2.50.0", "chalk": "^5.3.0", "commander": "^12.0.0", "glob": "^10.3.10", "ignore": "^7.0.5", "inquirer": "^9.2.15", "inquirer-search-checkbox": "^1.0.0", "inquirer-search-list": "^1.2.6", "jose": "^6.0.11", "json-schema-to-typescript": "^15.0.4", "object-hash": "^3.0.0", "prettier": "^3.6.2", "semver": "^7.6.0", "smol-toml": "^1.3.4", "ws": "^8.16.0", "zod": "^3.25.76" }, "bin": { "deco": "dist/cli.js", "deconfig": "dist/deconfig.js" } }, "sha512-fkYKYO81cK3NE4hb3zcPdMksKJiYM2mon0lKGBuvEOruVUfbhK0I7V777NZDrmaxVQXxDx0fa9i6fARjxT7muQ=="], - "registry/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "registry/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], - "registry/@types/node": ["@types/node@22.19.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw=="], + "registry/@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], "registry/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3397,7 +3424,7 @@ "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], - "slack-mcp/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "slack-mcp/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "slack-mcp/@decocms/runtime": ["@decocms/runtime@1.2.8", "", { "dependencies": { "@ai-sdk/provider": "^3.0.0", "@cloudflare/workers-types": "^4.20250617.0", "@decocms/bindings": "^1.0.7", "@modelcontextprotocol/sdk": "1.25.3", "hono": "^4.10.7", "jose": "^6.0.11", "zod": "^4.0.0" } }, "sha512-AG0nqc7ofe7oGeqQpqoqsmSo2t9bZ+xEVnhHDXKQqwmGgXHwRhfVTzROqUq0rasTGnrnTKKSRVLgsHWUgfCaxA=="], @@ -3437,13 +3464,13 @@ "vtex-docs/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "whatsapp-management/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "whatsapp-management/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "whatsapp-management/deco-cli": ["deco-cli@0.28.5", "", { "dependencies": { "@deco-cx/warp-node": "0.3.16", "@modelcontextprotocol/sdk": "1.20.2", "@supabase/ssr": "0.6.1", "@supabase/supabase-js": "2.50.0", "chalk": "^5.3.0", "commander": "^12.0.0", "glob": "^10.3.10", "ignore": "^7.0.5", "inquirer": "^9.2.15", "inquirer-search-checkbox": "^1.0.0", "inquirer-search-list": "^1.2.6", "jose": "^6.0.11", "json-schema-to-typescript": "^15.0.4", "object-hash": "^3.0.0", "prettier": "^3.6.2", "semver": "^7.6.0", "smol-toml": "^1.3.4", "zod": "^3.25.76" }, "bin": { "deco": "dist/cli.js", "deconfig": "dist/deconfig.js" } }, "sha512-DDzOPKrvMhoS6lu9u5nM8bP7LABClh8RKsVa6wHY+I6PUOtjKuk/mAgxJOt1uO9q2Ku9sgPle9FOyE/crM0Iqg=="], "whatsapp-management/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "whatsappagent/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "whatsappagent/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "whatsappagent/deco-cli": ["deco-cli@0.28.5", "", { "dependencies": { "@deco-cx/warp-node": "0.3.16", "@modelcontextprotocol/sdk": "1.20.2", "@supabase/ssr": "0.6.1", "@supabase/supabase-js": "2.50.0", "chalk": "^5.3.0", "commander": "^12.0.0", "glob": "^10.3.10", "ignore": "^7.0.5", "inquirer": "^9.2.15", "inquirer-search-checkbox": "^1.0.0", "inquirer-search-list": "^1.2.6", "jose": "^6.0.11", "json-schema-to-typescript": "^15.0.4", "object-hash": "^3.0.0", "prettier": "^3.6.2", "semver": "^7.6.0", "smol-toml": "^1.3.4", "zod": "^3.25.76" }, "bin": { "deco": "dist/cli.js", "deconfig": "dist/deconfig.js" } }, "sha512-DDzOPKrvMhoS6lu9u5nM8bP7LABClh8RKsVa6wHY+I6PUOtjKuk/mAgxJOt1uO9q2Ku9sgPle9FOyE/crM0Iqg=="], @@ -3453,7 +3480,7 @@ "whisper/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "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", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="], - "wrangler/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], + "wrangler/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -3497,9 +3524,9 @@ "@deco/mcp/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@decocms/openrouter/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "@decocms/openrouter/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -3589,9 +3616,11 @@ "@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=="], + "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="], + "ai-v5/@ai-sdk/gateway/@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], - "apify/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "apify/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "apify/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3599,7 +3628,7 @@ "cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "content-scraper/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "content-scraper/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "content-scraper/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3611,7 +3640,7 @@ "content-scraper/deco-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "data-for-seo/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "data-for-seo/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "data-for-seo/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3633,7 +3662,7 @@ "deco-cli/@supabase/supabase-js/@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], - "deco-llm/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "deco-llm/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "deco-news-weekly-digest/deco-cli/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3643,7 +3672,7 @@ "deco-news-weekly-digest/deco-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "discord-read/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "discord-read/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "discord-read/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3655,7 +3684,7 @@ "gemini-pro-vision/@modelcontextprotocol/sdk/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=="], - "github-repo-reports/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "github-repo-reports/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "github-repo-reports/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3669,49 +3698,49 @@ "github/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "google-apps-script/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-apps-script/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-apps-script/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-big-query/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-big-query/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-big-query/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-calendar/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-calendar/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-calendar/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-docs/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-docs/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-docs/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-drive/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-drive/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-drive/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-forms/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-forms/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-forms/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-gemini/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-gemini/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-gmail/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-gmail/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-gmail/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-meet/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-meet/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-meet/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-sheets/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-sheets/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-sheets/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-slides/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-slides/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-slides/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "google-tag-manager/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "google-tag-manager/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "google-tag-manager/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3725,7 +3754,7 @@ "grain/@modelcontextprotocol/sdk/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=="], - "hyperdx/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "hyperdx/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "hyperdx/@decocms/bindings/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3777,15 +3806,19 @@ "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/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "mcp-studio/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "mcp-studio/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], - "mcp-template-minimal/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "mcp-task-runner/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], + + "mcp-task-runner/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], + + "mcp-template-minimal/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "mcp-template-minimal/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], - "nanobanana/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "nanobanana/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "nanobanana/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3793,7 +3826,7 @@ "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "perplexity/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "perplexity/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "perplexity/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3825,7 +3858,7 @@ "reddit/deco-cli/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "registry/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "registry/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "registry/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], @@ -3837,7 +3870,7 @@ "replicate/@modelcontextprotocol/sdk/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=="], - "slack-mcp/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "slack-mcp/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "slack-mcp/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3849,7 +3882,7 @@ "sora/@modelcontextprotocol/sdk/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=="], - "strapi/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "strapi/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "strapi/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], @@ -3861,7 +3894,7 @@ "veo/@modelcontextprotocol/sdk/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=="], - "virtual-try-on/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.2.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.25.3", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-+4/VOOVERB8UixGKmN0VkLazxeMAahbG0A9xOYTPL+MJIAM30htrLG2aHI2Dm5ASgccAD4bW5RuLqv2PDFZZPA=="], + "virtual-try-on/@decocms/runtime/@decocms/bindings": ["@decocms/bindings@1.3.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.26.0", "@tanstack/react-router": "1.139.7", "react": "^19.2.0", "zod": "^4.0.0", "zod-from-json-schema": "^0.5.2" } }, "sha512-VNgibUQ1pl3ZWdXuwgiSqdBLrHVCD9K528F2Rd4iydUCAspclF9/rEnxB/rfHu409Mv3BT5YWnyJSnJnK4BlFg=="], "virtual-try-on/@decocms/runtime/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "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-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="], @@ -3879,7 +3912,7 @@ "vtex/@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "whatsapp-management/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "whatsapp-management/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "whatsapp-management/deco-cli/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "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", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="], @@ -3887,7 +3920,7 @@ "whatsapp-management/deco-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "whatsappagent/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "whatsappagent/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "whatsappagent/deco-cli/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.20.2", "", { "dependencies": { "ajv": "^6.12.6", "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", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg=="], @@ -3903,57 +3936,57 @@ "whisper/@modelcontextprotocol/sdk/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=="], - "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="], + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], - "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="], + "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], - "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="], + "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], - "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="], + "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], - "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="], + "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], - "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="], + "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], - "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="], + "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], - "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="], + "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], - "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="], + "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], - "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="], + "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], - "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="], + "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], - "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="], + "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], - "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="], + "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], - "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="], + "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], - "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="], + "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], - "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="], + "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="], + "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], - "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="], + "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], - "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="], + "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], - "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="], + "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], - "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="], + "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], - "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="], + "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], - "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="], + "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], - "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="], + "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], - "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="], + "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], - "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="], + "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], "@a2a-js/sdk/express/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -3975,6 +4008,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=="], + "@decocms/openrouter/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "@jsr/deco__codemod-toolkit/ts-morph/@ts-morph/common/mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], "@mastra/schema-compat/ai/@ai-sdk/gateway/@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], @@ -3983,7 +4020,11 @@ "@openrouter/ai-sdk-provider/ai/@ai-sdk/gateway/@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], - "apify/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="], + + "apify/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + + "content-scraper/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "content-scraper/deco-cli/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], @@ -3997,6 +4038,8 @@ "content-scraper/deco-cli/@supabase/supabase-js/@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], + "data-for-seo/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + "datajud/@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=="], "datajud/@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=="], @@ -4027,6 +4070,8 @@ "deco-cli/@supabase/supabase-js/@supabase/realtime-js/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], + "deco-llm/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "deco-news-weekly-digest/deco-cli/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "deco-news-weekly-digest/deco-cli/@supabase/supabase-js/@supabase/auth-js": ["@supabase/auth-js@2.70.0", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg=="], @@ -4039,6 +4084,8 @@ "deco-news-weekly-digest/deco-cli/@supabase/supabase-js/@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], + "discord-read/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "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=="], @@ -4067,7 +4114,7 @@ "gemini-pro-vision/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "github-repo-reports/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "github-repo-reports/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "github-repo-reports/@octokit/rest/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], @@ -4085,32 +4132,36 @@ "github-repo-reports/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], - "google-apps-script/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-apps-script/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + + "google-big-query/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-big-query/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-calendar/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-calendar/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-docs/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-docs/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-drive/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-drive/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-forms/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-forms/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-gemini/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], - "google-gmail/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-gmail/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-meet/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-meet/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-sheets/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-sheets/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-slides/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-slides/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "google-tag-manager/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "google-tag-manager/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "grain/@decocms/runtime/@deco/mcp/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], "grain/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "hyperdx/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "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/inquirer/cli-cursor/restore-cursor": ["restore-cursor@2.0.0", "", { "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q=="], @@ -4131,13 +4182,17 @@ "log-symbols/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - "mcp-template-minimal/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "mcp-studio/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "mcp-task-runner/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + + "mcp-template-minimal/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], - "nanobanana/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "nanobanana/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "ora/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - "perplexity/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "perplexity/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "pinecone/@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=="], @@ -4233,6 +4288,8 @@ "reddit/deco-cli/@supabase/supabase-js/@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], + "registry/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "replicate/@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=="], "replicate/@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=="], @@ -4261,6 +4318,8 @@ "replicate/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "slack-mcp/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "sora/@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=="], "sora/@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=="], @@ -4289,6 +4348,8 @@ "sora/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "strapi/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + "veo/@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=="], "veo/@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=="], @@ -4317,12 +4378,14 @@ "veo/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "virtual-try-on/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.3", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ=="], + "virtual-try-on/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "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.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "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.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], "vtex-docs/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "vtex/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "whatsapp-management/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "whatsapp-management/deco-cli/@modelcontextprotocol/sdk/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=="], "whatsapp-management/deco-cli/@supabase/supabase-js/@supabase/auth-js": ["@supabase/auth-js@2.70.0", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg=="], @@ -4335,6 +4398,8 @@ "whatsapp-management/deco-cli/@supabase/supabase-js/@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], + "whatsappagent/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "whatsappagent/deco-cli/@modelcontextprotocol/sdk/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=="], "whatsappagent/deco-cli/@supabase/supabase-js/@supabase/auth-js": ["@supabase/auth-js@2.70.0", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg=="], @@ -4375,6 +4440,14 @@ "whisper/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "@ts-morph/common/minimatch/brace-expansion/balanced-match/jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], + + "apify/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "content-scraper/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "data-for-seo/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "datajud/@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=="], "datajud/@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=="], @@ -4443,6 +4516,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=="], + "github-repo-reports/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "github-repo-reports/@octokit/rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], "github-repo-reports/@octokit/rest/@octokit/core/@octokit/request/fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], @@ -4453,6 +4528,28 @@ "github-repo-reports/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "google-apps-script/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-big-query/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-calendar/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-docs/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-drive/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-forms/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-gmail/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-meet/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-sheets/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-slides/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "google-tag-manager/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "grain/@decocms/runtime/@deco/mcp/@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "grain/@decocms/runtime/@deco/mcp/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], @@ -4469,6 +4566,14 @@ "inquirer-search-list/inquirer/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "mcp-task-runner/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "mcp-template-minimal/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "nanobanana/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "perplexity/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "pinecone/@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=="], "pinecone/@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=="], @@ -4639,6 +4744,8 @@ "sora/@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=="], + "strapi/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "veo/@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=="], "veo/@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=="], @@ -4673,6 +4780,8 @@ "veo/@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=="], + "virtual-try-on/@decocms/runtime/@decocms/bindings/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "whatsapp-management/deco-cli/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "whatsapp-management/deco-cli/@supabase/supabase-js/@supabase/realtime-js/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], @@ -4715,6 +4824,8 @@ "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=="], + "@ts-morph/common/minimatch/brace-expansion/balanced-match/jackspeak/@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], + "datajud/@decocms/runtime/@mastra/core/@mastra/schema-compat/zod-from-json-schema/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "datajud/@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/local-fs/README.md b/local-fs/README.md new file mode 100644 index 00000000..e1ae7b0d --- /dev/null +++ b/local-fs/README.md @@ -0,0 +1,196 @@ +# @decocms/mcp-local-fs + +Mount any local filesystem path as an MCP server. **Drop-in replacement** for the official MCP filesystem server, with additional MCP Mesh collection bindings. + +## Features + +- 📁 Mount any filesystem path dynamically +- 🔌 **Stdio transport** (default) - works with Claude Desktop, Cursor, and other MCP clients +- 🌐 **HTTP transport** - for MCP Mesh integration +- 🛠️ **Full MCP filesystem compatibility** - same tools as the official server +- 📋 **Collection bindings** for Files and Folders (Mesh-compatible) +- 🔄 **Backward compatible** - supports both official and Mesh tool names +- ⚡ Zero config needed + +## Quick Start + +### Using npx (stdio mode - recommended for Claude Desktop) + +```bash +# Mount current directory +npx @decocms/mcp-local-fs + +# Mount specific path +npx @decocms/mcp-local-fs /path/to/folder + +# Or with --path flag +npx @decocms/mcp-local-fs --path /path/to/folder +``` + +### Claude Desktop Configuration + +Add to your `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "local-fs": { + "command": "npx", + "args": ["@decocms/mcp-local-fs", "/path/to/folder"] + } + } +} +``` + +### Cursor Configuration + +Add to your Cursor MCP settings: + +```json +{ + "mcpServers": { + "local-fs": { + "command": "npx", + "args": ["@decocms/mcp-local-fs", "/path/to/folder"] + } + } +} +``` + +### Serve Mode with deco link (easiest for remote Mesh) + +**One command to expose your local files to Deco Mesh:** + +```bash +# Serve current directory with public URL +cd /path/to/your/project +bun run serve # if installed locally +bunx @decocms/mcp-local-fs serve # via bunx + +# Serve specific folder +bunx @decocms/mcp-local-fs serve /path/to/folder + +# With custom port +bunx @decocms/mcp-local-fs serve --port 8080 +``` + +This will: +1. Start the HTTP server locally +2. Create a public tunnel via `deco link` +3. Display a ready-to-add MCP URL +4. Copy the URL to your clipboard + +Just paste the URL in Deco Mesh > Connections > Add Custom MCP! + +### HTTP Mode (for local Mesh) + +```bash +# Start HTTP server on port 3456 +npx @decocms/mcp-local-fs --http + +# With custom port +npx @decocms/mcp-local-fs --http --port 8080 + +# Mount specific path +npx @decocms/mcp-local-fs --http --path /your/folder +``` + +Then connect using: +- `http://localhost:3456/mcp?path=/your/folder` +- `http://localhost:3456/mcp/your/folder` + +## Adding to MCP Mesh + +Add a new connection with: +- **Transport**: HTTP +- **URL**: `http://localhost:3456/mcp?path=/your/folder` + +Or use the path in URL format: +- **URL**: `http://localhost:3456/mcp/home/user/documents` + +## Available Tools + +### Official MCP Filesystem Tools + +These tools follow the exact same schema as the [official MCP filesystem server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem): + +| Tool | Description | +|------|-------------| +| `read_file` | Read a file (deprecated, use `read_text_file`) | +| `read_text_file` | Read a text file with optional head/tail params | +| `read_media_file` | Read binary/media files as base64 | +| `read_multiple_files` | Read multiple files at once | +| `write_file` | Write content to a file | +| `edit_file` | Search/replace edits with diff preview | +| `create_directory` | Create a directory (with nested support) | +| `list_directory` | List files and directories | +| `list_directory_with_sizes` | List with file sizes | +| `directory_tree` | Recursive tree view as JSON | +| `move_file` | Move or rename files/directories | +| `search_files` | Search files by glob pattern | +| `get_file_info` | Get detailed file/directory metadata | +| `list_allowed_directories` | Show allowed directories | + +### Additional Tools + +| Tool | Description | +|------|-------------| +| `delete_file` | Delete a file or directory (with recursive option) | +| `copy_file` | Copy a file to a new location | + +### MCP Mesh Collection Bindings + +These tools provide standard collection bindings for MCP Mesh compatibility: + +| Tool | Description | +|------|-------------| +| `COLLECTION_FILES_LIST` | List files with pagination | +| `COLLECTION_FILES_GET` | Get file metadata and content by path | +| `COLLECTION_FOLDERS_LIST` | List folders with pagination | +| `COLLECTION_FOLDERS_GET` | Get folder metadata by path | + +### MCP Mesh Compatibility Aliases + +For backward compatibility with existing Mesh connections, these aliases are also available: + +| Mesh Tool | Maps To | +|-----------|---------| +| `FILE_READ` | `read_text_file` | +| `FILE_WRITE` | `write_file` | +| `FILE_DELETE` | `delete_file` | +| `FILE_MOVE` | `move_file` | +| `FILE_COPY` | `copy_file` | +| `FILE_MKDIR` | `create_directory` | + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `MCP_LOCAL_FS_PATH` | Default path to mount | +| `PORT` | HTTP server port (default: 3456) | + +## Development + +```bash +# Install dependencies +npm install + +# Run in stdio mode (development) +npm run dev:stdio + +# Run in http mode (development) +npm run dev + +# Run tests +npm test + +# Type check +npm run check + +# Build for distribution +npm run build +``` + +## License + +MIT diff --git a/local-fs/bun.lock b/local-fs/bun.lock new file mode 100644 index 00000000..6e9d4d35 --- /dev/null +++ b/local-fs/bun.lock @@ -0,0 +1,206 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "@decocms/mcp-local-fs", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.20.2", + "kill-my-port": "^1.1.2", + "zod": "^3.24.0", + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.7.0", + }, + }, + }, + "packages": { + "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, ""], + + "@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", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ=="], + + "@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, ""], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "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=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "body-parser": ["body-parser@2.2.1", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hono": ["hono@4.11.3", "", {}, ""], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "kill-my-port": ["kill-my-port@1.1.2", "", { "bin": { "kill-my-port": "index.js" } }, "sha512-8T/8GdIGL1Ia1BbKykztZZigVQ7gRckGYQ2bnCOPZ+V+QrpCEAxz4rtVSRZRUZwr+50fBnitIMM8qEtUS8ZWfQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], + + "undici-types": ["undici-types@6.21.0", "", {}, ""], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "zod": ["zod@3.25.76", "", {}, ""], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, ""], + } +} diff --git a/local-fs/package.json b/local-fs/package.json new file mode 100644 index 00000000..813eb4a2 --- /dev/null +++ b/local-fs/package.json @@ -0,0 +1,59 @@ +{ + "name": "@decocms/mcp-local-fs", + "version": "1.1.0", + "description": "MCP server that mounts any local filesystem path. Supports stdio (default), HTTP, and serve mode with deco link.", + "type": "module", + "main": "./dist/cli.js", + "bin": { + "mcp-local-fs": "./dist/cli.js", + "local-fs-serve": "./server/serve.ts" + }, + "files": [ + "dist", + "server", + "README.md" + ], + "scripts": { + "build": "tsc", + "dev": "bun run --watch server/http.ts", + "dev:stdio": "bun run server/stdio.ts", + "serve": "bun run server/serve.ts", + "start": "node dist/cli.js", + "start:http": "node dist/cli.js --http", + "check": "tsc --noEmit", + "test": "bun test", + "test:watch": "bun test --watch", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.20.2", + "zod": "^3.24.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "keywords": [ + "mcp", + "model-context-protocol", + "filesystem", + "local-fs", + "ai", + "claude", + "mesh", + "stdio" + ], + "repository": { + "type": "git", + "url": "https://github.com/decocms/mcps.git", + "directory": "local-fs" + }, + "author": "DecoCMS", + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/local-fs/server/cli.ts b/local-fs/server/cli.ts new file mode 100644 index 00000000..9347ebf0 --- /dev/null +++ b/local-fs/server/cli.ts @@ -0,0 +1,29 @@ +#!/usr/bin/env node +/** + * MCP Local FS - CLI Entry Point + * + * Unified CLI that supports both stdio (default) and http transports. + * + * Usage: + * npx @decocms/mcp-local-fs /path/to/mount # stdio mode (default) + * npx @decocms/mcp-local-fs --http /path/to/mount # http mode + * npx @decocms/mcp-local-fs --http --port 8080 # http mode with custom port + */ + +const args = process.argv.slice(2); + +// Check for --http flag +const httpIndex = args.indexOf("--http"); +const isHttpMode = httpIndex !== -1; + +if (isHttpMode) { + // Remove --http flag from args before passing to http module + args.splice(httpIndex, 1); + process.argv = [process.argv[0], process.argv[1], ...args]; + + // Dynamic import of http module + import("./http.js"); +} else { + // Default to stdio mode + import("./stdio.js"); +} diff --git a/local-fs/server/http.ts b/local-fs/server/http.ts new file mode 100644 index 00000000..e2797dcb --- /dev/null +++ b/local-fs/server/http.ts @@ -0,0 +1,335 @@ +#!/usr/bin/env node +/** + * MCP Local FS - HTTP Entry Point + * + * Usage: + * npx @decocms/mcp-local-fs --http --path /path/to/mount + * curl http://localhost:3456/mcp?path=/my/folder + * + * The path can be provided via: + * 1. Query string: ?path=/my/folder + * 2. --path CLI flag + * 3. MCP_LOCAL_FS_PATH environment variable + */ + +import { + createServer, + type IncomingMessage, + type ServerResponse, +} from "node:http"; +import { spawn } from "node:child_process"; +import { platform } from "node:os"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { LocalFileStorage } from "./storage.js"; +import { registerTools } from "./tools.js"; +import { resolve } from "node:path"; + +/** + * Copy text to clipboard (cross-platform) + */ +function copyToClipboard(text: string): Promise { + return new Promise((resolvePromise) => { + const os = platform(); + let cmd: string; + let args: string[]; + + if (os === "darwin") { + cmd = "pbcopy"; + args = []; + } else if (os === "win32") { + cmd = "clip"; + args = []; + } else { + // Linux - try xclip first, then xsel + cmd = "xclip"; + args = ["-selection", "clipboard"]; + } + + try { + const proc = spawn(cmd, args, { stdio: ["pipe", "ignore", "ignore"] }); + proc.stdin?.write(text); + proc.stdin?.end(); + proc.on("close", (code) => resolvePromise(code === 0)); + proc.on("error", () => resolvePromise(false)); + } catch { + resolvePromise(false); + } + }); +} + +/** + * Create an MCP server for a given filesystem path + */ +function createMcpServerForPath(rootPath: string): McpServer { + const storage = new LocalFileStorage(rootPath); + + const server = new McpServer({ + name: "local-fs", + version: "1.0.0", + }); + + // Register all tools (includes OBJECT_STORAGE_BINDING tools + GET_ROOT) + registerTools(server, storage); + + return server; +} + +// Parse CLI args for port and path +function getPort(): number { + const args = process.argv.slice(2); + for (let i = 0; i < args.length; i++) { + if (args[i] === "--port" || args[i] === "-p") { + const port = parseInt(args[i + 1], 10); + if (!isNaN(port)) return port; + } + } + return parseInt(process.env.PORT || "3456", 10); +} + +function getDefaultPath(): string { + const args = process.argv.slice(2); + + // Check for explicit --path flag + for (let i = 0; i < args.length; i++) { + if (args[i] === "--path" || args[i] === "-d") { + const path = args[i + 1]; + if (path && !path.startsWith("-")) return path; + } + } + + // Check for positional argument (skip flags and their values) + const skipNext = new Set(); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + // Skip flag values + if (skipNext.has(i)) continue; + // Mark next arg to skip if this is a flag with value + if (arg === "--port" || arg === "-p" || arg === "--path" || arg === "-d") { + skipNext.add(i + 1); + continue; + } + // Skip flags + if (arg.startsWith("-")) continue; + // This is a positional argument - use it as path + return arg; + } + + return process.env.MCP_LOCAL_FS_PATH || process.cwd(); +} + +const port = getPort(); +const defaultPath = resolve(getDefaultPath()); + +// Session TTL in milliseconds (30 minutes) +const SESSION_TTL_MS = 30 * 60 * 1000; + +// Store active transports for session management with timestamps +const transports = new Map< + string, + { transport: StreamableHTTPServerTransport; lastAccess: number } +>(); + +// Cleanup stale sessions periodically (every 5 minutes) +const cleanupInterval = setInterval( + () => { + const now = Date.now(); + for (const [sessionId, session] of transports) { + if (now - session.lastAccess > SESSION_TTL_MS) { + transports.delete(sessionId); + console.log(`[mcp-local-fs] Session expired: ${sessionId}`); + } + } + }, + 5 * 60 * 1000, +); + +// Cleanup on process exit +process.on("SIGINT", () => { + clearInterval(cleanupInterval); + process.exit(0); +}); +process.on("SIGTERM", () => { + clearInterval(cleanupInterval); + process.exit(0); +}); + +// Create HTTP server +const httpServer = createServer( + async (req: IncomingMessage, res: ServerResponse) => { + try { + const url = new URL(req.url || "/", `http://localhost:${port}`); + + // CORS headers + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader( + "Access-Control-Allow-Methods", + "GET, POST, DELETE, OPTIONS", + ); + res.setHeader( + "Access-Control-Allow-Headers", + "Content-Type, mcp-session-id", + ); + + if (req.method === "OPTIONS") { + res.writeHead(204); + res.end(); + return; + } + + // Info endpoint + if (url.pathname === "/" && req.method === "GET") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + name: "mcp-local-fs", + version: "1.0.0", + description: "MCP server that mounts any local filesystem path", + endpoints: { + mcp: "/mcp?path=/your/path", + mcpWithPath: "/mcp/your/path", + health: "/health", + }, + defaultPath, + }), + ); + return; + } + + // Health check + if (url.pathname === "/health" && req.method === "GET") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ status: "ok" })); + return; + } + + // MCP endpoint + if (url.pathname.startsWith("/mcp")) { + // Get path from query string or URL path + let mountPath = defaultPath; + const queryPath = url.searchParams.get("path"); + if (queryPath) { + mountPath = resolve(queryPath); + } else if ( + url.pathname !== "/mcp" && + url.pathname.startsWith("/mcp/") + ) { + const pathFromUrl = url.pathname.replace("/mcp/", ""); + mountPath = resolve("/" + decodeURIComponent(pathFromUrl)); + } + + console.log(`[mcp-local-fs] Request for path: ${mountPath}`); + + // Get or create session + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + if (req.method === "POST") { + // Check for existing session + let session = sessionId ? transports.get(sessionId) : undefined; + + if (!session) { + // Create new transport and server for this session + const mcpServer = createMcpServerForPath(mountPath); + const newTransport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => crypto.randomUUID(), + onsessioninitialized: (newSessionId) => { + transports.set(newSessionId, { + transport: newTransport, + lastAccess: Date.now(), + }); + console.log( + `[mcp-local-fs] Session initialized: ${newSessionId}`, + ); + }, + }); + + // Connect server to transport + await mcpServer.connect(newTransport); + + // Handle the request + await newTransport.handleRequest(req, res); + return; + } + + // Update last access time + session.lastAccess = Date.now(); + + // Handle the request + await session.transport.handleRequest(req, res); + return; + } + + if (req.method === "GET") { + // SSE connection for server-sent events + const session = sessionId ? transports.get(sessionId) : undefined; + if (session) { + session.lastAccess = Date.now(); + await session.transport.handleRequest(req, res); + return; + } + res.writeHead(400, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "No session found" })); + return; + } + + if (req.method === "DELETE") { + // Session termination + const session = sessionId ? transports.get(sessionId) : undefined; + if (session) { + await session.transport.handleRequest(req, res); + transports.delete(sessionId!); + console.log(`[mcp-local-fs] Session terminated: ${sessionId}`); + return; + } + res.writeHead(404, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Session not found" })); + return; + } + } + + // 404 for unknown routes + res.writeHead(404, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ error: "Not found" })); + } catch (error) { + // Top-level error handler + console.error("[mcp-local-fs] Request error:", error); + if (!res.headersSent) { + res.writeHead(500, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + error: "Internal server error", + message: error instanceof Error ? error.message : "Unknown error", + }), + ); + } + } + }, +); + +// Build the full MCP URL +const mcpUrl = `http://localhost:${port}/mcp${defaultPath}`; + +// Copy to clipboard and show startup banner +(async () => { + const copied = await copyToClipboard(mcpUrl); + + console.log(` +╔════════════════════════════════════════════════════════════╗ +║ MCP Local FS Server ║ +╠════════════════════════════════════════════════════════════╣ +║ HTTP server running on port ${port.toString().padEnd(27)}║ +║ Default path: ${defaultPath.slice(0, 41).padEnd(41)}║ +║ ║ +║ MCP URL (${copied ? "copied to clipboard ✓" : "copy this"}): +║ ${mcpUrl} +║ ║ +║ Endpoints: ║ +║ GET / Server info ║ +║ GET /health Health check ║ +║ POST /mcp MCP endpoint (use ?path=...) ║ +║ POST /mcp/* MCP endpoint with path in URL ║ +╚════════════════════════════════════════════════════════════╝ +`); +})(); + +httpServer.listen(port); diff --git a/local-fs/server/logger.ts b/local-fs/server/logger.ts new file mode 100644 index 00000000..a857aaf5 --- /dev/null +++ b/local-fs/server/logger.ts @@ -0,0 +1,168 @@ +/** + * MCP Local FS - Logger + * + * Nice formatted logging that goes to stderr (to not interfere with stdio protocol) + * but uses colors/formatting that indicate it's informational, not an error. + */ + +// ANSI color codes +const colors = { + reset: "\x1b[0m", + dim: "\x1b[2m", + bold: "\x1b[1m", + + // Foreground colors + cyan: "\x1b[36m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + gray: "\x1b[90m", + white: "\x1b[37m", +}; + +// Operation type colors +const opColors: Record = { + READ: colors.cyan, + WRITE: colors.green, + DELETE: colors.yellow, + MOVE: colors.magenta, + COPY: colors.blue, + MKDIR: colors.blue, + LIST: colors.gray, + STAT: colors.gray, + EDIT: colors.green, + SEARCH: colors.cyan, +}; + +function timestamp(): string { + const now = new Date(); + return `${colors.dim}${now.toLocaleTimeString("en-US", { hour12: false })}${colors.reset}`; +} + +function formatPath(path: string): string { + return `${colors.white}${path}${colors.reset}`; +} + +function formatOp(op: string): string { + const color = opColors[op] || colors.white; + return `${color}${colors.bold}${op.padEnd(6)}${colors.reset}`; +} + +function formatSize(bytes: number): string { + const units = ["B", "KB", "MB", "GB"]; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${colors.dim}(${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]})${colors.reset}`; +} + +const prefix = `${colors.cyan}◆${colors.reset}`; + +/** + * Log a file operation + */ +export function logOp( + op: string, + path: string, + extra?: { + size?: number; + to?: string; + count?: number; + recursive?: boolean; + error?: string; + }, +): void { + let msg = `${prefix} ${timestamp()} ${formatOp(op)} ${formatPath(path)}`; + + if (extra?.to) { + msg += ` ${colors.dim}→${colors.reset} ${formatPath(extra.to)}`; + } + + if (extra?.size !== undefined) { + msg += ` ${formatSize(extra.size)}`; + } + + if (extra?.count !== undefined) { + const recursiveLabel = extra.recursive ? " recursive" : ""; + msg += ` ${colors.dim}(${extra.count}${recursiveLabel} items)${colors.reset}`; + } + + if (extra?.error) { + msg += ` ${colors.yellow}[${extra.error}]${colors.reset}`; + } + + console.error(msg); +} + +/** + * Log server startup + */ +export function logStart(rootPath: string): void { + console.error( + `\n${prefix} ${colors.cyan}${colors.bold}mcp-local-fs${colors.reset} ${colors.dim}started${colors.reset}`, + ); + console.error( + `${prefix} ${colors.dim}root:${colors.reset} ${colors.white}${rootPath}${colors.reset}\n`, + ); +} + +/** + * Log an error (still uses red, but with the prefix) + */ +export function logError(op: string, path: string, error: Error): void { + console.error( + `${prefix} ${timestamp()} ${colors.yellow}${colors.bold}ERR${colors.reset} ${formatOp(op)} ${formatPath(path)} ${colors.dim}${error.message}${colors.reset}`, + ); +} + +/** + * Log a tool call + */ +export function logTool( + toolName: string, + args: Record, + result?: { isError?: boolean }, +): void { + const argsStr = formatArgs(args); + const status = result?.isError + ? `${colors.yellow}✗${colors.reset}` + : `${colors.green}✓${colors.reset}`; + + if (result) { + console.error( + `${prefix} ${timestamp()} ${colors.magenta}${colors.bold}TOOL${colors.reset} ${colors.white}${toolName}${colors.reset}${argsStr} ${status}`, + ); + } else { + console.error( + `${prefix} ${timestamp()} ${colors.magenta}${colors.bold}TOOL${colors.reset} ${colors.white}${toolName}${colors.reset}${argsStr}`, + ); + } +} + +function formatArgs(args: Record): string { + const entries = Object.entries(args); + if (entries.length === 0) return ""; + + const parts = entries.map(([key, value]) => { + let valStr: string; + if (typeof value === "string") { + // Truncate long strings + valStr = value.length > 50 ? `"${value.slice(0, 47)}..."` : `"${value}"`; + } else if (Array.isArray(value)) { + valStr = `[${value.length} items]`; + } else if (typeof value === "object" && value !== null) { + valStr = "{...}"; + } else { + valStr = String(value); + } + return `${colors.dim}${key}=${colors.reset}${valStr}`; + }); + + return ` ${parts.join(" ")}`; +} diff --git a/local-fs/server/mcp.test.ts b/local-fs/server/mcp.test.ts new file mode 100644 index 00000000..056a8909 --- /dev/null +++ b/local-fs/server/mcp.test.ts @@ -0,0 +1,631 @@ +/** + * MCP Server Integration Tests + * + * Tests for the MCP server tools and protocol integration. + * Uses the actual registerTools function to test the real implementation. + */ + +import { + describe, + test, + expect, + beforeAll, + afterAll, + beforeEach, +} from "bun:test"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { LocalFileStorage } from "./storage.js"; +import { registerTools } from "./tools.js"; +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +describe("MCP Server Integration", () => { + let tempDir: string; + let storage: LocalFileStorage; + let server: McpServer; + let client: Client; + + beforeAll(async () => { + // Create temp directory + tempDir = await mkdtemp(join(tmpdir(), "mcp-server-test-")); + storage = new LocalFileStorage(tempDir); + + // Create MCP server with shared tools + server = new McpServer({ + name: "local-fs", + version: "1.0.0", + }); + registerTools(server, storage); + + // Create in-memory transport pair + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + // Connect server and client + await server.connect(serverTransport); + + client = new Client({ + name: "test-client", + version: "1.0.0", + }); + await client.connect(clientTransport); + }); + + afterAll(async () => { + await client.close(); + await server.close(); + await rm(tempDir, { recursive: true, force: true }); + }); + + beforeEach(async () => { + // Clean the temp directory before each test + const entries = await storage.list(""); + for (const entry of entries) { + await rm(join(tempDir, entry.path), { recursive: true, force: true }); + } + }); + + describe("tools/list", () => { + test("should list all official MCP filesystem tools", async () => { + const result = await client.listTools(); + + expect(result.tools.length).toBeGreaterThan(0); + + const toolNames = result.tools.map((t) => t.name); + + // Official MCP filesystem tools + expect(toolNames).toContain("read_file"); + expect(toolNames).toContain("read_text_file"); + expect(toolNames).toContain("read_media_file"); + expect(toolNames).toContain("read_multiple_files"); + expect(toolNames).toContain("write_file"); + expect(toolNames).toContain("edit_file"); + expect(toolNames).toContain("create_directory"); + expect(toolNames).toContain("list_directory"); + expect(toolNames).toContain("list_directory_with_sizes"); + expect(toolNames).toContain("directory_tree"); + expect(toolNames).toContain("move_file"); + expect(toolNames).toContain("search_files"); + expect(toolNames).toContain("get_file_info"); + expect(toolNames).toContain("list_allowed_directories"); + + // Additional tools + expect(toolNames).toContain("delete_file"); + expect(toolNames).toContain("copy_file"); + + // Mesh collection bindings + expect(toolNames).toContain("COLLECTION_FILES_LIST"); + expect(toolNames).toContain("COLLECTION_FILES_GET"); + expect(toolNames).toContain("COLLECTION_FOLDERS_LIST"); + expect(toolNames).toContain("COLLECTION_FOLDERS_GET"); + }); + + test("each tool should have a description", async () => { + const result = await client.listTools(); + + for (const tool of result.tools) { + expect(tool.description).toBeDefined(); + expect(tool.description!.length).toBeGreaterThan(0); + } + }); + }); + + describe("write_file tool", () => { + test("should write a file successfully", async () => { + const result = await client.callTool({ + name: "write_file", + arguments: { + path: "test-write.txt", + content: "Hello from MCP!", + }, + }); + + expect(result.isError).toBeFalsy(); + expect(result.content).toBeDefined(); + + // Verify file was written + const readResult = await storage.read("test-write.txt"); + expect(readResult.content).toBe("Hello from MCP!"); + }); + + test("should create nested directories", async () => { + const result = await client.callTool({ + name: "write_file", + arguments: { + path: "nested/path/file.txt", + content: "Nested content", + }, + }); + + expect(result.isError).toBeFalsy(); + + const readResult = await storage.read("nested/path/file.txt"); + expect(readResult.content).toBe("Nested content"); + }); + }); + + describe("read_text_file tool", () => { + test("should read a file successfully", async () => { + await storage.write("read-test.txt", "Content to read"); + + const result = await client.callTool({ + name: "read_text_file", + arguments: { + path: "read-test.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toBe("Content to read"); + }); + + test("should return error for non-existent file", async () => { + const result = await client.callTool({ + name: "read_text_file", + arguments: { + path: "does-not-exist.txt", + }, + }); + + expect(result.isError).toBe(true); + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("Error:"); + }); + + test("should support head parameter", async () => { + await storage.write( + "lines.txt", + "Line 1\nLine 2\nLine 3\nLine 4\nLine 5", + ); + + const result = await client.callTool({ + name: "read_text_file", + arguments: { + path: "lines.txt", + head: 2, + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toBe("Line 1\nLine 2"); + }); + + test("should support tail parameter", async () => { + await storage.write( + "lines.txt", + "Line 1\nLine 2\nLine 3\nLine 4\nLine 5", + ); + + const result = await client.callTool({ + name: "read_text_file", + arguments: { + path: "lines.txt", + tail: 2, + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toBe("Line 4\nLine 5"); + }); + }); + + describe("read_multiple_files tool", () => { + test("should read multiple files at once", async () => { + await storage.write("file1.txt", "Content 1"); + await storage.write("file2.txt", "Content 2"); + + const result = await client.callTool({ + name: "read_multiple_files", + arguments: { + paths: ["file1.txt", "file2.txt"], + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("file1.txt:"); + expect(textContent[0].text).toContain("Content 1"); + expect(textContent[0].text).toContain("file2.txt:"); + expect(textContent[0].text).toContain("Content 2"); + }); + }); + + describe("delete_file tool", () => { + test("should delete a file", async () => { + await storage.write("to-delete.txt", "Delete me"); + + const result = await client.callTool({ + name: "delete_file", + arguments: { + path: "to-delete.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + // Verify file was deleted + await expect(storage.getMetadata("to-delete.txt")).rejects.toThrow(); + }); + + test("should delete directory recursively", async () => { + await storage.write("dir-delete/file.txt", "content"); + + const result = await client.callTool({ + name: "delete_file", + arguments: { + path: "dir-delete", + recursive: true, + }, + }); + + expect(result.isError).toBeFalsy(); + + await expect(storage.getMetadata("dir-delete")).rejects.toThrow(); + }); + }); + + describe("list_directory tool", () => { + test("should list files and directories", async () => { + await storage.write("file.txt", "content"); + await storage.mkdir("subdir"); + + const result = await client.callTool({ + name: "list_directory", + arguments: { + path: "", + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("[FILE] file.txt"); + expect(textContent[0].text).toContain("[DIR] subdir"); + }); + }); + + describe("create_directory tool", () => { + test("should create a directory", async () => { + const result = await client.callTool({ + name: "create_directory", + arguments: { + path: "new-dir", + }, + }); + + expect(result.isError).toBeFalsy(); + + const meta = await storage.getMetadata("new-dir"); + expect(meta.isDirectory).toBe(true); + }); + + test("should create nested directories", async () => { + const result = await client.callTool({ + name: "create_directory", + arguments: { + path: "deep/nested/dir", + }, + }); + + expect(result.isError).toBeFalsy(); + + const meta = await storage.getMetadata("deep/nested/dir"); + expect(meta.isDirectory).toBe(true); + }); + }); + + describe("move_file tool", () => { + test("should move a file", async () => { + await storage.write("original.txt", "content"); + + const result = await client.callTool({ + name: "move_file", + arguments: { + source: "original.txt", + destination: "moved.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + await expect(storage.getMetadata("original.txt")).rejects.toThrow(); + const content = await storage.read("moved.txt"); + expect(content.content).toBe("content"); + }); + }); + + describe("copy_file tool", () => { + test("should copy a file", async () => { + await storage.write("original.txt", "content"); + + const result = await client.callTool({ + name: "copy_file", + arguments: { + source: "original.txt", + destination: "copy.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + const original = await storage.read("original.txt"); + const copy = await storage.read("copy.txt"); + expect(original.content).toBe("content"); + expect(copy.content).toBe("content"); + }); + }); + + describe("get_file_info tool", () => { + test("should return file metadata", async () => { + await storage.write("info-test.txt", "some content"); + + const result = await client.callTool({ + name: "get_file_info", + arguments: { + path: "info-test.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("type: file"); + expect(textContent[0].text).toContain("size:"); + }); + }); + + describe("search_files tool", () => { + test("should find files matching pattern", async () => { + await storage.write("test.txt", "content"); + await storage.write("test.js", "content"); + await storage.write("other.md", "content"); + + const result = await client.callTool({ + name: "search_files", + arguments: { + path: "", + pattern: "*.txt", + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("test.txt"); + expect(textContent[0].text).not.toContain("test.js"); + }); + }); + + describe("edit_file tool", () => { + test("should edit file with search and replace", async () => { + await storage.write("edit-test.txt", "Hello World"); + + const result = await client.callTool({ + name: "edit_file", + arguments: { + path: "edit-test.txt", + edits: [{ oldText: "World", newText: "MCP" }], + }, + }); + + expect(result.isError).toBeFalsy(); + + const content = await storage.read("edit-test.txt"); + expect(content.content).toBe("Hello MCP"); + }); + + test("should support dry run", async () => { + await storage.write("edit-test.txt", "Hello World"); + + const result = await client.callTool({ + name: "edit_file", + arguments: { + path: "edit-test.txt", + edits: [{ oldText: "World", newText: "MCP" }], + dryRun: true, + }, + }); + + expect(result.isError).toBeFalsy(); + + // File should not be changed + const content = await storage.read("edit-test.txt"); + expect(content.content).toBe("Hello World"); + + // Response should include diff preview + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain("Dry run"); + }); + }); + + describe("COLLECTION_FILES_LIST tool", () => { + test("should list files in root", async () => { + await storage.write("file1.txt", "content1"); + await storage.write("file2.txt", "content2"); + + const result = await client.callTool({ + name: "COLLECTION_FILES_LIST", + arguments: {}, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + const parsed = JSON.parse(textContent[0].text); + + expect(parsed.items.length).toBe(2); + expect(parsed.totalCount).toBe(2); + }); + + test("should list files recursively", async () => { + await storage.write("root.txt", "root"); + await storage.write("sub/nested.txt", "nested"); + + const result = await client.callTool({ + name: "COLLECTION_FILES_LIST", + arguments: { + recursive: true, + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + const parsed = JSON.parse(textContent[0].text); + + expect(parsed.items.length).toBe(2); + const paths = parsed.items.map((i: { path: string }) => i.path); + expect(paths).toContain("root.txt"); + expect(paths).toContain("sub/nested.txt"); + }); + + test("should respect limit parameter", async () => { + await storage.write("file1.txt", "1"); + await storage.write("file2.txt", "2"); + await storage.write("file3.txt", "3"); + + const result = await client.callTool({ + name: "COLLECTION_FILES_LIST", + arguments: { + limit: 2, + }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + const parsed = JSON.parse(textContent[0].text); + + expect(parsed.items.length).toBe(2); + expect(parsed.totalCount).toBe(3); + expect(parsed.hasMore).toBe(true); + }); + }); + + describe("COLLECTION_FOLDERS_LIST tool", () => { + test("should list folders", async () => { + await storage.mkdir("folder1"); + await storage.mkdir("folder2"); + await storage.write("file.txt", "content"); + + const result = await client.callTool({ + name: "COLLECTION_FOLDERS_LIST", + arguments: {}, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + const parsed = JSON.parse(textContent[0].text); + + expect(parsed.items.length).toBe(2); + expect( + parsed.items.every((i: { isDirectory: boolean }) => i.isDirectory), + ).toBe(true); + }); + }); + + describe("COLLECTION_FILES_GET tool", () => { + test("should return file metadata and content", async () => { + await storage.write("get-test.txt", "Hello from GET test!"); + + const result = await client.callTool({ + name: "COLLECTION_FILES_GET", + arguments: { id: "get-test.txt" }, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + const parsed = JSON.parse(textContent[0].text); + + expect(parsed.item).toBeDefined(); + expect(parsed.item.path).toBe("get-test.txt"); + expect(parsed.item.content).toBe("Hello from GET test!"); + expect(parsed.item.isDirectory).toBe(false); + }); + }); + + describe("list_allowed_directories tool", () => { + test("should return the root directory", async () => { + const result = await client.callTool({ + name: "list_allowed_directories", + arguments: {}, + }); + + expect(result.isError).toBeFalsy(); + + const textContent = result.content as Array<{ + type: string; + text: string; + }>; + expect(textContent[0].text).toContain(tempDir); + }); + }); + + describe("error handling", () => { + test("should handle invalid file paths gracefully", async () => { + const result = await client.callTool({ + name: "read_text_file", + arguments: { + path: "", + }, + }); + + // Should return an error response, not throw + expect(result.isError).toBe(true); + }); + }); +}); diff --git a/local-fs/server/serve.ts b/local-fs/server/serve.ts new file mode 100755 index 00000000..16aa2115 --- /dev/null +++ b/local-fs/server/serve.ts @@ -0,0 +1,215 @@ +#!/usr/bin/env bun +/** + * MCP Local FS - Serve & Link + * + * Exposes the current directory (or specified path) via deco link + * and provides a ready-to-add MCP URL for mesh. + * + * Usage: + * bunx @decocms/mcp-local-fs serve # Current directory + * bunx @decocms/mcp-local-fs serve /my/folder # Specific folder + * bunx @decocms/mcp-local-fs serve --port 8080 # Custom port + */ + +import { spawn } from "node:child_process"; +import { platform } from "node:os"; +import { resolve } from "node:path"; +import { existsSync } from "node:fs"; + +const PORT = parseInt(process.env.PORT || "3456", 10); + +/** + * Parse CLI arguments + */ +function parseArgs(): { path: string; port: number } { + const args = process.argv.slice(2); + let path = process.cwd(); + let port = PORT; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + // Skip "serve" command itself + if (arg === "serve") continue; + + // Port flag + if (arg === "--port" || arg === "-p") { + const p = parseInt(args[++i], 10); + if (!isNaN(p)) port = p; + continue; + } + + // Skip other flags + if (arg.startsWith("-")) continue; + + // Positional argument = path + path = resolve(arg); + } + + return { path, port }; +} + +/** + * Copy text to clipboard + */ +function copyToClipboard(text: string): Promise { + return new Promise((resolvePromise) => { + const os = platform(); + let cmd: string; + let args: string[]; + + if (os === "darwin") { + cmd = "pbcopy"; + args = []; + } else if (os === "win32") { + cmd = "clip"; + args = []; + } else { + cmd = "xclip"; + args = ["-selection", "clipboard"]; + } + + try { + const proc = spawn(cmd, args, { stdio: ["pipe", "ignore", "ignore"] }); + proc.stdin?.write(text); + proc.stdin?.end(); + proc.on("close", (code) => resolvePromise(code === 0)); + proc.on("error", () => resolvePromise(false)); + } catch { + resolvePromise(false); + } + }); +} + +const { path, port } = parseArgs(); + +// Validate path exists +if (!existsSync(path)) { + console.error(`\n❌ Path does not exist: ${path}\n`); + process.exit(1); +} + +// URL-encode the path for the MCP endpoint +const encodedPath = encodeURIComponent(path); + +// Track if we've shown the URL already +let publicUrl = ""; + +/** + * Show the MCP URL banner when we detect the tunnel URL + */ +async function showMcpUrl(tunnelUrl: string) { + if (publicUrl) return; // Already shown + publicUrl = tunnelUrl; + + // Build the full MCP URL with path + const mcpUrl = `${publicUrl}/mcp?path=${encodedPath}`; + + const copied = await copyToClipboard(mcpUrl); + + console.log(` + +╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ +║ ✅ Ready to Use! ║ +╠══════════════════════════════════════════════════════════════════════════════════════════════════╣ +║ ║ +║ Add this MCP URL to your Deco Mesh: ║ +║ ║ +║ ${mcpUrl} +║ ║ +║ ${copied ? "📋 Copied to clipboard!" : "Copy the URL above"} ║ +║ ║ +║ Steps: ║ +║ 1. Open mesh-admin.decocms.com ║ +║ 2. Go to Connections → Add Custom MCP ║ +║ 3. Paste the URL above ║ +║ ║ +╚══════════════════════════════════════════════════════════════════════════════════════════════════╝ +`); +} + +/** + * Check output for tunnel URL + */ +function checkForTunnelUrl(output: string) { + // Match URLs like https://localhost-xxx.deco.host or https://xxx.deco.site + const urlMatch = output.match(/https:\/\/[^\s()"']+\.deco\.(site|host)/); + if (urlMatch) { + const url = urlMatch[0].replace(/[()]/g, ""); + showMcpUrl(url); + } +} + +console.log(` +╔════════════════════════════════════════════════════════════╗ +║ MCP Local FS - Serve & Link ║ +╠════════════════════════════════════════════════════════════╣ +║ 📁 Serving: ${path.slice(0, 43).padEnd(43)}║ +║ 🔌 Port: ${port.toString().padEnd(47)}║ +║ ║ +║ ⚠️ Note: Only ONE deco link tunnel per machine. ║ +║ Stop other 'serve' commands first. ║ +╚════════════════════════════════════════════════════════════╝ + +Starting server and tunnel... +`); + +// Get the directory of this script to find http.ts +const scriptDir = import.meta.dirname || resolve(process.cwd(), "server"); +const httpScript = resolve(scriptDir, "http.ts"); + +// Start the HTTP server in background +const serverProcess = spawn( + "bun", + ["run", httpScript, "--port", port.toString(), "--path", path], + { + stdio: ["ignore", "pipe", "pipe"], + env: { ...process.env, PORT: port.toString() }, + }, +); + +serverProcess.stderr?.on("data", (data) => { + const output = data.toString().trim(); + if (output && !output.includes("MCP Local FS")) { + console.error(`[server] ${output}`); + } +}); + +// Wait for server to start +await new Promise((r) => setTimeout(r, 1500)); + +// Run deco link to get the public URL +const decoLink = spawn("deco", ["link", "-p", port.toString()], { + stdio: ["inherit", "pipe", "pipe"], +}); + +decoLink.stdout?.on("data", (data) => { + const output = data.toString(); + process.stdout.write(output); + checkForTunnelUrl(output); +}); + +decoLink.stderr?.on("data", (data) => { + const output = data.toString(); + process.stderr.write(output); + checkForTunnelUrl(output); +}); + +// Handle exit +process.on("SIGINT", () => { + serverProcess.kill(); + decoLink.kill(); + process.exit(0); +}); + +process.on("SIGTERM", () => { + serverProcess.kill(); + decoLink.kill(); + process.exit(0); +}); + +// Wait for deco link to exit +decoLink.on("close", (code) => { + serverProcess.kill(); + process.exit(code || 0); +}); diff --git a/local-fs/server/stdio.ts b/local-fs/server/stdio.ts new file mode 100644 index 00000000..76ddc6a0 --- /dev/null +++ b/local-fs/server/stdio.ts @@ -0,0 +1,82 @@ +#!/usr/bin/env node +/** + * MCP Local FS - Stdio Entry Point + * + * This is the main entry point for running the MCP server via stdio, + * which is the standard transport for CLI-based MCP servers. + * + * Usage: + * npx @decocms/mcp-local-fs /path/to/mount + * npx @decocms/mcp-local-fs --path /path/to/mount + */ + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { LocalFileStorage } from "./storage.js"; +import { registerTools } from "./tools.js"; +import { logStart } from "./logger.js"; +import { resolve } from "node:path"; + +/** + * Parse CLI arguments to get the path to mount + */ +function getPathFromArgs(): string { + const args = process.argv.slice(2); + + // Check for --path flag + for (let i = 0; i < args.length; i++) { + if (args[i] === "--path" || args[i] === "-p") { + const path = args[i + 1]; + if (path && !path.startsWith("-")) { + return path; + } + } + } + + // Check for positional argument (first non-flag argument) + for (const arg of args) { + if (!arg.startsWith("-")) { + return arg; + } + } + + // Check environment variable + if (process.env.MCP_LOCAL_FS_PATH) { + return process.env.MCP_LOCAL_FS_PATH; + } + + // Default to current working directory + return process.cwd(); +} + +/** + * Create and start the MCP server with stdio transport + */ +async function main() { + const mountPath = getPathFromArgs(); + const resolvedPath = resolve(mountPath); + + // Create storage instance + const storage = new LocalFileStorage(resolvedPath); + + // Create MCP server + const server = new McpServer({ + name: "local-fs", + version: "1.0.0", + }); + + // Register all tools + registerTools(server, storage); + + // Connect to stdio transport + const transport = new StdioServerTransport(); + await server.connect(transport); + + // Log startup (goes to stderr, nicely formatted) + logStart(resolvedPath); +} + +main().catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/local-fs/server/storage.test.ts b/local-fs/server/storage.test.ts new file mode 100644 index 00000000..cb73b8fb --- /dev/null +++ b/local-fs/server/storage.test.ts @@ -0,0 +1,525 @@ +/** + * Storage Layer Tests + * + * Tests for the LocalFileStorage class - file system operations. + */ + +import { + describe, + test, + expect, + beforeAll, + afterAll, + beforeEach, +} from "bun:test"; +import { LocalFileStorage, getExtensionFromMimeType } from "./storage.js"; +import { Readable } from "node:stream"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +describe("LocalFileStorage", () => { + let tempDir: string; + let storage: LocalFileStorage; + + beforeAll(async () => { + // Create a temp directory for tests + tempDir = await mkdtemp(join(tmpdir(), "mcp-local-fs-test-")); + storage = new LocalFileStorage(tempDir); + }); + + afterAll(async () => { + // Clean up temp directory + await rm(tempDir, { recursive: true, force: true }); + }); + + beforeEach(async () => { + // Clean the temp directory before each test + const entries = await storage.list(""); + for (const entry of entries) { + await rm(join(tempDir, entry.path), { recursive: true, force: true }); + } + }); + + describe("root property", () => { + test("should return the resolved root directory", () => { + expect(storage.root).toBe(tempDir); + }); + }); + + describe("write and read", () => { + test("should write and read a text file", async () => { + const content = "Hello, World!"; + await storage.write("test.txt", content); + + const result = await storage.read("test.txt"); + expect(result.content).toBe(content); + expect(result.metadata.path).toBe("test.txt"); + expect(result.metadata.mimeType).toBe("text/plain"); + }); + + test("should write and read a file with utf-8 encoding", async () => { + const content = "こんにちは世界 🌍"; + await storage.write("unicode.txt", content, { encoding: "utf-8" }); + + const result = await storage.read("unicode.txt", "utf-8"); + expect(result.content).toBe(content); + }); + + test("should write and read a file with base64 encoding", async () => { + const originalContent = "Binary test content"; + const base64Content = Buffer.from(originalContent).toString("base64"); + + await storage.write("binary.bin", base64Content, { encoding: "base64" }); + + const result = await storage.read("binary.bin", "base64"); + const decodedContent = Buffer.from(result.content, "base64").toString( + "utf-8", + ); + expect(decodedContent).toBe(originalContent); + }); + + test("should create parent directories when writing", async () => { + const content = "Nested file"; + await storage.write("nested/deep/file.txt", content, { + createParents: true, + }); + + const result = await storage.read("nested/deep/file.txt"); + expect(result.content).toBe(content); + }); + + test("should fail to overwrite when overwrite is false", async () => { + await storage.write("existing.txt", "original"); + + await expect( + storage.write("existing.txt", "new content", { overwrite: false }), + ).rejects.toThrow("File already exists"); + }); + + test("should overwrite when overwrite is true", async () => { + await storage.write("overwrite.txt", "original"); + await storage.write("overwrite.txt", "updated", { overwrite: true }); + + const result = await storage.read("overwrite.txt"); + expect(result.content).toBe("updated"); + }); + }); + + describe("getMetadata", () => { + test("should return metadata for a file", async () => { + await storage.write("meta-test.txt", "content"); + + const metadata = await storage.getMetadata("meta-test.txt"); + expect(metadata.id).toBe("meta-test.txt"); + expect(metadata.title).toBe("meta-test.txt"); + expect(metadata.isDirectory).toBe(false); + expect(metadata.mimeType).toBe("text/plain"); + expect(metadata.size).toBeGreaterThan(0); + expect(metadata.created_at).toBeDefined(); + expect(metadata.updated_at).toBeDefined(); + }); + + test("should return metadata for a directory", async () => { + await storage.mkdir("test-dir"); + + const metadata = await storage.getMetadata("test-dir"); + expect(metadata.isDirectory).toBe(true); + expect(metadata.mimeType).toBe("inode/directory"); + }); + + test("should throw for non-existent path", async () => { + await expect(storage.getMetadata("does-not-exist.txt")).rejects.toThrow(); + }); + }); + + describe("list", () => { + test("should list files in root directory", async () => { + await storage.write("file1.txt", "content1"); + await storage.write("file2.txt", "content2"); + + const items = await storage.list(""); + expect(items.length).toBe(2); + expect(items.map((i) => i.title)).toContain("file1.txt"); + expect(items.map((i) => i.title)).toContain("file2.txt"); + }); + + test("should list files in subdirectory", async () => { + await storage.mkdir("subdir"); + await storage.write("subdir/nested.txt", "nested content"); + + const items = await storage.list("subdir"); + expect(items.length).toBe(1); + expect(items[0].title).toBe("subdir/nested.txt"); + }); + + test("should list recursively when recursive=true", async () => { + await storage.write("root.txt", "root"); + await storage.write("level1/file1.txt", "level1"); + await storage.write("level1/level2/file2.txt", "level2"); + + const items = await storage.list("", { recursive: true }); + const paths = items.map((i) => i.path); + + expect(paths).toContain("root.txt"); + expect(paths).toContain("level1/file1.txt"); + expect(paths).toContain("level1/level2/file2.txt"); + }); + + test("should filter to files only when filesOnly=true", async () => { + await storage.mkdir("dir-only"); + await storage.write("file-only.txt", "content"); + + const items = await storage.list("", { filesOnly: true }); + expect(items.every((i) => !i.isDirectory)).toBe(true); + expect(items.map((i) => i.title)).toContain("file-only.txt"); + }); + + test("should return empty array for non-existent directory", async () => { + const items = await storage.list("non-existent"); + expect(items).toEqual([]); + }); + + test("should skip hidden files (starting with .)", async () => { + await writeFile(join(tempDir, ".hidden"), "hidden content"); + await storage.write("visible.txt", "visible content"); + + const items = await storage.list(""); + expect(items.map((i) => i.title)).not.toContain(".hidden"); + expect(items.map((i) => i.title)).toContain("visible.txt"); + }); + }); + + describe("mkdir", () => { + test("should create a directory", async () => { + const result = await storage.mkdir("new-dir"); + + expect(result.folder.isDirectory).toBe(true); + expect(result.folder.path).toBe("new-dir"); + }); + + test("should create nested directories with recursive=true", async () => { + const result = await storage.mkdir("a/b/c", true); + + expect(result.folder.path).toBe("a/b/c"); + + const metadata = await storage.getMetadata("a/b/c"); + expect(metadata.isDirectory).toBe(true); + }); + }); + + describe("delete", () => { + test("should delete a file", async () => { + await storage.write("to-delete.txt", "content"); + const result = await storage.delete("to-delete.txt"); + + expect(result.success).toBe(true); + await expect(storage.getMetadata("to-delete.txt")).rejects.toThrow(); + }); + + test("should delete an empty directory", async () => { + await storage.mkdir("empty-dir"); + const result = await storage.delete("empty-dir", true); + + expect(result.success).toBe(true); + }); + + test("should delete directory recursively", async () => { + await storage.write("dir-to-delete/file.txt", "content"); + const result = await storage.delete("dir-to-delete", true); + + expect(result.success).toBe(true); + await expect(storage.getMetadata("dir-to-delete")).rejects.toThrow(); + }); + + test("should fail to delete non-empty directory without recursive flag", async () => { + await storage.write("non-empty/file.txt", "content"); + + await expect(storage.delete("non-empty", false)).rejects.toThrow(); + }); + }); + + describe("move", () => { + test("should move a file", async () => { + await storage.write("source.txt", "content"); + const result = await storage.move("source.txt", "destination.txt"); + + expect(result.file.path).toBe("destination.txt"); + await expect(storage.getMetadata("source.txt")).rejects.toThrow(); + + const content = await storage.read("destination.txt"); + expect(content.content).toBe("content"); + }); + + test("should move a file to a subdirectory", async () => { + await storage.write("move-me.txt", "content"); + await storage.mkdir("target-dir"); + await storage.move("move-me.txt", "target-dir/moved.txt"); + + const content = await storage.read("target-dir/moved.txt"); + expect(content.content).toBe("content"); + }); + + test("should fail to overwrite without overwrite flag", async () => { + await storage.write("existing-dest.txt", "existing"); + await storage.write("new-source.txt", "new"); + + await expect( + storage.move("new-source.txt", "existing-dest.txt", false), + ).rejects.toThrow("Destination already exists"); + }); + + test("should overwrite with overwrite flag", async () => { + await storage.write("old.txt", "old content"); + await storage.write("new.txt", "new content"); + await storage.move("new.txt", "old.txt", true); + + const result = await storage.read("old.txt"); + expect(result.content).toBe("new content"); + }); + }); + + describe("copy", () => { + test("should copy a file", async () => { + await storage.write("original.txt", "content"); + const result = await storage.copy("original.txt", "copied.txt"); + + expect(result.file.path).toBe("copied.txt"); + + // Both files should exist + const original = await storage.read("original.txt"); + const copied = await storage.read("copied.txt"); + expect(original.content).toBe("content"); + expect(copied.content).toBe("content"); + }); + + test("should fail to overwrite without overwrite flag", async () => { + await storage.write("src.txt", "source"); + await storage.write("dst.txt", "destination"); + + await expect(storage.copy("src.txt", "dst.txt", false)).rejects.toThrow( + "Destination already exists", + ); + }); + + test("should overwrite with overwrite flag", async () => { + await storage.write("src.txt", "source content"); + await storage.write("dst.txt", "destination content"); + await storage.copy("src.txt", "dst.txt", true); + + const result = await storage.read("dst.txt"); + expect(result.content).toBe("source content"); + }); + }); + + describe("path sanitization", () => { + test("should prevent path traversal with ..", async () => { + await storage.write("safe.txt", "safe content"); + + // Attempting to traverse should be sanitized + const result = await storage.read("../safe.txt"); + // This should still find the file since .. is stripped + expect(result.content).toBe("safe content"); + }); + + test("should handle leading slashes", async () => { + await storage.write("leading-slash.txt", "content"); + + const result = await storage.read("/leading-slash.txt"); + expect(result.content).toBe("content"); + }); + }); + + describe("path normalization (stripping root prefix)", () => { + test("should strip root directory prefix from path", async () => { + await storage.write("normalize-test.txt", "normalized content"); + + // AI agents sometimes pass the full path including root + const fullPath = `${tempDir}/normalize-test.txt`; + const result = await storage.read(fullPath); + expect(result.content).toBe("normalized content"); + }); + + test("should strip root with colon separator", async () => { + await storage.write("colon-test.txt", "colon content"); + + // Some tools format paths as "root:filename" + const colonPath = `${tempDir}:colon-test.txt`; + const result = await storage.read(colonPath); + expect(result.content).toBe("colon content"); + }); + + test("normalizePath should return relative path", () => { + const relPath = storage.normalizePath(`${tempDir}/some/file.txt`); + expect(relPath).toBe("some/file.txt"); + }); + + test("normalizePath should handle already-relative paths", () => { + const relPath = storage.normalizePath("some/file.txt"); + expect(relPath).toBe("some/file.txt"); + }); + + test("normalizePath should handle colon separator", () => { + const relPath = storage.normalizePath(`${tempDir}:file.txt`); + expect(relPath).toBe("file.txt"); + }); + + test("normalizePath should strip leading slashes", () => { + const relPath = storage.normalizePath("/file.txt"); + expect(relPath).toBe("file.txt"); + }); + + test("normalizePath should NOT match paths that share prefix but are not inside root", () => { + // If rootDir is /tmp/root, a path like /tmp/rootEvil/file.txt should NOT + // be treated as inside the root directory + const relPath = storage.normalizePath(`${tempDir}Evil/file.txt`); + // Should return the full path unchanged (minus leading slash stripping) + expect(relPath).not.toBe("Evil/file.txt"); + // Instead it should be the original path with leading slash stripped + expect(relPath).toContain("Evil/file.txt"); + }); + }); + + describe("MIME type detection", () => { + const testCases = [ + { ext: ".txt", expected: "text/plain" }, + { ext: ".json", expected: "application/json" }, + { ext: ".html", expected: "text/html" }, + { ext: ".css", expected: "text/css" }, + { ext: ".js", expected: "application/javascript" }, + { ext: ".ts", expected: "text/typescript" }, + { ext: ".md", expected: "text/markdown" }, + { ext: ".png", expected: "image/png" }, + { ext: ".jpg", expected: "image/jpeg" }, + { ext: ".pdf", expected: "application/pdf" }, + { ext: ".unknown", expected: "application/octet-stream" }, + ]; + + for (const { ext, expected } of testCases) { + test(`should detect ${expected} for ${ext} files`, async () => { + await storage.write(`file${ext}`, "content"); + const metadata = await storage.getMetadata(`file${ext}`); + expect(metadata.mimeType).toBe(expected); + }); + } + }); + + describe("writeStream", () => { + test("should stream content to file", async () => { + const content = "Hello from stream!"; + const chunks = [Buffer.from(content)]; + + const stream = new Readable({ + read() { + const chunk = chunks.shift(); + this.push(chunk ?? null); + }, + }); + + const result = await storage.writeStream("stream-test.txt", stream); + + expect(result.bytesWritten).toBe(content.length); + expect(result.file.path).toBe("stream-test.txt"); + + const readBack = await storage.read("stream-test.txt"); + expect(readBack.content).toBe(content); + }); + + test("should stream large content without buffering", async () => { + // Create a 1MB stream in chunks + const chunkSize = 64 * 1024; // 64KB chunks + const totalSize = 1024 * 1024; // 1MB + let bytesGenerated = 0; + + const stream = new Readable({ + read() { + if (bytesGenerated >= totalSize) { + this.push(null); + return; + } + const size = Math.min(chunkSize, totalSize - bytesGenerated); + const chunk = Buffer.alloc(size, "x"); + bytesGenerated += size; + this.push(chunk); + }, + }); + + const result = await storage.writeStream("large-stream.bin", stream); + + expect(result.bytesWritten).toBe(totalSize); + + const metadata = await storage.getMetadata("large-stream.bin"); + expect(metadata.size).toBe(totalSize); + }); + + test("should create parent directories", async () => { + const stream = Readable.from([Buffer.from("nested content")]); + + const result = await storage.writeStream( + "deep/nested/path/file.txt", + stream, + { createParents: true }, + ); + + expect(result.file.path).toBe("deep/nested/path/file.txt"); + + const readBack = await storage.read("deep/nested/path/file.txt"); + expect(readBack.content).toBe("nested content"); + }); + + test("should fail if file exists and overwrite is false", async () => { + await storage.write("existing-stream.txt", "existing"); + + const stream = Readable.from([Buffer.from("new content")]); + + await expect( + storage.writeStream("existing-stream.txt", stream, { + overwrite: false, + }), + ).rejects.toThrow("File already exists"); + }); + + test("should overwrite if overwrite is true", async () => { + await storage.write("overwrite-stream.txt", "old"); + + const stream = Readable.from([Buffer.from("new content")]); + await storage.writeStream("overwrite-stream.txt", stream, { + overwrite: true, + }); + + const readBack = await storage.read("overwrite-stream.txt"); + expect(readBack.content).toBe("new content"); + }); + }); +}); + +describe("getExtensionFromMimeType", () => { + test("should return extension for known MIME types", () => { + expect(getExtensionFromMimeType("application/json")).toBe(".json"); + expect(getExtensionFromMimeType("image/png")).toBe(".png"); + expect(getExtensionFromMimeType("text/plain")).toBe(".txt"); + // .htm is shorter than .html so it's preferred + expect(getExtensionFromMimeType("text/html")).toBe(".htm"); + expect(getExtensionFromMimeType("application/pdf")).toBe(".pdf"); + }); + + test("should handle MIME types with charset", () => { + expect(getExtensionFromMimeType("application/json; charset=utf-8")).toBe( + ".json", + ); + expect(getExtensionFromMimeType("text/html; charset=UTF-8")).toBe(".htm"); + }); + + test("should return .ndjson for newline-delimited JSON", () => { + expect(getExtensionFromMimeType("application/x-ndjson")).toBe(".ndjson"); + expect(getExtensionFromMimeType("application/jsonl")).toBe(".jsonl"); + }); + + test("should return empty string for unknown MIME types", () => { + expect(getExtensionFromMimeType("application/x-unknown-format")).toBe(""); + }); + + test("should return .bin for octet-stream", () => { + expect(getExtensionFromMimeType("application/octet-stream")).toBe(".bin"); + }); +}); diff --git a/local-fs/server/storage.ts b/local-fs/server/storage.ts new file mode 100644 index 00000000..1b0b0ef4 --- /dev/null +++ b/local-fs/server/storage.ts @@ -0,0 +1,487 @@ +/** + * Local File Storage Implementation + * + * Portable filesystem operations that work with any mounted path. + */ + +import { + mkdir, + readFile, + writeFile, + unlink, + stat, + readdir, + rename, + copyFile, + rm, + open, +} from "node:fs/promises"; +import { dirname, basename, extname, resolve } from "node:path"; +import { existsSync } from "node:fs"; +import { Readable } from "node:stream"; +import { pipeline } from "node:stream/promises"; +import { logOp } from "./logger.js"; + +/** + * File entity returned by listing/metadata operations + */ +export interface FileEntity { + id: string; + title: string; + path: string; + parent: string; + mimeType: string; + size: number; + isDirectory: boolean; + created_at: string; + updated_at: string; +} + +/** + * MIME type lookup based on file extension + */ +const MIME_TYPES: Record = { + // Text + ".txt": "text/plain", + ".html": "text/html", + ".htm": "text/html", + ".css": "text/css", + ".csv": "text/csv", + // JavaScript/TypeScript + ".js": "application/javascript", + ".mjs": "application/javascript", + ".jsx": "text/javascript", + ".ts": "text/typescript", + ".tsx": "text/typescript", + // Data formats + ".json": "application/json", + ".xml": "application/xml", + ".yaml": "text/yaml", + ".yml": "text/yaml", + ".toml": "text/toml", + // Markdown + ".md": "text/markdown", + ".mdx": "text/mdx", + ".markdown": "text/markdown", + // Images + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".webp": "image/webp", + ".svg": "image/svg+xml", + ".ico": "image/x-icon", + ".avif": "image/avif", + // Documents + ".pdf": "application/pdf", + ".doc": "application/msword", + ".docx": + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".xls": "application/vnd.ms-excel", + ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + // Archives + ".zip": "application/zip", + ".tar": "application/x-tar", + ".gz": "application/gzip", + // Audio + ".mp3": "audio/mpeg", + ".wav": "audio/wav", + ".ogg": "audio/ogg", + // Video + ".mp4": "video/mp4", + ".webm": "video/webm", + ".mov": "video/quicktime", +}; + +function getMimeType(filename: string): string { + const ext = extname(filename).toLowerCase(); + return MIME_TYPES[ext] || "application/octet-stream"; +} + +/** + * Reverse MIME type lookup - get extension from MIME type + */ +const MIME_TO_EXT: Record = Object.entries(MIME_TYPES).reduce( + (acc, [ext, mime]) => { + // Don't overwrite if already set (prefer shorter extensions) + if (!acc[mime] || ext.length < acc[mime].length) { + acc[mime] = ext; + } + return acc; + }, + {} as Record, +); + +// Add common MIME types that might not have extensions in our map +Object.assign(MIME_TO_EXT, { + "application/octet-stream": ".bin", + "text/plain": ".txt", + "application/x-ndjson": ".ndjson", + "application/jsonl": ".jsonl", + "application/x-jsonlines": ".jsonl", +}); + +export function getExtensionFromMimeType(mimeType: string): string { + // Handle charset suffix (e.g., "application/json; charset=utf-8") + const baseMime = mimeType.split(";")[0].trim().toLowerCase(); + return MIME_TO_EXT[baseMime] || ""; +} + +function sanitizePath(path: string): string { + // Normalize backslashes to forward slashes (Windows compatibility) + return path + .replace(/\\/g, "/") + .split("/") + .filter((segment) => segment !== ".." && segment !== ".") + .join("/") + .replace(/^\/+/, ""); +} + +/** + * Local File Storage class + */ +export class LocalFileStorage { + private rootDir: string; + + constructor(rootDir: string) { + this.rootDir = resolve(rootDir); + } + + get root(): string { + return this.rootDir; + } + + /** + * Normalize a path by stripping the root directory prefix if present. + * This handles cases where AI agents mistakenly include the full root path. + */ + normalizePath(path: string): string { + let normalizedPath = path; + + // Strip root directory prefix if the path starts with it + // Must check for trailing slash, colon, or exact match to avoid matching paths like + // /tmp/rootEvil when root is /tmp/root + const rootWithSlash = this.rootDir + "/"; + const rootWithColon = this.rootDir + ":"; + if (normalizedPath.startsWith(rootWithSlash)) { + normalizedPath = normalizedPath.slice(rootWithSlash.length); + } else if (normalizedPath.startsWith(rootWithColon)) { + // Handle colon separator (e.g., "/path/to/root:filename.png") + normalizedPath = normalizedPath.slice(rootWithColon.length); + } else if (normalizedPath === this.rootDir) { + // Exact match - return root + normalizedPath = ""; + } + + // Handle standalone colon at start (edge case) + if (normalizedPath.startsWith(":")) { + normalizedPath = normalizedPath.slice(1); + } + + // Strip leading slashes + normalizedPath = normalizedPath.replace(/^\/+/, ""); + + return normalizedPath; + } + + /** + * Resolve a relative path to an absolute path within the storage root. + * Public for use by tools that need the absolute path (e.g., GET_PRESIGNED_URL). + */ + resolvePath(path: string): string { + const normalizedPath = this.normalizePath(path); + const sanitized = sanitizePath(normalizedPath); + const resolved = resolve(this.rootDir, sanitized); + + // Defense-in-depth: verify resolved path is within rootDir + if (!resolved.startsWith(this.rootDir)) { + throw new Error("Path traversal attempt detected"); + } + + return resolved; + } + + private async ensureDir(dir: string): Promise { + if (!existsSync(dir)) { + await mkdir(dir, { recursive: true }); + } + } + + async getMetadata(path: string): Promise { + const fullPath = this.resolvePath(path); + const stats = await stat(fullPath); + const name = basename(path) || path; + const parentPath = dirname(path); + const parent = parentPath === "." || parentPath === "/" ? "" : parentPath; + const isDirectory = stats.isDirectory(); + const mimeType = isDirectory ? "inode/directory" : getMimeType(name); + + return { + id: path || "/", + title: parent ? path : name || "Root", + path: path || "/", + parent, + mimeType, + size: stats.size, + isDirectory, + created_at: stats.birthtime.toISOString(), + updated_at: stats.mtime.toISOString(), + }; + } + + async read( + path: string, + encoding: "utf-8" | "base64" = "utf-8", + ): Promise<{ content: string; metadata: FileEntity }> { + const fullPath = this.resolvePath(path); + const buffer = await readFile(fullPath); + const content = + encoding === "base64" + ? buffer.toString("base64") + : buffer.toString("utf-8"); + const metadata = await this.getMetadata(path); + logOp("READ", path, { size: buffer.length }); + return { content, metadata }; + } + + async write( + path: string, + content: string, + options: { + encoding?: "utf-8" | "base64"; + createParents?: boolean; + overwrite?: boolean; + } = {}, + ): Promise<{ file: FileEntity }> { + const fullPath = this.resolvePath(path); + + if (options.createParents !== false) { + await this.ensureDir(dirname(fullPath)); + } + + if (options.overwrite === false && existsSync(fullPath)) { + throw new Error(`File already exists: ${path}`); + } + + const buffer = + options.encoding === "base64" + ? Buffer.from(content, "base64") + : Buffer.from(content, "utf-8"); + + await writeFile(fullPath, buffer); + const file = await this.getMetadata(path); + logOp("WRITE", path, { size: buffer.length }); + return { file }; + } + + async delete( + path: string, + recursive = false, + ): Promise<{ success: boolean; path: string }> { + const fullPath = this.resolvePath(path); + const stats = await stat(fullPath); + + if (stats.isDirectory()) { + if (!recursive) { + throw new Error("Cannot delete directory without recursive flag"); + } + await rm(fullPath, { recursive: true, force: true }); + } else { + await unlink(fullPath); + } + + logOp("DELETE", path); + return { success: true, path }; + } + + async list( + folder = "", + options: { recursive?: boolean; filesOnly?: boolean } = {}, + ): Promise { + const fullPath = this.resolvePath(folder); + + if (!existsSync(fullPath)) { + return []; + } + + if (options.recursive) { + const files = await this.listRecursive(folder, options.filesOnly); + logOp("LIST", folder || "/", { count: files.length, recursive: true }); + return files; + } + + const entries = await readdir(fullPath, { withFileTypes: true }); + let files: FileEntity[] = []; + + for (const entry of entries) { + if (entry.name.startsWith(".")) continue; + + // Skip directories if filesOnly is true + if (options.filesOnly && entry.isDirectory()) continue; + + const entryPath = folder ? `${folder}/${entry.name}` : entry.name; + try { + const metadata = await this.getMetadata(entryPath); + files.push(metadata); + } catch { + continue; + } + } + + // Sort: directories first, then by name (only relevant if not filesOnly) + files = files.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) return -1; + if (!a.isDirectory && b.isDirectory) return 1; + return a.title.localeCompare(b.title); + }); + + logOp("LIST", folder || "/", { count: files.length }); + return files; + } + + private async listRecursive( + folder = "", + filesOnly = false, + ): Promise { + const fullPath = this.resolvePath(folder); + + if (!existsSync(fullPath)) { + return []; + } + + const entries = await readdir(fullPath, { withFileTypes: true }); + const files: FileEntity[] = []; + + for (const entry of entries) { + if (entry.name.startsWith(".")) continue; + + const entryPath = folder ? `${folder}/${entry.name}` : entry.name; + + try { + const metadata = await this.getMetadata(entryPath); + + if (entry.isDirectory()) { + if (!filesOnly) { + files.push(metadata); + } + const subFiles = await this.listRecursive(entryPath, filesOnly); + files.push(...subFiles); + } else { + files.push(metadata); + } + } catch { + continue; + } + } + + return files; + } + + async mkdir(path: string, recursive = true): Promise<{ folder: FileEntity }> { + const fullPath = this.resolvePath(path); + await mkdir(fullPath, { recursive }); + const metadata = await this.getMetadata(path); + logOp("MKDIR", path); + return { folder: metadata }; + } + + async move( + from: string, + to: string, + overwrite = false, + ): Promise<{ file: FileEntity }> { + const fromPath = this.resolvePath(from); + const toPath = this.resolvePath(to); + + if (!overwrite && existsSync(toPath)) { + throw new Error(`Destination already exists: ${to}`); + } + + await this.ensureDir(dirname(toPath)); + await rename(fromPath, toPath); + const file = await this.getMetadata(to); + logOp("MOVE", from, { to }); + return { file }; + } + + async copy( + from: string, + to: string, + overwrite = false, + ): Promise<{ file: FileEntity }> { + const fromPath = this.resolvePath(from); + const toPath = this.resolvePath(to); + + if (!overwrite && existsSync(toPath)) { + throw new Error(`Destination already exists: ${to}`); + } + + await this.ensureDir(dirname(toPath)); + await copyFile(fromPath, toPath); + const file = await this.getMetadata(to); + logOp("COPY", from, { to }); + return { file }; + } + + /** + * Write a readable stream directly to disk without buffering in memory. + * Used for streaming large downloads directly to filesystem. + */ + async writeStream( + path: string, + stream: ReadableStream | NodeJS.ReadableStream, + options: { + createParents?: boolean; + overwrite?: boolean; + } = {}, + ): Promise<{ file: FileEntity; bytesWritten: number }> { + const fullPath = this.resolvePath(path); + + if (options.createParents !== false) { + await this.ensureDir(dirname(fullPath)); + } + + if (options.overwrite === false && existsSync(fullPath)) { + throw new Error(`File already exists: ${path}`); + } + + // Convert Web ReadableStream to Node.js Readable if needed + const nodeStream = + stream instanceof Readable + ? stream + : Readable.fromWeb( + stream as unknown as import("stream/web").ReadableStream, + ); + + // Track bytes written + let bytesWritten = 0; + + // Create write stream + const fileHandle = await open(fullPath, "w"); + const writeStream = fileHandle.createWriteStream(); + + // Create a passthrough that counts bytes + const countingStream = new Readable({ + read() {}, + }); + + nodeStream.on("data", (chunk: Buffer) => { + bytesWritten += chunk.length; + countingStream.push(chunk); + }); + + nodeStream.on("end", () => { + countingStream.push(null); + }); + + nodeStream.on("error", (err) => { + countingStream.destroy(err); + }); + + await pipeline(countingStream, writeStream); + + const file = await this.getMetadata(path); + logOp("WRITE_STREAM", path, { size: bytesWritten }); + return { file, bytesWritten }; + } +} diff --git a/local-fs/server/tools.ts b/local-fs/server/tools.ts new file mode 100644 index 00000000..72e1aad4 --- /dev/null +++ b/local-fs/server/tools.ts @@ -0,0 +1,1746 @@ +/** + * MCP Local FS - Tool Definitions + * + * This module contains all tool definitions following the official + * MCP filesystem server schema, plus collection bindings for Mesh. + * + * Uses registerTool() for proper annotation/hint support. + * + * @see https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem + */ + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { z } from "zod"; +import { + LocalFileStorage, + type FileEntity, + getExtensionFromMimeType, +} from "./storage.js"; +import { logTool } from "./logger.js"; + +/** + * Wrap a tool handler with logging + */ +function withLogging>( + toolName: string, + handler: (args: T) => Promise, +): (args: T) => Promise { + return async (args: T) => { + logTool(toolName, args as Record); + const result = await handler(args); + return result; + }; +} + +/** + * Register all filesystem tools on an MCP server + */ +export function registerTools(server: McpServer, storage: LocalFileStorage) { + // ============================================================ + // OFFICIAL MCP FILESYSTEM TOOLS + // Following exact schema from modelcontextprotocol/servers + // ============================================================ + + // read_file - primary file reading tool (official MCP name) + server.registerTool( + "read_file", + { + title: "Read File", + description: + "Read the complete contents of a file from the file system. " + + "Handles various text encodings and provides detailed error messages " + + "if the file cannot be read. Use this tool when you need to examine " + + "the contents of a single file. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the file to read"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("read_file", async (args) => + readTextFileHandler(storage, args), + ), + ); + + // read_text_file - alias for read_file with head/tail support + server.registerTool( + "read_text_file", + { + title: "Read Text File", + description: + "Read the complete contents of a file from the file system as text. " + + "Handles various text encodings and provides detailed error messages " + + "if the file cannot be read. Use this tool when you need to examine " + + "the contents of a single file. Use the 'head' parameter to read only " + + "the first N lines of a file, or the 'tail' parameter to read only " + + "the last N lines of a file. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the file to read"), + tail: z + .number() + .optional() + .describe("If provided, returns only the last N lines of the file"), + head: z + .number() + .optional() + .describe("If provided, returns only the first N lines of the file"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("read_text_file", async (args) => + readTextFileHandler(storage, args), + ), + ); + + // read_media_file - read binary files as base64 + server.registerTool( + "read_media_file", + { + title: "Read Media File", + description: + "Read an image or audio file. Returns the base64 encoded data and MIME type. " + + "Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the media file to read"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("read_media_file", async (args): Promise => { + try { + const result = await storage.read(args.path, "base64"); + const mimeType = result.metadata.mimeType; + const type = mimeType.startsWith("image/") + ? "image" + : mimeType.startsWith("audio/") + ? "audio" + : "blob"; + + const contentItem = { + type: type as "image" | "audio", + data: result.content, + mimeType, + }; + + // NOTE: Do NOT include structuredContent for media files + // The base64 data would get serialized to JSON and cause token explosion + return { + content: [contentItem], + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // read_multiple_files - read multiple files at once + server.registerTool( + "read_multiple_files", + { + title: "Read Multiple Files", + description: + "Read the contents of multiple files simultaneously. This is more " + + "efficient than reading files one by one when you need to analyze " + + "or compare multiple files. Each file's content is returned with its " + + "path as a reference. Failed reads for individual files won't stop " + + "the entire operation. Only works within allowed directories.", + inputSchema: { + paths: z + .array(z.string()) + .min(1) + .describe( + "Array of file paths to read. Each path must be a string pointing to a valid file.", + ), + }, + annotations: { readOnlyHint: true }, + }, + withLogging( + "read_multiple_files", + async (args): Promise => { + const results = await Promise.all( + args.paths.map(async (filePath: string) => { + try { + const result = await storage.read(filePath, "utf-8"); + return `${filePath}:\n${result.content}\n`; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + return `${filePath}: Error - ${errorMessage}`; + } + }), + ); + const text = results.join("\n---\n"); + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + }, + ), + ); + + // write_file - write content to a file + server.registerTool( + "write_file", + { + title: "Write File", + description: + "Create a new file or completely overwrite an existing file with new content. " + + "Use with caution as it will overwrite existing files without warning. " + + "Handles text content with proper encoding. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path where the file should be written"), + content: z.string().describe("Content to write to the file"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: true, + destructiveHint: true, + }, + }, + withLogging("write_file", async (args): Promise => { + try { + await storage.write(args.path, args.content, { + encoding: "utf-8", + createParents: true, + overwrite: true, + }); + const text = `Successfully wrote to ${args.path}`; + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // edit_file - make search/replace edits with diff preview + server.registerTool( + "edit_file", + { + title: "Edit File", + description: + "Make line-based edits to a text file. Each edit replaces exact text sequences " + + "with new content. Returns a git-style diff showing the changes made. " + + "Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the file to edit"), + edits: z.array( + z.object({ + oldText: z + .string() + .describe("Text to search for - must match exactly"), + newText: z.string().describe("Text to replace with"), + }), + ), + dryRun: z + .boolean() + .default(false) + .describe("Preview changes using git-style diff format"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: true, + }, + }, + withLogging("edit_file", async (args): Promise => { + try { + const result = await storage.read(args.path, "utf-8"); + let content = result.content; + const originalContent = content; + + // Apply all edits + for (const edit of args.edits) { + if (!content.includes(edit.oldText)) { + return { + content: [ + { + type: "text", + text: `Error: Could not find text to replace: "${edit.oldText.slice(0, 50)}..."`, + }, + ], + isError: true, + }; + } + content = content.replace(edit.oldText, edit.newText); + } + + // Generate diff + const diff = generateDiff(args.path, originalContent, content); + + if (args.dryRun) { + return { + content: [ + { + type: "text", + text: `Dry run - changes not applied:\n\n${diff}`, + }, + ], + structuredContent: { content: diff, dryRun: true }, + }; + } + + // Apply changes + await storage.write(args.path, content, { + encoding: "utf-8", + createParents: false, + overwrite: true, + }); + + return { + content: [{ type: "text", text: diff }], + structuredContent: { content: diff }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // create_directory - create directories + server.registerTool( + "create_directory", + { + title: "Create Directory", + description: + "Create a new directory or ensure a directory exists. Can create multiple " + + "nested directories in one operation. If the directory already exists, " + + "this operation will succeed silently. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path of the directory to create"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: true, + destructiveHint: false, + }, + }, + withLogging("create_directory", async (args): Promise => { + try { + await storage.mkdir(args.path, true); + const text = `Successfully created directory ${args.path}`; + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // list_directory - simple directory listing + server.registerTool( + "list_directory", + { + title: "List Directory", + description: + "Get a detailed listing of all files and directories in a specified path. " + + "Results clearly distinguish between files and directories with [FILE] and [DIR] " + + "prefixes. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path of the directory to list"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("list_directory", async (args): Promise => { + try { + const items = await storage.list(args.path); + const formatted = items + .map( + (entry) => + `${entry.isDirectory ? "[DIR]" : "[FILE]"} ${entry.title}`, + ) + .join("\n"); + return { + content: [{ type: "text", text: formatted || "Empty directory" }], + structuredContent: { content: formatted }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // list_directory_with_sizes - listing with file sizes + server.registerTool( + "list_directory_with_sizes", + { + title: "List Directory with Sizes", + description: + "Get a detailed listing of all files and directories in a specified path, including sizes. " + + "Results clearly distinguish between files and directories. " + + "Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path of the directory to list"), + sortBy: z + .enum(["name", "size"]) + .optional() + .default("name") + .describe("Sort entries by name or size"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging( + "list_directory_with_sizes", + async (args): Promise => { + try { + const items = await storage.list(args.path); + + // Sort entries + const sortedItems = [...items].sort((a, b) => { + if (args.sortBy === "size") { + return b.size - a.size; + } + return a.title.localeCompare(b.title); + }); + + // Format output + const formatted = sortedItems + .map( + (entry) => + `${entry.isDirectory ? "[DIR]" : "[FILE]"} ${entry.title.padEnd(30)} ${ + entry.isDirectory ? "" : formatSize(entry.size).padStart(10) + }`, + ) + .join("\n"); + + // Summary + const totalFiles = items.filter((e) => !e.isDirectory).length; + const totalDirs = items.filter((e) => e.isDirectory).length; + const totalSize = items.reduce( + (sum, entry) => sum + (entry.isDirectory ? 0 : entry.size), + 0, + ); + + const summary = `\nTotal: ${totalFiles} files, ${totalDirs} directories\nCombined size: ${formatSize(totalSize)}`; + const text = formatted + summary; + + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }, + ), + ); + + // directory_tree - recursive tree view as JSON + server.registerTool( + "directory_tree", + { + title: "Directory Tree", + description: + "Get a recursive tree view of files and directories as a JSON structure. " + + "Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " + + "Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path of the root directory for the tree"), + excludePatterns: z + .array(z.string()) + .optional() + .default([]) + .describe("Glob patterns to exclude from the tree"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("directory_tree", async (args): Promise => { + try { + const tree = await buildDirectoryTree( + storage, + args.path, + args.excludePatterns, + ); + const text = JSON.stringify(tree, null, 2); + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // move_file - move or rename files + server.registerTool( + "move_file", + { + title: "Move File", + description: + "Move or rename files and directories. Can move files between directories " + + "and rename them in a single operation. If the destination exists, the " + + "operation will fail. Only works within allowed directories.", + inputSchema: { + source: z.string().describe("Source path of the file or directory"), + destination: z.string().describe("Destination path"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: false, + }, + }, + withLogging("move_file", async (args): Promise => { + try { + await storage.move(args.source, args.destination, false); + const text = `Successfully moved ${args.source} to ${args.destination}`; + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // search_files - search with glob patterns + server.registerTool( + "search_files", + { + title: "Search Files", + description: + "Recursively search for files and directories matching a pattern. " + + "Searches file names (not content). Returns full paths to all matching items. " + + "Only searches within allowed directories.", + inputSchema: { + path: z.string().describe("Starting directory for the search"), + pattern: z + .string() + .describe("Search pattern (supports * and ** wildcards)"), + excludePatterns: z + .array(z.string()) + .optional() + .default([]) + .describe("Patterns to exclude from search"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("search_files", async (args): Promise => { + try { + const results = await searchFiles( + storage, + args.path, + args.pattern, + args.excludePatterns, + ); + const text = + results.length > 0 ? results.join("\n") : "No matches found"; + return { + content: [{ type: "text", text }], + structuredContent: { content: text, matches: results }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // get_file_info - get detailed file metadata + server.registerTool( + "get_file_info", + { + title: "Get File Info", + description: + "Retrieve detailed metadata about a file or directory. Returns comprehensive " + + "information including size, creation time, last modified time, and type. " + + "Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the file or directory"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("get_file_info", async (args): Promise => { + try { + const metadata = await storage.getMetadata(args.path); + const info = { + path: metadata.path, + type: metadata.isDirectory ? "directory" : "file", + size: metadata.size, + sizeFormatted: formatSize(metadata.size), + mimeType: metadata.mimeType, + created: metadata.created_at, + modified: metadata.updated_at, + }; + const text = Object.entries(info) + .map(([key, value]) => `${key}: ${value}`) + .join("\n"); + return { + content: [{ type: "text", text }], + structuredContent: info, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // list_allowed_directories - show the root directory + server.registerTool( + "list_allowed_directories", + { + title: "List Allowed Directories", + description: + "Returns the list of directories that this server is allowed to access. " + + "Use this to understand which directories are available.", + inputSchema: {}, + annotations: { readOnlyHint: true }, + }, + withLogging( + "list_allowed_directories", + async (): Promise => { + const text = `Allowed directories:\n${storage.root}`; + return { + content: [{ type: "text", text }], + structuredContent: { directories: [storage.root] }, + }; + }, + ), + ); + + // ============================================================ + // ADDITIONAL TOOLS (not in official, but useful) + // ============================================================ + + // delete_file - delete files or directories (official doesn't have this!) + server.registerTool( + "delete_file", + { + title: "Delete File", + description: + "Delete a file or directory. Use recursive=true to delete non-empty directories. " + + "Use with caution as this operation cannot be undone. Only works within allowed directories.", + inputSchema: { + path: z.string().describe("Path to the file or directory to delete"), + recursive: z + .boolean() + .default(false) + .describe( + "If true, recursively delete directories and their contents", + ), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: true, + }, + }, + withLogging("delete_file", async (args): Promise => { + try { + await storage.delete(args.path, args.recursive); + const text = `Successfully deleted ${args.path}`; + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // copy_file - copy files (official doesn't have this!) + server.registerTool( + "copy_file", + { + title: "Copy File", + description: + "Copy a file to a new location. The destination must not exist unless overwrite is true. " + + "Only works within allowed directories.", + inputSchema: { + source: z.string().describe("Source path of the file to copy"), + destination: z.string().describe("Destination path for the copy"), + overwrite: z + .boolean() + .default(false) + .describe("If true, overwrite the destination if it exists"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: true, + destructiveHint: false, + }, + }, + withLogging("copy_file", async (args): Promise => { + try { + await storage.copy(args.source, args.destination, args.overwrite); + const text = `Successfully copied ${args.source} to ${args.destination}`; + return { + content: [{ type: "text", text }], + structuredContent: { content: text }, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // fetch_to_file - fetch URL and stream directly to disk + server.registerTool( + "fetch_to_file", + { + title: "Fetch URL to File", + description: + "Fetch content from a URL and save it directly to disk using streaming. " + + "Content is streamed without loading into memory, making it efficient for large files. " + + "Filename is extracted from URL path or Content-Disposition header. " + + "File extension is intelligently determined from Content-Type when not in filename. " + + "Perfect for downloading large datasets, images, or any remote content without " + + "consuming context window tokens. Only works within allowed directories.", + inputSchema: { + url: z.string().describe("The URL to fetch content from"), + filename: z + .string() + .optional() + .describe( + "Optional filename to save as. If not provided, extracted from URL or Content-Disposition header", + ), + directory: z + .string() + .default("") + .describe( + "Directory to save the file in (relative to storage root). Defaults to root.", + ), + overwrite: z + .boolean() + .default(false) + .describe("If true, overwrite existing file"), + headers: z + .record(z.string(), z.string()) + .optional() + .describe( + "Optional HTTP headers to send with the request (e.g., Authorization)", + ), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: false, + }, + }, + withLogging("fetch_to_file", async (args): Promise => { + try { + const fetchHeaders: Record = { + "User-Agent": "MCP-LocalFS/1.0", + ...(args.headers || {}), + }; + + const response = await fetch(args.url, { + headers: fetchHeaders, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + if (!response.body) { + throw new Error("Response has no body"); + } + + // Determine filename + let filename = args.filename; + + if (!filename) { + // Try Content-Disposition header first + const disposition = response.headers.get("Content-Disposition"); + if (disposition) { + const filenameMatch = disposition.match( + /filename[*]?=(?:UTF-8'')?["']?([^"';\n]+)["']?/i, + ); + if (filenameMatch) { + filename = decodeURIComponent(filenameMatch[1].trim()); + } + } + + // Fall back to URL path + if (!filename) { + const urlObj = new URL(args.url); + const pathParts = urlObj.pathname.split("/").filter(Boolean); + filename = + pathParts.length > 0 + ? pathParts[pathParts.length - 1] + : "download"; + } + } + + // Check if filename has extension, if not try to add from Content-Type + const hasExtension = filename.includes("."); + if (!hasExtension) { + const contentType = response.headers.get("Content-Type"); + if (contentType) { + const ext = getExtensionFromMimeType(contentType); + if (ext) { + filename = filename + ext; + } + } + } + + // Sanitize filename + filename = filename.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_"); + + // Build full path + const directory = args.directory || ""; + const fullPath = directory ? `${directory}/${filename}` : filename; + + // Stream to disk + const result = await storage.writeStream(fullPath, response.body, { + createParents: true, + overwrite: args.overwrite, + }); + + const summary = { + path: result.file.path, + size: result.bytesWritten, + sizeFormatted: formatSize(result.bytesWritten), + mimeType: result.file.mimeType, + url: args.url, + }; + + const text = + `Successfully downloaded ${args.url}\n` + + `Saved to: ${result.file.path}\n` + + `Size: ${formatSize(result.bytesWritten)}`; + + return { + content: [{ type: "text", text }], + structuredContent: summary, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // ============================================================ + // OBJECT STORAGE BINDING TOOLS + // These implement the OBJECT_STORAGE_BINDING for the Files plugin + // ============================================================ + + // LIST_OBJECTS - List files with S3-like interface + server.registerTool( + "LIST_OBJECTS", + { + title: "List Objects", + description: + "List files and folders with S3-compatible interface. " + + "Use prefix to filter by folder path, delimiter '/' to group by folders.", + inputSchema: { + prefix: z + .string() + .optional() + .default("") + .describe( + "Filter objects by prefix (e.g., 'folder/' for folder contents)", + ), + maxKeys: z + .number() + .optional() + .default(1000) + .describe("Maximum number of keys to return"), + continuationToken: z + .string() + .optional() + .describe("Token for pagination (offset as string)"), + delimiter: z + .string() + .optional() + .describe("Delimiter for grouping keys (typically '/')"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("LIST_OBJECTS", async (args): Promise => { + try { + const prefix = args.prefix || ""; + const maxKeys = args.maxKeys || 1000; + const offset = args.continuationToken + ? parseInt(args.continuationToken, 10) + : 0; + const useDelimiter = args.delimiter === "/"; + + // List all items in the prefix directory + const allItems = await storage.list(prefix, { + recursive: !useDelimiter, + filesOnly: false, + }); + + // Build response + const objects: Array<{ + key: string; + size: number; + lastModified: string; + etag: string; + }> = []; + + const commonPrefixes: string[] = []; + const seenPrefixes = new Set(); + + for (const item of allItems) { + if (item.isDirectory) { + if (useDelimiter) { + // Add as common prefix (folder) + const folderPath = item.path.endsWith("/") + ? item.path + : item.path + "/"; + if (!seenPrefixes.has(folderPath)) { + seenPrefixes.add(folderPath); + commonPrefixes.push(folderPath); + } + } + } else { + objects.push({ + key: item.path, + size: item.size, + lastModified: item.updated_at || new Date().toISOString(), + etag: `"${item.id}"`, + }); + } + } + + // Apply pagination + const paginatedObjects = objects.slice(offset, offset + maxKeys); + const hasMore = offset + maxKeys < objects.length; + + const result = { + objects: paginatedObjects, + commonPrefixes: useDelimiter ? commonPrefixes : undefined, + isTruncated: hasMore, + nextContinuationToken: hasMore ? String(offset + maxKeys) : undefined, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // GET_OBJECT_METADATA - Get file metadata + server.registerTool( + "GET_OBJECT_METADATA", + { + title: "Get Object Metadata", + description: "Get metadata for a file (size, type, modified time).", + inputSchema: { + key: z.string().describe("Object key/path to get metadata for"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging( + "GET_OBJECT_METADATA", + async (args): Promise => { + try { + const metadata = await storage.getMetadata(args.key); + + const result = { + contentType: metadata.mimeType, + contentLength: metadata.size, + lastModified: metadata.updated_at || new Date().toISOString(), + etag: `"${metadata.id}"`, + metadata: {}, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }, + ), + ); + + // GET_PRESIGNED_URL - Return file URL for local filesystem + server.registerTool( + "GET_PRESIGNED_URL", + { + title: "Get Presigned URL", + description: + "Get a URL for downloading a file. For local filesystem, returns a file:// URL or base64 data URL for images.", + inputSchema: { + key: z.string().describe("Object key/path to generate URL for"), + expiresIn: z + .number() + .optional() + .describe("Ignored for local filesystem"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("GET_PRESIGNED_URL", async (args): Promise => { + try { + const metadata = await storage.getMetadata(args.key); + const absolutePath = storage.resolvePath(args.key); + + // For images, return data URL for preview in browser + if (metadata.mimeType.startsWith("image/")) { + const fileResult = await storage.read(args.key, "base64"); + const dataUrl = `data:${metadata.mimeType};base64,${fileResult.content}`; + + const result = { + url: dataUrl, + expiresIn: 3600, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } + + // For other files, return file:// URL + const result = { + url: `file://${absolutePath}`, + expiresIn: 3600, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // PUT_PRESIGNED_URL - Return upload instructions for local filesystem + server.registerTool( + "PUT_PRESIGNED_URL", + { + title: "Put Presigned URL", + description: + "Get a URL for uploading a file. For local filesystem, returns instructions to use write_file tool.", + inputSchema: { + key: z.string().describe("Object key/path for the upload"), + expiresIn: z + .number() + .optional() + .describe("Ignored for local filesystem"), + contentType: z.string().optional().describe("MIME type (optional)"), + }, + annotations: { readOnlyHint: true }, + }, + withLogging("PUT_PRESIGNED_URL", async (args): Promise => { + // For local filesystem, we can't provide a presigned URL + // Instead, provide the path and instructions + const absolutePath = storage.resolvePath(args.key); + + const result = { + url: `file://${absolutePath}`, + expiresIn: 3600, + // Note: The Files plugin may need to use write_file instead + _note: "Use write_file tool to upload content to this path", + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + }), + ); + + // DELETE_OBJECT - Delete a single file + server.registerTool( + "DELETE_OBJECT", + { + title: "Delete Object", + description: "Delete a single file or empty directory.", + inputSchema: { + key: z.string().describe("Object key/path to delete"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: true, + }, + }, + withLogging("DELETE_OBJECT", async (args): Promise => { + try { + await storage.delete(args.key, false); + + const result = { + success: true, + key: args.key, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } catch (error) { + const result = { + success: false, + key: args.key, + error: (error as Error).message, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } + }), + ); + + // DELETE_OBJECTS - Batch delete files + server.registerTool( + "DELETE_OBJECTS", + { + title: "Delete Objects", + description: "Delete multiple files in batch.", + inputSchema: { + keys: z + .array(z.string()) + .max(1000) + .describe("Array of object keys/paths to delete"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: true, + }, + }, + withLogging("DELETE_OBJECTS", async (args): Promise => { + const deleted: string[] = []; + const errors: Array<{ key: string; message: string }> = []; + + for (const key of args.keys) { + try { + await storage.delete(key, false); + deleted.push(key); + } catch (error) { + errors.push({ + key, + message: (error as Error).message, + }); + } + } + + const result = { deleted, errors }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + }), + ); + + // GET_ROOT - Get the root folder path (for Task Runner integration) + server.registerTool( + "GET_ROOT", + { + title: "Get Root Path", + description: + "Get the root folder path that this MCP is serving. " + + "Returns the absolute path to the workspace root directory.", + inputSchema: {}, + annotations: { readOnlyHint: true }, + }, + withLogging("GET_ROOT", async (): Promise => { + const result = { root: storage.root }; + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + }), + ); + + // SKILLS_LIST - List available skills from the skills/ directory + server.registerTool( + "SKILLS_LIST", + { + title: "List Skills", + description: + "List available skills from the skills/ directory. " + + "Each skill is defined by a SKILL.md file with YAML frontmatter.", + inputSchema: {}, + annotations: { readOnlyHint: true }, + }, + withLogging("SKILLS_LIST", async (): Promise => { + try { + const { readdir, readFile } = await import("node:fs/promises"); + const { join } = await import("node:path"); + + const skillsDir = join(storage.root, "skills"); + const skills: Array<{ + id: string; + name: string; + description: string; + path: string; + }> = []; + + try { + const entries = await readdir(skillsDir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isDirectory() && !entry.name.startsWith(".")) { + const skillPath = join(skillsDir, entry.name, "SKILL.md"); + try { + const content = await readFile(skillPath, "utf-8"); + + // Parse YAML frontmatter + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (frontmatterMatch) { + const frontmatter = frontmatterMatch[1]; + const nameMatch = frontmatter.match(/^name:\s*(.+)$/m); + const descMatch = frontmatter.match(/^description:\s*(.+)$/m); + + skills.push({ + id: entry.name, + name: nameMatch?.[1] || entry.name, + description: descMatch?.[1] || "", + path: `skills/${entry.name}/SKILL.md`, + }); + } + } catch { + // Skip if SKILL.md doesn't exist + } + } + } + } catch { + // skills/ directory doesn't exist + } + + const result = { skills }; + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } catch (error) { + return { + content: [ + { type: "text", text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } + }), + ); + + // ============================================================ + // EXEC TOOL - Execute commands in the workspace + // ============================================================ + + // EXEC - Execute a command in the workspace directory + server.registerTool( + "EXEC", + { + title: "Execute Command", + description: + "Execute a shell command in the workspace directory. " + + "Returns stdout, stderr, and exit code. " + + "Use this for running build tools, linters, dev servers, etc. " + + "Commands run with the workspace as the current working directory.", + inputSchema: { + command: z + .string() + .describe( + "The command to execute (e.g., 'deno task start', 'npm run build')", + ), + timeout: z + .number() + .optional() + .default(30000) + .describe("Timeout in milliseconds (default: 30000)"), + background: z + .boolean() + .optional() + .default(false) + .describe( + "Run in background and return immediately (for long-running processes like dev servers)", + ), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: false, + }, + }, + withLogging("EXEC", async (args): Promise => { + try { + const { spawn } = await import("node:child_process"); + const { promisify } = await import("node:util"); + + if (args.background) { + // For background processes, spawn detached and return immediately + const [cmd, ...cmdArgs] = args.command.split(" "); + const child = spawn(cmd, cmdArgs, { + cwd: storage.root, + detached: true, + stdio: "ignore", + shell: true, + }); + child.unref(); + + const result = { + success: true, + background: true, + pid: child.pid, + message: `Started background process: ${args.command} (PID: ${child.pid})`, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } + + // For foreground processes, wait for completion + const { exec } = await import("node:child_process"); + const execPromise = promisify(exec); + + const { stdout, stderr } = await execPromise(args.command, { + cwd: storage.root, + timeout: args.timeout, + maxBuffer: 10 * 1024 * 1024, // 10MB + }); + + const result = { + success: true, + exitCode: 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }; + + const text = + [ + result.stdout && `stdout:\n${result.stdout}`, + result.stderr && `stderr:\n${result.stderr}`, + ] + .filter(Boolean) + .join("\n\n") || "Command completed successfully (no output)"; + + return { + content: [{ type: "text", text }], + structuredContent: result, + }; + } catch (error: any) { + const result = { + success: false, + exitCode: error.code ?? 1, + stdout: error.stdout?.trim() ?? "", + stderr: error.stderr?.trim() ?? error.message, + error: error.message, + }; + + const text = [ + `Error: ${error.message}`, + result.stdout && `stdout:\n${result.stdout}`, + result.stderr && `stderr:\n${result.stderr}`, + ] + .filter(Boolean) + .join("\n\n"); + + return { + content: [{ type: "text", text }], + structuredContent: result, + isError: true, + }; + } + }), + ); + + // DENO_TASK - Convenience tool for running deno tasks + server.registerTool( + "DENO_TASK", + { + title: "Run Deno Task", + description: + "Run a deno task from the deno.json/deno.jsonc file. " + + "Equivalent to running 'deno task ' in the workspace.", + inputSchema: { + task: z + .string() + .describe("The task name (e.g., 'start', 'build', 'dev')"), + background: z + .boolean() + .optional() + .default(false) + .describe("Run in background (for dev servers)"), + }, + annotations: { + readOnlyHint: false, + idempotentHint: false, + destructiveHint: false, + }, + }, + withLogging("DENO_TASK", async (args): Promise => { + try { + const { spawn, exec } = await import("node:child_process"); + const { promisify } = await import("node:util"); + + const command = `deno task ${args.task}`; + + if (args.background) { + const child = spawn("deno", ["task", args.task], { + cwd: storage.root, + detached: true, + stdio: "ignore", + shell: true, + }); + child.unref(); + + const result = { + success: true, + background: true, + pid: child.pid, + task: args.task, + message: `Started deno task '${args.task}' in background (PID: ${child.pid})`, + }; + + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result, + }; + } + + const execPromise = promisify(exec); + const { stdout, stderr } = await execPromise(command, { + cwd: storage.root, + timeout: 60000, // 1 minute for tasks + maxBuffer: 10 * 1024 * 1024, + }); + + const result = { + success: true, + task: args.task, + exitCode: 0, + stdout: stdout.trim(), + stderr: stderr.trim(), + }; + + const text = [ + `Task '${args.task}' completed`, + result.stdout && `stdout:\n${result.stdout}`, + result.stderr && `stderr:\n${result.stderr}`, + ] + .filter(Boolean) + .join("\n\n"); + + return { + content: [{ type: "text", text }], + structuredContent: result, + }; + } catch (error: any) { + const result = { + success: false, + task: args.task, + exitCode: error.code ?? 1, + stdout: error.stdout?.trim() ?? "", + stderr: error.stderr?.trim() ?? error.message, + error: error.message, + }; + + return { + content: [ + { + type: "text", + text: `Error running task '${args.task}': ${error.message}`, + }, + ], + structuredContent: result, + isError: true, + }; + } + }), + ); +} + +// ============================================================ +// HELPER FUNCTIONS +// ============================================================ + +/** + * Handler for read_file and read_text_file + */ +async function readTextFileHandler( + storage: LocalFileStorage, + args: { path: string; head?: number; tail?: number }, +): Promise { + try { + if (args.head && args.tail) { + return { + content: [ + { + type: "text" as const, + text: "Error: Cannot specify both head and tail parameters simultaneously", + }, + ], + isError: true, + }; + } + + const result = await storage.read(args.path, "utf-8"); + let content = result.content; + + if (args.tail) { + const lines = content.split("\n"); + content = lines.slice(-args.tail).join("\n"); + } else if (args.head) { + const lines = content.split("\n"); + content = lines.slice(0, args.head).join("\n"); + } + + return { + content: [{ type: "text" as const, text: content }], + structuredContent: { content }, + }; + } catch (error) { + return { + content: [ + { type: "text" as const, text: `Error: ${(error as Error).message}` }, + ], + isError: true, + }; + } +} + +/** + * Format file size in human-readable format + */ +function formatSize(bytes: number): string { + const units = ["B", "KB", "MB", "GB", "TB"]; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`; +} + +/** + * Generate a simple diff between two strings + */ +function generateDiff( + path: string, + original: string, + modified: string, +): string { + const originalLines = original.split("\n"); + const modifiedLines = modified.split("\n"); + + const lines: string[] = [`--- a/${path}`, `+++ b/${path}`]; + + // Simple line-by-line diff + const maxLen = Math.max(originalLines.length, modifiedLines.length); + let inHunk = false; + let hunkStart = 0; + let hunkLines: string[] = []; + + for (let i = 0; i < maxLen; i++) { + const orig = originalLines[i]; + const mod = modifiedLines[i]; + + if (orig !== mod) { + if (!inHunk) { + inHunk = true; + hunkStart = i + 1; + // Add context before + if (i > 0) hunkLines.push(` ${originalLines[i - 1]}`); + } + + if (orig !== undefined) { + hunkLines.push(`-${orig}`); + } + if (mod !== undefined) { + hunkLines.push(`+${mod}`); + } + } else if (inHunk) { + hunkLines.push(` ${orig}`); + // Close hunk after context + lines.push( + `@@ -${hunkStart},${hunkLines.length} +${hunkStart},${hunkLines.length} @@`, + ); + lines.push(...hunkLines); + hunkLines = []; + inHunk = false; + } + } + + if (hunkLines.length > 0) { + lines.push( + `@@ -${hunkStart},${hunkLines.length} +${hunkStart},${hunkLines.length} @@`, + ); + lines.push(...hunkLines); + } + + return lines.join("\n"); +} + +/** + * Build a recursive directory tree + */ +interface TreeEntry { + name: string; + type: "file" | "directory"; + children?: TreeEntry[]; +} + +async function buildDirectoryTree( + storage: LocalFileStorage, + path: string, + excludePatterns: string[], +): Promise { + const items = await storage.list(path); + const result: TreeEntry[] = []; + + for (const item of items) { + // Check exclusions + const shouldExclude = excludePatterns.some((pattern) => { + if (pattern.includes("*")) { + return matchGlob(item.title, pattern); + } + return item.title === pattern; + }); + + if (shouldExclude) continue; + + const entry: TreeEntry = { + name: item.title.split("/").pop() || item.title, + type: item.isDirectory ? "directory" : "file", + }; + + if (item.isDirectory) { + entry.children = await buildDirectoryTree( + storage, + item.path, + excludePatterns, + ); + } + + result.push(entry); + } + + return result; +} + +/** + * Search for files matching a pattern + */ +async function searchFiles( + storage: LocalFileStorage, + basePath: string, + pattern: string, + excludePatterns: string[], +): Promise { + const items = await storage.list(basePath, { recursive: true }); + const results: string[] = []; + + for (const item of items) { + // Check exclusions + const shouldExclude = excludePatterns.some((p) => matchGlob(item.path, p)); + if (shouldExclude) continue; + + // Check pattern match + if (matchGlob(item.path, pattern) || matchGlob(item.title, pattern)) { + results.push(item.path); + } + } + + return results; +} + +/** + * Simple glob pattern matching + */ +function matchGlob(str: string, pattern: string): boolean { + // Convert glob to regex + const regex = pattern + .replace(/\*\*/g, "<<>>") + .replace(/\*/g, "[^/]*") + .replace(/<<>>/g, ".*") + .replace(/\?/g, ".") + .replace(/\./g, "\\."); + + return new RegExp(`^${regex}$`).test(str) || new RegExp(regex).test(str); +} diff --git a/local-fs/tsconfig.json b/local-fs/tsconfig.json new file mode 100644 index 00000000..bfc3e6aa --- /dev/null +++ b/local-fs/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "server", + "declaration": true, + "resolveJsonModule": true + }, + "include": ["server/**/*.ts"], + "exclude": ["node_modules", "dist", "server/**/*.test.ts"] +} diff --git a/package.json b/package.json index 880b6e88..ea438f34 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "workspaces": [ "apify", + "task-runner", "blog-post-generator", "content-scraper", "data-for-seo", diff --git a/task-runner/.beads/.gitignore b/task-runner/.beads/.gitignore new file mode 100644 index 00000000..22f79637 --- /dev/null +++ b/task-runner/.beads/.gitignore @@ -0,0 +1,45 @@ +# SQLite databases +*.db +*.db?* +*.db-journal +*.db-wal +*.db-shm + +# Daemon runtime files +daemon.lock +daemon.log +daemon.pid +bd.sock +sync-state.json +last-touched + +# Local version tracking (prevents upgrade notification spam after git ops) +.local_version + +# Legacy database files +db.sqlite +bd.db + +# Worktree redirect file (contains relative path to main repo's .beads/) +# Must not be committed as paths would be wrong in other clones +redirect + +# Merge artifacts (temporary files from 3-way merge) +beads.base.jsonl +beads.base.meta.json +beads.left.jsonl +beads.left.meta.json +beads.right.jsonl +beads.right.meta.json + +# Sync state (local-only, per-machine) +# These files are machine-specific and should not be shared across clones +.sync.lock +sync_base.jsonl +export-state/ + +# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. +# They would override fork protection in .git/info/exclude, allowing +# contributors to accidentally commit upstream issue databases. +# The JSONL files (issues.jsonl, interactions.jsonl) and config files +# are tracked by git by default since no pattern above ignores them. diff --git a/task-runner/.beads/README.md b/task-runner/.beads/README.md new file mode 100644 index 00000000..50f281f0 --- /dev/null +++ b/task-runner/.beads/README.md @@ -0,0 +1,81 @@ +# Beads - AI-Native Issue Tracking + +Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. + +## What is Beads? + +Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. + +**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) + +## Quick Start + +### Essential Commands + +```bash +# Create new issues +bd create "Add user authentication" + +# View all issues +bd list + +# View issue details +bd show + +# Update issue status +bd update --status in_progress +bd update --status done + +# Sync with git remote +bd sync +``` + +### Working with Issues + +Issues in Beads are: +- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code +- **AI-friendly**: CLI-first design works perfectly with AI coding agents +- **Branch-aware**: Issues can follow your branch workflow +- **Always in sync**: Auto-syncs with your commits + +## Why Beads? + +✨ **AI-Native Design** +- Built specifically for AI-assisted development workflows +- CLI-first interface works seamlessly with AI coding agents +- No context switching to web UIs + +🚀 **Developer Focused** +- Issues live in your repo, right next to your code +- Works offline, syncs when you push +- Fast, lightweight, and stays out of your way + +🔧 **Git Integration** +- Automatic sync with git commits +- Branch-aware issue tracking +- Intelligent JSONL merge resolution + +## Get Started with Beads + +Try Beads in your own projects: + +```bash +# Install Beads +curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash + +# Initialize in your repo +bd init + +# Create your first issue +bd create "Try out Beads" +``` + +## Learn More + +- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) +- **Quick Start Guide**: Run `bd quickstart` +- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) + +--- + +*Beads: Issue tracking that moves at the speed of thought* ⚡ diff --git a/task-runner/.beads/config.yaml b/task-runner/.beads/config.yaml new file mode 100644 index 00000000..ff8bc921 --- /dev/null +++ b/task-runner/.beads/config.yaml @@ -0,0 +1,67 @@ +# Beads Configuration File +# This file configures default behavior for all bd commands in this repository +# All settings can also be set via environment variables (BD_* prefix) +# or overridden with command-line flags + +# Issue prefix for this repository (used by bd init) +# If not set, bd init will auto-detect from directory name +# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. +# issue-prefix: "" + +# Use no-db mode: load from JSONL, no SQLite, write back after each command +# When true, bd will use .beads/issues.jsonl as the source of truth +# instead of SQLite database +# no-db: false + +# Disable daemon for RPC communication (forces direct database access) +# no-daemon: false + +# Disable auto-flush of database to JSONL after mutations +# no-auto-flush: false + +# Disable auto-import from JSONL when it's newer than database +# no-auto-import: false + +# Enable JSON output by default +# json: false + +# Default actor for audit trails (overridden by BD_ACTOR or --actor) +# actor: "" + +# Path to database (overridden by BEADS_DB or --db) +# db: "" + +# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON) +# auto-start-daemon: true + +# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE) +# flush-debounce: "5s" + +# Export events (audit trail) to .beads/events.jsonl on each flush/sync +# When enabled, new events are appended incrementally using a high-water mark. +# Use 'bd export --events' to trigger manually regardless of this setting. +# events-export: false + +# Git branch for beads commits (bd sync will commit to this branch) +# IMPORTANT: Set this for team projects so all clones use the same sync branch. +# This setting persists across clones (unlike database config which is gitignored). +# Can also use BEADS_SYNC_BRANCH env var for local override. +# If not set, bd sync will require you to run 'bd config set sync.branch '. +# sync-branch: "beads-sync" + +# Multi-repo configuration (experimental - bd-307) +# Allows hydrating from multiple repositories and routing writes to the correct JSONL +# repos: +# primary: "." # Primary repo (where this database lives) +# additional: # Additional repos to hydrate from (read-only) +# - ~/beads-planning # Personal planning repo +# - ~/work-planning # Work planning repo + +# Integration settings (access with 'bd config get/set') +# These are stored in the database, not in this file: +# - jira.url +# - jira.project +# - linear.url +# - linear.api-key +# - github.org +# - github.repo diff --git a/task-runner/.beads/interactions.jsonl b/task-runner/.beads/interactions.jsonl new file mode 100644 index 00000000..e69de29b diff --git a/task-runner/.beads/issues.jsonl b/task-runner/.beads/issues.jsonl new file mode 100644 index 00000000..e69de29b diff --git a/task-runner/.beads/metadata.json b/task-runner/.beads/metadata.json new file mode 100644 index 00000000..c787975e --- /dev/null +++ b/task-runner/.beads/metadata.json @@ -0,0 +1,4 @@ +{ + "database": "beads.db", + "jsonl_export": "issues.jsonl" +} \ No newline at end of file diff --git a/task-runner/.gitattributes b/task-runner/.gitattributes new file mode 100644 index 00000000..807d5983 --- /dev/null +++ b/task-runner/.gitattributes @@ -0,0 +1,3 @@ + +# Use bd merge for beads JSONL files +.beads/issues.jsonl merge=beads diff --git a/task-runner/AGENTS.md b/task-runner/AGENTS.md new file mode 100644 index 00000000..df7a4af9 --- /dev/null +++ b/task-runner/AGENTS.md @@ -0,0 +1,40 @@ +# Agent Instructions + +This project uses **bd** (beads) for issue tracking. Run `bd onboard` to get started. + +## Quick Reference + +```bash +bd ready # Find available work +bd show # View issue details +bd update --status in_progress # Claim work +bd close # Complete work +bd sync # Sync with git +``` + +## Landing the Plane (Session Completion) + +**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. + +**MANDATORY WORKFLOW:** + +1. **File issues for remaining work** - Create issues for anything that needs follow-up +2. **Run quality gates** (if code changed) - Tests, linters, builds +3. **Update issue status** - Close finished work, update in-progress items +4. **PUSH TO REMOTE** - This is MANDATORY: + ```bash + git pull --rebase + bd sync + git push + git status # MUST show "up to date with origin" + ``` +5. **Clean up** - Clear stashes, prune remote branches +6. **Verify** - All changes committed AND pushed +7. **Hand off** - Provide context for next session + +**CRITICAL RULES:** +- Work is NOT complete until `git push` succeeds +- NEVER stop before pushing - that leaves work stranded locally +- NEVER say "ready to push when you are" - YOU must push +- If push fails, resolve and retry until it succeeds + diff --git a/task-runner/README.md b/task-runner/README.md new file mode 100644 index 00000000..32df588c --- /dev/null +++ b/task-runner/README.md @@ -0,0 +1,133 @@ +# Task Runner MCP + +Orchestrate AI agents through task lists with **Beads** integration and **Ralph-style** execution loops. + +## Features + +- **Beads Integration**: Full wrapper around the `bd` CLI for task management +- **Ralph Loop Engine**: Automated task execution with completion detection +- **Skills System**: Reusable workflows for common development tasks +- **Budget Control**: Limit agent execution by iterations or tokens + +## Installation + +The Task Runner MCP is part of the `mcps` workspace. Install from the root: + +```bash +cd /path/to/mcps +bun install +``` + +### Prerequisites + +1. **Beads CLI**: Install the `bd` command: + ```bash + curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash + ``` + +2. **Claude CLI** (for agent execution): + ```bash + # Install Anthropic's Claude CLI + npm install -g @anthropic-ai/claude-cli + ``` + +## Development + +```bash +# From the task-runner directory +cd mcps/task-runner + +# Type check +bun run check + +# Run dev server +bun run dev +``` + +## Tools + +### Workspace Management + +| Tool | Description | +|------|-------------| +| `WORKSPACE_SET` | Set the working directory for all operations | +| `WORKSPACE_GET` | Get the current workspace | + +### Beads Integration + +| Tool | Description | +|------|-------------| +| `BEADS_INIT` | Initialize Beads in workspace (`bd init`) | +| `BEADS_READY` | Get tasks ready to work on (`bd ready`) | +| `BEADS_CREATE` | Create a new task (`bd create`) | +| `BEADS_UPDATE` | Update task status/details (`bd update`) | +| `BEADS_CLOSE` | Close completed tasks (`bd close`) | +| `BEADS_SYNC` | Sync with git (`bd sync`) | +| `BEADS_LIST` | List all tasks (`bd list`) | +| `BEADS_SHOW` | Show task details (`bd show`) | + +### Ralph Loop Engine + +| Tool | Description | +|------|-------------| +| `LOOP_START` | Start automated task execution | +| `LOOP_STATUS` | Get current loop status | +| `LOOP_PAUSE` | Pause after current task | +| `LOOP_STOP` | Stop and reset the loop | + +### Skills + +| Tool | Description | +|------|-------------| +| `SKILL_LIST` | List available skills | +| `SKILL_SHOW` | Show skill details | +| `SKILL_APPLY` | Apply skill to create tasks | + +## Skills + +Skills define reusable workflows. Currently available: + +- **build-mcp**: Create a new MCP server with tools + +### Using Skills + +```bash +# 1. Set workspace +WORKSPACE_SET { directory: "/path/to/project" } + +# 2. Apply a skill (creates Beads tasks) +SKILL_APPLY { skillId: "build-mcp" } + +# 3. Start the loop +LOOP_START { maxIterations: 10 } +``` + +## Ralph Loop Flow + +``` +SELECT → PROMPT → EXECUTE → EVALUATE → (repeat) + ↓ ↓ ↓ ↓ +Get ready Build Call Check for +tasks prompt Claude COMPLETE +from bd CLI Run quality gates +``` + +The loop continues until: +- All tasks completed +- Max iterations reached +- Token budget exhausted +- Manual pause/stop + +## Quality Gates + +After each task completion, quality gates are run: + +```javascript +qualityGates: ["bun run check"] +``` + +If gates fail, the task remains open for retry. + +## License + +MIT diff --git a/task-runner/app.json b/task-runner/app.json new file mode 100644 index 00000000..0fdca77c --- /dev/null +++ b/task-runner/app.json @@ -0,0 +1,16 @@ +{ + "name": "Task Runner", + "description": "Orchestrate AI agents through task lists with Beads integration and Ralph-style loops", + "icon": "https://raw.githubusercontent.com/decocx/apps/main/static/icons/cli.svg", + "runtime": "bun", + "categories": ["Developer Tools", "Automation"], + "repository": "https://github.com/decocx/mcps", + "keywords": ["tasks", "beads", "ralph", "agent", "orchestration", "automation"], + "form": { + "defaultWorkspace": { + "type": "string", + "title": "Default Workspace", + "description": "Default directory for Beads operations (optional)" + } + } +} diff --git a/task-runner/package.json b/task-runner/package.json new file mode 100644 index 00000000..1c5c44ec --- /dev/null +++ b/task-runner/package.json @@ -0,0 +1,26 @@ +{ + "name": "mcp-task-runner", + "version": "1.0.0", + "description": "Task Runner MCP - Beads integration, Ralph loop, agent orchestration", + "private": true, + "type": "module", + "scripts": { + "dev": "PORT=8100 bun run --watch server/main.ts", + "check": "tsc --noEmit", + "build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js", + "build": "bun run build:server" + }, + "dependencies": { + "@decocms/runtime": "1.2.5", + "zod": "^4.0.0" + }, + "devDependencies": { + "@decocms/mcps-shared": "workspace:*", + "@modelcontextprotocol/sdk": "1.25.1", + "deco-cli": "^0.28.0", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=22.0.0" + } +} diff --git a/task-runner/server/config.ts b/task-runner/server/config.ts new file mode 100644 index 00000000..6aac1360 --- /dev/null +++ b/task-runner/server/config.ts @@ -0,0 +1,315 @@ +/** + * Task Runner Configuration + * + * Configuration for the Task Runner MCP, including: + * - Claude Code agent settings + * - Allowed/denied tool patterns + * - Timeout and resource limits + * - MCP tools available to the agent + */ + +// ============================================================================ +// MCP Tool Definitions (subset exposed to Claude Code) +// ============================================================================ + +/** + * Tools that Claude Code can call via MCP. + * Each tool has a name, description, and when to use it. + */ +export interface AgentTool { + name: string; + description: string; + when: string; + required?: boolean; // Should agent use this before completion? +} + +/** + * Tools the agent should have access to for task execution. + * These are exposed via the task-runner MCP. + */ +export const agentMcpTools: AgentTool[] = [ + // Memory tools - for cross-iteration learning + { + name: "MEMORY_READ", + description: "Read project memory (MEMORY.md + recent daily logs)", + when: "At session start to load project context", + required: true, + }, + { + name: "MEMORY_WRITE", + description: "Write discoveries to memory (daily or longterm)", + when: "When you discover important patterns, decisions, or constraints", + }, + { + name: "MEMORY_RECORD_ERROR", + description: "Record an error pattern and its fix", + when: "After fixing any non-trivial error - helps future sessions avoid it", + required: true, + }, + { + name: "MEMORY_RECORD_LEARNING", + description: + "Record a learning under a category (architecture, patterns, decisions, etc.)", + when: "When you learn something important about the codebase", + }, + { + name: "MEMORY_SEARCH", + description: "Search memory files for keywords", + when: "When looking for past decisions or patterns about a specific topic", + }, + { + name: "MEMORY_KNOWLEDGE", + description: "Add LEARNED: entry to knowledge base", + when: "When you discover a convention, gotcha, or useful insight worth remembering", + }, + { + name: "MEMORY_RECALL", + description: "Search the knowledge base by keyword or tag", + when: "At session start or when looking for past learnings on a topic", + }, + { + name: "TASK_RETRO", + description: "Run a retrospective after completing a task", + when: "After completing a significant task - captures what worked and what didn't", + }, + + // Quality gates - for verification + { + name: "QUALITY_GATES_LIST", + description: "List configured quality gates for this project", + when: "Before starting work to understand success criteria", + }, + { + name: "QUALITY_GATES_RUN", + description: "Run all quality gates and report pass/fail", + when: "Before outputting completion token - all gates must pass", + required: true, + }, + + // Task management + { + name: "TASK_UPDATE", + description: "Update task status (open, in_progress, blocked, closed)", + when: "When task status changes", + }, + { + name: "TASK_CREATE", + description: "Create a follow-up task for remaining work", + when: "When you identify work that should be done but is out of scope for current task", + }, + { + name: "TASK_LIST", + description: "List all tasks in the project", + when: "To understand what other work is pending", + }, + + // Session landing + { + name: "SESSION_LAND", + description: + "Complete the session: run gates, commit, sync, record learnings", + when: "When task is complete OR when you cannot continue - ALWAYS call before ending", + required: true, + }, +]; + +/** + * Get tool descriptions for the agent prompt + */ +export function getToolDescriptionsForPrompt(): string { + const required = agentMcpTools.filter((t) => t.required); + const optional = agentMcpTools.filter((t) => !t.required); + + let prompt = "## Available MCP Tools\n\n"; + prompt += + "You have access to these tools via the `mcp__task-runner` server:\n\n"; + + prompt += "### Required (use these):\n"; + for (const tool of required) { + prompt += `- **${tool.name}**: ${tool.description}\n _When_: ${tool.when}\n`; + } + + prompt += "\n### Optional (use as needed):\n"; + for (const tool of optional) { + prompt += `- **${tool.name}**: ${tool.description}\n _When_: ${tool.when}\n`; + } + + return prompt; +} + +/** + * Get MCP tool names for the --allowedTools flag + */ +export function getMcpToolNames(): string[] { + return agentMcpTools.map((t) => `mcp__task-runner__${t.name}`); +} + +// ============================================================================ +// Agent Configuration +// ============================================================================ + +export const agentConfig = { + /** + * Path to the Claude CLI executable + * Can be overridden via CLAUDE_PATH environment variable + */ + claudePath: process.env.CLAUDE_PATH || "claude", + + /** + * Maximum time (ms) for a single task execution + * Default: 30 minutes + */ + timeout: 30 * 60 * 1000, + + /** + * Maximum concurrent agent processes + */ + maxConcurrentAgents: 3, + + /** + * Task Runner MCP server URL (for Claude Code to connect) + * Default: localhost:8100 (standard task-runner port) + */ + taskRunnerMcpUrl: process.env.TASK_RUNNER_MCP_URL || "http://localhost:8100", + + /** + * Allowed tools for Claude Code (whitelist approach) + * These are the only tools the agent can use + */ + allowedTools: [ + // File operations + "Edit", + "Read", + "Write", + + // Git commands (version control) + "Bash(git *)", + + // Package manager scripts + "Bash(bun run *)", + "Bash(npm run *)", + "Bash(pnpm run *)", + "Bash(yarn run *)", + "Bash(deno task *)", + + // Testing + "Bash(bun test *)", + "Bash(npm test *)", + "Bash(pnpm test *)", + + // MCP tools (added dynamically) + ...getMcpToolNames(), + ], + + /** + * Explicitly denied patterns (for documentation and validation) + * These patterns should NEVER be allowed even if they match allowedTools + */ + deniedPatterns: [ + // Dangerous file deletion + "rm -rf", + "rimraf", + "npx rimraf", + "del /s /q", + + // System-wide changes + "sudo rm", + "sudo chmod", + "chmod 777", + "chmod -R 777", + + // Force operations that can't be undone + "git push --force", + "git push -f", + "git reset --hard", + + // Package publishing (should be explicit) + "npm publish", + "pnpm publish", + "bun publish", + ], +}; + +/** + * Get the allowed tools string for Claude CLI --allowedTools flag + */ +export function getAllowedToolsString(): string { + return agentConfig.allowedTools.join(","); +} + +/** + * Check if a command matches any denied pattern + */ +export function isDeniedCommand(command: string): boolean { + const lowerCommand = command.toLowerCase(); + return agentConfig.deniedPatterns.some((pattern) => + lowerCommand.includes(pattern.toLowerCase()), + ); +} + +// ============================================================================ +// Loop Configuration +// ============================================================================ + +export const loopConfig = { + /** + * Default maximum iterations for the execution loop + */ + defaultMaxIterations: 10, + + /** + * Default maximum tokens to spend (budget limit) + * ~1M tokens ≈ $10-15 for Claude models + */ + defaultMaxTokens: 1_000_000, + + /** + * Default quality gates to run after each task + */ + defaultQualityGates: ["bun run check", "bun run lint"], + + /** + * Delay between loop iterations (ms) + * Helps prevent rate limiting + */ + iterationDelay: 1000, +}; + +// ============================================================================ +// Session Configuration +// ============================================================================ + +export const sessionConfig = { + /** + * Maximum inline output size (bytes) in sessions.json + * Larger outputs are stored in separate log files + */ + maxInlineOutput: 10_000, + + /** + * Session log directory relative to .beads/ + */ + logDirectory: "logs", + + /** + * How long to keep completed sessions (ms) + * Default: 7 days + */ + sessionRetention: 7 * 24 * 60 * 60 * 1000, +}; + +// ============================================================================ +// Beads Configuration +// ============================================================================ + +export const beadsConfig = { + /** + * Path to the Beads CLI + */ + beadsPath: process.env.BEADS_PATH || "bd", + + /** + * Auto-commit session state to git + */ + autoCommitSessions: true, +}; diff --git a/task-runner/server/main.ts b/task-runner/server/main.ts new file mode 100644 index 00000000..8f77991c --- /dev/null +++ b/task-runner/server/main.ts @@ -0,0 +1,25 @@ +/** + * Task Runner MCP Server + * + * This MCP provides tools for orchestrating AI agents through task lists: + * - Beads CLI integration for task storage + * - Ralph-style execution loops + * - Agent calling and budget control + */ +import { withRuntime } from "@decocms/runtime"; +import { serve } from "@decocms/mcps-shared/serve"; + +import { tools } from "./tools/index.ts"; +import type { Env } from "../shared/deco.gen.ts"; + +export type { Env }; + +const runtime = withRuntime({ + tools: (env: Env) => tools.map((createTool) => createTool(env)), +}); + +if (runtime.fetch) { + const port = process.env.PORT || 8100; + console.log(`\n MCP URL: http://localhost:${port}/mcp\n`); + serve(runtime.fetch); +} diff --git a/task-runner/server/prompts/safe-agent.ts b/task-runner/server/prompts/safe-agent.ts new file mode 100644 index 00000000..d19e52d5 --- /dev/null +++ b/task-runner/server/prompts/safe-agent.ts @@ -0,0 +1,194 @@ +/** + * Safe Agent Prompt Templates + * + * Prompt templates that enforce: + * - Frequent commits after each change + * - Beads task tracking + * - Safety rules (no rm -rf, rimraf, etc.) + * - Incremental, testable changes + */ + +export interface TaskInfo { + id: string; + title: string; + description?: string; + priority?: number | string; + epicId?: string; +} + +export interface PromptContext { + workspace: string; + qualityGates?: string[]; + projectGuidelines?: string; + recentContext?: string; +} + +/** + * Build the main safety rules section + */ +export function buildSafetyRules(): string { + return `## MANDATORY SAFETY RULES + +1. **COMMIT FREQUENTLY**: After each logical change, run: + \`\`\`bash + git add -A && git commit -m "descriptive message" + \`\`\` + Do NOT accumulate many changes before committing. + +2. **NEVER DELETE DIRECTORIES**: + - Do NOT use \`rm -rf\`, \`rimraf\`, \`npx rimraf\`, or similar + - If you need to remove files, delete them individually + - Ask for confirmation before any bulk deletions + +3. **SMALL INCREMENTAL CHANGES**: + - Make one logical change at a time + - Test after each change if possible + - If a change is complex, break it into smaller steps + +4. **ASK IF UNSURE**: + - If requirements are ambiguous, explain the ambiguity + - Propose your interpretation but wait for confirmation on major decisions + - Do NOT guess on architectural decisions`; +} + +/** + * Build the task context section + */ +export function buildTaskSection(task: TaskInfo): string { + const priority = task.priority ? ` (Priority: ${task.priority})` : ""; + const epic = task.epicId ? `\n- Epic: ${task.epicId}` : ""; + + return `## Current Task + +**${task.title}**${priority} +- Task ID: ${task.id} +- Status: in_progress${epic} + +### Description +${task.description || "No additional description provided."}`; +} + +/** + * Build the quality gates section + */ +export function buildQualityGates(gates?: string[]): string { + const defaultGates = ["bun run check", "bun run lint"]; + const qualityGates = gates && gates.length > 0 ? gates : defaultGates; + + return `## Quality Gates + +Before marking complete, run these commands (if available): +${qualityGates.map((g) => `- \`${g}\``).join("\n")} + +**IMPORTANT - Scope of responsibility:** +- Only fix errors in files YOU modified during this task +- If the codebase has PRE-EXISTING errors (errors in files you didn't touch), do NOT try to fix them +- If quality gates fail due to pre-existing issues, WARN about them and proceed with your task +- Your job is to not make things worse, not to fix everything + +If a command is not available in this project, skip it.`; +} + +/** + * Build the completion section + */ +export function buildCompletionSection(): string { + return `## Completion Protocol + +When the task is FULLY complete: +1. Ensure all changes are committed +2. Run quality gates +3. If gates fail ONLY in files you modified, fix those errors +4. If gates fail in files you DID NOT modify, WARN about pre-existing issues and continue +5. Output exactly: \`COMPLETE\` + +**IMPORTANT**: +- Only output the completion token if YOUR task is truly finished +- Pre-existing codebase issues are NOT your responsibility to fix +- If you cannot complete the task, explain what's blocking you and do NOT output the token`; +} + +/** + * Build the full safe prompt for a task + */ +export function buildSafePrompt( + task: TaskInfo, + context: PromptContext, +): string { + const sections: string[] = []; + + // Header + sections.push(`You are working on a coding task in: ${context.workspace}`); + + // Project guidelines if provided + if (context.projectGuidelines) { + sections.push(`## Project Guidelines + +${context.projectGuidelines}`); + } + + // Recent context if provided + if (context.recentContext) { + sections.push(`## Recent Context + +${context.recentContext}`); + } + + // Safety rules + sections.push(buildSafetyRules()); + + // Task details + sections.push(buildTaskSection(task)); + + // Quality gates + sections.push(buildQualityGates(context.qualityGates)); + + // Completion protocol + sections.push(buildCompletionSection()); + + return sections.join("\n\n"); +} + +/** + * Build a minimal prompt for quick tasks + */ +export function buildQuickPrompt(task: TaskInfo, workspace: string): string { + return `Working in: ${workspace} + +Task: ${task.title} (${task.id}) +${task.description || ""} + +Rules: +1. Commit after each change: git add -A && git commit -m "message" +2. No rm -rf or rimraf +3. When done: COMPLETE`; +} + +/** + * Parse completion status from agent output + */ +export function detectCompletion(output: string): boolean { + return output.includes("COMPLETE"); +} + +/** + * Extract any error messages or blocking reasons from output + */ +export function extractBlockingReason(output: string): string | null { + // Look for common patterns indicating the agent couldn't complete + const patterns = [ + /cannot complete.*?because[:\s]+(.+?)(?:\n|$)/i, + /blocked by[:\s]+(.+?)(?:\n|$)/i, + /unable to proceed[:\s]+(.+?)(?:\n|$)/i, + /missing.*?requirement[:\s]+(.+?)(?:\n|$)/i, + ]; + + for (const pattern of patterns) { + const match = output.match(pattern); + if (match) { + return match[1].trim(); + } + } + + return null; +} diff --git a/task-runner/server/sessions.ts b/task-runner/server/sessions.ts new file mode 100644 index 00000000..216f2602 --- /dev/null +++ b/task-runner/server/sessions.ts @@ -0,0 +1,442 @@ +/** + * Session Persistence for Task Runner + * + * Persists agent sessions to .beads/sessions.json for durability across + * server restarts. Large outputs are stored in .beads/logs/ as separate files. + */ + +import { mkdir } from "node:fs/promises"; + +// ============================================================================ +// Types +// ============================================================================ + +export interface ToolCall { + name: string; + input?: Record; + timestamp: string; +} + +export interface AgentMessage { + role: "user" | "assistant" | "tool"; + content: string; + timestamp: string; + toolCall?: ToolCall; +} + +export interface AgentSession { + id: string; + taskId: string; + taskTitle?: string; + pid: number; + status: "running" | "completed" | "failed" | "stopped"; + startedAt: string; + completedAt?: string; + output: string; // Captured stdout/stderr (truncated if too large) + exitCode?: number; + error?: string; + // Structured logs + toolCalls?: ToolCall[]; + messages?: AgentMessage[]; +} + +export interface SessionStore { + sessions: AgentSession[]; + lastUpdated: string; +} + +// ============================================================================ +// Constants +// ============================================================================ + +const SESSIONS_FILE = ".beads/sessions.json"; +const LOG_DIR = ".beads/logs"; +const MAX_INLINE_OUTPUT = 10000; // 10KB inline, rest in log file + +// ============================================================================ +// Session Storage Functions +// ============================================================================ + +/** + * Load sessions from .beads/sessions.json + */ +export async function loadSessions(workspace: string): Promise { + const path = `${workspace}/${SESSIONS_FILE}`; + try { + const content = await Bun.file(path).text(); + return JSON.parse(content); + } catch { + return { sessions: [], lastUpdated: new Date().toISOString() }; + } +} + +/** + * Save sessions to .beads/sessions.json + */ +export async function saveSessions( + workspace: string, + store: SessionStore, +): Promise { + const path = `${workspace}/${SESSIONS_FILE}`; + store.lastUpdated = new Date().toISOString(); + await Bun.write(path, JSON.stringify(store, null, 2)); +} + +/** + * Add a new session to the store + */ +export async function addSession( + workspace: string, + session: AgentSession, +): Promise { + const store = await loadSessions(workspace); + store.sessions.push(session); + await saveSessions(workspace, store); +} + +/** + * Update an existing session + */ +export async function updateSession( + workspace: string, + sessionId: string, + updates: Partial, +): Promise { + const store = await loadSessions(workspace); + const idx = store.sessions.findIndex((s) => s.id === sessionId); + if (idx >= 0) { + store.sessions[idx] = { ...store.sessions[idx], ...updates }; + await saveSessions(workspace, store); + } +} + +/** + * Get a session by ID + */ +export async function getSession( + workspace: string, + sessionId: string, +): Promise { + const store = await loadSessions(workspace); + return store.sessions.find((s) => s.id === sessionId); +} + +/** + * Get all sessions, optionally filtered by status + */ +export async function getSessions( + workspace: string, + status?: AgentSession["status"], +): Promise { + const store = await loadSessions(workspace); + if (status) { + return store.sessions.filter((s) => s.status === status); + } + return store.sessions; +} + +/** + * Clean up stale sessions where the process is no longer alive. + * This handles cases where the MCP server restarted and lost track of processes. + */ +export async function cleanupStaleSessions(workspace: string): Promise { + const store = await loadSessions(workspace); + let cleaned = 0; + + for (const session of store.sessions) { + if (session.status === "running") { + // Check if the process is still alive + const alive = await isProcessAlive(session.pid); + if (!alive) { + session.status = "failed"; + session.completedAt = new Date().toISOString(); + session.error = "Process died or server restarted"; + session.exitCode = -1; + cleaned++; + console.log( + `[sessions] Cleaned up stale session ${session.id} (pid ${session.pid})`, + ); + } + } + } + + if (cleaned > 0) { + await saveSessions(workspace, store); + } + + return cleaned; +} + +// ============================================================================ +// Log File Functions +// ============================================================================ + +/** + * Ensure the logs directory exists + */ +export async function ensureLogDir(workspace: string): Promise { + const logPath = `${workspace}/${LOG_DIR}`; + await mkdir(logPath, { recursive: true }); +} + +/** + * Get the log file path for a session + */ +export function getLogPath(workspace: string, sessionId: string): string { + return `${workspace}/${LOG_DIR}/session-${sessionId}.log`; +} + +/** + * Append output to a session's log file + */ +export async function appendOutput( + workspace: string, + sessionId: string, + chunk: string, +): Promise { + await ensureLogDir(workspace); + const logPath = getLogPath(workspace, sessionId); + + // Read existing content and append + let existing = ""; + try { + existing = await Bun.file(logPath).text(); + } catch { + // File doesn't exist yet + } + await Bun.write(logPath, existing + chunk); +} + +/** + * Read the full log for a session + */ +export async function readLog( + workspace: string, + sessionId: string, +): Promise { + const logPath = getLogPath(workspace, sessionId); + try { + return await Bun.file(logPath).text(); + } catch { + return ""; + } +} + +/** + * Truncate output for inline storage in sessions.json + */ +export function truncateOutput(output: string): string { + if (output.length <= MAX_INLINE_OUTPUT) { + return output; + } + const truncated = output.slice(0, MAX_INLINE_OUTPUT); + return `${truncated}\n\n... [truncated, see log file for full output]`; +} + +// ============================================================================ +// Process Utilities +// ============================================================================ + +/** + * Check if a process is still running by PID + */ +export async function isProcessAlive(pid: number): Promise { + try { + // Send signal 0 to check if process exists + process.kill(pid, 0); + return true; + } catch { + return false; + } +} + +/** + * Recover orphaned sessions on server restart + * Marks sessions that were "running" but whose processes are dead as "failed" + */ +export async function recoverSessions(workspace: string): Promise { + const store = await loadSessions(workspace); + let updated = false; + + for (const session of store.sessions) { + if (session.status === "running") { + const alive = await isProcessAlive(session.pid); + if (!alive) { + session.status = "failed"; + session.error = "Process died unexpectedly (server restart)"; + session.completedAt = new Date().toISOString(); + updated = true; + } + } + } + + if (updated) { + await saveSessions(workspace, store); + } +} + +/** + * Generate a unique session ID + */ +export function generateSessionId(): string { + const timestamp = Date.now().toString(36); + const random = Math.random().toString(36).substring(2, 8); + return `session-${timestamp}-${random}`; +} + +// ============================================================================ +// Claude Output Parsing +// ============================================================================ + +/** + * Parse a line of Claude's stream-json output + * Returns parsed event or null if not valid JSON + */ +export function parseClaudeEvent( + line: string, +): { type: string; [key: string]: unknown } | null { + try { + const trimmed = line.trim(); + if (!trimmed || !trimmed.startsWith("{")) { + return null; + } + return JSON.parse(trimmed); + } catch { + return null; + } +} + +/** + * Extract tool call from Claude event + */ +export function extractToolCall(event: { + type: string; + [key: string]: unknown; +}): ToolCall | null { + const timestamp = new Date().toISOString(); + + // Claude stream-json format has tool_use nested inside: + // {"type":"assistant","message":{"content":[{"type":"tool_use","name":"Read","input":{...}}]}} + if (event.type === "assistant" && event.message) { + const message = event.message as { + content?: Array<{ + type: string; + name?: string; + input?: Record; + }>; + }; + const toolUse = message.content?.find((c) => c.type === "tool_use"); + if (toolUse && toolUse.name) { + return { + name: toolUse.name, + input: toolUse.input, + timestamp, + }; + } + } + + // Also handle direct tool_use events (fallback) + if (event.type === "tool_use" && typeof event.name === "string") { + return { + name: event.name, + input: event.input as Record | undefined, + timestamp, + }; + } + + return null; +} + +/** + * Extract message from Claude event + */ +export function extractMessage(event: { + type: string; + [key: string]: unknown; +}): AgentMessage | null { + const timestamp = new Date().toISOString(); + + // Assistant text - direct format + if (event.type === "text" && typeof event.text === "string") { + return { + role: "assistant", + content: event.text, + timestamp, + }; + } + + // Assistant message - nested format + // {"type":"assistant","message":{"content":[{"type":"text","text":"..."}]}} + if (event.type === "assistant" && event.message) { + const message = event.message as { + content?: Array<{ type: string; text?: string }>; + }; + const textContent = message.content?.find((c) => c.type === "text"); + if (textContent?.text) { + return { + role: "assistant", + content: textContent.text, + timestamp, + }; + } + } + + // Assistant message completion (legacy format) + if (event.type === "assistant" && typeof event.message === "string") { + return { + role: "assistant", + content: event.message, + timestamp, + }; + } + + // Tool result + if (event.type === "tool_result") { + return { + role: "tool", + content: + typeof event.output === "string" + ? event.output + : JSON.stringify(event.output), + timestamp, + }; + } + + return null; +} + +/** + * Add a tool call to a session + */ +export async function addToolCall( + workspace: string, + sessionId: string, + toolCall: ToolCall, +): Promise { + const store = await loadSessions(workspace); + const idx = store.sessions.findIndex((s) => s.id === sessionId); + if (idx >= 0) { + const session = store.sessions[idx]; + session.toolCalls = session.toolCalls || []; + session.toolCalls.push(toolCall); + await saveSessions(workspace, store); + } +} + +/** + * Add a message to a session + */ +export async function addMessage( + workspace: string, + sessionId: string, + message: AgentMessage, +): Promise { + const store = await loadSessions(workspace); + const idx = store.sessions.findIndex((s) => s.id === sessionId); + if (idx >= 0) { + const session = store.sessions[idx]; + session.messages = session.messages || []; + session.messages.push(message); + await saveSessions(workspace, store); + } +} diff --git a/task-runner/server/skills/build-mcp.ts b/task-runner/server/skills/build-mcp.ts new file mode 100644 index 00000000..2241c861 --- /dev/null +++ b/task-runner/server/skills/build-mcp.ts @@ -0,0 +1,185 @@ +/** + * Build MCP Skill + * + * Skill for creating new MCP servers with tools and resources. + */ + +import type { Skill } from "./index.ts"; + +export const buildMcpSkill: Skill = { + id: "build-mcp", + name: "Build MCP Server", + description: + "Create a new MCP server with tools, following the mcps/ patterns", + stack: ["bun", "typescript"], + + userStories: [ + { + id: "US-001", + title: "Create MCP scaffold", + asA: "developer", + iWant: "a working MCP server entry point", + soThat: "I can add tools incrementally", + acceptanceCriteria: [ + "package.json exists with correct dependencies (@decocms/runtime, zod)", + "tsconfig.json configured for Bun and bundler mode", + "server/main.ts creates and starts MCP server using withRuntime", + "shared/deco.gen.ts defines Env type", + "Server starts without errors (bun run dev)", + ], + }, + { + id: "US-002", + title: "Add first tool", + asA: "developer", + iWant: "a working tool implementation", + soThat: "I can verify the pattern works", + acceptanceCriteria: [ + "Tool file created in server/tools/", + "Tool uses createPrivateTool from @decocms/runtime/tools", + "Tool has Zod inputSchema and outputSchema", + "Tool exported from server/tools/index.ts", + "Tool registered in main.ts tools array", + "bun run check passes with no errors", + ], + dependsOn: ["US-001"], + }, + { + id: "US-003", + title: "Add app.json for registry", + asA: "developer", + iWant: "the MCP registered in the Mesh registry", + soThat: "users can install it", + acceptanceCriteria: [ + "app.json exists with name, description, icon", + "categories and keywords defined", + "form schema defined if config is needed", + ], + dependsOn: ["US-001"], + }, + { + id: "US-004", + title: "Add additional tools", + asA: "developer", + iWant: "all required tools implemented", + soThat: "the MCP is feature-complete", + acceptanceCriteria: [ + "All tools listed in requirements are implemented", + "Each tool has proper error handling", + "bun run check passes", + ], + dependsOn: ["US-002"], + }, + ], + + qualityGates: { + bun: ["bun run check"], + "*": ["bun run check"], + }, + + prompts: { + system: `You are an expert developer building MCP (Model Context Protocol) servers. + +## Key Patterns + +1. **Entry Point** (server/main.ts): +\`\`\`typescript +import { withRuntime } from "@decocms/runtime"; +import { serve } from "@decocms/mcps-shared/serve"; +import { tools } from "./tools/index.ts"; +import type { Env } from "../shared/deco.gen.ts"; + +const runtime = withRuntime({ + tools: (env: Env) => tools.map((createTool) => createTool(env)), +}); + +if (runtime.fetch) { + serve(runtime.fetch); +} +\`\`\` + +2. **Tool Definition** (server/tools/example.ts): +\`\`\`typescript +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; + +export const createExampleTool = (_env: Env) => + createPrivateTool({ + id: "EXAMPLE_TOOL", + description: "What this tool does", + inputSchema: z.object({ + param: z.string().describe("Parameter description"), + }), + outputSchema: z.object({ + result: z.string(), + }), + execute: async ({ context }) => { + const { param } = context; + return { result: \`Processed: \${param}\` }; + }, + }); + +export const exampleTools = [createExampleTool]; +\`\`\` + +3. **Tools Index** (server/tools/index.ts): +\`\`\`typescript +import { exampleTools } from "./example.ts"; +export const tools = [...exampleTools]; +\`\`\` + +## File Structure + +\`\`\` +my-mcp/ +├── package.json # dependencies, scripts +├── tsconfig.json # TypeScript config +├── app.json # Mesh registry config +├── server/ +│ ├── main.ts # Entry point +│ └── tools/ +│ ├── index.ts # Export all tools +│ └── *.ts # Tool implementations +└── shared/ + └── deco.gen.ts # Type definitions +\`\`\` + +## Dependencies + +- @decocms/runtime: 1.2.5 +- @decocms/mcps-shared: workspace:* +- zod: ^4.0.0 + +## Quality Gates + +Always run \`bun run check\` to verify TypeScript types. +`, + + taskTemplate: `## Current Task +**{{task.title}}** ({{task.id}}) + +{{#if task.description}} +{{task.description}} +{{/if}} + +## Acceptance Criteria +{{#each task.acceptanceCriteria}} +- [ ] {{this}} +{{/each}} + +{{#if task.dependsOn}} +## Dependencies +This task depends on: {{#each task.dependsOn}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} +{{/if}} + +## Instructions +1. Review the current state of the codebase +2. Make the necessary changes to satisfy the acceptance criteria +3. Run quality gates to verify: {{#each qualityGates}}\`{{this}}\`{{#unless @last}}, {{/unless}}{{/each}} +4. When all criteria are met, output: COMPLETE + +If you cannot complete the task, explain why and do NOT output the completion token. +`, + }, +}; diff --git a/task-runner/server/skills/index.ts b/task-runner/server/skills/index.ts new file mode 100644 index 00000000..89157d4e --- /dev/null +++ b/task-runner/server/skills/index.ts @@ -0,0 +1,44 @@ +/** + * Skills Registry + * + * Skills define reusable workflows for common development tasks. + * Each skill has user stories, quality gates, and prompts. + */ + +import { buildMcpSkill } from "./build-mcp.ts"; + +export interface UserStory { + id: string; + title: string; + asA: string; + iWant: string; + soThat: string; + acceptanceCriteria: string[]; + dependsOn?: string[]; +} + +export interface Skill { + id: string; + name: string; + description: string; + stack: string[]; // ["*"] for any, or specific like ["deco", "bun"] + userStories: UserStory[]; + qualityGates: Record; + prompts: { + system: string; + taskTemplate: string; + }; +} + +// Registry of all skills +export const skills: Record = { + "build-mcp": buildMcpSkill, +}; + +export function getSkill(id: string): Skill | undefined { + return skills[id]; +} + +export function listSkills(): Skill[] { + return Object.values(skills); +} diff --git a/task-runner/server/tools/agent.ts b/task-runner/server/tools/agent.ts new file mode 100644 index 00000000..9de7f9ba --- /dev/null +++ b/task-runner/server/tools/agent.ts @@ -0,0 +1,1148 @@ +/** + * Agent Tools + * + * Tools for spawning and managing Claude Code agents. + * Uses Drover-style subprocess spawning with safety constraints. + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getWorkspace, setWorkspace } from "./workspace.ts"; +import { + addSession, + updateSession, + getSession, + getSessions, + appendOutput, + truncateOutput, + generateSessionId, + ensureLogDir, + readLog, + isProcessAlive, + parseClaudeEvent, + extractToolCall, + extractMessage, + addToolCall, + addMessage, + cleanupStaleSessions, + type AgentSession, +} from "../sessions.ts"; + +import { + agentConfig, + getAllowedToolsString, + getToolDescriptionsForPrompt, +} from "../config.ts"; + +// In-memory process references (for stopping) +const runningProcesses: Map< + string, + { proc: ReturnType; abortController: AbortController } +> = new Map(); + +// ============================================================================ +// Helper Functions +// ============================================================================ + +// ============================================================================ +// Types for enhanced task context +// ============================================================================ + +interface AcceptanceCriterion { + id: string; + description: string; + completed?: boolean; +} + +interface QualityGate { + name: string; + command: string; + required: boolean; +} + +interface QualityGatesBaseline { + verified: boolean; + verifiedAt: string; + allPassed: boolean; + acknowledged: boolean; + failingGates: string[]; +} + +interface SiteContext { + /** Whether this is a Deco site */ + isDeco?: boolean; + /** Dev server URL if running */ + serverUrl?: string; + /** List of page paths available */ + pages?: string[]; + /** Deco imports found in deno.json */ + decoImports?: string[]; + /** Site type (e.g., "deco", "next", "unknown") */ + siteType?: string; + /** Additional site-specific guidelines */ + guidelines?: string; +} + +interface TaskContext { + taskId: string; + taskTitle: string; + taskDescription?: string; + acceptanceCriteria?: AcceptanceCriterion[]; + qualityGates?: QualityGate[]; + workspace: string; + siteContext?: SiteContext; +} + +/** + * Load quality gates and baseline from project config + */ +async function loadQualityGatesWithBaseline(workspace: string): Promise<{ + gates: QualityGate[]; + baseline?: QualityGatesBaseline; +}> { + try { + const configPath = `${workspace}/.beads/project-config.json`; + const content = await Bun.file(configPath).text(); + const config = JSON.parse(content) as { + qualityGates?: QualityGate[]; + qualityGatesBaseline?: QualityGatesBaseline; + }; + return { + gates: config.qualityGates ?? [], + baseline: config.qualityGatesBaseline, + }; + } catch { + // Return defaults if no config + return { + gates: [ + { name: "Type Check", command: "bun run check", required: true }, + { name: "Lint", command: "bun run lint", required: true }, + ], + }; + } +} + +/** + * Load project memory summary + */ +async function loadMemorySummary(workspace: string): Promise { + const summaries: string[] = []; + + // Try to read MEMORY.md + try { + const memoryPath = `${workspace}/MEMORY.md`; + const content = await Bun.file(memoryPath).text(); + // Get last 50 lines or 2000 chars + const lines = content.split("\n").slice(-50); + summaries.push("### From MEMORY.md:\n" + lines.join("\n").slice(-2000)); + } catch { + // No memory file yet + } + + // Try to read today's memory + const today = new Date().toISOString().split("T")[0]; + try { + const todayPath = `${workspace}/memory/${today}.md`; + const content = await Bun.file(todayPath).text(); + summaries.push("### From today's notes:\n" + content.slice(-1000)); + } catch { + // No today's notes yet + } + + return summaries.length > 0 + ? summaries.join("\n\n") + : "No project memory yet. Start writing discoveries to MEMORY.md"; +} + +/** + * Build site context section for the prompt + */ +function buildSiteContextSection(siteContext?: SiteContext): string { + if (!siteContext) return ""; + + const sections: string[] = []; + + if (siteContext.isDeco) { + sections.push(`## Site Context (Deco Site) + +This workspace contains a **Deco site**. Use the skills and patterns from the \`skills/\` folder.`); + + if (siteContext.serverUrl) { + sections.push(`- **Dev Server:** Running at ${siteContext.serverUrl}`); + } + + if (siteContext.pages && siteContext.pages.length > 0) { + sections.push(`- **Available Pages:** +${siteContext.pages.map((p) => ` - ${p}`).join("\n")}`); + } + + if (siteContext.decoImports && siteContext.decoImports.length > 0) { + sections.push( + `- **Deco Imports:** ${siteContext.decoImports.slice(0, 5).join(", ")}${siteContext.decoImports.length > 5 ? "..." : ""}`, + ); + } + + sections.push(` +### Deco Site Patterns +- Page configs are in \`.deco/blocks/pages-{slug}.json\` +- Sections go in \`sections/{ComponentName}.tsx\` +- Use the color system: dc-950 (darkest) to dc-100 (lightest), primary-light (#D0EC1A) +- Always provide default props in sections +- Check \`skills/decocms-landing-pages/SKILL.md\` for detailed patterns`); + } else if (siteContext.siteType) { + sections.push(`## Site Context + +Site type: ${siteContext.siteType}`); + } + + if (siteContext.guidelines) { + sections.push(`### Site Guidelines +${siteContext.guidelines}`); + } + + return sections.join("\n\n"); +} + +/** + * Build the context-rich prompt for an agent + */ +async function buildAgentPrompt(ctx: TaskContext): Promise { + const { + taskId, + taskTitle, + taskDescription, + acceptanceCriteria, + workspace, + siteContext, + } = ctx; + + // Load project-specific data + const { gates: qualityGates, baseline } = + await loadQualityGatesWithBaseline(workspace); + const memorySummary = await loadMemorySummary(workspace); + + // Build acceptance criteria section + let acceptanceCriteriaSection = ""; + if (acceptanceCriteria && acceptanceCriteria.length > 0) { + acceptanceCriteriaSection = ` +## Acceptance Criteria (ALL must be verified) +${acceptanceCriteria.map((c, i) => `${i + 1}. [ ] ${c.description}`).join("\n")} + +You must verify EACH criterion before completing. Check them off mentally as you work. +`; + } + + // Build quality gates section based on baseline status + const requiredGates = qualityGates.filter((g) => g.required); + let qualityGatesSection = ""; + + if (requiredGates.length > 0) { + const hasAcknowledgedFailures = + baseline?.verified && + !baseline.allPassed && + baseline.acknowledged && + baseline.failingGates.length > 0; + + if (hasAcknowledgedFailures) { + // Pre-existing failures acknowledged - agent should NOT try to fix them + const failingGateNames = baseline.failingGates; + const passingGates = requiredGates.filter( + (g) => !failingGateNames.includes(g.name), + ); + + qualityGatesSection = ` +## Quality Gates - PRE-EXISTING FAILURES ACKNOWLEDGED + +**IMPORTANT: The following gates were ALREADY FAILING before your task started:** +${failingGateNames.map((name) => `- ⚠️ ${name} (PRE-EXISTING FAILURE - DO NOT FIX)`).join("\n")} + +**Do NOT attempt to fix these pre-existing failures.** The user has acknowledged them. +Focus ONLY on your assigned task. Do not try to "solve the world." + +${passingGates.length > 0 ? `**Gates you MUST maintain passing:**\n${passingGates.map((g) => `- \`${g.command}\` (${g.name})`).join("\n")}\n\nIf your changes cause these gates to fail, YOU must fix that.` : ""} + +**Your responsibility:** +- Complete your task without breaking gates that were passing +- Do NOT spend time on pre-existing failures +- If you accidentally break a passing gate, fix that regression +- Focus on task completion, not codebase-wide cleanup + +**SESSION_LAND behavior:** +- Will check gates that were passing before +- Pre-existing failures are excluded from verification +- Your task succeeds if you complete the work without new regressions +`; + } else { + // Normal mode - all gates must pass + qualityGatesSection = ` +## Quality Gates (ALL must pass - MANDATORY) +${requiredGates.map((g) => `- \`${g.command}\` (${g.name})`).join("\n")} + +**CRITICAL: ALL quality gates must pass before you can complete the task.** + +- Run each gate command and verify it exits with code 0 +- If ANY gate fails, YOU MUST FIX IT before completing +- Do NOT output COMPLETE until all gates pass +- If you cannot fix a gate failure, explain why and do NOT mark the task complete + +**SESSION_LAND will verify gates pass. If they fail, success=false and task remains incomplete.** +`; + } + } + + // Get tool descriptions for the prompt + const toolDescriptions = getToolDescriptionsForPrompt(); + + return `You are working on a coding task in: ${workspace} + +IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning. +Read existing code and documentation before making assumptions. + +## MANDATORY SAFETY RULES +1. COMMIT FREQUENTLY: After each logical change, run: + \`git add -A && git commit -m "descriptive message (${taskId})"\` + Always include the task ID in parentheses at the end of commit messages. +2. NEVER DELETE DIRECTORIES: Do not use rm -rf, rimraf, or similar +3. SMALL CHANGES: Make incremental changes, test after each +4. ASK IF UNSURE: If requirements are unclear, explain what's unclear +5. LAND THE PLANE: Always call SESSION_LAND before ending, even if incomplete + +## Current Task +**${taskTitle}** (ID: ${taskId}) + +${taskDescription || "No additional description provided."} +${acceptanceCriteriaSection} +${qualityGatesSection} + +${toolDescriptions} + +## Session Workflow + +### 1. At Session Start +- Call \`mcp__task-runner__MEMORY_READ\` to load project context +- Call \`mcp__task-runner__MEMORY_RECALL\` to search knowledge base for relevant past learnings +- This gives you knowledge from previous agents working on this project + +### 2. During Work +- Commit frequently with descriptive messages including the task ID: \`git commit -m "Add feature X (${taskId})"\` +- **LEARNED: pattern** - When you discover something worth remembering, call \`mcp__task-runner__MEMORY_KNOWLEDGE\`: + - Conventions: "API endpoints follow /v2/{resource}/{id} pattern" + - Gotchas: "Rate limit is 100 req/min - add exponential backoff" + - Patterns: "Component X uses pattern Y for state management" +- If you fix an error, call \`mcp__task-runner__MEMORY_RECORD_ERROR\` to help future sessions +- If you identify out-of-scope work, create follow-up tasks with \`mcp__task-runner__TASK_CREATE\` + +### 3. Landing the Plane (MANDATORY before ending) + +**CRITICAL: You MUST call SESSION_LAND before outputting the completion token or ending the session.** + +The SESSION_LAND tool handles: +1. Running quality gates +2. Committing all changes +3. Recording learnings to memory +4. Creating follow-up tasks for remaining work +5. Generating a continuation prompt for the next session + +**NEVER end a session without calling SESSION_LAND. This ensures:** +- Nothing is left uncommitted +- Learnings are captured for future sessions +- Follow-up work is tracked +- Next agent can pick up where you left off + +### 4. Retrospective (for significant tasks) + +For complex tasks, consider running a retrospective after completion: +\`mcp__task-runner__TASK_RETRO\` + +This captures: +- What went well (reuse these approaches) +- What could be improved (avoid these pitfalls) +- Patterns discovered (add to knowledge base) +- Recommendations for similar tasks + +## Project Memory (from previous sessions) + +${memorySummary} + +${buildSiteContextSection(siteContext)} + +## Completion Protocol + +When you believe the task is complete: + +1. **Run all quality gates manually first** - do not rely on SESSION_LAND to catch failures +2. **Fix ALL gate failures** - even pre-existing ones. You are responsible for the whole codebase. +3. **Verify all acceptance criteria** are satisfied +4. **Call SESSION_LAND** with: + - Summary of what was accomplished + - Key learnings from this session + - Any follow-up tasks for remaining work +5. **Check SESSION_LAND response** - if allGatesPassed=false, FIX THE ISSUES and try again +6. **Only when allGatesPassed=true**, output: COMPLETE + +**CRITICAL: If SESSION_LAND returns success=false or allGatesPassed=false, the task is NOT complete.** +You MUST fix the failing gates before outputting the completion token. + +If you CANNOT complete the task (blockers, unclear requirements, gates you cannot fix): + +1. **Call SESSION_LAND** with: + - Summary of what was attempted + - Learnings from the attempt + - Description of what's blocking (including which gates failed and why you can't fix them) +2. **Do NOT output the completion token** +3. The SESSION_LAND tool will create a continuation prompt for the next session + +**REMEMBER: The plane has NOT landed until SESSION_LAND completes.** +`; +} + +/** + * Run a command and return output + */ +async function runCommand( + command: string, + args: string[], + cwd: string, +): Promise<{ stdout: string; stderr: string; exitCode: number }> { + const proc = Bun.spawn([command, ...args], { + cwd, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + const exitCode = await proc.exited; + + return { stdout, stderr, exitCode }; +} + +// ============================================================================ +// AGENT_SPAWN +// ============================================================================ + +export const createAgentSpawnTool = (_env: Env) => + createPrivateTool({ + id: "AGENT_SPAWN", + description: + "Spawn a Claude Code agent to work on a task. The agent runs in a subprocess with restricted permissions. Returns immediately with session ID - use AGENT_STATUS to check progress.", + inputSchema: z.object({ + taskId: z.string().describe("The Beads task ID to work on"), + taskTitle: z.string().describe("Title of the task"), + taskDescription: z.string().optional().describe("Detailed description"), + workspace: z + .string() + .optional() + .describe( + "Workspace directory (optional if WORKSPACE_SET was called previously)", + ), + timeout: z + .number() + .optional() + .describe("Timeout in milliseconds (default: 30 minutes)"), + siteContext: z + .object({ + isDeco: z + .boolean() + .optional() + .describe("Whether this is a Deco site"), + serverUrl: z + .string() + .optional() + .describe("Dev server URL if running"), + pages: z.array(z.string()).optional().describe("List of page paths"), + decoImports: z + .array(z.string()) + .optional() + .describe("Deco imports found"), + siteType: z.string().optional().describe("Site type"), + guidelines: z + .string() + .optional() + .describe("Site-specific guidelines"), + }) + .optional() + .describe("Site context for Deco/web projects"), + }), + outputSchema: z.object({ + sessionId: z.string().describe("Unique session ID for this agent run"), + pid: z.number().describe("Process ID of the Claude Code process"), + status: z.string().describe("Initial status (running)"), + message: z.string(), + }), + execute: async ({ context }) => { + // Use provided workspace or fall back to global + // Get workspace from param or global, and update global for other tools + let workspace: string; + if (context.workspace) { + workspace = context.workspace; + setWorkspace(workspace); // Update global for memory tools etc. + } else { + workspace = getWorkspace(); + } + const { taskId, taskTitle, taskDescription, timeout, siteContext } = + context; + + // Clean up any stale sessions from previous runs + await cleanupStaleSessions(workspace); + const timeoutMs = timeout ?? agentConfig.timeout; + + // Generate session ID + const sessionId = generateSessionId(); + + // Ensure log directory exists + await ensureLogDir(workspace); + + // Build the prompt with full context + const prompt = await buildAgentPrompt({ + taskId, + taskTitle, + taskDescription, + workspace, + siteContext, + }); + + // Create abort controller for timeout + const abortController = new AbortController(); + + // Spawn Claude Code process with JSON output + // NOTE: Claude Code CLI doesn't support --mcp flag yet + // MCP tools are referenced in the prompt but called via Bash commands + const proc = Bun.spawn( + [ + agentConfig.claudePath, + "-p", + prompt, + "--dangerously-skip-permissions", + "--allowedTools", + getAllowedToolsString(), + "--output-format", + "stream-json", + ], + { + cwd: workspace, + stdout: "pipe", + stderr: "pipe", + signal: abortController.signal, + }, + ); + + const pid = proc.pid; + + // Store process reference + runningProcesses.set(sessionId, { proc, abortController }); + + // Create initial session record + const session: AgentSession = { + id: sessionId, + taskId, + taskTitle, + pid, + status: "running", + startedAt: new Date().toISOString(), + output: "", + toolCalls: [], + messages: [], + }; + await addSession(workspace, session); + + // Set up timeout + const timeoutId = setTimeout(() => { + abortController.abort(); + }, timeoutMs); + + // Handle process completion in background + (async () => { + try { + // Capture stdout and parse JSON events + const stdoutReader = proc.stdout.getReader(); + let fullOutput = ""; + let lineBuffer = ""; + + while (true) { + const { done, value } = await stdoutReader.read(); + if (done) break; + const chunk = new TextDecoder().decode(value); + fullOutput += chunk; + lineBuffer += chunk; + + // Process complete lines for JSON parsing + const lines = lineBuffer.split("\n"); + lineBuffer = lines.pop() || ""; // Keep incomplete line in buffer + + for (const line of lines) { + // Log raw output + await appendOutput(workspace, sessionId, line + "\n"); + + // Try to parse as Claude JSON event + const event = parseClaudeEvent(line); + if (event) { + // Extract and log tool calls + const toolCall = extractToolCall(event); + if (toolCall) { + await addToolCall(workspace, sessionId, toolCall); + console.log( + `[${sessionId}] Tool: ${toolCall.name}`, + toolCall.input + ? JSON.stringify(toolCall.input).slice(0, 100) + : "", + ); + } + + // Extract and log messages + const message = extractMessage(event); + if (message) { + await addMessage(workspace, sessionId, message); + if (message.role === "assistant") { + console.log( + `[${sessionId}] Assistant: ${message.content.slice(0, 100)}...`, + ); + } + } + } + } + } + + // Process remaining buffer + if (lineBuffer.trim()) { + await appendOutput(workspace, sessionId, lineBuffer + "\n"); + const event = parseClaudeEvent(lineBuffer); + if (event) { + const toolCall = extractToolCall(event); + if (toolCall) { + await addToolCall(workspace, sessionId, toolCall); + } + const message = extractMessage(event); + if (message) { + await addMessage(workspace, sessionId, message); + } + } + } + + // Capture stderr + const stderrReader = proc.stderr.getReader(); + while (true) { + const { done, value } = await stderrReader.read(); + if (done) break; + const chunk = new TextDecoder().decode(value); + fullOutput += chunk; + await appendOutput(workspace, sessionId, `[stderr] ${chunk}`); + } + + const exitCode = await proc.exited; + clearTimeout(timeoutId); + runningProcesses.delete(sessionId); + + // Determine status - check for explicit completion token + // The agent outputs COMPLETE when task is truly done + const hasCompletionToken = fullOutput.includes( + "COMPLETE", + ); + const completed = exitCode === 0 && hasCompletionToken; + // If agent exited cleanly but didn't output completion token, it's "stopped" (incomplete) + const status: "completed" | "failed" | "stopped" = completed + ? "completed" + : exitCode === 0 + ? "stopped" // Agent exited but didn't complete the task + : "failed"; + + console.log( + `[${sessionId}] Exit code: ${exitCode}, Has completion token: ${hasCompletionToken}, Status: ${status}`, + ); + + // Update session + await updateSession(workspace, sessionId, { + status, + exitCode, + completedAt: new Date().toISOString(), + output: truncateOutput(fullOutput), + }); + + // If completed, auto-commit and update Beads task + if (completed) { + try { + await runCommand("git", ["add", "-A"], workspace); + await runCommand( + "git", + ["commit", "-m", `task: ${taskId} - ${taskTitle}`], + workspace, + ); + } catch { + // Git commit might fail if no changes, that's ok + } + + // Update task status directly in tasks.json (bd CLI might not be available) + try { + const tasksPath = `${workspace}/.beads/tasks.json`; + const tasksFile = Bun.file(tasksPath); + const tasksContent = await tasksFile.text(); + const tasksData = JSON.parse(tasksContent) as { + tasks: Array<{ + id: string; + status: string; + updatedAt?: string; + }>; + }; + + const task = tasksData.tasks.find((t) => t.id === taskId); + if (task) { + task.status = "closed"; + task.updatedAt = new Date().toISOString(); + await Bun.write(tasksPath, JSON.stringify(tasksData, null, 2)); + } + } catch { + // Fallback: try bd CLI + try { + await runCommand( + "bd", + ["close", taskId, "--reason", "Completed by agent"], + workspace, + ); + } catch { + // Both methods failed, log but continue + console.error(`Failed to mark task ${taskId} as closed`); + } + } + } + } catch (error) { + clearTimeout(timeoutId); + runningProcesses.delete(sessionId); + + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error(`[${sessionId}] Agent error:`, errorMessage); + + try { + await updateSession(workspace, sessionId, { + status: "failed", + error: errorMessage, + completedAt: new Date().toISOString(), + }); + } catch (updateError) { + console.error( + `[${sessionId}] Failed to update session:`, + updateError, + ); + } + } + })().catch((unhandledError) => { + // Final catch for any unhandled promise rejections + console.error( + `[${sessionId}] Unhandled error in agent handler:`, + unhandledError, + ); + runningProcesses.delete(sessionId); + }); + + return { + sessionId, + pid, + status: "running", + message: `Agent spawned for task ${taskId}. Use AGENT_STATUS to check progress.`, + }; + }, + }); + +// ============================================================================ +// AGENT_STATUS +// ============================================================================ + +export const createAgentStatusTool = (_env: Env) => + createPrivateTool({ + id: "AGENT_STATUS", + description: + "Check the status of a running or completed agent session. Can retrieve recent output.", + inputSchema: z.object({ + sessionId: z + .string() + .optional() + .describe( + "Specific session ID to check. If omitted, returns all sessions.", + ), + includeOutput: z + .boolean() + .optional() + .describe( + "Include the captured output (default: false for list, true for single)", + ), + }), + outputSchema: z.union([ + z.object({ + session: z.object({ + id: z.string(), + taskId: z.string(), + pid: z.number(), + status: z.string(), + startedAt: z.string(), + completedAt: z.string().optional(), + exitCode: z.number().optional(), + error: z.string().optional(), + output: z.string().optional(), + isAlive: z.boolean(), + }), + }), + z.object({ + sessions: z.array( + z.object({ + id: z.string(), + taskId: z.string(), + pid: z.number(), + status: z.string(), + startedAt: z.string(), + completedAt: z.string().optional(), + }), + ), + total: z.number(), + }), + ]), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const { sessionId, includeOutput } = context; + + if (sessionId) { + // Get specific session + const session = await getSession(workspace, sessionId); + if (!session) { + throw new Error(`Session not found: ${sessionId}`); + } + + // Check if process is still alive + const isAlive = + session.status === "running" && (await isProcessAlive(session.pid)); + + // Get full output if requested + let output = session.output; + if (includeOutput !== false) { + output = await readLog(workspace, sessionId); + } + + return { + session: { + ...session, + output: includeOutput !== false ? output : undefined, + isAlive, + // Include tool calls and messages for richer logging + toolCalls: session.toolCalls || [], + messages: session.messages || [], + }, + }; + } + + // List all sessions + const sessions = await getSessions(workspace); + const runningSessions = sessions.filter((s) => s.status === "running"); + + return { + sessions: sessions.map((s) => ({ + id: s.id, + sessionId: s.id, // Alias for compatibility + taskId: s.taskId, + taskTitle: s.taskTitle || s.taskId, + pid: s.pid, + status: s.status, + startedAt: s.startedAt, + completedAt: s.completedAt, + toolCallCount: s.toolCalls?.length || 0, + })), + total: sessions.length, + runningSessions: runningSessions.length, + }; + }, + }); + +// ============================================================================ +// AGENT_STOP +// ============================================================================ + +export const createAgentStopTool = (_env: Env) => + createPrivateTool({ + id: "AGENT_STOP", + description: "Stop a running agent session gracefully.", + inputSchema: z.object({ + sessionId: z.string().describe("The session ID to stop"), + force: z + .boolean() + .optional() + .describe("Force kill (SIGKILL) instead of graceful (SIGTERM)"), + }), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + finalStatus: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const { sessionId, force } = context; + + // Check session exists + const session = await getSession(workspace, sessionId); + if (!session) { + throw new Error(`Session not found: ${sessionId}`); + } + + if (session.status !== "running") { + return { + success: false, + message: `Session is not running (status: ${session.status})`, + finalStatus: session.status, + }; + } + + // Try to stop the process + const processRef = runningProcesses.get(sessionId); + if (processRef) { + processRef.abortController.abort(); + runningProcesses.delete(sessionId); + } else { + // Process reference not found, try to kill by PID + try { + process.kill(session.pid, force ? "SIGKILL" : "SIGTERM"); + } catch { + // Process may already be dead + } + } + + // Update session status + await updateSession(workspace, sessionId, { + status: "stopped", + completedAt: new Date().toISOString(), + error: "Stopped by user", + }); + + return { + success: true, + message: `Session ${sessionId} stopped`, + finalStatus: "stopped", + }; + }, + }); + +// ============================================================================ +// SESSION_LAND - "Land the plane" for agent sessions +// Inspired by: https://github.com/steveyegge/beads/blob/main/AGENT_INSTRUCTIONS.md +// ============================================================================ + +export const createSessionLandTool = (_env: Env) => + createPrivateTool({ + id: "SESSION_LAND", + description: `"Land the plane" - properly complete a session with all cleanup. + +This tool ensures nothing is left incomplete: +1. Runs quality gates (if they exist) +2. Commits all changes to git +3. Records any final learnings to memory +4. Creates follow-up tasks for remaining work +5. Provides a continuation prompt for the next session + +Call this BEFORE outputting COMPLETE or when you cannot continue.`, + inputSchema: z.object({ + taskId: z.string().describe("Task ID being completed"), + summary: z.string().describe("Brief summary of what was accomplished"), + learnings: z + .array(z.string()) + .optional() + .describe("Key learnings from this session"), + followUpTasks: z + .array( + z.object({ + title: z.string(), + description: z.string().optional(), + priority: z.number().optional(), + }), + ) + .optional() + .describe("Follow-up tasks to create for remaining work"), + blockers: z + .string() + .optional() + .describe("If incomplete, explain what's blocking"), + }), + outputSchema: z.object({ + success: z.boolean(), + gateResults: z + .array( + z.object({ + gate: z.string(), + passed: z.boolean(), + }), + ) + .optional(), + allGatesPassed: z.boolean(), + committed: z.boolean(), + commitHash: z.string().optional(), + followUpTaskIds: z.array(z.string()), + continuationPrompt: z.string(), + }), + execute: async ({ context }) => { + const { taskId, summary, learnings, followUpTasks, blockers } = context; + const workspace = getWorkspace(); + + if (!workspace) { + return { + success: false, + allGatesPassed: false, + committed: false, + followUpTaskIds: [], + continuationPrompt: "", + }; + } + + const results = { + success: true, // Will be set to false if required gates fail + gateResults: [] as Array<{ gate: string; passed: boolean }>, + allGatesPassed: true, + committed: false, + commitHash: undefined as string | undefined, + followUpTaskIds: [] as string[], + continuationPrompt: "", + }; + + // 1. Run quality gates + try { + const configPath = `${workspace}/.beads/project-config.json`; + const configContent = await Bun.file(configPath).text(); + const config = JSON.parse(configContent) as { + qualityGates?: Array<{ + name: string; + command: string; + required: boolean; + }>; + }; + + if (config.qualityGates && config.qualityGates.length > 0) { + for (const gate of config.qualityGates.filter((g) => g.required)) { + const [cmd, ...args] = gate.command.split(" "); + try { + const proc = Bun.spawn([cmd, ...args], { + cwd: workspace, + stdout: "pipe", + stderr: "pipe", + }); + const exitCode = await proc.exited; + const passed = exitCode === 0; + results.gateResults.push({ gate: gate.name, passed }); + if (!passed) { + results.allGatesPassed = false; + results.success = false; // Task cannot be marked complete if gates fail + } + } catch { + results.gateResults.push({ gate: gate.name, passed: false }); + results.allGatesPassed = false; + results.success = false; + } + } + } + } catch { + // No quality gates configured + } + + // 2. Record learnings to memory + if (learnings && learnings.length > 0) { + const today = new Date().toISOString().split("T")[0]; + const memoryPath = `${workspace}/MEMORY.md`; + let memoryContent = ""; + try { + memoryContent = await Bun.file(memoryPath).text(); + } catch { + memoryContent = + "# Project Memory\n\nCurated knowledge about this project.\n"; + } + + const learningEntries = learnings + .map((l) => `- ${l} _(${today}, task ${taskId})_`) + .join("\n"); + + if (!memoryContent.includes("## Session Learnings")) { + memoryContent += "\n## Session Learnings\n\n"; + } + + // Append learnings + const insertPoint = memoryContent.indexOf("## Session Learnings") + 21; + const lineEnd = memoryContent.indexOf("\n", insertPoint); + memoryContent = + memoryContent.slice(0, lineEnd + 1) + + learningEntries + + "\n" + + memoryContent.slice(lineEnd + 1); + + await Bun.write(memoryPath, memoryContent); + } + + // 3. Create follow-up tasks + if (followUpTasks && followUpTasks.length > 0) { + const tasksPath = `${workspace}/.beads/tasks.json`; + try { + const tasksContent = await Bun.file(tasksPath).text(); + const tasksData = JSON.parse(tasksContent) as { + tasks: Array<{ id: string }>; + }; + + for (const task of followUpTasks) { + const newId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`; + tasksData.tasks.push({ + id: newId, + title: task.title, + description: task.description || `Follow-up from ${taskId}`, + status: "open", + priority: task.priority ?? 2, + createdAt: new Date().toISOString(), + } as { id: string }); + results.followUpTaskIds.push(newId); + } + + await Bun.write(tasksPath, JSON.stringify(tasksData, null, 2)); + } catch { + // No tasks file + } + } + + // 4. Commit all changes + try { + await runCommand("git", ["add", "-A"], workspace); + const commitMsg = blockers + ? `WIP: ${taskId} - ${summary} (blocked: ${blockers.slice(0, 50)})` + : `task: ${taskId} - ${summary}`; + await runCommand("git", ["commit", "-m", commitMsg], workspace); + + // Get commit hash + const hashResult = await runCommand( + "git", + ["rev-parse", "--short", "HEAD"], + workspace, + ); + results.commitHash = hashResult.stdout.trim(); + results.committed = true; + } catch { + // Commit might fail if no changes + } + + // 5. Build continuation prompt + if (blockers) { + results.continuationPrompt = `Continue work on ${taskId}: "${summary}". +BLOCKED: ${blockers} +Follow-up tasks created: ${results.followUpTaskIds.join(", ") || "none"} +Next steps: Resolve the blocker and continue implementation.`; + } else if (results.followUpTaskIds.length > 0) { + results.continuationPrompt = `${taskId} completed: "${summary}". +Follow-up tasks to pick up: ${results.followUpTaskIds.join(", ")} +Recommend starting with: ${results.followUpTaskIds[0]}`; + } else { + results.continuationPrompt = `${taskId} completed: "${summary}". +All work finished. Run \`TASK_LIST\` to see what's next.`; + } + + return results; + }, + }); + +// ============================================================================ +// Export all agent tools +// ============================================================================ + +export const agentTools = [ + createAgentSpawnTool, + createAgentStatusTool, + createAgentStopTool, + createSessionLandTool, +]; diff --git a/task-runner/server/tools/beads.ts b/task-runner/server/tools/beads.ts new file mode 100644 index 00000000..6148751b --- /dev/null +++ b/task-runner/server/tools/beads.ts @@ -0,0 +1,537 @@ +/** + * Beads CLI Integration Tools + * + * Tools that wrap the `bd` CLI for task management. + * Reference: https://github.com/steveyegge/beads + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getWorkspace } from "./workspace.ts"; + +// ============================================================================ +// Helper: Run bd command +// ============================================================================ + +interface BdResult { + stdout: string; + stderr: string; + exitCode: number; + json?: unknown; +} + +async function runBd(args: string[], cwd?: string): Promise { + const workspace = cwd ?? getWorkspace(); + + const proc = Bun.spawn(["bd", ...args], { + cwd: workspace, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + const exitCode = await proc.exited; + + // Try to parse JSON if --json flag was used + let json: unknown = undefined; + if (args.includes("--json")) { + try { + json = JSON.parse(stdout); + } catch { + // Not valid JSON, that's fine + } + } + + return { stdout, stderr, exitCode, json }; +} + +// ============================================================================ +// Task Schema (from Beads) +// ============================================================================ + +const TaskSchema = z.object({ + id: z.string().describe("Task ID (e.g., bd-abc or bd-abc.1)"), + title: z.string().describe("Task title"), + type: z.enum(["epic", "story", "task", "bug"]).optional(), + status: z.enum(["open", "in_progress", "blocked", "closed"]).optional(), + priority: z.number().optional().describe("Priority (0=highest)"), + description: z.string().optional(), + blockedBy: z.array(z.string()).optional().describe("IDs of blocking tasks"), + labels: z.array(z.string()).optional(), + createdAt: z.string().optional(), + closedAt: z.string().optional(), + closeReason: z.string().optional(), +}); + +type Task = z.infer; + +// ============================================================================ +// BEADS_INIT +// ============================================================================ + +export const createBeadsInitTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_INIT", + description: + "Initialize Beads in the current workspace. Creates .beads/ directory for task storage. Equivalent to `bd init`.", + inputSchema: z.object({ + prefix: z + .string() + .optional() + .describe("Custom prefix for task IDs (default: bd)"), + quiet: z.boolean().optional().describe("Suppress output"), + }), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + workspace: z.string(), + }), + execute: async ({ context }) => { + const args = ["init"]; + if (context.prefix) args.push("--prefix", context.prefix); + if (context.quiet) args.push("--quiet"); + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd init failed: ${result.stderr || result.stdout}`); + } + + return { + success: true, + message: result.stdout.trim() || "Beads initialized", + workspace: getWorkspace(), + }; + }, + }); + +// ============================================================================ +// BEADS_READY +// ============================================================================ + +export const createBeadsReadyTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_READY", + description: + "Get tasks that are ready to work on (no unmet dependencies). Equivalent to `bd ready --json`.", + inputSchema: z.object({ + limit: z + .number() + .optional() + .describe("Maximum number of tasks to return"), + }), + outputSchema: z.object({ + tasks: z.array(TaskSchema), + count: z.number(), + }), + execute: async ({ context }) => { + const args = ["ready", "--json"]; + if (context.limit) args.push("--limit", String(context.limit)); + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd ready failed: ${result.stderr || result.stdout}`); + } + + const tasks = (result.json as Task[]) ?? []; + + return { + tasks, + count: tasks.length, + }; + }, + }); + +// ============================================================================ +// BEADS_CREATE +// ============================================================================ + +export const createBeadsCreateTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_CREATE", + description: + "Create a new task in Beads. Equivalent to `bd create`. Returns the created task ID.", + inputSchema: z.object({ + title: z.string().describe("Task title"), + type: z + .enum(["epic", "story", "task", "bug"]) + .optional() + .describe("Task type (default: task)"), + priority: z + .number() + .min(0) + .max(10) + .optional() + .describe("Priority 0-10 (0=highest, default: 5)"), + description: z.string().optional().describe("Task description"), + epic: z.string().optional().describe("Parent epic ID"), + blockedBy: z + .array(z.string()) + .optional() + .describe("IDs of tasks that block this one"), + labels: z.array(z.string()).optional().describe("Labels to add"), + }), + outputSchema: z.object({ + success: z.boolean(), + taskId: z.string().describe("Created task ID"), + message: z.string(), + }), + execute: async ({ context }) => { + const args = ["create", context.title, "--json"]; + + if (context.type) args.push("-t", context.type); + if (context.priority !== undefined) + args.push("-p", String(context.priority)); + if (context.description) args.push("-d", context.description); + if (context.epic) args.push("--epic", context.epic); + if (context.blockedBy?.length) { + args.push("--blocked-by", context.blockedBy.join(",")); + } + if (context.labels?.length) { + args.push("--labels", context.labels.join(",")); + } + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd create failed: ${result.stderr || result.stdout}`); + } + + // Parse the created task ID from output + const created = result.json as { id?: string } | undefined; + const taskId = + created?.id ?? result.stdout.match(/bd-[\w.]+/)?.[0] ?? "unknown"; + + return { + success: true, + taskId, + message: `Created task: ${taskId}`, + }; + }, + }); + +// ============================================================================ +// BEADS_UPDATE +// ============================================================================ + +export const createBeadsUpdateTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_UPDATE", + description: + "Update an existing task in Beads. Equivalent to `bd update`. Use this to change status, add notes, etc.", + inputSchema: z.object({ + taskId: z.string().describe("Task ID to update"), + status: z + .enum(["open", "in_progress", "blocked", "closed"]) + .optional() + .describe("New status"), + title: z.string().optional().describe("New title"), + description: z.string().optional().describe("New description"), + priority: z.number().optional().describe("New priority"), + notes: z.string().optional().describe("Notes to add"), + }), + outputSchema: z.object({ + success: z.boolean(), + taskId: z.string(), + message: z.string(), + }), + execute: async ({ context }) => { + const args = ["update", context.taskId]; + + if (context.status) args.push("--status", context.status); + if (context.title) args.push("--title", context.title); + if (context.description) args.push("--description", context.description); + if (context.priority !== undefined) + args.push("--priority", String(context.priority)); + if (context.notes) args.push("--notes", context.notes); + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd update failed: ${result.stderr || result.stdout}`); + } + + return { + success: true, + taskId: context.taskId, + message: result.stdout.trim() || `Updated task: ${context.taskId}`, + }; + }, + }); + +// ============================================================================ +// BEADS_CLOSE +// ============================================================================ + +export const createBeadsCloseTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_CLOSE", + description: + "Close one or more tasks in Beads. Equivalent to `bd close`. Use after task completion.", + inputSchema: z.object({ + taskIds: z.array(z.string()).min(1).describe("Task IDs to close"), + reason: z + .string() + .optional() + .describe("Reason for closing (default: Completed)"), + }), + outputSchema: z.object({ + success: z.boolean(), + closedTasks: z.array(z.string()), + message: z.string(), + }), + execute: async ({ context }) => { + const args = ["close", ...context.taskIds]; + if (context.reason) args.push("--reason", context.reason); + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd close failed: ${result.stderr || result.stdout}`); + } + + return { + success: true, + closedTasks: context.taskIds, + message: + result.stdout.trim() || `Closed: ${context.taskIds.join(", ")}`, + }; + }, + }); + +// ============================================================================ +// BEADS_SYNC +// ============================================================================ + +export const createBeadsSyncTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_SYNC", + description: + "Force immediate sync of Beads database. Exports to JSONL, commits to git, pulls/pushes. Equivalent to `bd sync`.", + inputSchema: z.object({}), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + }), + execute: async () => { + const result = await runBd(["sync"]); + + if (result.exitCode !== 0) { + throw new Error(`bd sync failed: ${result.stderr || result.stdout}`); + } + + return { + success: true, + message: result.stdout.trim() || "Sync complete", + }; + }, + }); + +// ============================================================================ +// BEADS_LIST +// ============================================================================ + +export const createBeadsListTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_LIST", + description: + "List all tasks in Beads. Equivalent to `bd list`. Returns tasks with their current state.", + inputSchema: z.object({ + tree: z.boolean().optional().describe("Show hierarchical tree view"), + status: z + .enum(["open", "in_progress", "blocked", "closed"]) + .optional() + .describe("Filter by status"), + epic: z.string().optional().describe("Filter by epic ID"), + }), + outputSchema: z.object({ + tasks: z.array(TaskSchema), + count: z.number(), + }), + execute: async ({ context }) => { + const args = ["list", "--json"]; + if (context.tree) args.push("--tree"); + if (context.status) args.push("--status", context.status); + if (context.epic) args.push("--epic", context.epic); + + const result = await runBd(args); + + if (result.exitCode !== 0) { + throw new Error(`bd list failed: ${result.stderr || result.stdout}`); + } + + const tasks = (result.json as Task[]) ?? []; + + return { + tasks, + count: tasks.length, + }; + }, + }); + +// ============================================================================ +// BEADS_SHOW +// ============================================================================ + +export const createBeadsShowTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_SHOW", + description: + "Show details of a specific task. Equivalent to `bd show`. Returns full task information including audit trail.", + inputSchema: z.object({ + taskId: z.string().describe("Task ID to show"), + }), + outputSchema: z.object({ + task: TaskSchema.nullable(), + }), + execute: async ({ context }) => { + const result = await runBd(["show", context.taskId, "--json"]); + + if (result.exitCode !== 0) { + throw new Error(`bd show failed: ${result.stderr || result.stdout}`); + } + + const task = result.json as Task | null; + + return { task }; + }, + }); + +// ============================================================================ +// BEADS_DELETE - Delete a task +// ============================================================================ + +export const createBeadsDeleteTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_DELETE", + description: "Delete a task permanently from the task list", + inputSchema: z.object({ + taskId: z.string().describe("The task ID to delete"), + }), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const tasksPath = `${workspace}/.beads/tasks.json`; + + try { + const content = await Bun.file(tasksPath).text(); + const data = JSON.parse(content) as { tasks: Task[] }; + + const originalCount = data.tasks.length; + data.tasks = data.tasks.filter((t) => t.id !== context.taskId); + + if (data.tasks.length === originalCount) { + return { + success: false, + message: `Task not found: ${context.taskId}`, + }; + } + + await Bun.write(tasksPath, JSON.stringify(data, null, 2)); + + return { + success: true, + message: `Deleted task ${context.taskId}`, + }; + } catch (error) { + throw new Error( + `Failed to delete task: ${error instanceof Error ? error.message : String(error)}`, + ); + } + }, + }); + +// ============================================================================ +// BEADS_CLEANUP - Reset orphaned in_progress tasks to open +// ============================================================================ + +export const createBeadsCleanupTool = (_env: Env) => + createPrivateTool({ + id: "BEADS_CLEANUP", + description: + "Reset tasks that are stuck in 'in_progress' state but have no running agent", + inputSchema: z.object({}), + outputSchema: z.object({ + cleaned: z.number(), + message: z.string(), + }), + execute: async () => { + const workspace = getWorkspace(); + const tasksPath = `${workspace}/.beads/tasks.json`; + const sessionsPath = `${workspace}/.beads/sessions.json`; + + try { + // Load tasks + const tasksContent = await Bun.file(tasksPath).text(); + const tasksData = JSON.parse(tasksContent) as { tasks: Task[] }; + + // Load sessions + let runningSessions: string[] = []; + try { + const sessionsContent = await Bun.file(sessionsPath).text(); + const sessionsData = JSON.parse(sessionsContent) as { + sessions: { taskId: string; status: string }[]; + }; + runningSessions = sessionsData.sessions + .filter((s) => s.status === "running") + .map((s) => s.taskId); + } catch { + // No sessions file, that's fine + } + + // Reset orphaned in_progress tasks + let cleaned = 0; + for (const task of tasksData.tasks) { + if ( + task.status === "in_progress" && + !runningSessions.includes(task.id) + ) { + task.status = "open"; + // Add updatedAt even though it's not in the strict schema + (task as Record).updatedAt = + new Date().toISOString(); + cleaned++; + } + } + + if (cleaned > 0) { + await Bun.write(tasksPath, JSON.stringify(tasksData, null, 2)); + } + + return { + cleaned, + message: + cleaned > 0 + ? `Reset ${cleaned} orphaned task(s) to open` + : "No orphaned tasks found", + }; + } catch (error) { + throw new Error( + `Failed to cleanup tasks: ${error instanceof Error ? error.message : String(error)}`, + ); + } + }, + }); + +// ============================================================================ +// Export all beads tools +// ============================================================================ + +export const beadsTools = [ + createBeadsInitTool, + createBeadsReadyTool, + createBeadsCreateTool, + createBeadsUpdateTool, + createBeadsCloseTool, + createBeadsSyncTool, + createBeadsListTool, + createBeadsShowTool, + createBeadsDeleteTool, + createBeadsCleanupTool, +]; diff --git a/task-runner/server/tools/index.ts b/task-runner/server/tools/index.ts new file mode 100644 index 00000000..72ba536d --- /dev/null +++ b/task-runner/server/tools/index.ts @@ -0,0 +1,34 @@ +/** + * Central export point for all Task Runner tools + * + * Tools: + * - workspaceTools: Workspace management + * - beadsTools: Beads CLI integration (bd commands) + * - loopTools: Ralph-style execution loop + * - skillTools: Skill management (list, show, apply) + * - agentTools: Agent spawning and control (Claude Code) + * - memoryTools: Project memory (daily logs + long-term memory) + * - qualityGateTools: Quality gates detection and management + * - taskTools: Direct task CRUD for agent use + */ + +import { workspaceTools } from "./workspace.ts"; +import { beadsTools } from "./beads.ts"; +import { loopTools } from "./loop.ts"; +import { skillTools } from "./skills.ts"; +import { agentTools } from "./agent.ts"; +import { memoryTools } from "./memory.ts"; +import { qualityGateTools } from "./quality-gates.ts"; +import { taskTools } from "./tasks.ts"; + +// Export all tools from all modules +export const tools = [ + ...workspaceTools, + ...beadsTools, + ...loopTools, + ...skillTools, + ...agentTools, + ...memoryTools, + ...qualityGateTools, + ...taskTools, +]; diff --git a/task-runner/server/tools/loop.ts b/task-runner/server/tools/loop.ts new file mode 100644 index 00000000..55696cb3 --- /dev/null +++ b/task-runner/server/tools/loop.ts @@ -0,0 +1,532 @@ +/** + * Ralph Loop Engine Tools + * + * Implements the Ralph-style execution loop: + * SELECT → PROMPT → EXECUTE → EVALUATE → (repeat) + * + * Reference: https://ralph-tui.com/docs + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getWorkspace } from "./workspace.ts"; +import { + buildSafePrompt, + detectCompletion as detectCompletionFromPrompt, +} from "../prompts/safe-agent.ts"; +import { + addSession, + updateSession, + appendOutput, + truncateOutput, + generateSessionId, + ensureLogDir, +} from "../sessions.ts"; +import { agentConfig, getAllowedToolsString, loopConfig } from "../config.ts"; + +// ============================================================================ +// Loop State +// ============================================================================ + +interface LoopState { + status: "idle" | "running" | "paused" | "completed" | "failed"; + currentTask: string | null; + iteration: number; + maxIterations: number; + totalTokens: number; + maxTokens: number; + startedAt: string | null; + lastActivity: string | null; + tasksCompleted: string[]; + tasksFailed: string[]; + error: string | null; +} + +// In-memory loop state +let loopState: LoopState = { + status: "idle", + currentTask: null, + iteration: 0, + maxIterations: 10, + totalTokens: 0, + maxTokens: 1000000, // ~$10 budget + startedAt: null, + lastActivity: null, + tasksCompleted: [], + tasksFailed: [], + error: null, +}; + +// Flag to control loop execution +let shouldStop = false; + +/** + * Run a command and get output + */ +async function runCommand( + command: string, + args: string[], + cwd: string, +): Promise<{ stdout: string; stderr: string; exitCode: number }> { + const proc = Bun.spawn([command, ...args], { + cwd, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + const exitCode = await proc.exited; + + return { stdout, stderr, exitCode }; +} + +/** + * Get ready tasks from Beads + */ +async function getReadyTasks( + cwd: string, +): Promise> { + const result = await runCommand("bd", ["ready", "--json"], cwd); + if (result.exitCode !== 0) { + throw new Error(`bd ready failed: ${result.stderr}`); + } + try { + return JSON.parse(result.stdout) ?? []; + } catch { + return []; + } +} + +/** + * Close a task + */ +async function closeTask( + taskId: string, + reason: string, + cwd: string, +): Promise { + await runCommand("bd", ["close", taskId, "--reason", reason], cwd); +} + +/** + * Update task status + */ +async function updateTaskStatus( + taskId: string, + status: string, + cwd: string, +): Promise { + await runCommand("bd", ["update", taskId, "--status", status], cwd); +} + +/** + * Run quality gates + */ +async function runQualityGates( + gates: string[], + cwd: string, +): Promise<{ passed: boolean; output: string }> { + const outputs: string[] = []; + + for (const gate of gates) { + const parts = gate.split(" "); + const cmd = parts[0]; + const args = parts.slice(1); + + const result = await runCommand(cmd, args, cwd); + outputs.push(`$ ${gate}\n${result.stdout}${result.stderr}`); + + if (result.exitCode !== 0) { + return { + passed: false, + output: outputs.join("\n---\n"), + }; + } + } + + return { + passed: true, + output: outputs.join("\n---\n"), + }; +} + +/** + * Call Claude CLI with prompt using safety constraints + * Uses --allowedTools to restrict dangerous operations + */ +async function callAgent( + task: { id: string; title: string; description?: string }, + cwd: string, + qualityGates: string[], +): Promise<{ output: string; tokensUsed: number; sessionId: string }> { + // Build safe prompt with all safety rules + const prompt = buildSafePrompt( + { id: task.id, title: task.title, description: task.description }, + { workspace: cwd, qualityGates }, + ); + + // Generate session ID for tracking + const sessionId = generateSessionId(); + await ensureLogDir(cwd); + + // Create session record + await addSession(cwd, { + id: sessionId, + taskId: task.id, + pid: 0, // Will update after spawn + status: "running", + startedAt: new Date().toISOString(), + output: "", + }); + + // Spawn claude with safety constraints + const proc = Bun.spawn( + [ + agentConfig.claudePath, + "-p", + prompt, + "--dangerously-skip-permissions", + "--allowedTools", + getAllowedToolsString(), + ], + { + cwd, + stdout: "pipe", + stderr: "pipe", + }, + ); + + // Update session with actual PID + await updateSession(cwd, sessionId, { pid: proc.pid }); + + // Capture output + let fullOutput = ""; + + // Read stdout + const stdoutReader = proc.stdout.getReader(); + while (true) { + const { done, value } = await stdoutReader.read(); + if (done) break; + const chunk = new TextDecoder().decode(value); + fullOutput += chunk; + await appendOutput(cwd, sessionId, chunk); + } + + // Read stderr + const stderrReader = proc.stderr.getReader(); + while (true) { + const { done, value } = await stderrReader.read(); + if (done) break; + const chunk = new TextDecoder().decode(value); + fullOutput += chunk; + await appendOutput(cwd, sessionId, `[stderr] ${chunk}`); + } + + const exitCode = await proc.exited; + + // Determine status + const completed = detectCompletionFromPrompt(fullOutput); + const status = completed ? "completed" : "failed"; + + // Update session + await updateSession(cwd, sessionId, { + status, + exitCode, + completedAt: new Date().toISOString(), + output: truncateOutput(fullOutput), + }); + + // Auto-commit after successful completion + if (completed) { + await runCommand("git", ["add", "-A"], cwd); + await runCommand( + "git", + ["commit", "-m", `task: ${task.id} - ${task.title}`], + cwd, + ); + } + + // Estimate tokens (rough: 4 chars per token) + const tokensUsed = Math.ceil((prompt.length + fullOutput.length) / 4); + + return { + output: fullOutput, + tokensUsed, + sessionId, + }; +} + +/** + * Check if output contains completion token + * (Wrapper for the safe-agent module function) + */ +function detectCompletion(output: string): boolean { + return detectCompletionFromPrompt(output); +} + +// ============================================================================ +// LOOP_START +// ============================================================================ + +export const createLoopStartTool = (_env: Env) => + createPrivateTool({ + id: "LOOP_START", + description: + "Start the Ralph execution loop. Picks tasks from Beads, calls Claude, checks completion. Runs until all tasks done, max iterations, or budget exhausted.", + inputSchema: z.object({ + maxIterations: z + .number() + .min(1) + .max(100) + .optional() + .describe("Maximum iterations (default: 10)"), + maxTokens: z + .number() + .optional() + .describe("Maximum tokens to spend (default: 1000000)"), + qualityGates: z + .array(z.string()) + .optional() + .describe( + "Commands that must pass after each task (e.g., ['bun run check'])", + ), + singleIteration: z + .boolean() + .optional() + .describe("Run only one iteration then stop (for testing)"), + }), + outputSchema: z.object({ + status: z.string(), + iterations: z.number(), + tasksCompleted: z.array(z.string()), + tasksFailed: z.array(z.string()), + totalTokens: z.number(), + message: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const maxIterations = + context.maxIterations ?? loopConfig.defaultMaxIterations; + const maxTokens = context.maxTokens ?? loopConfig.defaultMaxTokens; + const qualityGates = + context.qualityGates ?? loopConfig.defaultQualityGates; + const singleIteration = context.singleIteration ?? false; + + // Initialize loop state + loopState = { + status: "running", + currentTask: null, + iteration: 0, + maxIterations, + totalTokens: 0, + maxTokens, + startedAt: new Date().toISOString(), + lastActivity: new Date().toISOString(), + tasksCompleted: [], + tasksFailed: [], + error: null, + }; + shouldStop = false; + + try { + // Main loop + while ( + loopState.iteration < maxIterations && + loopState.totalTokens < maxTokens && + !shouldStop + ) { + loopState.iteration++; + loopState.lastActivity = new Date().toISOString(); + + // SELECT: Get ready tasks + const tasks = await getReadyTasks(workspace); + + if (tasks.length === 0) { + loopState.status = "completed"; + break; + } + + const task = tasks[0]; + loopState.currentTask = task.id; + + // Mark as in progress + await updateTaskStatus(task.id, "in_progress", workspace); + + // EXECUTE: Call agent with safety constraints + const { output, tokensUsed } = await callAgent( + task, + workspace, + qualityGates, + ); + loopState.totalTokens += tokensUsed; + + // DETECT: Check for completion token + const completed = detectCompletion(output); + + if (completed) { + // GATES: Run quality gates + const gatesResult = await runQualityGates(qualityGates, workspace); + + if (gatesResult.passed) { + // Success! Close the task + await closeTask(task.id, "Completed", workspace); + loopState.tasksCompleted.push(task.id); + } else { + // Gates failed, keep task open for retry + await updateTaskStatus(task.id, "open", workspace); + loopState.tasksFailed.push(task.id); + } + } else { + // Agent didn't complete, mark as failed for this iteration + await updateTaskStatus(task.id, "open", workspace); + } + + // Single iteration mode + if (singleIteration) { + break; + } + } + + // Determine final status + if (shouldStop) { + loopState.status = "paused"; + } else if (loopState.iteration >= maxIterations) { + loopState.status = "completed"; + } else if (loopState.totalTokens >= maxTokens) { + loopState.status = "completed"; + } + + loopState.currentTask = null; + + return { + status: loopState.status, + iterations: loopState.iteration, + tasksCompleted: loopState.tasksCompleted, + tasksFailed: loopState.tasksFailed, + totalTokens: loopState.totalTokens, + message: `Loop ${loopState.status} after ${loopState.iteration} iterations`, + }; + } catch (error) { + loopState.status = "failed"; + loopState.error = String(error); + throw error; + } + }, + }); + +// ============================================================================ +// LOOP_STATUS +// ============================================================================ + +export const createLoopStatusTool = (_env: Env) => + createPrivateTool({ + id: "LOOP_STATUS", + description: "Get the current status of the execution loop.", + inputSchema: z.object({}), + outputSchema: z.object({ + status: z.string(), + currentTask: z.string().nullable(), + iteration: z.number(), + maxIterations: z.number(), + totalTokens: z.number(), + maxTokens: z.number(), + tasksCompleted: z.array(z.string()), + tasksFailed: z.array(z.string()), + startedAt: z.string().nullable(), + lastActivity: z.string().nullable(), + error: z.string().nullable(), + }), + execute: async () => { + return loopState; + }, + }); + +// ============================================================================ +// LOOP_PAUSE +// ============================================================================ + +export const createLoopPauseTool = (_env: Env) => + createPrivateTool({ + id: "LOOP_PAUSE", + description: "Pause the execution loop after the current task completes.", + inputSchema: z.object({}), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + }), + execute: async () => { + if (loopState.status !== "running") { + return { + success: false, + message: `Cannot pause: loop is ${loopState.status}`, + }; + } + + shouldStop = true; + return { + success: true, + message: "Loop will pause after current task", + }; + }, + }); + +// ============================================================================ +// LOOP_STOP +// ============================================================================ + +export const createLoopStopTool = (_env: Env) => + createPrivateTool({ + id: "LOOP_STOP", + description: "Stop the execution loop and reset state.", + inputSchema: z.object({}), + outputSchema: z.object({ + success: z.boolean(), + finalState: z.object({ + iterations: z.number(), + tasksCompleted: z.array(z.string()), + tasksFailed: z.array(z.string()), + }), + }), + execute: async () => { + shouldStop = true; + + const finalState = { + iterations: loopState.iteration, + tasksCompleted: loopState.tasksCompleted, + tasksFailed: loopState.tasksFailed, + }; + + // Reset state + loopState = { + status: "idle", + currentTask: null, + iteration: 0, + maxIterations: 10, + totalTokens: 0, + maxTokens: 1000000, + startedAt: null, + lastActivity: null, + tasksCompleted: [], + tasksFailed: [], + error: null, + }; + + return { + success: true, + finalState, + }; + }, + }); + +// ============================================================================ +// Export all loop tools +// ============================================================================ + +export const loopTools = [ + createLoopStartTool, + createLoopStatusTool, + createLoopPauseTool, + createLoopStopTool, +]; diff --git a/task-runner/server/tools/memory.ts b/task-runner/server/tools/memory.ts new file mode 100644 index 00000000..3b6e8ae6 --- /dev/null +++ b/task-runner/server/tools/memory.ts @@ -0,0 +1,891 @@ +/** + * Memory tools for Task Runner + * + * Inspired by OpenClaw's memory system: + * - memory/YYYY-MM-DD.md: Daily log (append-only) + * - MEMORY.md: Curated long-term memory + * + * The agent writes discoveries, decisions, and learnings to these files + * so the project accumulates knowledge over time. + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { + existsSync, + mkdirSync, + readFileSync, + writeFileSync, + appendFileSync, + readdirSync, +} from "fs"; +import { join } from "path"; +import { getWorkspace } from "./workspace.ts"; + +// Get today's date in YYYY-MM-DD format +function getToday(): string { + return new Date().toISOString().split("T")[0]; +} + +// Get yesterday's date in YYYY-MM-DD format +function getYesterday(): string { + const date = new Date(); + date.setDate(date.getDate() - 1); + return date.toISOString().split("T")[0]; +} + +// Get memory directory path +function getMemoryDir(): string | null { + const workspace = getWorkspace(); + if (!workspace) return null; + return join(workspace, "memory"); +} + +// Get MEMORY.md path +function getLongTermMemoryPath(): string | null { + const workspace = getWorkspace(); + if (!workspace) return null; + return join(workspace, "MEMORY.md"); +} + +// Ensure memory directory exists +function ensureMemoryDir(): string | null { + const memoryDir = getMemoryDir(); + if (!memoryDir) return null; + if (!existsSync(memoryDir)) { + mkdirSync(memoryDir, { recursive: true }); + } + return memoryDir; +} + +// ============================================================================ +// MEMORY_WRITE +// ============================================================================ + +export const createMemoryWriteTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_WRITE", + description: `Write a memory entry. Use this to record discoveries, decisions, and learnings. + +Types: +- "daily": Day-to-day notes, discoveries, running context → memory/YYYY-MM-DD.md +- "longterm": Durable facts, decisions, preferences, architecture → MEMORY.md + +Examples of what to write: +- "Discovered that component X uses pattern Y" +- "Decision: Using approach A over B because..." +- "The user prefers camelCase for variable names" +- "Important: API endpoint requires auth header X"`, + inputSchema: z.object({ + type: z + .enum(["daily", "longterm"]) + .describe( + "Memory type: 'daily' for running notes, 'longterm' for curated facts", + ), + content: z.string().describe("The content to write (Markdown)"), + category: z + .string() + .optional() + .describe( + "Optional category tag (e.g., 'architecture', 'decision', 'discovery')", + ), + }), + execute: async ({ context }) => { + const { type, content, category } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { + success: false, + error: "No workspace set. Call WORKSPACE_SET first.", + }; + } + + try { + let filePath: string; + let formattedContent: string; + + if (type === "daily") { + const memoryDir = ensureMemoryDir(); + if (!memoryDir) { + return { + success: false, + error: "Could not create memory directory", + }; + } + filePath = join(memoryDir, `${getToday()}.md`); + + // Format with timestamp and optional category + const timestamp = new Date().toLocaleTimeString("en-US", { + hour12: false, + }); + const categoryTag = category ? ` [${category}]` : ""; + formattedContent = `\n## ${timestamp}${categoryTag}\n\n${content}\n`; + } else { + // longterm + const longTermPath = getLongTermMemoryPath(); + if (!longTermPath) { + return { + success: false, + error: "Could not determine MEMORY.md path", + }; + } + filePath = longTermPath; + + // Format with category header if provided + const categoryHeader = category ? `### ${category}\n\n` : ""; + formattedContent = `\n${categoryHeader}${content}\n`; + } + + // Create file with header if it doesn't exist + if (!existsSync(filePath)) { + const header = + type === "daily" + ? `# Daily Log - ${getToday()}\n\nRunning notes and discoveries.\n` + : `# Project Memory\n\nCurated knowledge about this project.\n`; + writeFileSync(filePath, header, "utf-8"); + } + + // Append the content + appendFileSync(filePath, formattedContent, "utf-8"); + + return { + success: true, + file: filePath, + type, + message: `Memory written to ${type === "daily" ? `memory/${getToday()}.md` : "MEMORY.md"}`, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_READ +// ============================================================================ + +export const createMemoryReadTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_READ", + description: `Read memory files to recall past discoveries and decisions. + +By default reads today + yesterday's daily logs and MEMORY.md. +Use this at session start to load context about the project.`, + inputSchema: z.object({ + type: z + .enum(["daily", "longterm", "recent", "all"]) + .optional() + .default("recent") + .describe( + "'daily': Today's log, 'longterm': MEMORY.md, 'recent': Today + yesterday + MEMORY.md, 'all': Everything", + ), + date: z + .string() + .optional() + .describe("Specific date for daily log (YYYY-MM-DD)"), + }), + execute: async ({ context }) => { + const { type = "recent", date } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { + success: false, + error: "No workspace set. Call WORKSPACE_SET first.", + }; + } + + try { + const results: { file: string; content: string }[] = []; + const memoryDir = getMemoryDir(); + const longTermPath = getLongTermMemoryPath(); + + // Read MEMORY.md + if (type === "longterm" || type === "recent" || type === "all") { + if (longTermPath && existsSync(longTermPath)) { + results.push({ + file: "MEMORY.md", + content: readFileSync(longTermPath, "utf-8"), + }); + } + } + + // Read daily logs + if (type === "daily" || type === "recent" || type === "all") { + if (memoryDir && existsSync(memoryDir)) { + if (date) { + // Specific date + const dailyPath = join(memoryDir, `${date}.md`); + if (existsSync(dailyPath)) { + results.push({ + file: `memory/${date}.md`, + content: readFileSync(dailyPath, "utf-8"), + }); + } + } else if (type === "all") { + // All daily logs + const files = readdirSync(memoryDir) + .filter((f) => f.endsWith(".md")) + .sort() + .reverse(); // Most recent first + for (const file of files.slice(0, 7)) { + // Last 7 days max + const dailyPath = join(memoryDir, file); + results.push({ + file: `memory/${file}`, + content: readFileSync(dailyPath, "utf-8"), + }); + } + } else { + // Today + yesterday (recent or daily without date) + for (const day of [getToday(), getYesterday()]) { + const dailyPath = join(memoryDir, `${day}.md`); + if (existsSync(dailyPath)) { + results.push({ + file: `memory/${day}.md`, + content: readFileSync(dailyPath, "utf-8"), + }); + } + } + } + } + } + + if (results.length === 0) { + return { + success: true, + message: + "No memory files found. Start writing memories to build project knowledge.", + files: [], + }; + } + + return { + success: true, + files: results, + totalFiles: results.length, + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_SEARCH +// ============================================================================ + +export const createMemorySearchTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_SEARCH", + description: "Search memory files for specific keywords or phrases", + inputSchema: z.object({ + query: z.string().describe("Search query (case-insensitive)"), + }), + execute: async ({ context }) => { + const { query } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { + success: false, + error: "No workspace set. Call WORKSPACE_SET first.", + }; + } + + try { + const results: { file: string; matches: string[] }[] = []; + const queryLower = query.toLowerCase(); + const memoryDir = getMemoryDir(); + const longTermPath = getLongTermMemoryPath(); + + // Search MEMORY.md + if (longTermPath && existsSync(longTermPath)) { + const content = readFileSync(longTermPath, "utf-8"); + const lines = content.split("\n"); + const matches = lines.filter((line) => + line.toLowerCase().includes(queryLower), + ); + if (matches.length > 0) { + results.push({ file: "MEMORY.md", matches }); + } + } + + // Search daily logs + if (memoryDir && existsSync(memoryDir)) { + const files = readdirSync(memoryDir) + .filter((f) => f.endsWith(".md")) + .sort() + .reverse(); + for (const file of files) { + const content = readFileSync(join(memoryDir, file), "utf-8"); + const lines = content.split("\n"); + const matches = lines.filter((line) => + line.toLowerCase().includes(queryLower), + ); + if (matches.length > 0) { + results.push({ + file: `memory/${file}`, + matches: matches.slice(0, 10), + }); // Limit matches per file + } + } + } + + return { + success: true, + query, + results, + totalMatches: results.reduce((sum, r) => sum + r.matches.length, 0), + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_RECORD_ERROR - Special tool for recording error patterns +// ============================================================================ + +export const createMemoryRecordErrorTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_RECORD_ERROR", + description: `Record an error pattern and its fix. This helps future agents avoid the same mistake. + +Use this whenever you: +1. Encounter an error and successfully fix it +2. Discover a non-obvious gotcha in the codebase +3. Find a workaround for a tricky issue + +The error will be saved to MEMORY.md under "## Error Patterns" so future sessions can learn from it.`, + inputSchema: z.object({ + errorType: z + .string() + .describe( + "Brief name for the error (e.g., 'TypeScript Generic Constraint')", + ), + errorMessage: z.string().describe("The error message or symptom"), + fix: z.string().describe("How you fixed it or worked around it"), + context: z + .string() + .optional() + .describe("Additional context about when this occurs"), + }), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + }), + execute: async ({ context }) => { + const { errorType, errorMessage, fix, context: extraContext } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, message: "No workspace set" }; + } + + const longTermPath = getLongTermMemoryPath(); + if (!longTermPath) { + return { + success: false, + message: "Could not determine MEMORY.md path", + }; + } + + const timestamp = new Date().toISOString().split("T")[0]; + const contextNote = extraContext + ? `\n- **Context**: ${extraContext}` + : ""; + + const entry = ` +### ${errorType} (${timestamp}) +- **Error**: \`${errorMessage.slice(0, 200)}${errorMessage.length > 200 ? "..." : ""}\` +- **Fix**: ${fix}${contextNote} + +`; + + try { + // Read existing content or create new + let existingContent = ""; + if (existsSync(longTermPath)) { + existingContent = readFileSync(longTermPath, "utf-8"); + } else { + existingContent = + "# Project Memory\n\nCurated knowledge about this project.\n"; + } + + // Check if Error Patterns section exists + if (!existingContent.includes("## Error Patterns")) { + existingContent += + "\n## Error Patterns\n\nCommon errors and their fixes:\n"; + } + + // Insert the new entry after "## Error Patterns" + const insertPoint = existingContent.indexOf("## Error Patterns"); + const sectionEnd = existingContent.indexOf("\n## ", insertPoint + 16); + + let newContent: string; + if (sectionEnd === -1) { + // No more sections after, append to end + newContent = existingContent + entry; + } else { + // Insert before the next section + newContent = + existingContent.slice(0, sectionEnd) + + entry + + existingContent.slice(sectionEnd); + } + + writeFileSync(longTermPath, newContent, "utf-8"); + + return { + success: true, + message: `Recorded error pattern: ${errorType}`, + }; + } catch (error) { + return { + success: false, + message: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_RECORD_LEARNING - Record a general learning from a session +// ============================================================================ + +export const createMemoryRecordLearningTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_RECORD_LEARNING", + description: `Record a key learning or insight from the current session. + +Use this to capture: +1. Important patterns discovered in the codebase +2. Decisions made and their rationale +3. Key dependencies or constraints +4. Preferred approaches for this project + +Learnings are saved to MEMORY.md under appropriate categories.`, + inputSchema: z.object({ + category: z + .enum([ + "architecture", + "patterns", + "decisions", + "constraints", + "preferences", + "dependencies", + "gotchas", + ]) + .describe("Category for the learning"), + learning: z.string().describe("The learning or insight to record"), + importance: z + .enum(["low", "medium", "high"]) + .optional() + .default("medium") + .describe("How important is this learning?"), + }), + outputSchema: z.object({ + success: z.boolean(), + message: z.string(), + }), + execute: async ({ context }) => { + const { category, learning, importance = "medium" } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, message: "No workspace set" }; + } + + const longTermPath = getLongTermMemoryPath(); + if (!longTermPath) { + return { + success: false, + message: "Could not determine MEMORY.md path", + }; + } + + const timestamp = new Date().toISOString().split("T")[0]; + const importanceEmoji = + importance === "high" ? "🔴" : importance === "medium" ? "🟡" : "🟢"; + const categoryTitle = + category.charAt(0).toUpperCase() + category.slice(1); + const sectionHeader = `## ${categoryTitle}`; + + const entry = `- ${importanceEmoji} ${learning} _(${timestamp})_\n`; + + try { + // Read existing content or create new + let existingContent = ""; + if (existsSync(longTermPath)) { + existingContent = readFileSync(longTermPath, "utf-8"); + } else { + existingContent = + "# Project Memory\n\nCurated knowledge about this project.\n"; + } + + // Check if section exists + if (!existingContent.includes(sectionHeader)) { + existingContent += `\n${sectionHeader}\n\n`; + } + + // Find the section and append after the header line + const sectionIndex = existingContent.indexOf(sectionHeader); + const afterHeader = sectionIndex + sectionHeader.length; + // Skip to end of line + const lineEnd = existingContent.indexOf("\n", afterHeader); + + let newContent: string; + if (lineEnd === -1) { + newContent = existingContent + "\n" + entry; + } else { + // Insert after the header line (and any blank line after) + let insertPoint = lineEnd + 1; + while (existingContent[insertPoint] === "\n") insertPoint++; + newContent = + existingContent.slice(0, insertPoint) + + entry + + existingContent.slice(insertPoint); + } + + writeFileSync(longTermPath, newContent, "utf-8"); + + return { + success: true, + message: `Recorded ${category} learning`, + }; + } catch (error) { + return { + success: false, + message: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_KNOWLEDGE - Knowledge base with LEARNED: prefix pattern +// Inspired by: https://github.com/AvivK5498/Claude-Code-Beads-Orchestration +// ============================================================================ + +export const createMemoryKnowledgeTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_KNOWLEDGE", + description: `Add an entry to the project knowledge base using the LEARNED: pattern. + +Use this to voluntarily capture conventions, gotchas, and insights as you work. +Entries are tagged and searchable for future sessions. + +Examples: +- "TaskGroup requires @Sendable closures in strict concurrency mode" +- "API rate limit is 100 req/min - add exponential backoff" +- "Component X uses pattern Y for state management"`, + inputSchema: z.object({ + insight: z.string().describe("The knowledge/insight to record"), + tags: z + .array(z.string()) + .optional() + .describe("Tags for categorization (e.g., ['api', 'performance'])"), + taskId: z.string().optional().describe("Source task ID if applicable"), + }), + outputSchema: z.object({ + success: z.boolean(), + entryId: z.string(), + message: z.string(), + }), + execute: async ({ context }) => { + const { insight, tags, taskId } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, entryId: "", message: "No workspace set" }; + } + + const memoryDir = getMemoryDir(); + if (!memoryDir) { + return { + success: false, + entryId: "", + message: "Could not determine memory directory", + }; + } + + // Ensure memory directory exists + if (!existsSync(memoryDir)) { + mkdirSync(memoryDir, { recursive: true }); + } + + const knowledgePath = join(memoryDir, "knowledge.jsonl"); + const entryId = `kn-${Date.now()}-${Math.random().toString(36).slice(2, 4)}`; + + const entry = { + id: entryId, + insight, + tags: tags ?? [], + taskId, + timestamp: new Date().toISOString(), + }; + + try { + appendFileSync(knowledgePath, JSON.stringify(entry) + "\n", "utf-8"); + return { + success: true, + entryId, + message: `LEARNED: ${insight.slice(0, 50)}...`, + }; + } catch (error) { + return { + success: false, + entryId: "", + message: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + +// ============================================================================ +// MEMORY_RECALL - Search the knowledge base +// ============================================================================ + +export const createMemoryRecallTool = (_env: Env) => + createPrivateTool({ + id: "MEMORY_RECALL", + description: `Search the project knowledge base for past learnings. + +Search by keyword or tag to find relevant insights from previous sessions.`, + inputSchema: z.object({ + query: z.string().optional().describe("Keyword search"), + tags: z.array(z.string()).optional().describe("Filter by tags"), + limit: z + .number() + .optional() + .default(10) + .describe("Max results to return"), + }), + outputSchema: z.object({ + entries: z.array( + z.object({ + id: z.string(), + insight: z.string(), + tags: z.array(z.string()), + taskId: z.string().optional(), + timestamp: z.string(), + }), + ), + total: z.number(), + }), + execute: async ({ context }) => { + const { query, tags, limit } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { entries: [], total: 0 }; + } + + const memoryDir = getMemoryDir(); + if (!memoryDir) { + return { entries: [], total: 0 }; + } + + const knowledgePath = join(memoryDir, "knowledge.jsonl"); + if (!existsSync(knowledgePath)) { + return { entries: [], total: 0 }; + } + + try { + const content = readFileSync(knowledgePath, "utf-8"); + const lines = content.trim().split("\n").filter(Boolean); + + let entries = lines + .map((line) => { + try { + return JSON.parse(line); + } catch { + return null; + } + }) + .filter(Boolean); + + // Filter by query + if (query) { + const lowerQuery = query.toLowerCase(); + entries = entries.filter((e: { insight: string }) => + e.insight.toLowerCase().includes(lowerQuery), + ); + } + + // Filter by tags + if (tags && tags.length > 0) { + entries = entries.filter((e: { tags: string[] }) => + tags.some((t) => e.tags.includes(t)), + ); + } + + // Sort by timestamp (newest first) and limit + entries.sort((a: { timestamp: string }, b: { timestamp: string }) => + b.timestamp.localeCompare(a.timestamp), + ); + entries = entries.slice(0, limit ?? 10); + + return { + entries, + total: entries.length, + }; + } catch (error) { + console.error("Knowledge recall error:", error); + return { entries: [], total: 0 }; + } + }, + }); + +// ============================================================================ +// TASK_RETRO - Run a retrospective after task completion +// Inspired by: https://github.com/JeremyKalmus/parade +// ============================================================================ + +export const createTaskRetroTool = (_env: Env) => + createPrivateTool({ + id: "TASK_RETRO", + description: `Run a retrospective analysis after completing a task. + +Captures: +- What went well +- What could be improved +- Patterns discovered +- Recommendations for similar future tasks + +Retrospectives are stored in .beads/retrospectives/ for future reference.`, + inputSchema: z.object({ + taskId: z.string().describe("Task ID that was completed"), + wentWell: z.array(z.string()).describe("What went well"), + couldImprove: z.array(z.string()).describe("What could be improved"), + patternsDiscovered: z + .array(z.string()) + .optional() + .describe("New patterns or approaches found"), + recommendations: z + .array(z.string()) + .optional() + .describe("Recommendations for similar tasks"), + efficiencyScore: z + .number() + .min(1) + .max(10) + .optional() + .describe("Self-assessed efficiency (1-10)"), + }), + outputSchema: z.object({ + success: z.boolean(), + retroId: z.string(), + path: z.string(), + }), + execute: async ({ context }) => { + const { + taskId, + wentWell, + couldImprove, + patternsDiscovered, + recommendations, + efficiencyScore, + } = context; + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, retroId: "", path: "" }; + } + + const retroDir = join(workspace, ".beads", "retrospectives"); + if (!existsSync(retroDir)) { + mkdirSync(retroDir, { recursive: true }); + } + + const retroId = `retro-${taskId}-${Date.now()}`; + const retroPath = join(retroDir, `${retroId}.md`); + const timestamp = new Date().toISOString(); + + const content = `# Retrospective: ${taskId} + +**Date:** ${timestamp} +**Efficiency Score:** ${efficiencyScore ?? "N/A"}/10 + +## What Went Well +${wentWell.map((w) => `- ${w}`).join("\n")} + +## What Could Be Improved +${couldImprove.map((c) => `- ${c}`).join("\n")} + +${ + patternsDiscovered && patternsDiscovered.length > 0 + ? `## Patterns Discovered +${patternsDiscovered.map((p) => `- ${p}`).join("\n")} +` + : "" +} +${ + recommendations && recommendations.length > 0 + ? `## Recommendations +${recommendations.map((r) => `- ${r}`).join("\n")} +` + : "" +} +--- +*Generated by Task Runner retrospective* +`; + + try { + writeFileSync(retroPath, content, "utf-8"); + + // Also add patterns to knowledge base + if (patternsDiscovered) { + const knowledgePath = join(workspace, "memory", "knowledge.jsonl"); + const memDir = join(workspace, "memory"); + if (!existsSync(memDir)) { + mkdirSync(memDir, { recursive: true }); + } + + for (const pattern of patternsDiscovered) { + const entry = { + id: `kn-${Date.now()}-${Math.random().toString(36).slice(2, 4)}`, + insight: pattern, + tags: ["pattern", "retro"], + taskId, + timestamp, + }; + appendFileSync( + knowledgePath, + JSON.stringify(entry) + "\n", + "utf-8", + ); + } + } + + return { + success: true, + retroId, + path: retroPath, + }; + } catch (error) { + console.error("Retro error:", error); + return { success: false, retroId: "", path: "" }; + } + }, + }); + +// ============================================================================ +// Export all memory tools +// ============================================================================ + +export const memoryTools = [ + createMemoryWriteTool, + createMemoryReadTool, + createMemorySearchTool, + createMemoryRecordErrorTool, + createMemoryRecordLearningTool, + createMemoryKnowledgeTool, + createMemoryRecallTool, + createTaskRetroTool, +]; diff --git a/task-runner/server/tools/quality-gates.ts b/task-runner/server/tools/quality-gates.ts new file mode 100644 index 00000000..9ed8e613 --- /dev/null +++ b/task-runner/server/tools/quality-gates.ts @@ -0,0 +1,649 @@ +/** + * Quality Gates Detection and Management + * + * Auto-detects quality gates from project configuration (package.json, etc.) + * and provides tools for managing project-level success criteria. + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getWorkspace } from "./workspace.ts"; + +// ============================================================================ +// Types +// ============================================================================ + +export interface QualityGate { + id: string; + name: string; + command: string; + description?: string; + required: boolean; + source: "auto" | "manual"; // auto = detected from package.json, manual = user-defined +} + +export interface QualityGateResult { + gate: string; + command: string; + passed: boolean; + output: string; + duration: number; +} + +export interface QualityGatesBaseline { + verified: boolean; + verifiedAt: string; + allPassed: boolean; + acknowledged: boolean; // User acknowledged pre-existing failures + failingGates: string[]; // Names of gates that were failing + results: QualityGateResult[]; +} + +export interface ProjectConfig { + qualityGates: QualityGate[]; + qualityGatesBaseline?: QualityGatesBaseline; + completionToken: string; + memoryDir: string; + lastUpdated: string; +} + +// ============================================================================ +// Constants +// ============================================================================ + +const CONFIG_FILE = ".beads/project-config.json"; +const DEFAULT_COMPLETION_TOKEN = "COMPLETE"; +const DEFAULT_MEMORY_DIR = "memory"; + +// Common quality gate patterns to detect +const QUALITY_GATE_PATTERNS: Array<{ + scripts: string[]; + name: string; + description: string; +}> = [ + { + scripts: ["check", "typecheck", "type-check", "tsc"], + name: "Type Check", + description: "TypeScript type checking", + }, + { + scripts: ["lint", "eslint", "oxlint"], + name: "Lint", + description: "Code linting", + }, + { + scripts: ["test", "test:unit", "vitest", "jest"], + name: "Test", + description: "Run tests", + }, + { + scripts: ["build"], + name: "Build", + description: "Build the project", + }, + { + scripts: ["fmt", "format", "prettier"], + name: "Format", + description: "Code formatting", + }, +]; + +// ============================================================================ +// Helper Functions +// ============================================================================ + +async function loadProjectConfig(workspace: string): Promise { + const configPath = `${workspace}/${CONFIG_FILE}`; + try { + const content = await Bun.file(configPath).text(); + return JSON.parse(content); + } catch { + return { + qualityGates: [], + completionToken: DEFAULT_COMPLETION_TOKEN, + memoryDir: DEFAULT_MEMORY_DIR, + lastUpdated: new Date().toISOString(), + }; + } +} + +async function saveProjectConfig( + workspace: string, + config: ProjectConfig, +): Promise { + const configPath = `${workspace}/${CONFIG_FILE}`; + config.lastUpdated = new Date().toISOString(); + await Bun.write(configPath, JSON.stringify(config, null, 2)); +} + +async function detectQualityGatesFromPackageJson( + workspace: string, +): Promise { + const gates: QualityGate[] = []; + + try { + const packageJsonPath = `${workspace}/package.json`; + const content = await Bun.file(packageJsonPath).text(); + const pkg = JSON.parse(content) as { scripts?: Record }; + + if (!pkg.scripts) return gates; + + const scriptNames = Object.keys(pkg.scripts); + + for (const pattern of QUALITY_GATE_PATTERNS) { + for (const scriptName of pattern.scripts) { + if (scriptNames.includes(scriptName)) { + // Detect package manager + let runner = "npm run"; + try { + await Bun.file(`${workspace}/bun.lock`).text(); + runner = "bun run"; + } catch { + try { + await Bun.file(`${workspace}/pnpm-lock.yaml`).text(); + runner = "pnpm run"; + } catch { + try { + await Bun.file(`${workspace}/yarn.lock`).text(); + runner = "yarn"; + } catch { + // Default to npm + } + } + } + + gates.push({ + id: `gate-${scriptName}`, + name: pattern.name, + command: `${runner} ${scriptName}`, + description: pattern.description, + required: scriptName !== "build", // Build is optional by default + source: "auto", + }); + break; // Only add one gate per pattern + } + } + } + } catch { + // No package.json or parse error + } + + return gates; +} + +// ============================================================================ +// QUALITY_GATES_DETECT +// ============================================================================ + +export const createQualityGatesDetectTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_DETECT", + description: + "Auto-detect quality gates from the project's package.json scripts. Finds common patterns like 'check', 'lint', 'test', etc.", + inputSchema: z.object({ + save: z + .boolean() + .optional() + .describe("Save detected gates to project config"), + }), + outputSchema: z.object({ + gates: z.array( + z.object({ + id: z.string(), + name: z.string(), + command: z.string(), + description: z.string().optional(), + required: z.boolean(), + source: z.enum(["auto", "manual"]), + }), + ), + saved: z.boolean(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const gates = await detectQualityGatesFromPackageJson(workspace); + + if (context.save && gates.length > 0) { + const config = await loadProjectConfig(workspace); + // Merge with existing manual gates + const manualGates = config.qualityGates.filter( + (g) => g.source === "manual", + ); + config.qualityGates = [...gates, ...manualGates]; + await saveProjectConfig(workspace, config); + } + + return { + gates, + saved: context.save ?? false, + }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_LIST +// ============================================================================ + +export const createQualityGatesListTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_LIST", + description: "List all configured quality gates for the project", + inputSchema: z.object({}), + outputSchema: z.object({ + gates: z.array( + z.object({ + id: z.string(), + name: z.string(), + command: z.string(), + description: z.string().optional(), + required: z.boolean(), + source: z.enum(["auto", "manual"]), + }), + ), + completionToken: z.string(), + }), + execute: async () => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + + return { + gates: config.qualityGates, + completionToken: config.completionToken, + }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_ADD +// ============================================================================ + +export const createQualityGatesAddTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_ADD", + description: "Add a custom quality gate to the project", + inputSchema: z.object({ + name: z.string().describe("Display name for the gate"), + command: z.string().describe("Command to run (e.g., 'bun run test')"), + description: z + .string() + .optional() + .describe("Description of what this gate checks"), + required: z + .boolean() + .optional() + .describe("Whether this gate must pass (default: true)"), + }), + outputSchema: z.object({ + success: z.boolean(), + gate: z.object({ + id: z.string(), + name: z.string(), + command: z.string(), + description: z.string().optional(), + required: z.boolean(), + source: z.enum(["auto", "manual"]), + }), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + + const gate: QualityGate = { + id: `gate-${Date.now()}`, + name: context.name, + command: context.command, + description: context.description, + required: context.required ?? true, + source: "manual", + }; + + config.qualityGates.push(gate); + await saveProjectConfig(workspace, config); + + return { + success: true, + gate, + }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_RUN +// ============================================================================ + +export const createQualityGatesRunTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_RUN", + description: + "Run all quality gates and report which pass/fail. Use before marking a task complete.", + inputSchema: z.object({ + requiredOnly: z + .boolean() + .optional() + .describe("Only run required gates (default: true)"), + }), + outputSchema: z.object({ + allPassed: z.boolean(), + results: z.array( + z.object({ + gate: z.string(), + command: z.string(), + passed: z.boolean(), + output: z.string(), + duration: z.number(), + }), + ), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + const requiredOnly = context.requiredOnly ?? true; + + const gatesToRun = requiredOnly + ? config.qualityGates.filter((g) => g.required) + : config.qualityGates; + + const results: Array<{ + gate: string; + command: string; + passed: boolean; + output: string; + duration: number; + }> = []; + + for (const gate of gatesToRun) { + const start = Date.now(); + try { + const [cmd, ...args] = gate.command.split(" "); + const proc = Bun.spawn([cmd, ...args], { + cwd: workspace, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + const exitCode = await proc.exited; + + results.push({ + gate: gate.name, + command: gate.command, + passed: exitCode === 0, + output: (stdout + stderr).slice(-500), // Last 500 chars + duration: Date.now() - start, + }); + } catch (error) { + results.push({ + gate: gate.name, + command: gate.command, + passed: false, + output: error instanceof Error ? error.message : String(error), + duration: Date.now() - start, + }); + } + } + + return { + allPassed: results.every((r) => r.passed), + results, + }; + }, + }); + +// ============================================================================ +// PROJECT_CONFIG_GET +// ============================================================================ + +export const createProjectConfigGetTool = (_env: Env) => + createPrivateTool({ + id: "PROJECT_CONFIG_GET", + description: + "Get the full project configuration including quality gates and settings", + inputSchema: z.object({}), + outputSchema: z.object({ + config: z.object({ + qualityGates: z.array( + z.object({ + id: z.string(), + name: z.string(), + command: z.string(), + description: z.string().optional(), + required: z.boolean(), + source: z.enum(["auto", "manual"]), + }), + ), + completionToken: z.string(), + memoryDir: z.string(), + lastUpdated: z.string(), + }), + }), + execute: async () => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + return { config }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_VERIFY +// ============================================================================ + +export const createQualityGatesVerifyTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_VERIFY", + description: + "Run quality gates to establish a baseline. Must be done before creating tasks. Returns current state and allows acknowledging pre-existing failures.", + inputSchema: z.object({}), + outputSchema: z.object({ + allPassed: z.boolean(), + results: z.array( + z.object({ + gate: z.string(), + command: z.string(), + passed: z.boolean(), + output: z.string(), + duration: z.number(), + }), + ), + baseline: z.object({ + verified: z.boolean(), + verifiedAt: z.string(), + allPassed: z.boolean(), + acknowledged: z.boolean(), + failingGates: z.array(z.string()), + }), + }), + execute: async () => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + const requiredGates = config.qualityGates.filter((g) => g.required); + + const results: QualityGateResult[] = []; + + for (const gate of requiredGates) { + const start = Date.now(); + try { + const [cmd, ...args] = gate.command.split(" "); + const proc = Bun.spawn([cmd, ...args], { + cwd: workspace, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + const exitCode = await proc.exited; + + results.push({ + gate: gate.name, + command: gate.command, + passed: exitCode === 0, + output: (stdout + stderr).slice(-500), + duration: Date.now() - start, + }); + } catch (error) { + results.push({ + gate: gate.name, + command: gate.command, + passed: false, + output: error instanceof Error ? error.message : String(error), + duration: Date.now() - start, + }); + } + } + + const allPassed = results.every((r) => r.passed); + const failingGates = results.filter((r) => !r.passed).map((r) => r.gate); + + // Update baseline - not acknowledged yet if there are failures + const baseline: QualityGatesBaseline = { + verified: true, + verifiedAt: new Date().toISOString(), + allPassed, + acknowledged: allPassed, // Auto-acknowledge if all pass + failingGates, + results, + }; + + config.qualityGatesBaseline = baseline; + await saveProjectConfig(workspace, config); + + return { + allPassed, + results, + baseline: { + verified: baseline.verified, + verifiedAt: baseline.verifiedAt, + allPassed: baseline.allPassed, + acknowledged: baseline.acknowledged, + failingGates: baseline.failingGates, + }, + }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_ACKNOWLEDGE +// ============================================================================ + +export const createQualityGatesAcknowledgeTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_ACKNOWLEDGE", + description: + "Acknowledge pre-existing quality gate failures. After acknowledging, agents will NOT attempt to fix these failures - they will focus only on their assigned task.", + inputSchema: z.object({ + acknowledge: z.boolean().describe("Set to true to acknowledge failures"), + }), + outputSchema: z.object({ + success: z.boolean(), + baseline: z + .object({ + verified: z.boolean(), + allPassed: z.boolean(), + acknowledged: z.boolean(), + failingGates: z.array(z.string()), + }) + .optional(), + error: z.string().optional(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + + if (!config.qualityGatesBaseline?.verified) { + return { + success: false, + error: + "No baseline verified. Run QUALITY_GATES_VERIFY first to establish baseline.", + }; + } + + if (config.qualityGatesBaseline.allPassed) { + return { + success: true, + baseline: { + verified: true, + allPassed: true, + acknowledged: true, + failingGates: [], + }, + }; + } + + config.qualityGatesBaseline.acknowledged = context.acknowledge; + await saveProjectConfig(workspace, config); + + return { + success: true, + baseline: { + verified: config.qualityGatesBaseline.verified, + allPassed: config.qualityGatesBaseline.allPassed, + acknowledged: config.qualityGatesBaseline.acknowledged, + failingGates: config.qualityGatesBaseline.failingGates, + }, + }; + }, + }); + +// ============================================================================ +// QUALITY_GATES_BASELINE_GET +// ============================================================================ + +export const createQualityGatesBaselineGetTool = (_env: Env) => + createPrivateTool({ + id: "QUALITY_GATES_BASELINE_GET", + description: "Get the current quality gates baseline verification status", + inputSchema: z.object({}), + outputSchema: z.object({ + hasBaseline: z.boolean(), + baseline: z + .object({ + verified: z.boolean(), + verifiedAt: z.string(), + allPassed: z.boolean(), + acknowledged: z.boolean(), + failingGates: z.array(z.string()), + }) + .optional(), + canCreateTasks: z.boolean(), + }), + execute: async () => { + const workspace = getWorkspace(); + const config = await loadProjectConfig(workspace); + + const baseline = config.qualityGatesBaseline; + const hasBaseline = !!baseline?.verified; + const canCreateTasks = + hasBaseline && (baseline.allPassed || baseline.acknowledged); + + return { + hasBaseline, + baseline: baseline + ? { + verified: baseline.verified, + verifiedAt: baseline.verifiedAt, + allPassed: baseline.allPassed, + acknowledged: baseline.acknowledged, + failingGates: baseline.failingGates, + } + : undefined, + canCreateTasks, + }; + }, + }); + +// ============================================================================ +// Export all quality gate tools +// ============================================================================ + +export const qualityGateTools = [ + createQualityGatesDetectTool, + createQualityGatesListTool, + createQualityGatesAddTool, + createQualityGatesRunTool, + createProjectConfigGetTool, + createQualityGatesVerifyTool, + createQualityGatesAcknowledgeTool, + createQualityGatesBaselineGetTool, +]; diff --git a/task-runner/server/tools/skills.ts b/task-runner/server/tools/skills.ts new file mode 100644 index 00000000..f6a1aec5 --- /dev/null +++ b/task-runner/server/tools/skills.ts @@ -0,0 +1,208 @@ +/** + * Skill Management Tools + * + * Tools for listing, viewing, and applying skills to create tasks. + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getSkill, listSkills, type UserStory } from "../skills/index.ts"; +import { getWorkspace } from "./workspace.ts"; + +// ============================================================================ +// Schemas +// ============================================================================ + +const UserStorySchema = z.object({ + id: z.string(), + title: z.string(), + asA: z.string(), + iWant: z.string(), + soThat: z.string(), + acceptanceCriteria: z.array(z.string()), + dependsOn: z.array(z.string()).optional(), +}); + +const SkillSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string(), + stack: z.array(z.string()), + userStories: z.array(UserStorySchema), + qualityGates: z.record(z.string(), z.array(z.string())), +}); + +// ============================================================================ +// Helper: Run bd create +// ============================================================================ + +async function createBeadTask(story: UserStory, cwd: string): Promise { + const description = [ + `As a ${story.asA}, I want ${story.iWant}, so that ${story.soThat}`, + "", + "## Acceptance Criteria", + ...story.acceptanceCriteria.map((ac) => `- [ ] ${ac}`), + ].join("\n"); + + const args = ["create", story.title, "--json"]; + args.push("-d", description); + args.push("-t", "task"); + + if (story.dependsOn?.length) { + args.push("--blocked-by", story.dependsOn.join(",")); + } + + const proc = Bun.spawn(["bd", ...args], { + cwd, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = await new Response(proc.stdout).text(); + const exitCode = await proc.exited; + + if (exitCode !== 0) { + throw new Error(`Failed to create task: ${story.title}`); + } + + try { + const result = JSON.parse(stdout); + return result.id ?? story.id; + } catch { + // Try to extract ID from output + const match = stdout.match(/bd-[\w.]+/); + return match?.[0] ?? story.id; + } +} + +// ============================================================================ +// SKILL_LIST +// ============================================================================ + +export const createSkillListTool = (_env: Env) => + createPrivateTool({ + id: "SKILL_LIST", + description: + "List all available skills. Skills define reusable workflows for common development tasks.", + inputSchema: z.object({}), + outputSchema: z.object({ + skills: z.array( + z.object({ + id: z.string(), + name: z.string(), + description: z.string(), + stack: z.array(z.string()), + storyCount: z.number(), + }), + ), + }), + execute: async () => { + const allSkills = listSkills(); + + return { + skills: allSkills.map((skill) => ({ + id: skill.id, + name: skill.name, + description: skill.description, + stack: skill.stack, + storyCount: skill.userStories.length, + })), + }; + }, + }); + +// ============================================================================ +// SKILL_SHOW +// ============================================================================ + +export const createSkillShowTool = (_env: Env) => + createPrivateTool({ + id: "SKILL_SHOW", + description: + "Show details of a specific skill, including user stories and quality gates.", + inputSchema: z.object({ + skillId: z.string().describe("Skill ID to show (e.g., 'build-mcp')"), + }), + outputSchema: z.object({ + skill: SkillSchema.nullable(), + }), + execute: async ({ context }) => { + const skill = getSkill(context.skillId); + + if (!skill) { + return { skill: null }; + } + + return { skill }; + }, + }); + +// ============================================================================ +// SKILL_APPLY +// ============================================================================ + +export const createSkillApplyTool = (_env: Env) => + createPrivateTool({ + id: "SKILL_APPLY", + description: + "Apply a skill to the current workspace. Creates Beads tasks for each user story in the skill.", + inputSchema: z.object({ + skillId: z.string().describe("Skill ID to apply"), + customization: z + .object({ + prefix: z.string().optional().describe("Custom task ID prefix"), + extraContext: z + .string() + .optional() + .describe("Additional context to add to tasks"), + }) + .optional(), + }), + outputSchema: z.object({ + success: z.boolean(), + tasksCreated: z.array(z.string()), + message: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + const skill = getSkill(context.skillId); + + if (!skill) { + throw new Error(`Skill not found: ${context.skillId}`); + } + + // Map story IDs to Beads task IDs + const idMap: Record = {}; + const createdTasks: string[] = []; + + // Create tasks in order (respecting dependencies) + for (const story of skill.userStories) { + // Remap dependencies to Beads IDs + const remappedStory: UserStory = { + ...story, + dependsOn: story.dependsOn?.map((dep) => idMap[dep]).filter(Boolean), + }; + + const taskId = await createBeadTask(remappedStory, workspace); + idMap[story.id] = taskId; + createdTasks.push(taskId); + } + + return { + success: true, + tasksCreated: createdTasks, + message: `Applied skill '${skill.name}': created ${createdTasks.length} tasks`, + }; + }, + }); + +// ============================================================================ +// Export all skill tools +// ============================================================================ + +export const skillTools = [ + createSkillListTool, + createSkillShowTool, + createSkillApplyTool, +]; diff --git a/task-runner/server/tools/tasks.ts b/task-runner/server/tools/tasks.ts new file mode 100644 index 00000000..87369a7e --- /dev/null +++ b/task-runner/server/tools/tasks.ts @@ -0,0 +1,545 @@ +/** + * Task Management Tools + * + * Direct task operations for agent use. + * These wrap .beads/tasks.json for simplicity. + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; +import { getWorkspace } from "./workspace.ts"; + +// ============================================================================ +// Types +// ============================================================================ + +interface TaskPlan { + summary: string; + acceptanceCriteria: Array<{ + id: string; + description: string; + verifiable?: boolean; + }>; + subtasks: Array<{ + id: string; + title: string; + description: string; + estimatedComplexity?: "trivial" | "simple" | "moderate" | "complex"; + filesToModify?: string[]; + }>; + risks?: string[]; + estimatedComplexity?: "trivial" | "simple" | "moderate" | "complex"; +} + +interface Task { + id: string; + title: string; + description?: string; + status: "open" | "in_progress" | "blocked" | "closed"; + priority?: number; + createdAt?: string; + updatedAt?: string; + acceptanceCriteria?: Array<{ + id: string; + description: string; + completed?: boolean; + }>; + plan?: TaskPlan; + planStatus?: "draft" | "approved" | "rejected"; +} + +interface TaskStore { + tasks: Task[]; +} + +// ============================================================================ +// Helpers +// ============================================================================ + +async function loadTasks(workspace: string): Promise { + const tasksPath = `${workspace}/.beads/tasks.json`; + try { + const content = await Bun.file(tasksPath).text(); + return JSON.parse(content); + } catch { + return { tasks: [] }; + } +} + +async function saveTasks(workspace: string, store: TaskStore): Promise { + const tasksPath = `${workspace}/.beads/tasks.json`; + await Bun.write(tasksPath, JSON.stringify(store, null, 2)); +} + +function generateTaskId(): string { + return `task-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`; +} + +// ============================================================================ +// TASK_LIST +// ============================================================================ + +export const createTaskListTool = (_env: Env) => + createPrivateTool({ + id: "TASK_LIST", + description: "List all tasks in the project", + inputSchema: z.object({ + status: z + .enum(["open", "in_progress", "blocked", "closed", "all"]) + .optional() + .default("all") + .describe("Filter by status"), + }), + outputSchema: z.object({ + tasks: z.array( + z.object({ + id: z.string(), + title: z.string(), + status: z.string(), + priority: z.number().optional(), + }), + ), + total: z.number(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + if (!workspace) { + return { tasks: [], total: 0 }; + } + + const store = await loadTasks(workspace); + let tasks = store.tasks; + + if (context.status !== "all") { + tasks = tasks.filter((t) => t.status === context.status); + } + + // Sort by priority (lower = higher priority) + tasks.sort((a, b) => (a.priority ?? 5) - (b.priority ?? 5)); + + return { + tasks: tasks.map((t) => ({ + id: t.id, + title: t.title, + status: t.status, + priority: t.priority, + })), + total: tasks.length, + }; + }, + }); + +// ============================================================================ +// TASK_CREATE +// ============================================================================ + +export const createTaskCreateTool = (_env: Env) => + createPrivateTool({ + id: "TASK_CREATE", + description: `Create a new task. Use this to track follow-up work. + +Good for: +- Work discovered during implementation that's out of scope +- Refactoring opportunities found but not addressed +- Bugs discovered but not fixed +- Features requested but deferred`, + inputSchema: z.object({ + title: z.string().describe("Task title (concise)"), + description: z.string().optional().describe("Detailed description"), + priority: z + .number() + .min(0) + .max(10) + .optional() + .default(2) + .describe("Priority 0-10 (0=highest, default: 2)"), + acceptanceCriteria: z + .array(z.string()) + .optional() + .describe("List of acceptance criteria"), + }), + outputSchema: z.object({ + success: z.boolean(), + taskId: z.string(), + message: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, taskId: "", message: "No workspace set" }; + } + + const store = await loadTasks(workspace); + const taskId = generateTaskId(); + + const task: Task = { + id: taskId, + title: context.title, + description: context.description, + status: "open", + priority: context.priority ?? 2, + createdAt: new Date().toISOString(), + }; + + if (context.acceptanceCriteria) { + task.acceptanceCriteria = context.acceptanceCriteria.map((desc, i) => ({ + id: `ac-${i + 1}`, + description: desc, + completed: false, + })); + } + + store.tasks.push(task); + await saveTasks(workspace, store); + + return { + success: true, + taskId, + message: `Created task: ${taskId} - ${context.title}`, + }; + }, + }); + +// ============================================================================ +// TASK_UPDATE +// ============================================================================ + +export const createTaskUpdateTool = (_env: Env) => + createPrivateTool({ + id: "TASK_UPDATE", + description: "Update task status or details", + inputSchema: z.object({ + id: z.string().describe("Task ID"), + status: z + .enum(["open", "in_progress", "blocked", "closed"]) + .optional() + .describe("New status"), + title: z.string().optional().describe("New title"), + description: z.string().optional().describe("New description"), + priority: z.number().optional().describe("New priority"), + }), + outputSchema: z.object({ + success: z.boolean(), + task: z + .object({ + id: z.string(), + title: z.string(), + status: z.string(), + }) + .nullable(), + message: z.string(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + if (!workspace) { + return { success: false, task: null, message: "No workspace set" }; + } + + const store = await loadTasks(workspace); + const task = store.tasks.find((t) => t.id === context.id); + + if (!task) { + return { + success: false, + task: null, + message: `Task not found: ${context.id}`, + }; + } + + if (context.status) task.status = context.status; + if (context.title) task.title = context.title; + if (context.description) task.description = context.description; + if (context.priority !== undefined) task.priority = context.priority; + task.updatedAt = new Date().toISOString(); + + await saveTasks(workspace, store); + + return { + success: true, + task: { + id: task.id, + title: task.title, + status: task.status, + }, + message: `Updated task: ${task.id}`, + }; + }, + }); + +// ============================================================================ +// TASK_GET +// ============================================================================ + +export const createTaskGetTool = (_env: Env) => + createPrivateTool({ + id: "TASK_GET", + description: "Get full details of a specific task", + inputSchema: z.object({ + id: z.string().describe("Task ID"), + }), + outputSchema: z.object({ + task: z + .object({ + id: z.string(), + title: z.string(), + description: z.string().optional(), + status: z.string(), + priority: z.number().optional(), + createdAt: z.string().optional(), + updatedAt: z.string().optional(), + acceptanceCriteria: z + .array( + z.object({ + id: z.string(), + description: z.string(), + completed: z.boolean().optional(), + }), + ) + .optional(), + }) + .nullable(), + }), + execute: async ({ context }) => { + const workspace = getWorkspace(); + if (!workspace) { + return { task: null }; + } + + const store = await loadTasks(workspace); + const task = store.tasks.find((t) => t.id === context.id); + + return { task: task || null }; + }, + }); + +// ============================================================================ +// TASK_SET_PLAN - Save a plan generated by the AI agent +// The agent analyzes the task, explores code, then calls this to save the plan +// Inspired by: https://github.com/JeremyKalmus/parade (discover → approve-spec → run) +// ============================================================================ + +export const createTaskSetPlanTool = (_env: Env) => + createPrivateTool({ + id: "TASK_SET_PLAN", + description: `Save a detailed plan for a task. + +Call this AFTER you have analyzed the task and codebase to save your plan. +The plan should include: +1. A clear summary of what needs to be done +2. Specific, verifiable acceptance criteria +3. Subtasks broken down by complexity +4. Any risks or considerations + +The user will review and approve the plan before execution begins.`, + inputSchema: z.object({ + workspace: z + .string() + .describe("Absolute path to the workspace/project directory"), + taskId: z.string().describe("Task ID to set plan for"), + plan: z.object({ + summary: z + .string() + .describe("1-2 sentence summary of the implementation approach"), + acceptanceCriteria: z + .array( + z.object({ + id: z.string().describe("Unique ID like 'ac-1'"), + description: z.string().describe("Clear, verifiable criterion"), + verifiable: z + .boolean() + .describe("Can this be objectively verified?"), + }), + ) + .describe("Specific success criteria - be concrete, not generic"), + subtasks: z + .array( + z.object({ + id: z.string().describe("Unique ID like 'st-1'"), + title: z.string().describe("Short title for the subtask"), + description: z.string().describe("What needs to be done"), + estimatedComplexity: z.enum([ + "trivial", + "simple", + "moderate", + "complex", + ]), + filesToModify: z + .array(z.string()) + .optional() + .describe("Files that will likely be modified"), + }), + ) + .describe("Breakdown of work into smaller steps"), + risks: z + .array(z.string()) + .optional() + .describe("Potential issues or blockers"), + estimatedComplexity: z + .enum(["trivial", "simple", "moderate", "complex"]) + .describe("Overall task complexity"), + }), + }), + outputSchema: z.object({ + success: z.boolean(), + taskId: z.string(), + message: z.string(), + }), + execute: async ({ context }) => { + const { workspace, taskId, plan } = context; + + // Load the task + const store = await loadTasks(workspace); + const taskIndex = store.tasks.findIndex((t) => t.id === taskId); + + if (taskIndex === -1) { + throw new Error(`Task not found: ${taskId}`); + } + + const task = store.tasks[taskIndex]; + + // Save plan to the task + store.tasks[taskIndex] = { + ...task, + plan, + planStatus: "draft" as const, + updatedAt: new Date().toISOString(), + }; + + await saveTasks(workspace, store); + + return { + success: true, + taskId, + message: `Plan saved with ${plan.acceptanceCriteria.length} acceptance criteria and ${plan.subtasks.length} subtasks. User will review and approve.`, + }; + }, + }); + +// ============================================================================ +// TASK_APPROVE_PLAN - Approve a task plan for execution +// ============================================================================ + +export const createTaskApprovePlanTool = (_env: Env) => + createPrivateTool({ + id: "TASK_APPROVE_PLAN", + description: `Approve or modify a task plan before execution. + +After reviewing the generated plan, use this to: +1. Approve the plan as-is +2. Modify acceptance criteria +3. Add/remove subtasks +4. Reject the plan for re-planning`, + inputSchema: z.object({ + taskId: z.string().describe("Task ID"), + action: z + .enum(["approve", "reject"]) + .describe("Approve or reject the plan"), + modifications: z + .object({ + acceptanceCriteria: z + .array( + z.object({ + id: z.string(), + description: z.string(), + }), + ) + .optional() + .describe("Modified acceptance criteria"), + subtasks: z + .array( + z.object({ + id: z.string(), + title: z.string(), + description: z.string(), + }), + ) + .optional() + .describe("Modified subtasks"), + }) + .optional(), + reason: z.string().optional().describe("Reason for rejection"), + }), + outputSchema: z.object({ + success: z.boolean(), + taskId: z.string(), + planStatus: z.enum(["draft", "approved", "rejected"]), + message: z.string(), + }), + execute: async ({ context }) => { + const { taskId, action, modifications, reason } = context; + const workspace = getWorkspace(); + if (!workspace) { + throw new Error("No workspace set"); + } + + const store = await loadTasks(workspace); + const taskIndex = store.tasks.findIndex((t) => t.id === taskId); + + if (taskIndex === -1) { + return { + success: false, + taskId, + planStatus: "draft" as const, + message: "Task not found", + }; + } + + const task = store.tasks[taskIndex] as Task & { + plan?: unknown; + planStatus?: string; + }; + + if (action === "approve") { + // Apply modifications if provided + if (modifications?.acceptanceCriteria && task.plan) { + (task.plan as { acceptanceCriteria: unknown[] }).acceptanceCriteria = + modifications.acceptanceCriteria; + } + if (modifications?.subtasks && task.plan) { + (task.plan as { subtasks: unknown[] }).subtasks = + modifications.subtasks; + } + + task.planStatus = "approved"; + task.acceptanceCriteria = ( + task.plan as { + acceptanceCriteria: Array<{ id: string; description: string }>; + } + )?.acceptanceCriteria?.map((ac) => ({ + id: ac.id, + description: ac.description, + completed: false, + })); + } else { + task.planStatus = "rejected"; + } + + task.updatedAt = new Date().toISOString(); + store.tasks[taskIndex] = task; + await saveTasks(workspace, store); + + return { + success: true, + taskId, + planStatus: task.planStatus as "draft" | "approved" | "rejected", + message: + action === "approve" + ? "Plan approved. Task is ready for execution." + : `Plan rejected: ${reason || "No reason provided"}`, + }; + }, + }); + +// ============================================================================ +// Export all task tools +// ============================================================================ + +export const taskTools = [ + createTaskListTool, + createTaskCreateTool, + createTaskUpdateTool, + createTaskGetTool, + createTaskSetPlanTool, + createTaskApprovePlanTool, +]; diff --git a/task-runner/server/tools/workspace.ts b/task-runner/server/tools/workspace.ts new file mode 100644 index 00000000..1cff53d0 --- /dev/null +++ b/task-runner/server/tools/workspace.ts @@ -0,0 +1,127 @@ +/** + * Workspace Management Tools + * + * Tools for setting and getting the current workspace directory + */ + +import { createPrivateTool } from "@decocms/runtime/tools"; +import { z } from "zod"; +import type { Env } from "../../shared/deco.gen.ts"; + +// In-memory workspace state (per connection in production) +let currentWorkspace: string | null = null; + +/** + * Get current workspace, throws if not set + */ +export function getWorkspace(): string { + if (!currentWorkspace) { + throw new Error( + "Workspace not set. Use WORKSPACE_SET to set a workspace directory first.", + ); + } + return currentWorkspace; +} + +/** + * Set the current workspace + */ +export function setWorkspace(dir: string): void { + currentWorkspace = dir; +} + +// ============================================================================ +// WORKSPACE_SET +// ============================================================================ + +export const createWorkspaceSetTool = (_env: Env) => + createPrivateTool({ + id: "WORKSPACE_SET", + description: + "Set the working directory for all Task Runner operations. This is where Beads will look for .beads/ and where agents will make changes.", + inputSchema: z.object({ + directory: z + .string() + .describe("Absolute path to the workspace directory"), + }), + outputSchema: z.object({ + success: z.boolean(), + workspace: z.string().describe("The workspace that was set"), + hasBeads: z.boolean().describe("Whether .beads/ directory exists"), + }), + execute: async ({ context }) => { + const { directory } = context; + + // Check if directory exists + const file = Bun.file(directory); + const stat = await file.exists(); + + if (!stat) { + throw new Error(`Directory does not exist: ${directory}`); + } + + // Set the workspace + setWorkspace(directory); + + // Check if .beads/ exists + const beadsDir = `${directory}/.beads`; + const beadsFile = Bun.file(beadsDir); + const hasBeads = await beadsFile.exists(); + + return { + success: true, + workspace: directory, + hasBeads, + }; + }, + }); + +// ============================================================================ +// WORKSPACE_GET +// ============================================================================ + +export const createWorkspaceGetTool = (_env: Env) => + createPrivateTool({ + id: "WORKSPACE_GET", + description: "Get the current workspace directory.", + inputSchema: z.object({}), + outputSchema: z.object({ + workspace: z + .string() + .nullable() + .describe("Current workspace, or null if not set"), + hasBeads: z + .boolean() + .nullable() + .describe("Whether .beads/ directory exists"), + }), + execute: async () => { + if (!currentWorkspace) { + return { + workspace: null, + hasBeads: null, + }; + } + + // Check if .beads/ exists + const beadsDir = `${currentWorkspace}/.beads`; + const beadsFile = Bun.file(beadsDir); + const hasBeads = await beadsFile.exists(); + + return { + workspace: currentWorkspace, + hasBeads, + }; + }, + }); + +// ============================================================================ +// Export all workspace tools +// ============================================================================ + +// Note: WORKSPACE_SET and WORKSPACE_GET are NOT exposed to agents. +// The workspace is passed directly to tools like AGENT_SPAWN. +// The tool creators are already exported above for debugging/admin use. +export const workspaceTools: (( + env: Env, +) => ReturnType)[] = []; diff --git a/task-runner/shared/deco.gen.ts b/task-runner/shared/deco.gen.ts new file mode 100644 index 00000000..5b2af8f7 --- /dev/null +++ b/task-runner/shared/deco.gen.ts @@ -0,0 +1,48 @@ +// Generated types for Task Runner MCP + +import { z } from "zod"; + +/** + * Mesh request context injected by the Deco runtime + * Contains authentication and metadata for the current request + */ +export interface MeshRequestContext { + /** Bearer token from Authorization header */ + authorization?: string; + /** Comma-separated list of valid API keys for authentication */ + apiKeys?: string; + /** Internal state for OAuth flow */ + state?: string; + /** JWT token for the request */ + token?: string; + /** URL of the mesh server */ + meshUrl?: string; + /** Connection ID for this session */ + connectionId?: string; + /** Function to ensure user is authenticated */ + ensureAuthenticated?: () => Promise; +} + +/** + * Environment type for Task Runner MCP + * Extends process env with Deco runtime context + */ +export interface Env { + /** Mesh request context injected by runtime */ + MESH_REQUEST_CONTEXT: MeshRequestContext; + /** Self-reference MCP (if needed) */ + SELF?: unknown; + /** Whether running locally */ + IS_LOCAL?: boolean; +} + +/** + * State schema for validation + */ +export const StateSchema = z.object({ + /** Default workspace directory for Beads operations */ + defaultWorkspace: z + .string() + .optional() + .describe("Default workspace directory"), +}); diff --git a/task-runner/tsconfig.json b/task-runner/tsconfig.json new file mode 100644 index 00000000..8a653f54 --- /dev/null +++ b/task-runner/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2023", "ES2024", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "allowJs": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* Path Aliases */ + "baseUrl": ".", + "paths": { + "server/*": ["./server/*"] + } + }, + "include": [ + "server", + "shared" + ] +}