diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index 5b76614..f95ffb3 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -8,9 +8,12 @@ "name": "web", "version": "0.1.0", "dependencies": { + "@anthropic-ai/sdk": "^0.33.1", + "@modelcontextprotocol/sdk": "^1.0.4", "@nivo/core": "^0.88.0", "@nivo/treemap": "^0.88.0", "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-collapsible": "^1.1.2", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-scroll-area": "^1.2.2", @@ -19,9 +22,12 @@ "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.6", "@shadcn/ui": "^0.0.4", + "@types/ws": "^8.5.13", + "chalk": "^5.4.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "dotenv": "^16.4.7", "init": "^0.1.2", "lucide-react": "^0.468.0", "next": "15.1.1", @@ -33,7 +39,9 @@ "recharts": "^2.15.0", "shadcn-ui": "^0.9.4", "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "ts-node": "^10.9.2", + "ws": "^8.18.0" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -41,6 +49,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "concurrently": "^9.1.1", "eslint": "^9", "eslint-config-next": "15.1.1", "postcss": "^8", @@ -89,6 +98,56 @@ "nun": "bin/nun.mjs" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.33.1.tgz", + "integrity": "sha512-VrlbxiAdVRGuKP2UQlCnsShDHJKWepzvfRCkZMpU+oaUdKLpOfmylLMRojGrAgebV+kDtPjewCVP0laHXg+vsA==", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", + "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -504,6 +563,26 @@ "node": ">=6.9.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -839,6 +918,17 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.4.tgz", + "integrity": "sha512-C+jw1lF6HSGzs7EZpzHbXfzz9rj9him4BaoumlTciW/IDDgIpweF/qiCWKlP02QKg5PPcgY6xY2WCt5y2tpYow==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "raw-body": "^3.0.0", + "zod": "^3.23.8" + } + }, "node_modules/@next/env": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.1.tgz", @@ -1206,6 +1296,36 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz", + "integrity": "sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", @@ -2001,6 +2121,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -2140,12 +2280,21 @@ "version": "20.17.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -2177,6 +2326,14 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", @@ -2419,11 +2576,22 @@ "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2442,6 +2610,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -2451,6 +2630,18 @@ "node": ">= 14" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2728,6 +2919,12 @@ "dev": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2914,6 +3111,15 @@ "node": ">=10.16.0" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3014,17 +3220,12 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -3153,6 +3354,78 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -3222,6 +3495,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -3249,6 +3534,83 @@ "dev": true, "license": "MIT" }, + "node_modules/concurrently": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.1.tgz", + "integrity": "sha512-6VX8lrBIycgZKTwBsWS+bLrmkGRkDmvtGsYylRN9b93CygN6CbK46HmnQ3rdSOR8HRjdahDrxb5MqD9cEFOg5Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3281,6 +3643,11 @@ } } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3631,6 +3998,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3715,6 +4100,17 @@ "csstype": "^3.0.2" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4359,6 +4755,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -4446,6 +4859,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -4667,6 +5089,48 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4758,6 +5222,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", @@ -5095,6 +5568,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/https-proxy-agent": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz", @@ -5117,6 +5606,27 @@ "node": ">=14.18.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -6006,18 +6516,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -6056,6 +6554,11 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6689,6 +7192,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -7172,18 +7696,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7622,6 +8134,21 @@ ], "license": "MIT" }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -7960,6 +8487,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -8079,6 +8615,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -8137,6 +8682,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", @@ -8190,6 +8741,12 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shadcn-ui": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/shadcn-ui/-/shadcn-ui-0.9.4.tgz", @@ -8356,6 +8913,18 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -8496,6 +9065,15 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -9025,6 +9603,30 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -9075,6 +9677,61 @@ "code-block-writer": "^12.0.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -9222,7 +9879,6 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, "license": "MIT" }, "node_modules/unified": { @@ -9321,6 +9977,15 @@ "node": ">= 10.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -9410,6 +10075,11 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9478,6 +10148,22 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9686,6 +10372,35 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -9705,6 +10420,82 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/apps/web/package.json b/apps/web/package.json index 4a26fbd..c545d8f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "ts-node --project tsconfig.server.json server.ts", "build": "next build", - "start": "next start", - "lint": "next lint" + "start": "NODE_ENV=production ts-node --project tsconfig.server.json server.ts" }, "dependencies": { + "@anthropic-ai/sdk": "^0.33.1", + "@modelcontextprotocol/sdk": "^1.0.4", "@nivo/core": "^0.88.0", "@nivo/treemap": "^0.88.0", "@radix-ui/react-checkbox": "^1.1.3", @@ -21,9 +22,12 @@ "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.6", "@shadcn/ui": "^0.0.4", + "@types/ws": "^8.5.13", + "chalk": "^5.4.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "dotenv": "^16.4.7", "init": "^0.1.2", "lucide-react": "^0.468.0", "next": "15.1.1", @@ -35,7 +39,9 @@ "recharts": "^2.15.0", "shadcn-ui": "^0.9.4", "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "ts-node": "^10.9.2", + "ws": "^8.18.0" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -43,6 +49,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "concurrently": "^9.1.1", "eslint": "^9", "eslint-config-next": "15.1.1", "postcss": "^8", diff --git a/apps/web/server.ts b/apps/web/server.ts new file mode 100644 index 0000000..e710e06 --- /dev/null +++ b/apps/web/server.ts @@ -0,0 +1,57 @@ +import { createServer } from 'http' +import { parse } from 'url' +import next from 'next' +import { WebSocketServer } from 'ws' +import { MCPServer } from './src/lib/mcp-server' + +const dev = process.env.NODE_ENV !== 'production' +const hostname = 'localhost' +const port = 3000 +const wsPort = 3001 + +// Initialize Next.js +const app = next({ dev, hostname, port }) +const handle = app.getRequestHandler() + +app.prepare().then(() => { + // Create HTTP server + const server = createServer(async (req, res) => { + try { + const parsedUrl = parse(req.url!, true) + await handle(req, res, parsedUrl) + } catch (err) { + console.error('Error occurred handling', req.url, err) + res.statusCode = 500 + res.end('internal server error') + } + }) + + // Start WebSocket server on different port + const wss = new WebSocketServer({ port: wsPort }) + const mcpServer = new MCPServer() + mcpServer.start().catch(console.error) + + wss.on('connection', (ws) => { + console.log('MCP Client connected') + mcpServer.addClient(ws) + + ws.on('message', async (message) => { + try { + await mcpServer.handleMessage(message.toString(), ws) + } catch (error) { + console.error('Error handling message:', error) + ws.send(JSON.stringify({ error: 'Internal server error' })) + } + }) + + ws.on('close', () => { + console.log('MCP Client disconnected') + mcpServer.removeClient(ws) + }) + }) + + server.listen(port, () => { + console.log(`> Ready on http://${hostname}:${port}`) + console.log(`> WebSocket server running on ws://${hostname}:${wsPort}`) + }) +}) \ No newline at end of file diff --git a/apps/web/src/app/api/mcp/route.ts b/apps/web/src/app/api/mcp/route.ts new file mode 100644 index 0000000..8d87055 --- /dev/null +++ b/apps/web/src/app/api/mcp/route.ts @@ -0,0 +1,6 @@ +import WebSocketSingleton from '@/lib/websocket-server'; + +export async function GET() { + await WebSocketSingleton.getInstance(); + return new Response('WebSocket server is running', { status: 200 }); +} \ No newline at end of file diff --git a/apps/web/src/app/api/ws/route.ts b/apps/web/src/app/api/ws/route.ts new file mode 100644 index 0000000..0a599ba --- /dev/null +++ b/apps/web/src/app/api/ws/route.ts @@ -0,0 +1,56 @@ +import { WebSocketServer } from 'ws'; +import { MCPServer } from '@/lib/mcp-server'; + +const SUBPROTOCOL = "mcp"; +const WS_PORT = 3001; + +let wss: WebSocketServer | null = null; + +if (process.env.NODE_ENV === 'development' && !wss) { + try { + wss = new WebSocketServer({ + port: WS_PORT, + handleProtocols: (protocols: Set) => { + return Array.from(protocols).includes(SUBPROTOCOL) ? SUBPROTOCOL : false; + } + }); + + const mcpServer = new MCPServer(); + mcpServer.start().catch(console.error); + + wss.on('connection', (ws) => { + console.log('MCP Client connected'); + mcpServer.addClient(ws); + + ws.on('message', async (message) => { + try { + await mcpServer.handleMessage(message.toString(), ws); + } catch (error) { + console.error('Error handling message:', error); + ws.send(JSON.stringify({ error: 'Internal server error' })); + } + }); + + ws.on('close', () => { + console.log('MCP Client disconnected'); + mcpServer.removeClient(ws); + }); + + ws.on('error', (error) => { + console.error('WebSocket error:', error); + }); + }); + + wss.on('error', (error) => { + console.error('WebSocket server error:', error); + }); + + console.log(`WebSocket server is running on port ${WS_PORT}`); + } catch (error) { + console.error('Failed to start WebSocket server:', error); + } +} + +export async function GET() { + return new Response('WebSocket server status: ' + (wss ? 'running' : 'not running')); +} \ No newline at end of file diff --git a/apps/web/src/app/mcp_client.ts b/apps/web/src/app/mcp_client.ts new file mode 100644 index 0000000..79432a5 --- /dev/null +++ b/apps/web/src/app/mcp_client.ts @@ -0,0 +1,242 @@ +import { Anthropic } from '@anthropic-ai/sdk'; + +import { + StdioClientTransport, + StdioServerParameters, +} from '@modelcontextprotocol/sdk/client/stdio.js'; +import { + ListToolsResultSchema, + CallToolResultSchema, +} from '@modelcontextprotocol/sdk/types.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import chalk from 'chalk'; +import readline from 'readline/promises'; +import { Tool } from '@anthropic-ai/sdk/resources/index.mjs'; +import { Stream } from '@anthropic-ai/sdk/streaming.mjs'; + +const EXIT_COMMAND = 'exit'; + +const styles = { + prompt: chalk.green('You: '), + assistant: chalk.blue('Claude: '), + tool: { + name: chalk.cyan.bold, + args: chalk.yellow, + bracket: chalk.dim, + }, + error: chalk.red, + info: chalk.blue, + success: chalk.green, + warning: chalk.yellow, + separator: chalk.gray('─'.repeat(50)), +}; + +interface Message { + role: 'user' | 'assistant'; + content: string; +} + +export class InteractiveCLI { + private anthropicClient: Anthropic; + private messages: Message[] = []; + private mcpClient: Client; + private transport: StdioClientTransport; + private tools: Tool[] = []; + private rl: readline.Interface; + + constructor(serverConfig: StdioServerParameters) { + this.anthropicClient = new Anthropic({ + apiKey: process.env.ANTHROPIC_API_KEY, + }); + + this.mcpClient = new Client( + { name: 'cli-client', version: '1.0.0' }, + { capabilities: {} }, + ); + + this.transport = new StdioClientTransport(serverConfig); + this.rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + } + + async start() { + try { + console.log(styles.separator); + console.log(styles.info('🤖 Interactive Claude CLI')); + console.log( + styles.info(`Type your queries or "${EXIT_COMMAND}" to exit`), + ); + console.log(styles.separator); + + await this.mcpClient.connect(this.transport); + await this.initMCPTools(); + + await this.chat_loop(); + } catch (error) { + console.error(styles.error('Failed to initialize tools:'), error); + process.exit(1); + } finally { + this.rl.close(); + process.exit(0); + } + } + + private async initMCPTools() { + const toolsResults = await this.mcpClient.request( + { method: 'tools/list' }, + ListToolsResultSchema, + ); + this.tools = toolsResults.tools.map(({ inputSchema, ...tool }) => ({ + ...tool, + input_schema: inputSchema, + })); + } + + private formatToolCall(toolName: string, args: any): string { + return ( + '\n' + + styles.tool.bracket('[') + + styles.tool.name(toolName) + + styles.tool.bracket('] ') + + styles.tool.args(JSON.stringify(args, null, 2)) + + '\n' + ); + } + + private formatJSON(json: string): string { + return json + .replace(/"([^"]+)":/g, chalk.blue('"$1":')) + .replace(/: "([^"]+)"/g, ': ' + chalk.green('"$1"')); + } + + private async processStream( + stream: Stream, + ): Promise { + let currentMessage = ''; + let currentToolName = ''; + let currentToolInputString = ''; + + process.stdout.write(styles.assistant); + for await (const chunk of stream) { + switch (chunk.type) { + case 'message_start': + case 'content_block_stop': + continue; + + case 'content_block_start': + if (chunk.content_block?.type === 'tool_use') { + currentToolName = chunk.content_block.name; + } + break; + + case 'content_block_delta': + if (chunk.delta.type === 'text_delta') { + process.stdout.write(chunk.delta.text); + currentMessage += chunk.delta.text; + } else if (chunk.delta.type === 'input_json_delta') { + if (currentToolName && chunk.delta.partial_json) { + currentToolInputString += chunk.delta.partial_json; + } + } + break; + + case 'message_delta': + if (chunk.delta.stop_reason === 'tool_use') { + const toolArgs = currentToolInputString + ? JSON.parse(currentToolInputString) + : {}; + + console.log(this.formatToolCall(currentToolName, toolArgs)); + const toolResult = await this.mcpClient.request( + { + method: 'tools/call', + params: { + name: currentToolName, + arguments: toolArgs, + }, + }, + CallToolResultSchema, + ); + + if (currentMessage) { + this.messages.push({ + role: 'assistant', + content: currentMessage, + }); + } + + const formattedResult = this.formatJSON( + JSON.stringify(toolResult.content.flatMap((c) => c.text)), + ); + + this.messages.push({ + role: 'user', + content: formattedResult, + }); + + const nextStream = await this.anthropicClient.messages.create({ + messages: this.messages, + model: 'claude-3-5-sonnet-20241022', + max_tokens: 8192, + tools: this.tools, + stream: true, + }); + await this.processStream(nextStream); + } + break; + + case 'message_stop': + break; + + default: + console.warn( + styles.warning(`Unknown event type: ${JSON.stringify(chunk)}`), + ); + } + } + } + + private async processQuery(query: string) { + try { + this.messages.push({ role: 'user', content: query }); + + const stream = await this.anthropicClient.messages.create({ + messages: this.messages, + model: 'claude-3-5-sonnet-20241022', + max_tokens: 8192, + tools: this.tools, + stream: true, + }); + await this.processStream(stream); + } catch (error) { + console.error(styles.error('\nError during query processing:'), error); + if (error instanceof Error) { + process.stdout.write( + styles.assistant + + 'I apologize, but I encountered an error: ' + + error.message + + '\n', + ); + } + } + } + + private async chat_loop() { + while (true) { + try { + const query = (await this.rl.question(styles.prompt)).trim(); + if (query.toLowerCase() === EXIT_COMMAND) { + console.log(styles.warning('\nGoodbye! 👋')); + break; + } + + await this.processQuery(query); + console.log('\n' + styles.separator); + } catch (error) { + console.error(styles.error('\nError:'), error); + } + } + } +} \ No newline at end of file diff --git a/apps/web/src/components/tools/auth0/chat.tsx b/apps/web/src/components/tools/auth0/chat.tsx new file mode 100644 index 0000000..ccc1677 --- /dev/null +++ b/apps/web/src/components/tools/auth0/chat.tsx @@ -0,0 +1,113 @@ +'use client' + +import { useState, useEffect, useRef } from "react" +import { MCPClient, MessageCallback } from "@/lib/mcp-client" +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { ScrollArea } from "@/components/ui/scroll-area" + +export function Chat() { + const [messages, setMessages] = useState>([]) + const [input, setInput] = useState('') + const [isLoading, setIsLoading] = useState(false) + const clientRef = useRef(new MCPClient()) + + useEffect(() => { + let isInitialized = false; + + const initClient = async () => { + if (isInitialized) return; + isInitialized = true; + + try { + await clientRef.current.start() + console.log('MCP client initialized') + } catch (error) { + console.error('Failed to initialize MCP client:', error) + } + } + + initClient() + }, []) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!input.trim() || isLoading) return + + const userMessage = input.trim() + setInput('') + setMessages(prev => [...prev, { role: 'user', content: userMessage }]) + + try { + setIsLoading(true) + let currentMessage = { role: 'assistant', content: '' } + + const onMessage: MessageCallback = (message) => { + if (message.content === '\n\n') { + setMessages(prev => [ + ...prev, message + ]) + currentMessage.content = '' + } else { + currentMessage.content += message.content + setMessages(prev => [ + ...prev.slice(0, -1), // Remove previous assistant message + { ...currentMessage } // Add updated message + ]) + } + } + + await clientRef.current.sendMessage(userMessage, onMessage) + } catch (error) { + console.error('Error sending message:', error) + } finally { + setIsLoading(false) + } + } + + return ( + + + Chat with Claude + + +
+ +
+ {messages.map((message, i) => ( +
+
+ {message.content} +
+
+ ))} +
+
+
+ setInput(e.target.value)} + placeholder="Type a message..." + disabled={isLoading} + /> + +
+
+
+
+ ) +} \ No newline at end of file diff --git a/apps/web/src/components/tools/auth0/dashboard.tsx b/apps/web/src/components/tools/auth0/dashboard.tsx index f32eb4d..0f59d72 100644 --- a/apps/web/src/components/tools/auth0/dashboard.tsx +++ b/apps/web/src/components/tools/auth0/dashboard.tsx @@ -31,6 +31,7 @@ import { CollapsibleTrigger, } from "@/components/ui/collapsible" import { ChevronDown, ChevronUp } from "lucide-react" +import { Chat } from './chat' interface SummaryMetrics { total_users: number @@ -105,6 +106,7 @@ export default function Auth0Dashboard() { unique_emails: number }>>([]) const [isDomainsOpen, setIsDomainsOpen] = useState(false) + const [isOpen, setIsOpen] = useState(false) useEffect(() => { async function fetchInitialData() { @@ -490,6 +492,25 @@ export default function Auth0Dashboard() { /> + + + +
+ Chat Assistant + + + +
+
+ + + + + +
+
) diff --git a/apps/web/src/lib/mcp-client.ts b/apps/web/src/lib/mcp-client.ts new file mode 100644 index 0000000..74c3af5 --- /dev/null +++ b/apps/web/src/lib/mcp-client.ts @@ -0,0 +1,174 @@ +import { Anthropic } from '@anthropic-ai/sdk'; +import { + ListToolsResultSchema, + CallToolResultSchema, +} from '@modelcontextprotocol/sdk/types.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js'; +import { Tool } from '@anthropic-ai/sdk/resources/index.mjs'; +import { Stream } from '@anthropic-ai/sdk/streaming.mjs'; + +interface Message { + role: 'user' | 'assistant'; + content: string; +} + +export type MessageCallback = (message: Message) => void; + +export class MCPClient { + private anthropicClient: Anthropic; + private messages: Message[] = []; + private mcpClient: Client; + private transport: WebSocketClientTransport; + private tools: Tool[] = []; + + constructor() { + this.anthropicClient = new Anthropic({ + apiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY, + dangerouslyAllowBrowser: true, + }); + + this.mcpClient = new Client( + { name: 'web-client', version: '1.0.0' }, + { capabilities: { + jsonrpc: { + request_response: true, + notifications: true + } + } }, + ); + + // Connect to the local MCP server + this.transport = new WebSocketClientTransport( + new URL('ws://localhost:3001') + ); + } + + async start() { + try { + console.log('Connecting to MCP server...') + await this.mcpClient.connect(this.transport) + console.log('Connected to MCP server') + await this.initMCPTools() + console.log('Initialized MCP tools') + } catch (error) { + console.error('Failed to connect to MCP server:', error) + throw error + } + } + + private async initMCPTools() { + try { + const toolsResults = await this.mcpClient.request( + { method: 'tools/list' }, + ListToolsResultSchema, + ) + this.tools = toolsResults.tools.map(({ inputSchema, ...tool }) => ({ + ...tool, + input_schema: inputSchema, + })) + } catch (error) { + console.error('Failed to initialize tools:', error) + throw error + } + } + + private async processStream( + stream: Stream, + onMessage: MessageCallback + ): Promise { + let currentMessage = ''; + let currentToolName = ''; + let currentToolInputString = ''; + + for await (const chunk of stream) { + switch (chunk.type) { + case 'message_start': + case 'content_block_stop': + // onMessage({ role: 'assistant', content: '\n\n' }); + continue; + + case 'content_block_start': + if (chunk.content_block?.type === 'tool_use') { + currentToolName = chunk.content_block.name; + } + break; + + case 'content_block_delta': + if (chunk.delta.type === 'text_delta') { + currentMessage += chunk.delta.text; + // Send incremental updates + onMessage({ role: 'assistant', content: chunk.delta.text }); + } else if (chunk.delta.type === 'input_json_delta') { + if (currentToolName && chunk.delta.partial_json) { + currentToolInputString += chunk.delta.partial_json; + } + } + break; + + case 'message_delta': + if (chunk.delta.stop_reason === 'tool_use') { + const toolArgs = currentToolInputString + ? JSON.parse(currentToolInputString) + : {}; + + const toolResult = await this.mcpClient.request( + { + method: 'tools/call', + params: { + name: currentToolName, + arguments: toolArgs, + }, + }, + CallToolResultSchema, + ); + + if (currentMessage) { + this.messages.push({ + role: 'assistant', + content: currentMessage, + }); + } + + const formattedResult = JSON.stringify(toolResult.content.flatMap((c) => c.text)); + this.messages.push({ + role: 'user', + content: formattedResult, + }); + + const nextStream = await this.anthropicClient.messages.create({ + messages: this.messages, + model: 'claude-3-5-sonnet-latest', + max_tokens: 8192, + tools: this.tools, + stream: true, + }); + await this.processStream(nextStream, onMessage); + } + break; + + case 'message_stop': + break; + } + } + } + + async sendMessage(message: string, onMessage: MessageCallback) { + try { + this.messages.push({ role: 'user', content: message }); + + const stream = await this.anthropicClient.messages.create({ + messages: this.messages, + model: 'claude-3-5-sonnet-latest', + max_tokens: 8192, + tools: this.tools, + stream: true, + }); + + await this.processStream(stream, onMessage); + } catch (error) { + console.error('Error during message processing:', error); + throw error; + } + } +} \ No newline at end of file diff --git a/apps/web/src/lib/mcp-server.ts b/apps/web/src/lib/mcp-server.ts new file mode 100644 index 0000000..58d5244 --- /dev/null +++ b/apps/web/src/lib/mcp-server.ts @@ -0,0 +1,60 @@ +import { spawn } from 'child_process'; +import { WebSocket } from 'ws'; + +export class MCPServer { + private process: any; + private clients: Set = new Set(); + private isReady: boolean = false; + + async start() { + console.log('Starting MCP server...'); + + if (this.isReady) { + return; + } + + this.process = spawn('uv', [ + '--directory', + '/Users/alrocar/gr/mcp-tinybird', + 'run', + 'mcp-tinybird' + ], { + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, PYTHONUNBUFFERED: '1' } + }); + + // Log everything immediately + this.process.stdout.on('data', (data: Buffer) => { + console.log('MCP stdout:', data.toString()); + }); + + this.process.stderr.on('data', (data: Buffer) => { + console.log('MCP stderr:', data.toString()); + }); + } + + addClient(ws: WebSocket) { + this.clients.add(ws); + } + + removeClient(ws: WebSocket) { + this.clients.delete(ws); + } + + async handleMessage(message: string, sender: WebSocket) { + // Forward message to MCP server process + this.process.stdin.write(message + '\n'); + + // Handle response from MCP server + this.process.stdout.once('data', (data: Buffer) => { + sender.send(data.toString()); + }); + } + + stop() { + if (this.process) { + this.process.kill(); + } + this.clients.clear(); + } +} \ No newline at end of file diff --git a/apps/web/tsconfig.server.json b/apps/web/tsconfig.server.json new file mode 100644 index 0000000..84c0d06 --- /dev/null +++ b/apps/web/tsconfig.server.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "noEmit": false, + "isolatedModules": false + }, + "include": ["server.ts"] +} \ No newline at end of file diff --git a/apps/web/ws-server.ts b/apps/web/ws-server.ts new file mode 100644 index 0000000..226ae20 --- /dev/null +++ b/apps/web/ws-server.ts @@ -0,0 +1,58 @@ +import { WebSocketServer } from 'ws'; +import { MCPServer } from './src/lib/mcp-server'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import * as dotenv from 'dotenv'; + +// Load environment variables +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +dotenv.config({ path: join(__dirname, '.env') }); + +const SUBPROTOCOL = "mcp"; +const WS_PORT = 3001; + +async function startWebSocketServer() { + console.log('Starting WebSocket server...'); + + const wss = new WebSocketServer({ + port: WS_PORT, + handleProtocols: (protocols: Set) => { + return Array.from(protocols).includes(SUBPROTOCOL) ? SUBPROTOCOL : false; + } + }); + + const mcpServer = new MCPServer(); + await mcpServer.start(); + + wss.on('connection', (ws) => { + console.log('MCP Client connected'); + mcpServer.addClient(ws); + + ws.on('message', async (message) => { + try { + await mcpServer.handleMessage(message.toString(), ws); + } catch (error) { + console.error('Error handling message:', error); + ws.send(JSON.stringify({ error: 'Internal server error' })); + } + }); + + ws.on('close', () => { + console.log('MCP Client disconnected'); + mcpServer.removeClient(ws); + }); + + ws.on('error', (error) => { + console.error('WebSocket error:', error); + }); + }); + + wss.on('error', (error) => { + console.error('WebSocket server error:', error); + }); + + console.log(`WebSocket server is running on port ${WS_PORT}`); +} + +startWebSocketServer().catch(console.error); \ No newline at end of file