diff --git a/.env.example b/.env.example index 8570476..62513c3 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ +PUBLIC_ENABLE_MCP= HYPERBOLIC_API_KEY= COHERE_API_KEY= TOGETHER_API_KEY= @@ -9,4 +10,3 @@ FAL_API_KEY= HF_TOKEN= MODELS_FILE= - diff --git a/eslint.config.mts b/eslint.config.mts index 7071a50..19877f2 100644 --- a/eslint.config.mts +++ b/eslint.config.mts @@ -66,6 +66,7 @@ export default ts.config( "object-shorthand": ["error", "always"], "svelte/no-at-html-tags": "off", "svelte/require-each-key": "off", + "svelte/no-navigation-without-resolve": "off", "local/enforce-ext": [ "error", { @@ -92,6 +93,7 @@ export default ts.config( "**/package-lock.json", "**/yarn.lock", "context_length.json", + ".claude/**/*", ], }, { diff --git a/package.json b/package.json index 3bdf315..2a80dd0 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "lint": "prettier . --check . && eslint src/", "format": "prettier . --write .", "clean": "rm -rf ./node_modules/ && rm -rf ./.svelte-kit/ && ni && echo 'Project cleaned!'", + "update-ctx-length": "jiti scripts/update-ctx-length.ts", "test:unit": "vitest --browser.headless", - "test": "npm run test:unit", + "test": "npm run test:unit -- --run", "test:e2e": "playwright test" }, "devDependencies": { @@ -30,10 +31,10 @@ "@playwright/test": "^1.49.1", "@ryoppippi/unplugin-typia": "^1.0.0", "@samchon/openapi": "^3.0.0", - "@sveltejs/adapter-auto": "^3.2.2", - "@sveltejs/adapter-node": "^5.2.0", - "@sveltejs/kit": "^2.5.27", - "@sveltejs/vite-plugin-svelte": "^4.0.0", + "@sveltejs/adapter-auto": "^3.3.1", + "@sveltejs/adapter-node": "^5.3.1", + "@sveltejs/kit": "^2.37.1", + "@sveltejs/vite-plugin-svelte": "^4.0.4", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/postcss": "^4.0.9", "@testing-library/jest-dom": "^6.6.3", @@ -59,8 +60,8 @@ "prettier-plugin-tailwindcss": "^0.6.11", "runed": "^0.25.0", "shiki": "^3.4.0", - "svelte": "^5.36.16", - "svelte-check": "^4.0.0", + "svelte": "^5.38.7", + "svelte-check": "^4.3.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.9", "ts-patch": "^3.3.0", @@ -74,11 +75,13 @@ }, "type": "module", "dependencies": { + "@modelcontextprotocol/sdk": "^1.13.3", "@tailwindcss/typography": "^0.5.16", "dequal": "^2.0.3", - "eslint-plugin-svelte": "^3.11.0", + "eslint-plugin-svelte": "^3.12.2", "marked": "^16.1.2", "remult": "^3.0.2", + "tailwindcss-spring": "^1.0.1", "typia": "^8.0.0" }, "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af9b4ca..84bd8b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.13.3 + version: 1.17.5 '@tailwindcss/typography': specifier: ^0.5.16 version: 0.5.16(tailwindcss@4.0.9) @@ -15,14 +18,17 @@ importers: specifier: ^2.0.3 version: 2.0.3 eslint-plugin-svelte: - specifier: ^3.11.0 - version: 3.11.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.36.16) + specifier: ^3.12.2 + version: 3.12.2(eslint@9.22.0(jiti@2.4.2))(svelte@5.38.7) marked: specifier: ^16.1.2 version: 16.1.2 remult: specifier: ^3.0.2 version: 3.0.2 + tailwindcss-spring: + specifier: ^1.0.1 + version: 1.0.1(tailwindcss@4.0.9) typia: specifier: ^8.0.0 version: 8.0.0(@samchon/openapi@3.0.0)(typescript@5.8.2) @@ -67,17 +73,17 @@ importers: specifier: ^3.0.0 version: 3.0.0 '@sveltejs/adapter-auto': - specifier: ^3.2.2 - version: 3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) + specifier: ^3.3.1 + version: 3.3.1(@sveltejs/kit@2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) '@sveltejs/adapter-node': - specifier: ^5.2.0 - version: 5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) + specifier: ^5.3.1 + version: 5.3.1(@sveltejs/kit@2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) '@sveltejs/kit': - specifier: ^2.5.27 - version: 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + specifier: ^2.37.1 + version: 2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) '@sveltejs/vite-plugin-svelte': - specifier: ^4.0.0 - version: 4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + specifier: ^4.0.4 + version: 4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@4.0.9) @@ -89,7 +95,7 @@ importers: version: 6.6.3 '@testing-library/svelte': specifier: ^5.2.4 - version: 5.2.8(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) + version: 5.2.8(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) '@types/node': specifier: ^22.14.1 version: 22.14.1 @@ -128,10 +134,10 @@ importers: version: 26.1.0 melt: specifier: ^0.36.0 - version: 0.36.0(@floating-ui/dom@1.6.13)(svelte@5.36.16) + version: 0.36.0(@floating-ui/dom@1.6.13)(svelte@5.38.7) openai: specifier: ^4.90.0 - version: 4.90.0(ws@8.18.2) + version: 4.90.0(ws@8.18.2)(zod@3.25.76) playwright: specifier: ^1.52.0 version: 1.52.0 @@ -143,22 +149,22 @@ importers: version: 3.5.3 prettier-plugin-svelte: specifier: ^3.4.0 - version: 3.4.0(prettier@3.5.3)(svelte@5.36.16) + version: 3.4.0(prettier@3.5.3)(svelte@5.38.7) prettier-plugin-tailwindcss: specifier: ^0.6.11 - version: 0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.36.16))(prettier@3.5.3) + version: 0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.38.7))(prettier@3.5.3) runed: specifier: ^0.25.0 - version: 0.25.0(svelte@5.36.16) + version: 0.25.0(svelte@5.38.7) shiki: specifier: ^3.4.0 version: 3.4.0 svelte: - specifier: ^5.36.16 - version: 5.36.16 + specifier: ^5.38.7 + version: 5.38.7 svelte-check: - specifier: ^4.0.0 - version: 4.1.5(picomatch@4.0.2)(svelte@5.36.16)(typescript@5.8.2) + specifier: ^4.3.1 + version: 4.3.1(picomatch@4.0.2)(svelte@5.38.7)(typescript@5.8.2) tailwind-merge: specifier: ^3.0.2 version: 3.0.2 @@ -179,7 +185,7 @@ importers: version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) unplugin-icons: specifier: ^22.1.0 - version: 22.1.0(svelte@5.36.16) + version: 22.1.0(svelte@5.38.7) vite: specifier: ^5.4.4 version: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) @@ -188,7 +194,7 @@ importers: version: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) vitest-browser-svelte: specifier: ^0.1.0 - version: 0.1.0(@vitest/browser@3.1.4)(svelte@5.36.16)(vitest@3.1.4) + version: 0.1.0(@vitest/browser@3.1.4)(svelte@5.38.7)(vitest@3.1.4) packages: @@ -199,10 +205,6 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@antfu/install-pkg@1.0.0': resolution: {integrity: sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==} @@ -774,24 +776,32 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@modelcontextprotocol/sdk@1.17.5': + resolution: {integrity: sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==} + engines: {node: '>=18'} + '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -1014,6 +1024,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@sveltejs/acorn-typescript@1.0.5': resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} peerDependencies: @@ -1024,19 +1037,23 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/adapter-node@5.2.12': - resolution: {integrity: sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==} + '@sveltejs/adapter-node@5.3.1': + resolution: {integrity: sha512-PSoGfa9atkmuixe7jvuS2tsUohVZF20So87ASzfMRGTTNqEd8s48KAodlv3CzHwq9XO/BM8KsQLpqqsr/6dmuA==} peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/kit@2.18.0': - resolution: {integrity: sha512-4DGCGiwNzgnPJySlMe/Qi6rKMK3ntphJaV95BTW+aggaTIAVZ5x3Bp+LURVLMxAEAtWAI5U449NafVxTS+kXbQ==} + '@sveltejs/kit@2.37.1': + resolution: {integrity: sha512-4T9rF2Roe7RGvHfcn6+n92Yc2NF88k7ljFz9+wE0jWxyencqRpadr2/CvlcQbbTXf1ozmFxgMO6af+qm+1mPFw==} engines: {node: '>=18.13'} hasBin: true peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true '@sveltejs/vite-plugin-svelte-inspector@3.0.1': resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==} @@ -1297,6 +1314,10 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1307,6 +1328,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} @@ -1371,6 +1397,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. @@ -1388,6 +1418,10 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1396,6 +1430,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1504,13 +1542,33 @@ packages: resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1572,6 +1630,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1588,8 +1650,8 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - devalue@5.1.1: - resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -1619,9 +1681,16 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -1662,6 +1731,9 @@ packages: engines: {node: '>=18'} hasBin: true + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1690,8 +1762,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-svelte@3.11.0: - resolution: {integrity: sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==} + eslint-plugin-svelte@3.12.2: + resolution: {integrity: sha512-NDYltSWcDybvnXD5P3NtrLAfdrgr2lklZsXpyIoSlQfg2d80p/E853XXccu+uVn+w4+Q/iHy4oRw00GJH9I/Cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.1 || ^9.0.0 @@ -1759,14 +1831,36 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + exsolve@1.0.4: resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} @@ -1825,6 +1919,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-cache-dir@5.0.0: resolution: {integrity: sha512-OuWNfjfP05JcpAP3JPgAKUhWefjMRfI5iAoSsvE24ANYWJaepAtlSgWECSVEuRgSXpyNEc9DJwG/TZpgcOqyig==} engines: {node: '>=16'} @@ -1858,6 +1956,14 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1964,6 +2070,10 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -1983,6 +2093,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2016,6 +2130,10 @@ packages: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -2049,6 +2167,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -2266,12 +2387,20 @@ packages: mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + melt@0.36.0: resolution: {integrity: sha512-lJdUuPvsCZs7zpcL2iSvxerHxv3QuM91FoTbdsliOQ2+J3fR4ADqUN878J4kkQSzzHlWqyedQmEBDP6U3iEWgA==} peerDependencies: '@floating-ui/dom': ^1.6.0 svelte: ^5.30.1 + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2299,10 +2428,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -2364,6 +2501,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -2380,10 +2521,25 @@ packages: nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -2457,6 +2613,10 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2472,6 +2632,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -2493,6 +2656,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} @@ -2641,10 +2808,18 @@ packages: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + quansync@0.2.8: resolution: {integrity: sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==} @@ -2655,6 +2830,14 @@ packages: resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} engines: {node: '>=4'} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -2722,6 +2905,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -2772,13 +2959,24 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.2: resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2794,6 +2992,22 @@ packages: shiki@3.4.0: resolution: {integrity: sha512-Ni80XHcqhOEXv5mmDAvf5p6PAJqbUc/RzFeaOqk+zP5DLvTPS3j0ckvA+MI87qoxTQ5RGJDVTbdl/ENLSyyAnQ==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2820,6 +3034,14 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -2853,8 +3075,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.1.5: - resolution: {integrity: sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==} + svelte-check@4.3.1: + resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: @@ -2870,8 +3092,8 @@ packages: svelte: optional: true - svelte@5.36.16: - resolution: {integrity: sha512-C7HnyISfvZEofs7T4p7+bmjrbQlhd6lZfgV2tLYg6Eb3nUFM/Zu9dGlSg+GWbUBU/WPw6zDPOFNZAx9qXsoCkg==} + svelte@5.38.7: + resolution: {integrity: sha512-1ld9TPZSdUS3EtYGQzisU2nhwXoIzNQcZ71IOU9fEmltaUofQnVfW5CQuhgM/zFsZ43arZXS1BRKi0MYgUV91w==} engines: {node: '>=18'} symbol-tree@3.2.4: @@ -2884,6 +3106,11 @@ packages: tailwind-merge@3.0.2: resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} + tailwindcss-spring@1.0.1: + resolution: {integrity: sha512-v8ibKyVCgbFcHzP9EFSbsvR0vH7Vk2Skl9JnJ9HUrnA16NhjvI4SlV9Y4SkinL+4bNrm1XpftZwX1DAD1mNZFA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + tailwindcss@4.0.9: resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==} @@ -2935,6 +3162,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -2982,6 +3213,10 @@ packages: resolution: {integrity: sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==} engines: {node: '>=16'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typescript-eslint@8.26.1: resolution: {integrity: sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3037,6 +3272,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + unplugin-icons@22.1.0: resolution: {integrity: sha512-ect2ZNtk1Zgwb0NVHd0C1IDW/MV+Jk/xaq4t8o6rYdVS3+L660ZdD5kTSQZvsgdwCvquRw+/wYn75hsweRjoIA==} peerDependencies: @@ -3078,6 +3317,10 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} @@ -3263,6 +3506,9 @@ packages: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.2: resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} @@ -3306,6 +3552,14 @@ packages: zimmerframe@1.1.2: resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -3315,11 +3569,6 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - '@antfu/install-pkg@1.0.0': dependencies: package-manager-detector: 0.2.11 @@ -3723,23 +3972,49 @@ snapshots: dependencies: '@sinclair/typebox': 0.27.8 - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@modelcontextprotocol/sdk@1.17.5': + dependencies: + ajv: 6.12.6 + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.1.0 + express-rate-limit: 7.5.1(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.1 + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + transitivePeerDependencies: + - supports-color + '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': @@ -3949,57 +4224,65 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@standard-schema/spec@1.0.0': {} + '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.0)': dependencies: acorn: 8.14.0 - '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': + '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': dependencies: - '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + acorn: 8.15.0 + + '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': + dependencies: + '@sveltejs/kit': 2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) import-meta-resolve: 4.1.0 - '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': + '@sveltejs/adapter-node@5.3.1(@sveltejs/kit@2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': dependencies: '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9) '@rollup/plugin-json': 6.1.0(rollup@4.34.9) '@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9) - '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/kit': 2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) rollup: 4.34.9 - '@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/kit@2.37.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) '@types/cookie': 0.6.0 + acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.1.1 + devalue: 5.3.2 esm-env: 1.2.2 - import-meta-resolve: 4.1.0 kleur: 4.1.5 magic-string: 0.30.17 mrmime: 2.0.1 sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.1 - svelte: 5.36.16 + svelte: 5.38.7 vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) - '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) debug: 4.4.0 - svelte: 5.36.16 + svelte: 5.38.7 vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 - svelte: 5.36.16 + svelte: 5.38.7 vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) vitefu: 1.0.6(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) transitivePeerDependencies: @@ -4100,10 +4383,10 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.36.16)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4)': + '@testing-library/svelte@5.2.8(svelte@5.38.7)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4)': dependencies: '@testing-library/dom': 10.4.0 - svelte: 5.36.16 + svelte: 5.38.7 optionalDependencies: vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) @@ -4287,12 +4570,19 @@ snapshots: dependencies: event-target-shim: 5.0.1 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 acorn@8.14.0: {} + acorn@8.15.0: {} + agent-base@7.1.3: {} agentkeepalive@4.6.0: @@ -4346,6 +4636,20 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.1 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + boolean@3.2.0: {} brace-expansion@1.1.11: @@ -4366,6 +4670,8 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -4373,6 +4679,11 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} ccount@2.0.1: {} @@ -4465,10 +4776,25 @@ snapshots: consola@3.4.0: {} + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + cookie@0.6.0: {} + cookie@0.7.2: {} + core-util-is@1.0.3: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4521,6 +4847,8 @@ snapshots: delayed-stream@1.0.0: {} + depd@2.0.0: {} + dequal@2.0.3: {} detect-libc@1.0.3: {} @@ -4529,7 +4857,7 @@ snapshots: detect-node@2.1.0: {} - devalue@5.1.1: {} + devalue@5.3.2: {} devlop@1.1.0: dependencies: @@ -4553,8 +4881,12 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + ee-first@1.1.1: {} + emoji-regex@8.0.0: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -4635,6 +4967,8 @@ snapshots: '@esbuild/win32-ia32': 0.25.1 '@esbuild/win32-x64': 0.25.1 + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -4652,7 +4986,7 @@ snapshots: optionalDependencies: eslint-config-prettier: 10.1.1(eslint@9.22.0(jiti@2.4.2)) - eslint-plugin-svelte@3.11.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.36.16): + eslint-plugin-svelte@3.12.2(eslint@9.22.0(jiti@2.4.2))(svelte@5.38.7): dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.22.0(jiti@2.4.2)) '@jridgewell/sourcemap-codec': 1.5.0 @@ -4664,9 +4998,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.3) postcss-safe-parser: 7.0.1(postcss@8.5.3) semver: 7.7.2 - svelte-eslint-parser: 1.3.0(svelte@5.36.16) + svelte-eslint-parser: 1.3.0(svelte@5.38.7) optionalDependencies: - svelte: 5.36.16 + svelte: 5.38.7 transitivePeerDependencies: - ts-node @@ -4753,10 +5087,54 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + event-target-shim@5.0.1: {} + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + expect-type@1.2.1: {} + express-rate-limit@7.5.1(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.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.1 + 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.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.4: {} external-editor@3.1.0: @@ -4807,6 +5185,17 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.0: + 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.2 + transitivePeerDependencies: + - supports-color + find-cache-dir@5.0.0: dependencies: common-path-prefix: 3.0.0 @@ -4845,6 +5234,10 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fsevents@2.3.2: optional: true @@ -4957,6 +5350,14 @@ snapshots: html-void-elements@3.0.0: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 @@ -4983,6 +5384,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -5020,6 +5425,8 @@ snapshots: through: 2.3.8 wrap-ansi: 6.2.0 + ipaddr.js@1.9.1: {} + is-arrayish@0.3.2: {} is-core-module@2.16.1: @@ -5042,6 +5449,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 @@ -5225,7 +5634,7 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 marked@16.1.2: {} @@ -5247,14 +5656,18 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 - melt@0.36.0(@floating-ui/dom@1.6.13)(svelte@5.36.16): + media-typer@1.1.0: {} + + melt@0.36.0(@floating-ui/dom@1.6.13)(svelte@5.38.7): dependencies: '@floating-ui/dom': 1.6.13 dequal: 2.0.3 jest-axe: 9.0.0 nanoid: 5.1.5 - runed: 0.23.4(svelte@5.36.16) - svelte: 5.36.16 + runed: 0.23.4(svelte@5.38.7) + svelte: 5.38.7 + + merge-descriptors@2.0.0: {} merge2@1.4.1: {} @@ -5282,10 +5695,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} min-indent@1.0.1: {} @@ -5329,6 +5748,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + node-domexception@1.0.0: {} node-fetch@2.7.0: @@ -5337,8 +5758,20 @@ snapshots: nwsapi@2.2.20: {} + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + object-keys@1.1.1: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -5370,7 +5803,7 @@ snapshots: platform: 1.3.6 protobufjs: 7.4.0 - openai@4.90.0(ws@8.18.2): + openai@4.90.0(ws@8.18.2)(zod@3.25.76): dependencies: '@types/node': 18.19.84 '@types/node-fetch': 2.6.12 @@ -5381,6 +5814,7 @@ snapshots: node-fetch: 2.7.0 optionalDependencies: ws: 8.18.2 + zod: 3.25.76 transitivePeerDependencies: - encoding @@ -5435,6 +5869,8 @@ snapshots: dependencies: entities: 6.0.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -5443,6 +5879,8 @@ snapshots: path-parse@1.0.7: {} + path-to-regexp@8.3.0: {} + pathe@1.1.2: {} pathe@2.0.3: {} @@ -5455,6 +5893,8 @@ snapshots: picomatch@4.0.2: {} + pkce-challenge@5.0.0: {} + pkg-dir@7.0.0: dependencies: find-up: 6.3.0 @@ -5518,16 +5958,16 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.36.16): + prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.38.7): dependencies: prettier: 3.5.3 - svelte: 5.36.16 + svelte: 5.38.7 - prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.36.16))(prettier@3.5.3): + prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.38.7))(prettier@3.5.3): dependencies: prettier: 3.5.3 optionalDependencies: - prettier-plugin-svelte: 3.4.0(prettier@3.5.3)(svelte@5.36.16) + prettier-plugin-svelte: 3.4.0(prettier@3.5.3)(svelte@5.38.7) prettier@3.5.3: {} @@ -5560,8 +6000,17 @@ snapshots: '@types/node': 22.14.1 long: 5.3.1 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + punycode@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + quansync@0.2.8: {} queue-microtask@1.2.3: {} @@ -5571,6 +6020,15 @@ snapshots: drange: 1.1.1 ret: 0.2.2 + range-parser@1.2.1: {} + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + react-is@17.0.2: {} react-is@18.3.1: {} @@ -5660,6 +6118,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.34.9 fsevents: 2.3.3 + 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.3.0 + transitivePeerDependencies: + - supports-color + rrweb-cssom@0.8.0: {} run-async@2.4.1: {} @@ -5668,15 +6136,15 @@ snapshots: dependencies: queue-microtask: 1.2.3 - runed@0.23.4(svelte@5.36.16): + runed@0.23.4(svelte@5.38.7): dependencies: esm-env: 1.2.2 - svelte: 5.36.16 + svelte: 5.38.7 - runed@0.25.0(svelte@5.36.16): + runed@0.25.0(svelte@5.38.7): dependencies: esm-env: 1.2.2 - svelte: 5.36.16 + svelte: 5.38.7 rxjs@7.8.2: dependencies: @@ -5700,12 +6168,39 @@ snapshots: semver@7.7.2: {} + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + set-cookie-parser@2.7.1: {} + setprototypeof@1.2.0: {} + sharp@0.34.2: dependencies: color: 4.2.3 @@ -5751,6 +6246,34 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -5773,6 +6296,10 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: {} + + statuses@2.0.2: {} + std-env@3.9.0: {} string-width@4.2.3: @@ -5806,19 +6333,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.36.16)(typescript@5.8.2): + svelte-check@4.3.1(picomatch@4.0.2)(svelte@5.38.7)(typescript@5.8.2): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 fdir: 6.4.3(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.36.16 + svelte: 5.38.7 typescript: 5.8.2 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.3.0(svelte@5.36.16): + svelte-eslint-parser@1.3.0(svelte@5.38.7): dependencies: eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -5827,11 +6354,11 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.3) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.36.16 + svelte: 5.38.7 - svelte@5.36.16: + svelte@5.38.7: dependencies: - '@ampproject/remapping': 2.3.0 + '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.0) '@types/estree': 1.0.6 @@ -5855,6 +6382,10 @@ snapshots: tailwind-merge@3.0.2: {} + tailwindcss-spring@1.0.1(tailwindcss@4.0.9): + dependencies: + tailwindcss: 4.0.9 + tailwindcss@4.0.9: {} tapable@2.2.1: {} @@ -5899,6 +6430,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + totalist@3.0.1: {} tough-cookie@5.1.2: @@ -5938,6 +6471,12 @@ snapshots: type-fest@4.37.0: {} + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typescript-eslint@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2): dependencies: '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) @@ -6001,7 +6540,9 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - unplugin-icons@22.1.0(svelte@5.36.16): + unpipe@1.0.0: {} + + unplugin-icons@22.1.0(svelte@5.38.7): dependencies: '@antfu/install-pkg': 1.0.0 '@iconify/utils': 2.3.0 @@ -6009,7 +6550,7 @@ snapshots: local-pkg: 1.1.1 unplugin: 2.2.0 optionalDependencies: - svelte: 5.36.16 + svelte: 5.38.7 transitivePeerDependencies: - supports-color @@ -6031,6 +6572,8 @@ snapshots: uuid@8.3.2: {} + vary@1.1.2: {} + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 @@ -6085,10 +6628,10 @@ snapshots: optionalDependencies: vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) - vitest-browser-svelte@0.1.0(@vitest/browser@3.1.4)(svelte@5.36.16)(vitest@3.1.4): + vitest-browser-svelte@0.1.0(@vitest/browser@3.1.4)(svelte@5.38.7)(vitest@3.1.4): dependencies: '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) - svelte: 5.36.16 + svelte: 5.38.7 vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) vitest@3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1): @@ -6182,6 +6725,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrappy@1.0.2: {} + ws@8.18.2: {} xml-name-validator@5.0.0: {} @@ -6201,4 +6746,10 @@ snapshots: zimmerframe@1.1.2: {} + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + zwitch@2.0.4: {} diff --git a/src/app.css b/src/app.css index 5e2d757..9f61d9d 100644 --- a/src/app.css +++ b/src/app.css @@ -2,6 +2,7 @@ @import "tailwindcss"; @plugin '@tailwindcss/container-queries'; +@plugin 'tailwindcss-spring'; @plugin '@tailwindcss/typography'; @custom-variant dark (&:where(.dark, .dark *)); diff --git a/src/lib/components/inference-playground/code-snippets.svelte b/src/lib/components/inference-playground/code-snippets.svelte index 91559c6..0fb4015 100644 --- a/src/lib/components/inference-playground/code-snippets.svelte +++ b/src/lib/components/inference-playground/code-snippets.svelte @@ -206,6 +206,7 @@ diff --git a/src/lib/components/inference-playground/generation-config.svelte b/src/lib/components/inference-playground/generation-config.svelte index 05af254..85f40a4 100644 --- a/src/lib/components/inference-playground/generation-config.svelte +++ b/src/lib/components/inference-playground/generation-config.svelte @@ -7,8 +7,10 @@ import IconX from "~icons/carbon/close"; import ExtraParamsModal, { openExtraParamsModal } from "./extra-params-modal.svelte"; import { GENERATION_CONFIG_KEYS, GENERATION_CONFIG_SETTINGS } from "./generation-config-settings.js"; + import MCPModal from "./mcp-modal.svelte"; import StructuredOutputModal, { openStructuredOutputModal } from "./structured-output-modal.svelte"; - + import { mcpServers } from "$lib/state/mcps.svelte.js"; + import { isMcpEnabled } from "$lib/constants.js"; interface Props { conversation: ConversationClass; classNames?: string; @@ -43,6 +45,7 @@ }); } + let editingMCP = $state(false); const extraParamsLen = $derived(Object.keys(conversation.data.extraParams ?? {}).length); @@ -124,6 +127,21 @@ {/if} + + {#if isMcpEnabled()} +
+ MCP Servers +
+ {#if mcpServers.enabled.length > 0} + + {mcpServers.enabled.length} enabled + + {/if} + +
+
+ {/if} +
Extra parameters +{#if isMcpEnabled()} + +{/if} diff --git a/src/lib/components/inference-playground/mcp-card.svelte b/src/lib/components/inference-playground/mcp-card.svelte new file mode 100644 index 0000000..ef3e195 --- /dev/null +++ b/src/lib/components/inference-playground/mcp-card.svelte @@ -0,0 +1,110 @@ + + +
+
+
+
+ Server Icon + {server.name} +
+

+ + {server.protocol} + + + {urlWithoutSubpaths(server.url)} + +

+ {#if server.headers && Object.keys(server.headers).length > 0} +

+ Headers: {Object.keys(server.headers).length} configured +

+ {/if} +
+
+ isEnabled, v => setEnabled(v)} /> +
+ {#if !editing} + + + {/if} +
+
+
+ + {#if editing} +
+ (editing = false)} /> +
+ {/if} +
diff --git a/src/lib/components/inference-playground/mcp-form.svelte b/src/lib/components/inference-playground/mcp-form.svelte new file mode 100644 index 0000000..ef4c84d --- /dev/null +++ b/src/lib/components/inference-playground/mcp-form.svelte @@ -0,0 +1,160 @@ + + +
+ + + + +
+

Protocol

+
+ {#each protocolOptions as protocol} + + {/each} +
+
+ +
+

Headers

+ {#each formState.headers || [] as _, i (i)} +
+ + : + + +
+ {/each} + +
+ +
+ + +
+
diff --git a/src/lib/components/inference-playground/mcp-modal.svelte b/src/lib/components/inference-playground/mcp-modal.svelte new file mode 100644 index 0000000..c3edafe --- /dev/null +++ b/src/lib/components/inference-playground/mcp-modal.svelte @@ -0,0 +1,53 @@ + + + { + open = false; + }} +> + +
+ {#if mcpServers.all.length === 0} +

No MCP servers configured yet.

+ {:else} +
+ {#each mcpServers.all as server (server.id)} + + {/each} +
+ {/if} +
+ + {#if showAddForm} +
+

Add New Server

+ (showAddForm = false)} submitLabel="Add Server" /> +
+ {:else} + + {/if} +
diff --git a/src/lib/components/inference-playground/structured-output-modal.svelte b/src/lib/components/inference-playground/structured-output-modal.svelte index 46fd2c6..4245ff4 100644 --- a/src/lib/components/inference-playground/structured-output-modal.svelte +++ b/src/lib/components/inference-playground/structured-output-modal.svelte @@ -31,6 +31,7 @@ const modes = ["form", "code"] as const; const radioGroup = new RadioGroup({ value: modes[0], + orientation: "horizontal", }); type Schema = { diff --git a/src/lib/components/switch.svelte b/src/lib/components/switch.svelte new file mode 100644 index 0000000..84758fd --- /dev/null +++ b/src/lib/components/switch.svelte @@ -0,0 +1,57 @@ + + + diff --git a/src/lib/constants.ts b/src/lib/constants.ts index feb71a8..fa7fb89 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,3 +1,5 @@ +import { env } from "$env/dynamic/public"; + export enum TEST_IDS { checkpoints_trigger, checkpoints_menu, @@ -7,3 +9,15 @@ export enum TEST_IDS { message, } + +export function isMcpEnabled(): boolean { + const envEnabled = env.PUBLIC_ENABLE_MCP === "true"; + if (envEnabled) return true; + + if (typeof window === "undefined") return false; + + const urlParams = new URLSearchParams(window.location.search); + const hasQueryParam = urlParams.has("mcp") || urlParams.has("enable-mcp"); + + return hasQueryParam; +} diff --git a/src/lib/data/context_length.json b/src/lib/data/context_length.json new file mode 100644 index 0000000..41465a1 --- /dev/null +++ b/src/lib/data/context_length.json @@ -0,0 +1,269 @@ +{ + "replicate": {}, + "sambanova": { + "DeepSeek-R1-0528": 32768, + "DeepSeek-R1-Distill-Llama-70B": 131072, + "DeepSeek-V3-0324": 32768, + "E5-Mistral-7B-Instruct": 4096, + "Llama-4-Maverick-17B-128E-Instruct": 131072, + "Meta-Llama-3.1-8B-Instruct": 16384, + "Meta-Llama-3.3-70B-Instruct": 131072, + "Qwen3-32B": 32768, + "Whisper-Large-v3": 4096 + }, + "nebius": { + "meta-llama/Meta-Llama-3.1-8B-Instruct-fast": 131072, + "meta-llama/Meta-Llama-3.1-8B-Instruct": 131072, + "meta-llama/Meta-Llama-3.1-70B-Instruct": 131072, + "meta-llama/Meta-Llama-3.1-405B-Instruct": 131072, + "meta-llama/Llama-Guard-3-8B": 131072, + "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": 131072, + "mistralai/Mistral-Nemo-Instruct-2407": 128000, + "google/gemma-2-2b-it": 8192, + "google/gemma-2-9b-it-fast": 8192, + "Qwen/Qwen2.5-Coder-7B-fast": 32768, + "Qwen/Qwen2.5-Coder-7B": 32768, + "Qwen/Qwen2.5-Coder-32B-Instruct-fast": 131072, + "Qwen/Qwen2.5-Coder-32B-Instruct": 131072, + "Qwen/Qwen2.5-32B-Instruct-fast": 131072, + "Qwen/Qwen2.5-32B-Instruct": 131072, + "Qwen/Qwen2.5-72B-Instruct-fast": 131072, + "Qwen/Qwen2.5-72B-Instruct": 131072, + "Qwen/Qwen2-VL-72B-Instruct": 32768, + "aaditya/Llama3-OpenBioLLM-70B": 8192, + "BAAI/bge-en-icl": 32768, + "BAAI/bge-multilingual-gemma2": 8192, + "intfloat/e5-mistral-7b-instruct": 32768, + "meta-llama/Llama-3.3-70B-Instruct": 131072, + "meta-llama/Llama-3.3-70B-Instruct-fast": 131072, + "microsoft/phi-4": 16384, + "deepseek-ai/DeepSeek-V3": 163840, + "deepseek-ai/DeepSeek-R1": 163840, + "deepseek-ai/DeepSeek-R1-0528": 131072, + "NousResearch/Hermes-3-Llama-405B": 131072, + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B": 131072, + "deepseek-ai/DeepSeek-R1-fast": 163840, + "Qwen/QwQ-32B-fast": 131072, + "Qwen/QwQ-32B": 131072, + "Qwen/Qwen3-235B-A22B": 40960, + "Qwen/Qwen3-30B-A3B": 40960, + "Qwen/Qwen3-30B-A3B-fast": 40960, + "Qwen/Qwen3-32B": 40960, + "Qwen/Qwen3-32B-fast": 40960, + "Qwen/Qwen3-14B": 40960, + "Qwen/Qwen3-4B-fast": 40960, + "nvidia/Llama-3_3-Nemotron-Super-49B-v1": 131072, + "mistralai/Mistral-Small-3.1-24B-Instruct-2503": 131072, + "mistralai/Devstral-Small-2505": 128000, + "google/gemma-3-27b-it": 110000, + "google/gemma-3-27b-it-fast": 110000, + "Qwen/Qwen2.5-VL-72B-Instruct": 32000, + "Qwen/Qwen3-Embedding-8B": 40960, + "deepseek-ai/DeepSeek-V3-0324": 163840, + "deepseek-ai/DeepSeek-V3-0324-fast": 163840, + "black-forest-labs/flux-dev": 0, + "black-forest-labs/flux-schnell": 0, + "stability-ai/sdxl": 0 + }, + "novita": { + "deepseek/deepseek-r1-0528": 163840, + "deepseek/deepseek-v3-0324": 163840, + "baidu/ernie-4.5-vl-424b-a47b": 123000, + "baidu/ernie-4.5-300b-a47b-paddle": 123000, + "qwen/qwen3-30b-a3b-fp8": 40960, + "minimaxai/minimax-m1-80k": 128000, + "deepseek/deepseek-r1-0528-qwen3-8b": 128000, + "qwen/qwen3-32b-fp8": 40960, + "qwen/qwen2.5-vl-72b-instruct": 32768, + "qwen/qwen3-235b-a22b-fp8": 40960, + "deepseek/deepseek-v3-turbo": 64000, + "meta-llama/llama-4-maverick-17b-128e-instruct-fp8": 1048576, + "google/gemma-3-27b-it": 32000, + "deepseek/deepseek-r1-turbo": 64000, + "Sao10K/L3-8B-Stheno-v3.2": 8192, + "gryphe/mythomax-l2-13b": 4096, + "deepseek/deepseek-prover-v2-671b": 160000, + "meta-llama/llama-4-scout-17b-16e-instruct": 131072, + "deepseek/deepseek-r1-distill-llama-8b": 32000, + "meta-llama/llama-3.1-8b-instruct": 16384, + "deepseek/deepseek-r1-distill-qwen-14b": 64000, + "meta-llama/llama-3.3-70b-instruct": 131072, + "qwen/qwen-2.5-72b-instruct": 32000, + "mistralai/mistral-nemo": 60288, + "deepseek/deepseek-r1-distill-qwen-32b": 64000, + "meta-llama/llama-3-8b-instruct": 8192, + "microsoft/wizardlm-2-8x22b": 65535, + "deepseek/deepseek-r1-distill-llama-70b": 32000, + "mistralai/mistral-7b-instruct": 32768, + "meta-llama/llama-3-70b-instruct": 8192, + "nousresearch/hermes-2-pro-llama-3-8b": 8192, + "sao10k/l3-70b-euryale-v2.1": 8192, + "cognitivecomputations/dolphin-mixtral-8x22b": 16000, + "sophosympatheia/midnight-rose-70b": 4096, + "sao10k/l3-8b-lunaris": 8192, + "baidu/ernie-4.5-vl-28b-a3b": 30000, + "baidu/ernie-4.5-21B-a3b": 120000, + "baidu/ernie-4.5-0.3b": 120000, + "google/gemma-3-1b-it": 32768, + "qwen/qwen3-8b-fp8": 128000, + "qwen/qwen3-4b-fp8": 128000, + "thudm/glm-4-32b-0414": 32000, + "qwen/qwen2.5-7b-instruct": 32000, + "meta-llama/llama-3.2-1b-instruct": 131000, + "meta-llama/llama-3.2-3b-instruct": 32768, + "meta-llama/llama-3.1-8b-instruct-bf16": 8192, + "sao10k/l31-70b-euryale-v2.2": 8192 + }, + "fal": { + "fal/model-name": 4096 + }, + "cerebras": { + "cerebras/model-name": 8192 + }, + "hf-inference": { + "google/gemma-2-9b-it": 8192, + "meta-llama/Meta-Llama-3-8B-Instruct": 8192 + }, + "hyperbolic": { + "Qwen/Qwen2.5-72B-Instruct": 131072, + "Qwen/Qwen2.5-VL-72B-Instruct": 32768, + "meta-llama/Meta-Llama-3-70B-Instruct": 8192, + "deepseek-ai/DeepSeek-V3": 131072, + "deepseek-ai/DeepSeek-V3-0324": 163840, + "meta-llama/Llama-3.3-70B-Instruct": 131072, + "Qwen/Qwen2.5-Coder-32B-Instruct": 32768, + "meta-llama/Llama-3.2-3B-Instruct": 131072, + "NousResearch/Hermes-3-Llama-3.1-70B": 12288, + "meta-llama/Meta-Llama-3.1-405B-Instruct": 131000, + "meta-llama/Meta-Llama-3.1-70B-Instruct": 131072, + "meta-llama/Meta-Llama-3.1-8B-Instruct": 131072, + "mistralai/Pixtral-12B-2409": 32768, + "Qwen/Qwen2.5-VL-7B-Instruct": 32768, + "meta-llama/Meta-Llama-3.1-405B-FP8": 32768, + "deepseek-ai/DeepSeek-R1": 163840, + "Qwen/QwQ-32B": 131072 + }, + "cohere": { + "embed-english-light-v3.0": 512, + "embed-multilingual-v2.0": 256, + "rerank-v3.5": 4096, + "embed-v4.0": 8192, + "rerank-english-v3.0": 4096, + "command-r-08-2024": 132096, + "embed-english-light-v3.0-image": 0, + "embed-english-v3.0-image": 0, + "command-nightly": 288000, + "command-a-03-2025": 288000, + "command-r-plus-08-2024": 132096, + "c4ai-aya-vision-32b": 16384, + "command-r": 132096, + "command-r7b-12-2024": 132000, + "command-r7b-arabic-02-2025": 128000, + "command-light-nightly": 4096, + "embed-english-v3.0": 512, + "embed-multilingual-light-v3.0-image": 0, + "embed-multilingual-v3.0-image": 0, + "c4ai-aya-expanse-32b": 128000, + "command": 4096 + }, + "together": { + "togethercomputer/m2-bert-80M-32k-retrieval": 32768, + "cartesia/sonic": 0, + "meta-llama/Meta-Llama-3-8B-Instruct-Lite": 8192, + "intfloat/multilingual-e5-large-instruct": 514, + "Alibaba-NLP/gte-modernbert-base": 8192, + "meta-llama/Llama-4-Scout-17B-16E-Instruct": 1048576, + "meta-llama/LlamaGuard-2-8b": 8192, + "Qwen/Qwen2.5-Coder-32B-Instruct": 16384, + "cartesia/sonic-2": 0, + "Qwen/Qwen3-235B-A22B-fp8-tput": 40960, + "togethercomputer/MoA-1": 32768, + "meta-llama/Meta-Llama-3-70B-Instruct-Turbo": 8192, + "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": 131072, + "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": 131072, + "meta-llama/Meta-Llama-Guard-3-8B": 8192, + "arcee_ai/arcee-spotlight": 131072, + "google/gemma-3-27b-it": 65536, + "arcee-ai/AFM-4.5B-Preview": 65536, + "deepseek-ai/DeepSeek-V3": 131072, + "lgai/exaone-3-5-32b-instruct": 32768, + "deepseek-ai/DeepSeek-R1-0528-tput": 163840, + "mistralai/Mixtral-8x7B-Instruct-v0.1": 32768, + "meta-llama/Llama-Vision-Free": 131072, + "meta-llama/Llama-3-8b-chat-hf": 8192, + "mistralai/Mistral-7B-Instruct-v0.1": 32768, + "Qwen/QwQ-32B": 131072, + "meta-llama/Llama-2-70b-hf": 4096, + "togethercomputer/MoA-1-Turbo": 32768, + "black-forest-labs/FLUX.1-kontext-max": 0, + "perplexity-ai/r1-1776": 163840, + "mistralai/Mistral-7B-Instruct-v0.2": 32768, + "deepseek-ai/DeepSeek-V3-p-dp": 131072, + "Qwen/Qwen2-72B-Instruct": 32768, + "mistralai/Mistral-7B-Instruct-v0.3": 32768, + "NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO": 32768, + "meta-llama/Llama-Guard-3-11B-Vision-Turbo": 131072, + "Qwen/Qwen2-VL-72B-Instruct": 32768, + "scb10x/scb10x-llama3-1-typhoon2-70b-instruct": 8192, + "arcee-ai/maestro-reasoning": 131072, + "meta-llama/Llama-3.2-3B-Instruct-Turbo": 131072, + "arcee-ai/virtuoso-medium-v2": 131072, + "arcee-ai/coder-large": 32768, + "meta-llama/Llama-Guard-4-12B": 1048576, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B": 131072, + "arcee-ai/virtuoso-large": 131072, + "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B": 131072, + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B": 131072, + "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF": 32768, + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B-free": 8192, + "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": 1048576, + "togethercomputer/Refuel-Llm-V2-Small": 8192, + "togethercomputer/Refuel-Llm-V2": 16384, + "Qwen/Qwen2.5-VL-72B-Instruct": 32768, + "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": 130815, + "scb10x/scb10x-typhoon-2-1-gemma3-12b": 131072, + "arcee-ai/caller": 32768, + "lgai/exaone-deep-32b": 32768, + "black-forest-labs/FLUX.1-kontext-pro": 0, + "google/gemma-3n-E4B-it": 32768, + "deepseek-ai/DeepSeek-R1": 163840, + "Qwen/Qwen2.5-72B-Instruct-Turbo": 131072, + "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo": 131072, + "arcee-ai/arcee-blitz": 32768, + "meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo": 131072, + "meta-llama/Llama-3-70b-chat-hf": 8192, + "google/gemma-2-27b-it": 8192, + "meta-llama/Llama-3.3-70B-Instruct-Turbo-Free": 131072, + "Qwen/Qwen2.5-7B-Instruct-Turbo": 32768, + "mistralai/Mistral-Small-24B-Instruct-2501": 32768, + "Salesforce/Llama-Rank-V1": 8192, + "meta-llama/Llama-3.3-70B-Instruct-Turbo": 131072, + "marin-community/marin-8b-instruct": 4096, + "Qwen/Qwen3-32B-FP8": 0 + }, + "fireworks-ai": { + "accounts/perplexity/models/r1-1776": 163840, + "accounts/fireworks/models/deepseek-r1-0528": 163840, + "accounts/fireworks/models/qwen3-30b-a3b": 40000, + "accounts/fireworks/models/llama-guard-3-8b": 131072, + "accounts/fireworks/models/llama4-scout-instruct-basic": 10485760, + "accounts/fireworks/models/llama4-maverick-instruct-basic": 1048576, + "accounts/fireworks/models/llama-v3p1-8b-instruct": 131072, + "accounts/fireworks/models/firesearch-ocr-v6": 8192, + "accounts/fireworks/models/llama-v3p1-405b-instruct": 131072, + "accounts/fireworks/models/qwen2-vl-72b-instruct": 32768, + "accounts/fireworks/models/mixtral-8x22b-instruct": 65536, + "accounts/fireworks/models/qwen2p5-72b-instruct": 32768, + "accounts/fireworks/models/deepseek-r1-basic": 163840, + "accounts/fireworks/models/llama-v3p1-70b-instruct": 131072, + "accounts/fireworks/models/qwen3-235b-a22b": 128000, + "accounts/fireworks/models/llama-v3p3-70b-instruct": 131072, + "accounts/fireworks/models/deepseek-r1": 163840, + "accounts/sentientfoundation/models/dobby-unhinged-llama-3-3-70b-new": 131072, + "accounts/sentientfoundation-serverless/models/dobby-mini-unhinged-plus-llama-3-1-8b": 131072, + "accounts/fireworks/models/deepseek-v3": 131072, + "accounts/fireworks/models/deepseek-v3-0324": 163840, + "accounts/fireworks/models/qwq-32b": 131072, + "accounts/fireworks/models/qwen2p5-vl-32b-instruct": 128000 + } +} \ No newline at end of file diff --git a/src/lib/state/mcps.svelte.ts b/src/lib/state/mcps.svelte.ts new file mode 100644 index 0000000..14a6bef --- /dev/null +++ b/src/lib/state/mcps.svelte.ts @@ -0,0 +1,86 @@ +import { idb } from "$lib/remult.js"; +import { dequal } from "dequal"; +import { Entity, Fields, repo, type MembersOnly } from "remult"; +import { projects } from "./projects.svelte"; + +export type MCPProtocol = "sse" | "http"; + +export interface MCPServer { + id: string; + name: string; + url: string; + protocol: MCPProtocol; + headers?: Record; +} + +@Entity("mcp_server") +export class MCPServerEntity { + @Fields.cuid() + id!: string; + + @Fields.string() + name!: string; + + @Fields.string() + url!: string; + + @Fields.string() + protocol: MCPProtocol = "sse"; + + @Fields.json() + headers?: Record; +} + +export type MCPServerEntityMembers = MembersOnly; + +export type MCPFormData = { + name: string; + url: string; + protocol: MCPProtocol; + headers: Record; +}; + +const mcpServersRepo = repo(MCPServerEntity, idb); + +class MCPServers { + #servers: Record = $state({}); + + constructor() { + mcpServersRepo.find().then(res => { + res.forEach(server => { + if (dequal(this.#servers[server.id], server)) return; + this.#servers[server.id] = server; + }); + }); + } + + async create(args: Omit): Promise { + const server = await mcpServersRepo.save({ ...args }); + this.#servers[server.id] = server; + return server.id; + } + + get all() { + return Object.values(this.#servers); + } + + get enabled() { + const currentProject = projects.current; + if (!currentProject) return []; + return this.all.filter(server => currentProject.enabledMCPs?.includes(server.id)); + } + + async update(data: MCPServerEntity) { + if (!data.id) return; + await mcpServersRepo.upsert({ where: { id: data.id }, set: data }); + this.#servers[data.id] = { ...data }; + } + + async delete(id: string) { + if (!id) return; + await mcpServersRepo.delete(id); + delete this.#servers[id]; + } +} + +export const mcpServers = new MCPServers(); diff --git a/src/lib/state/projects.svelte.ts b/src/lib/state/projects.svelte.ts index cea67d8..355e5ba 100644 --- a/src/lib/state/projects.svelte.ts +++ b/src/lib/state/projects.svelte.ts @@ -16,6 +16,9 @@ export class ProjectEntity { @Fields.string() systemMessage?: string; + @Fields.json() + enabledMCPs?: string[]; + @Fields.string() branchedFromId?: string | null; diff --git a/src/lib/utils/business.svelte.ts b/src/lib/utils/business.svelte.ts index 041d07f..5bed3fd 100644 --- a/src/lib/utils/business.svelte.ts +++ b/src/lib/utils/business.svelte.ts @@ -6,11 +6,12 @@ * **/ +import ctxLengthData from "$lib/data/context_length.json"; import { pricing } from "$lib/state/pricing.svelte.js"; -import { InferenceClient, snippets } from "@huggingface/inference"; +import { snippets } from "@huggingface/inference"; import { ConversationClass, type ConversationEntityMembers } from "$lib/state/conversations.svelte"; import { token } from "$lib/state/token.svelte"; -import { billing } from "$lib/state/billing.svelte"; +import { isMcpEnabled } from "$lib/constants.js"; import { isCustomModel, isHFModel, @@ -25,11 +26,12 @@ import { omit } from "$lib/utils/object.svelte.js"; import type { ChatCompletionInputMessage, InferenceSnippet } from "@huggingface/tasks"; import { type ChatCompletionOutputMessage } from "@huggingface/tasks"; import { AutoTokenizer, PreTrainedTokenizer } from "@huggingface/transformers"; -import OpenAI from "openai"; import { images } from "$lib/state/images.svelte.js"; import { projects } from "$lib/state/projects.svelte.js"; +import { mcpServers } from "$lib/state/mcps.svelte.js"; import { modifySnippet } from "$lib/utils/snippets.js"; import { models } from "$lib/state/models.svelte"; +import { StreamReader } from "$lib/utils/stream.js"; type ChatCompletionInputMessageChunk = NonNullable extends string | (infer U)[] ? U : never; @@ -56,20 +58,6 @@ async function parseMessage(message: ConversationMessage): Promise[0]; -}; - -type OpenAICompletionMetadata = { - type: "openai"; - client: OpenAI; - args: OpenAI.ChatCompletionCreateParams; -}; - -type CompletionMetadata = HFCompletionMetadata | OpenAICompletionMetadata; - export function maxAllowedTokens(conversation: ConversationClass) { const model = conversation.model; const { provider } = conversation.data; @@ -78,11 +66,29 @@ export function maxAllowedTokens(conversation: ConversationClass) { return customMaxTokens[conversation.model.id] ?? 100000; } - // Try to get context length from router data + // Try to get context length from pricing/router data first const ctxLength = pricing.getContextLength(model.id, provider); + if (ctxLength) return ctxLength; + + // Fall back to local context length data if available + const providerData = ctxLengthData[provider as keyof typeof ctxLengthData] as Record | undefined; + const localCtxLength = providerData?.[model.id]; + if (localCtxLength) return localCtxLength; + + // Final fallback to custom max tokens + return customMaxTokens[conversation.model.id] ?? 100000; +} + +function getEnabledMCPs() { + if (!isMcpEnabled()) return []; - if (!ctxLength) return customMaxTokens[conversation.model.id] ?? 100000; - return ctxLength; + return mcpServers.enabled.map(server => ({ + id: server.id, + name: server.name, + url: server.url, + protocol: server.protocol, + headers: server.headers, + })); } function getResponseFormatObj(conversation: ConversationClass | Conversation) { @@ -112,10 +118,11 @@ function getResponseFormatObj(conversation: ConversationClass | Conversation) { } } -async function getCompletionMetadata( +export async function handleStreamingResponse( conversation: ConversationClass | Conversation, - signal?: AbortSignal, -): Promise { + onChunk: (content: string) => void, + abortController: AbortController, +): Promise { const data = conversation instanceof ConversationClass ? conversation.data : conversation; const model = conversation.model; const systemMessage = projects.current?.systemMessage; @@ -126,98 +133,38 @@ async function getCompletionMetadata( ]; const parsed = await Promise.all(messages.map(parseMessage)); - const extraParams = data.extraParams - ? Object.fromEntries( - Object.entries(data.extraParams).map(([key, value]) => { - try { - return [key, JSON.parse(value as string)]; - } catch { - return [key, value]; - } - }), - ) - : {}; - - const baseArgs = { - ...data.config, - ...extraParams, + const requestBody = { + model: { + id: model.id, + isCustom: isCustomModel(model), + accessToken: isCustomModel(model) ? model.accessToken : undefined, + endpointUrl: isCustomModel(model) ? model.endpointUrl : undefined, + }, messages: parsed, - model: model.id, - response_format: getResponseFormatObj(conversation), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - - // Handle OpenAI-compatible models - if (isCustomModel(model)) { - const openai = new OpenAI({ - apiKey: model.accessToken, - baseURL: model.endpointUrl, - dangerouslyAllowBrowser: true, - fetch: (...args: Parameters) => { - return fetch(args[0], { ...args[1], signal }); - }, - }); - - const args = { - ...baseArgs, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - - return { - type: "openai", - client: openai, - args, - }; - } - const args = { - ...baseArgs, + config: data.config, provider: data.provider, - // max_tokens: maxAllowedTokens(conversation) - currTokens, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - - // Handle HuggingFace models - const clientOptions: ConstructorParameters[1] = {}; - if (billing.organization) { - clientOptions.billTo = billing.organization; - } - - return { - type: "huggingface", - client: new InferenceClient(token.value, clientOptions), - args, + streaming: true, + response_format: getResponseFormatObj(conversation), + accessToken: token.value, + enabledMCPs: getEnabledMCPs(), }; -} -export async function handleStreamingResponse( - conversation: ConversationClass | Conversation, - onChunk: (content: string) => void, - abortController: AbortController, -): Promise { - const metadata = await getCompletionMetadata(conversation, abortController.signal); - - if (metadata.type === "openai") { - const stream = await metadata.client.chat.completions.create({ - ...metadata.args, - stream: true, - } as OpenAI.ChatCompletionCreateParamsStreaming); - - let out = ""; - for await (const chunk of stream) { - if (chunk.choices[0]?.delta?.content) { - out += chunk.choices[0].delta.content; - onChunk(out); - } - } - return; - } + const reader = await StreamReader.fromFetch("/api/generate", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + signal: abortController.signal, + }); - // HuggingFace streaming let out = ""; - for await (const chunk of metadata.client.chatCompletionStream(metadata.args, { signal: abortController.signal })) { - if (chunk.choices && chunk.choices.length > 0 && chunk.choices[0]?.delta?.content) { - out += chunk.choices[0].delta.content; + for await (const chunk of reader.read()) { + if (chunk.type === "chunk" && chunk.content) { + out += chunk.content; onChunk(out); + } else if (chunk.type === "error") { + throw new Error(chunk.error || "Stream error"); } } } @@ -225,34 +172,46 @@ export async function handleStreamingResponse( export async function handleNonStreamingResponse( conversation: ConversationClass | Conversation, ): Promise<{ message: ChatCompletionOutputMessage; completion_tokens: number }> { - const metadata = await getCompletionMetadata(conversation); - - if (metadata.type === "openai") { - const response = await metadata.client.chat.completions.create({ - ...metadata.args, - stream: false, - } as OpenAI.ChatCompletionCreateParamsNonStreaming); - - if (response.choices && response.choices.length > 0 && response.choices[0]?.message) { - return { - message: { - role: "assistant", - content: response.choices[0].message.content || "", - }, - completion_tokens: response.usage?.completion_tokens || 0, - }; - } - throw new Error("No response from the model"); - } + const data = conversation instanceof ConversationClass ? conversation.data : conversation; + const model = conversation.model; + const systemMessage = projects.current?.systemMessage; - // HuggingFace non-streaming - const response = await metadata.client.chatCompletion(metadata.args); - if (response.choices && response.choices.length > 0) { - const { message } = response.choices[0]!; - const { completion_tokens } = response.usage; - return { message, completion_tokens }; + const messages: ConversationMessage[] = [ + ...(isSystemPromptSupported(model) && systemMessage?.length ? [{ role: "system", content: systemMessage }] : []), + ...(data.messages || []), + ]; + const parsed = await Promise.all(messages.map(parseMessage)); + + const requestBody = { + model: { + id: model.id, + isCustom: isCustomModel(model), + accessToken: isCustomModel(model) ? model.accessToken : undefined, + endpointUrl: isCustomModel(model) ? model.endpointUrl : undefined, + }, + messages: parsed, + config: data.config, + provider: data.provider, + streaming: false, + response_format: getResponseFormatObj(conversation), + accessToken: token.value, + enabledMCPs: getEnabledMCPs(), + }; + + const response = await fetch("/api/generate", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || "Failed to generate response"); } - throw new Error("No response from the model"); + + return await response.json(); } export function isSystemPromptSupported(model: Model | CustomModel) { diff --git a/src/lib/utils/stream.ts b/src/lib/utils/stream.ts new file mode 100644 index 0000000..63dbbe6 --- /dev/null +++ b/src/lib/utils/stream.ts @@ -0,0 +1,144 @@ +export interface StreamChunk { + type: "chunk" | "done" | "error"; + content?: string; + error?: string; +} + +export class StreamReader { + private decoder = new TextDecoder(); + private buffer = ""; + + constructor(private response: Response) { + if (!response.body) { + throw new Error("Response has no body"); + } + } + + async *read(): AsyncGenerator { + const reader = this.response.body!.getReader(); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + this.buffer += this.decoder.decode(value, { stream: true }); + const lines = this.buffer.split("\n"); + this.buffer = lines.pop() || ""; + + for (const line of lines) { + if (line.startsWith("data: ")) { + const data = line.slice(6).trim(); + if (!data) continue; + + try { + const parsed = JSON.parse(data) as StreamChunk; + yield parsed; + if (parsed.type === "done") return; + } catch { + // Ignore malformed JSON + } + } + } + } + } finally { + reader.releaseLock(); + } + } + + static async fromFetch(url: string, options?: RequestInit): Promise { + const response = await fetch(url, options); + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || "Request failed"); + } + return new StreamReader(response); + } +} + +export class StreamWriter { + private encoder = new TextEncoder(); + private controller?: ReadableStreamDefaultController; + public readonly stream: ReadableStream; + + constructor() { + this.stream = new ReadableStream({ + start: controller => { + this.controller = controller; + }, + }); + } + + write(chunk: StreamChunk): void { + if (!this.controller) { + return; + } + + try { + const data = JSON.stringify(chunk); + this.controller.enqueue(this.encoder.encode(`data: ${data}\n\n`)); + } catch { + // Controller might be closed + } + } + + writeChunk(content: string): void { + this.write({ type: "chunk", content }); + } + + writeError(error: string): void { + this.write({ type: "error", error }); + } + + end(): void { + if (!this.controller) return; + try { + this.write({ type: "done" }); + this.controller.close(); + } catch { + // Controller might already be closed + } + this.controller = undefined; + } + + error(error: Error): void { + if (!this.controller) return; + try { + this.writeError(error.message); + this.controller.close(); + } catch { + // Controller might already be closed + } + this.controller = undefined; + } + + createResponse(): Response { + return new Response(this.stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "Connection": "keep-alive", + }, + }); + } +} + +export async function streamFromAsyncIterable( + iterable: AsyncIterable, + transform: (item: T) => StreamChunk, +): Promise> { + const writer = new StreamWriter(); + + (async () => { + try { + for await (const item of iterable) { + writer.write(transform(item)); + } + writer.end(); + } catch (error) { + writer.error(error instanceof Error ? error : new Error(String(error))); + } + })(); + + return writer.stream; +} diff --git a/src/lib/utils/styles.ts b/src/lib/utils/styles.ts new file mode 100644 index 0000000..b6257bb --- /dev/null +++ b/src/lib/utils/styles.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function classes(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/lib/utils/url.spec.ts b/src/lib/utils/url.spec.ts new file mode 100644 index 0000000..47efc82 --- /dev/null +++ b/src/lib/utils/url.spec.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from "vitest"; +import { extractDomain } from "./url.js"; + +describe("extractDomain", () => { + it("should handle regular domains", () => { + expect(extractDomain("https://example.com")).toBe("example.com"); + }); + + it("should handle single subdomains", () => { + expect(extractDomain("https://sub.example.com")).toBe("example.com"); + }); + + it("should handle multiple subdomains", () => { + expect(extractDomain("https://sub1.sub2.example.com")).toBe("example.com"); + expect(extractDomain("https://deep.sub.domain.co.uk")).toBe("domain.co.uk"); + expect(extractDomain("https://a.b.c.d.e.f.g.example.net")).toBe("example.net"); + }); + + it("should handle special TLDs like co.uk", () => { + expect(extractDomain("https://domain.co.uk")).toBe("domain.co.uk"); + expect(extractDomain("https://sub.domain.co.uk")).toBe("domain.co.uk"); + expect(extractDomain("https://another.domain.com.au")).toBe("domain.com.au"); + }); + + it("should handle localhost", () => { + expect(extractDomain("http://localhost:3000")).toBe("localhost"); + expect(extractDomain("http://localhost")).toBe("localhost"); + }); + + it("should handle non-HTTP/HTTPS protocols", () => { + expect(extractDomain("ftp://files.example.com")).toBe("example.com"); + expect(extractDomain("ws://websocket.example.com")).toBe("example.com"); + }); + + it("should handle non-string values", () => { + expect(extractDomain(12345)).toBeNull(); + expect(extractDomain({})).toBeNull(); + expect(extractDomain(["https://example.com"])).toBeNull(); + expect(extractDomain(true)).toBeNull(); + }); + + it("should handle URL instances", () => { + expect(extractDomain(new URL("https://test.example.com"))).toBe("example.com"); + expect(extractDomain(new URL("https://deeply.nested.subdomain.example.org"))).toBe("example.org"); + }); + + it("should handle invalid URLs gracefully", () => { + expect(extractDomain("not-a-url")).toBeNull(); + expect(extractDomain("htt:invalid-url")).toBeNull(); + expect(extractDomain("example")).toBeNull(); + expect(extractDomain("http://")).toBeNull(); + }); + + it("should handle tricky but valid URLs", () => { + expect(extractDomain("https://example.com#hash")).toBe("example.com"); + expect(extractDomain("https://example.com/path")).toBe("example.com"); + expect(extractDomain("https://example.com/path?query=string")).toBe("example.com"); + }); +}); diff --git a/src/lib/utils/url.ts b/src/lib/utils/url.ts index 26b25dc..9abf942 100644 --- a/src/lib/utils/url.ts +++ b/src/lib/utils/url.ts @@ -6,3 +6,43 @@ export function isValidURL(url: string): boolean { return false; } } + +export const extractDomain = (value: unknown) => { + const url = toURL(value); + + if (!url) { + return null; + } + + const hostnameParts = url.hostname.split("."); + + // Determine typical hostnames like "domain.com" or "domain.org" + if (hostnameParts.length <= 2) { + return url.hostname; + } + + // Determine two-part TLD if second last part of the hostname matches one of the prefixes + const prefixes = ["com", "co", "org", "net", "gov", "edu"]; + const potentialTwoPartTLD = `${hostnameParts[hostnameParts.length - 2]}.${hostnameParts[hostnameParts.length - 1]}`; + + return prefixes.includes(hostnameParts[hostnameParts.length - 2]!) + ? `${hostnameParts[hostnameParts.length - 3]}.${potentialTwoPartTLD}` + : hostnameParts.slice(-2).join("."); // Fallback to last two parts of hostname +}; + +const toURL = (value: unknown) => { + if (value instanceof URL) { + return value; + } + + if (typeof value !== "string" || !value) { + return null; + } + + try { + const url = new URL(value); + return url.hostname ? url : null; + } catch { + return null; + } +}; diff --git a/src/routes/api/generate/+server.ts b/src/routes/api/generate/+server.ts new file mode 100644 index 0000000..f389b9b --- /dev/null +++ b/src/routes/api/generate/+server.ts @@ -0,0 +1,202 @@ +import { last } from "$lib/utils/array.js"; +import { StreamWriter } from "$lib/utils/stream.js"; +import { json } from "@sveltejs/kit"; +import type { ChatCompletionMessage } from "openai/resources/index.mjs"; +import type { RequestHandler } from "./$types.js"; +import { createAdapter, type GenerationArgs } from "./adapter.js"; +import { connectToMCPServers, executeMcpTool, type MCPServerConnection } from "./mcp.js"; +import type { FinishReason, GenerateRequest } from "./types.js"; +import { debugLog } from "./utils.js"; + +type AssistantResponse = { message: ChatCompletionMessage; finish_reason: FinishReason }; + +type GenerateLoopArgs = { + args: GenerationArgs; + getAssistantResponse: (args: GenerationArgs) => Promise; + connections: MCPServerConnection[]; +}; + +async function generateLoop({ args, getAssistantResponse, connections }: GenerateLoopArgs) { + let finish_reason: FinishReason | null = null; + const abortReasons: FinishReason[] = ["stop", "content_filter", "length"]; + + while (!abortReasons.includes(finish_reason)) { + debugLog("finish reason", finish_reason); + switch (finish_reason) { + case null: { + const res = await getAssistantResponse(args); + args.messages.push(res.message); + finish_reason = res.finish_reason; + break; + } + case "tool_calls": { + const toolCalls = last(args.messages)?.tool_calls; + if (!toolCalls) { + debugLog("No tool calls found"); + finish_reason = null; + break; + } + + debugLog("Executing tool calls"); + debugLog(JSON.stringify(toolCalls, null, 2)); + + await Promise.allSettled( + toolCalls.map(async toolCall => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response = await executeMcpTool(connections, toolCall as any); + debugLog("Tool call response", response); + args.messages.push(response); + }), + ); + + finish_reason = null; + + break; + } + default: { + finish_reason = "stop"; + break; + } + } + } +} + +export const POST: RequestHandler = async ({ request }) => { + try { + const body: GenerateRequest = await request.json(); + const { model, messages, config, provider, streaming, response_format, enabledMCPs } = body; + + if (enabledMCPs?.length === 0) { + debugLog(`MCP: Enabled MCP servers: ${enabledMCPs?.join(", ")}`); + } + + // Connect to enabled MCP servers + const connections = await connectToMCPServers(enabledMCPs || []); + const tools = connections.flatMap(conn => conn.tools); + + debugLog(`MCP: Connected to ${connections.length} servers with ${tools.length} tools available`); + + const adapter = createAdapter(body); + + const args = { + model: model.id, + messages, + provider, + ...config, + tools, + response_format, + stream: streaming, + }; + + if (streaming) { + const writer = new StreamWriter(); + + generateLoop({ + args, + connections, + getAssistantResponse: async args => { + debugLog("Generating streaming response"); + const res: AssistantResponse = { + message: { + role: "assistant", + content: "", + // refusal: null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + finish_reason: null, + }; + + try { + const adapterStream = await adapter.stream(args); + for await (const chunk of adapterStream) { + const choice = chunk.choices[0]; + if (!choice) continue; + + if (choice.delta.content) { + res.message.content += choice.delta.content; + writer.writeChunk(choice.delta.content || ""); + } + if (choice.delta.tool_calls) { + res.message.tool_calls = res.message.tool_calls ?? []; + + for (const toolCall of choice.delta.tool_calls) { + res.message.tool_calls[toolCall.index] = res.message.tool_calls[toolCall.index] ?? { + id: toolCall.id ?? "", + type: "function", + function: { + name: "", + arguments: "", + }, + }; + + if (toolCall.function?.name) { + res.message.tool_calls[toolCall.index]!.function.name += toolCall.function.name; + } + if (toolCall.function?.arguments) { + res.message.tool_calls[toolCall.index]!.function.arguments += toolCall.function.arguments; + } + } + } + if (choice.finish_reason) { + res.finish_reason = choice.finish_reason; + } + } + } catch (error) { + console.error("stream error", error); + writer.error(error instanceof Error ? error : new Error(String(error))); + res.finish_reason = "stop"; + return res; + } + + debugLog("Generated message"); + debugLog(JSON.stringify(res.message, null, 2)); + return res; + }, + }) + .then(() => writer.end()) + .catch(error => { + console.error("Generation loop error:", error); + writer.error(error instanceof Error ? error : new Error(String(error))); + }); + + debugLog("Creating response..."); + + return writer.createResponse(); + } + + const message: ChatCompletionMessage = { + role: "assistant", + content: "", + // refusal: null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await generateLoop({ + args, + connections, + getAssistantResponse: async args => { + debugLog("Generating non-streaming response"); + const response = await adapter.generate(args); + debugLog("Generated the response"); + debugLog(JSON.stringify(response, null, 2)); + + if (response.choices && response.choices.length > 0) { + message.content += response.choices[0]!.message.content ?? ""; + // const { completion_tokens } = response.usage || { completion_tokens: 0 }; + + return { + message: response.choices[0]!.message, + finish_reason: response.choices[0]!.finish_reason, + }; + } + throw new Error("No response from the model"); + }, + }); + + return json({ message /* ,completion_tokens */ }); + } catch (error) { + debugLog(JSON.stringify(error, null, 2)); + console.error("Generation error:", error); + return json({ error: error instanceof Error ? error.message : "Unknown error occurred" }, { status: 500 }); + } +}; diff --git a/src/routes/api/generate/adapter.ts b/src/routes/api/generate/adapter.ts new file mode 100644 index 0000000..0c66f35 --- /dev/null +++ b/src/routes/api/generate/adapter.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-explicit-any -- Sorry */ +import { omit } from "$lib/utils/object.svelte.js"; +import { InferenceClient } from "@huggingface/inference"; +import type { ChatCompletionInputMessage } from "@huggingface/tasks"; +import OpenAI from "openai"; +import type { Stream } from "openai/streaming.mjs"; +import type { GenerateRequest, OpenAIFunctionSchema } from "./types.js"; +import type { ChatCompletionMessage } from "openai/resources/index.mjs"; + +export type GenerationArgs = { + model: string; + messages: Array; + provider?: string; + config?: Record; + tools?: OpenAIFunctionSchema[]; + response_format?: unknown; +}; + +export interface Adapter { + stream: (args: GenerationArgs) => Promise>; + generate: (args: GenerationArgs) => Promise; +} + +function createCustomAdapter({ model }: GenerateRequest): Adapter { + // Handle OpenAI-compatible custom models + const openai = new OpenAI({ + apiKey: model.accessToken, + baseURL: model.endpointUrl, + }); + + return { + stream: async (args: GenerationArgs) => { + return await openai.chat.completions.create({ + ...omit(args, "provider"), + stream: true, + } as OpenAI.ChatCompletionCreateParamsStreaming); + }, + generate: (args: GenerationArgs) => { + return openai.chat.completions.create({ + ...omit(args, "provider"), + stream: false, + } as OpenAI.ChatCompletionCreateParamsNonStreaming); + }, + }; +} + +function createHFAdapter({ accessToken }: GenerateRequest): Adapter { + const client = new InferenceClient(accessToken); + return { + stream: (args: GenerationArgs) => { + return client.chatCompletionStream({ + ...args, + provider: args.provider as any, + response_format: args.response_format as any, + tools: args.tools as any, + } as any) as any; + }, + generate: (args: GenerationArgs) => { + return client.chatCompletion(args as any) as any; + }, + }; +} + +export function createAdapter(body: GenerateRequest): Adapter { + const { model } = body; + + if (model.isCustom) { + return createCustomAdapter(body); + } + return createHFAdapter(body); +} diff --git a/src/routes/api/generate/mcp.ts b/src/routes/api/generate/mcp.ts new file mode 100644 index 0000000..c9b2ffe --- /dev/null +++ b/src/routes/api/generate/mcp.ts @@ -0,0 +1,106 @@ +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { MCPServerConfig, McpToolSchema, OpenAIFunctionSchema } from "./types.js"; +import { debugError, debugLog } from "./utils.js"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; + +export const mcpToolToOpenAIFunction = (tool: McpToolSchema): OpenAIFunctionSchema => { + return { + type: "function", + function: { + name: tool.name, + description: tool.name, + parameters: tool.inputSchema, + strict: true, + }, + }; +}; + +export type MCPServerConnection = { client: Client; tools: OpenAIFunctionSchema[] }; + +export const connectToMCPServers = async (servers: MCPServerConfig[]): Promise => { + const connections: MCPServerConnection[] = []; + + await Promise.allSettled( + servers.map(async server => { + try { + const conn: MCPServerConnection = { + client: new Client({ + name: "playground-client" + crypto.randomUUID(), + version: "0.0.1", + }), + tools: [], + }; + + debugLog(`Connecting to MCP server: ${server.name} (${server.url})`); + + let transport; + const url = new URL(server.url); + if (server.protocol === "sse") { + transport = new SSEClientTransport(url); + } else { + transport = new StreamableHTTPClientTransport(url); + } + + await conn.client.connect(transport); + + const { tools: mcpTools } = await conn.client.listTools(); + const serverTools = mcpTools.map(mcpToolToOpenAIFunction); + conn.tools.push(...serverTools); + debugLog(`Connected to ${server.name} with ${mcpTools.length} tools`); + connections.push(conn); + } catch (error) { + debugError(`Failed to connect to MCP server ${server.name}:`, error); + } + }), + ); + + return connections; +}; + +export const executeMcpTool = async ( + connections: MCPServerConnection[], + toolCall: { id: string; function: { name: string; arguments: string } }, +) => { + try { + debugLog(`Executing tool: ${toolCall.function.name}`); + debugLog(`Tool arguments:`, JSON.parse(toolCall.function.arguments)); + + // Try to find the tool in any of the connected clients + let result = null; + for (const conn of connections) { + try { + const toolExists = conn.tools.some(tool => tool.function?.name === toolCall.function.name); + if (!toolExists) continue; + debugLog(`Found tool ${toolCall.function.name}`); + result = await conn.client.callTool({ + name: toolCall.function.name, + arguments: JSON.parse(toolCall.function.arguments), + }); + } catch (clientError) { + debugError(`Failed to execute tool on client:`, clientError); + continue; + } + } + + if (!result) { + throw new Error(`Tool ${toolCall.function.name} not found in any connected MCP server`); + } + + // mcpLog(`Tool result:`, result.content); + + return { + tool_call_id: toolCall.id, + role: "tool" as const, + content: JSON.stringify(result.content), + }; + } catch (error) { + debugError(`Tool execution failed:`, error); + + return { + tool_call_id: toolCall.id, + role: "tool" as const, + content: JSON.stringify({ error: error instanceof Error ? error.message : "Tool execution failed" }), + }; + } +}; diff --git a/src/routes/api/generate/types.ts b/src/routes/api/generate/types.ts new file mode 100644 index 0000000..7a77ab9 --- /dev/null +++ b/src/routes/api/generate/types.ts @@ -0,0 +1,53 @@ +import type { ChatCompletionInputMessage } from "@huggingface/tasks"; +import type { ChatCompletionChunk } from "openai/resources/index.mjs"; + +export interface MCPServerConfig { + id: string; + name: string; + url: string; + protocol: "sse" | "http"; + headers?: Record; +} + +export interface GenerateRequest { + model: { + id: string; + isCustom?: boolean; + accessToken?: string; + endpointUrl?: string; + }; + messages: ChatCompletionInputMessage[]; + config: Record; + provider?: string; + streaming?: boolean; + response_format?: unknown; + accessToken: string; + enabledMCPs?: MCPServerConfig[]; +} + +export interface OpenAIFunctionSchema { + type?: string; + function?: { + name?: string; + description?: string; + parameters?: { + type?: string; + required?: string[]; + additionalProperties?: boolean; + [key: string]: unknown; + }; + strict?: boolean; + }; +} + +export type McpToolSchema = { + name: string; + inputSchema: { + type: string; + required?: string[]; + additionalProperties?: boolean; + [key: string]: unknown; + }; +}; + +export type FinishReason = ChatCompletionChunk.Choice["finish_reason"]; diff --git a/src/routes/api/generate/utils.ts b/src/routes/api/generate/utils.ts new file mode 100644 index 0000000..0a9af00 --- /dev/null +++ b/src/routes/api/generate/utils.ts @@ -0,0 +1,11 @@ +const DEBUG_LOG = true; + +export const debugLog = (...args: unknown[]) => { + if (!DEBUG_LOG) return; + console.log("[LOG DEBUG]", ...args); +}; + +export const debugError = (...args: unknown[]) => { + if (!DEBUG_LOG) return; + console.error("[LOG DEBUG]", ...args); +};