From a96ba2152bc357e20c37f6f22ceb31c3defa0fb5 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lopes" <26071571+TGlide@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:16:12 +0100 Subject: [PATCH 01/10] issue templates, no code changes --- .github/ISSUE_TEMPLATE/bug_report.yml | 42 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 35 ++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..4b975ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,42 @@ +name: '🐛 Bug report' +description: Report an issue with thom.chat +labels: ['bug'] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: bug-description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. Feel free to include screenshots or video recordings here as well! If you intend to submit a PR for this issue, tell us how in the description. Thanks! + placeholder: Bug description + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs + description: 'Please include browser console and server logs around the time this bug occurred. Optional if provided reproduction. Please try not to insert an image but copy paste the log text.' + render: bash + - type: textarea + id: system-info + attributes: + label: System Info + description: Output of `npx envinfo --system --npmPackages svelte,@sveltejs/kit,@melt-ui/svelte --binaries --browsers` + render: bash + placeholder: System, Binaries, Browsers + validations: + required: true + - type: dropdown + id: severity + attributes: + label: Severity + description: Select the severity of this issue + options: + - annoyance + - blocking an upgrade + - blocking all usage of thom.chat + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..2d21f5d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,35 @@ +name: '💫 Feature Request' +description: Request a new feature +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to request this feature! + - type: textarea + id: problem + attributes: + label: Describe the problem + description: Please provide a clear and concise description the problem this feature would solve. The more information you can provide here, the better. + placeholder: I'm always frustrated when... + validations: + required: true + - type: textarea + id: solution + attributes: + label: Describe the proposed solution + description: Please provide a clear and concise description of what you would like to happen. + placeholder: I would like to see... + validations: + required: true + - type: dropdown + id: importance + attributes: + label: Importance + description: How important is this feature to you? + options: + - Nice to have + - Would make my life easier + - I cannot use thom.chat without it + validations: + required: true From 7b9595e5718f841cb5394e4d0e9608dc75deb7bc Mon Sep 17 00:00:00 2001 From: Aidan Bleser <117548273+ieedan@users.noreply.github.com> Date: Thu, 10 Jul 2025 06:45:02 -0500 Subject: [PATCH 02/10] Post Hackathon Stuff (#40) Co-authored-by: Thomas G. Lopes <26071571+TGlide@users.noreply.github.com> --- README.md | 1 - package.json | 6 +- pnpm-lock.yaml | 100 +++- src/app.css | 2 + src/lib/backend/convex/messages.ts | 12 +- src/lib/backend/convex/schema.ts | 11 +- src/lib/backend/convex/user_enabled_models.ts | 30 +- src/lib/backend/convex/user_keys.ts | 22 +- .../components/animations/shiny-text.svelte | 2 +- src/lib/components/model-picker/index.ts | 3 + .../model-picker/model-picker.svelte | 545 ++++++++++++++++++ .../dropdown-menu-checkbox-item.svelte | 41 ++ .../dropdown-menu-content.svelte | 27 + .../dropdown-menu-group-heading.svelte | 22 + .../dropdown-menu/dropdown-menu-group.svelte | 7 + .../dropdown-menu/dropdown-menu-item.svelte | 27 + .../dropdown-menu/dropdown-menu-label.svelte | 24 + .../dropdown-menu-radio-group.svelte | 16 + .../dropdown-menu-radio-item.svelte | 31 + .../dropdown-menu-separator.svelte | 17 + .../dropdown-menu-shortcut.svelte | 20 + .../dropdown-menu-sub-content.svelte | 20 + .../dropdown-menu-sub-trigger.svelte | 29 + .../dropdown-menu-trigger.svelte | 7 + src/lib/components/ui/dropdown-menu/index.ts | 49 ++ src/lib/components/ui/kbd/kbd.svelte | 1 + src/lib/components/ui/popover/index.ts | 16 + .../ui/popover/popover-content.svelte | 28 + .../ui/popover/popover-trigger.svelte | 16 + src/lib/spells/persisted-obj.svelte.ts | 21 +- src/lib/state/settings.svelte.ts | 1 + src/lib/types.ts | 20 + src/lib/utils/casing.ts | 333 +++++++++++ src/lib/utils/is-letter.ts | 25 + src/lib/utils/model-capabilities.ts | 4 + src/routes/account/+layout.svelte | 8 + src/routes/account/models/+page.svelte | 42 +- src/routes/account/models/model-card.svelte | 47 +- src/routes/api/generate-message/+server.ts | 22 +- src/routes/chat/+layout.svelte | 42 +- src/routes/chat/[id]/+page.svelte | 33 +- src/routes/chat/[id]/message.svelte | 123 +++- src/routes/chat/model-picker.svelte | 345 ----------- 43 files changed, 1798 insertions(+), 400 deletions(-) create mode 100644 src/lib/components/model-picker/index.ts create mode 100644 src/lib/components/model-picker/model-picker.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte create mode 100644 src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte create mode 100644 src/lib/components/ui/dropdown-menu/index.ts create mode 100644 src/lib/components/ui/popover/index.ts create mode 100644 src/lib/components/ui/popover/popover-content.svelte create mode 100644 src/lib/components/ui/popover/popover-trigger.svelte create mode 100644 src/lib/utils/casing.ts create mode 100644 src/lib/utils/is-letter.ts delete mode 100644 src/routes/chat/model-picker.svelte diff --git a/README.md b/README.md index 03222a7..48d8d73 100644 --- a/README.md +++ b/README.md @@ -172,4 +172,3 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file 💡 Request Feature

- diff --git a/package.json b/package.json index 12a176b..7c9e83e 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.4", "@vercel/functions": "^2.2.0", + "bits-ui": "^2.8.5", "clsx": "^2.1.1", "concurrently": "^9.1.2", "convex": "^1.24.8", @@ -44,9 +45,10 @@ "globals": "^16.0.0", "isomorphic-dompurify": "^2.25.0", "jsdom": "^26.0.0", - "melt": "https://pkg.vc/-/@melt-ui/melt@42e572f", + "melt": "^0.38.0", "mode-watcher": "^1.0.8", "neverthrow": "^8.2.0", + "openai": "^5.5.1", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", @@ -58,6 +60,7 @@ "tailwind-merge": "^3.3.1", "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.0", + "tw-animate-css": "^1.3.4", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", "unplugin-icons": "^22.1.0", @@ -84,7 +87,6 @@ "convex-helpers": "^0.1.94", "hastscript": "^9.0.1", "markdown-it-async": "^2.2.0", - "openai": "^5.3.0", "zod": "^3.25.64" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e00d949..fe3f148 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,9 +47,6 @@ importers: markdown-it-async: specifier: ^2.2.0 version: 2.2.0 - openai: - specifier: ^5.3.0 - version: 5.3.0(ws@8.18.2)(zod@3.25.64) zod: specifier: ^3.25.64 version: 3.25.64 @@ -99,6 +96,9 @@ importers: '@vercel/functions': specifier: ^2.2.0 version: 2.2.0 + bits-ui: + specifier: ^2.8.5 + version: 2.8.5(@internationalized/date@3.8.2)(svelte@5.34.1) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -133,14 +133,17 @@ importers: specifier: ^26.0.0 version: 26.1.0 melt: - specifier: https://pkg.vc/-/@melt-ui/melt@42e572f - version: https://pkg.vc/-/@melt-ui/melt@42e572f(@floating-ui/dom@1.7.1)(svelte@5.34.1) + specifier: ^0.38.0 + version: 0.38.0(@floating-ui/dom@1.7.1)(svelte@5.34.1) mode-watcher: specifier: ^1.0.8 version: 1.0.8(svelte@5.34.1) neverthrow: specifier: ^8.2.0 version: 8.2.0 + openai: + specifier: ^5.5.1 + version: 5.5.1(ws@8.18.2)(zod@3.25.64) prettier: specifier: ^3.4.2 version: 3.5.3 @@ -174,6 +177,9 @@ importers: tailwindcss: specifier: ^4.0.0 version: 4.1.10 + tw-animate-css: + specifier: ^1.3.4 + version: 1.3.4 typescript: specifier: ^5.0.0 version: 5.8.3 @@ -677,6 +683,9 @@ packages: '@iconify/utils@2.3.0': resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + '@internationalized/date@3.8.2': + resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==} + '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -921,6 +930,9 @@ packages: svelte: ^5.0.0 vite: ^6.0.0 + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + '@tailwindcss/node@4.1.10': resolution: {integrity: sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==} @@ -1241,6 +1253,13 @@ packages: better-call@1.0.9: resolution: {integrity: sha512-Qfm0gjk0XQz0oI7qvTK1hbqTsBY4xV2hsHAxF8LZfUYl3RaECCIifXuVqtPpZJWvlCCMlQSvkvhhyuApGUba6g==} + bits-ui@2.8.5: + resolution: {integrity: sha512-GVVDcmc+mziNNWdzlBviN3HjFAIdEFddQFvTA5cjronMan8PnIhpNhc2+DKL5CYdTbrz6kuyt2YvuvnoWYmovw==} + engines: {node: '>=20'} + peerDependencies: + '@internationalized/date': ^3.8.1 + svelte: ^5.33.0 + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -1602,6 +1621,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + focus-trap@7.6.5: + resolution: {integrity: sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1919,9 +1941,8 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - melt@https://pkg.vc/-/@melt-ui/melt@42e572f: - resolution: {tarball: https://pkg.vc/-/@melt-ui/melt@42e572f} - version: 0.35.0 + melt@0.38.0: + resolution: {integrity: sha512-fVUZdZSYUeYw4uwZkY+KoxmbVVMY14qEA21wjIR0ELAMlWc/eSeG0JcZn0wSB7dB1JdWo7KsVrZssieiQWSp5A==} peerDependencies: '@floating-ui/dom': ^1.6.0 svelte: ^5.30.1 @@ -1997,11 +2018,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} - engines: {node: ^18 || >=20} - hasBin: true - nanostores@0.11.4: resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2022,8 +2038,8 @@ packages: oniguruma-to-es@4.3.3: resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} - openai@5.3.0: - resolution: {integrity: sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w==} + openai@5.5.1: + resolution: {integrity: sha512-5i19097mGotHA1eFsM6Tjd/tJ8uo9sa5Ysv4Q6bKJ2vtN6rc0MzMrUefXnLXYAJcmMQrC1Efhj0AvfIkXrQamw==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -2420,6 +2436,12 @@ packages: peerDependencies: svelte: ^5.0.0 + svelte-toolbelt@0.9.2: + resolution: {integrity: sha512-REb1cENGnFbhNSmIdCb1SDIpjEa3n1kXhNVHqGNEesjmPX3bG87gUZiCG8cqOt9AAarqzTzOtI2jEEWr/ZbHwA==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.30.2 + svelte@5.34.1: resolution: {integrity: sha512-jWNnN2hZFNtnzKPptCcJHBWrD9CtbHPDwIRIODufOYaWkR0kLmAIlM384lMt4ucwuIRX4hCJwD2D8ZtEcGJQ0Q==} engines: {node: '>=18'} @@ -2427,6 +2449,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@3.0.2: resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} @@ -2514,6 +2539,9 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tw-animate-css@1.3.4: + resolution: {integrity: sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -3102,6 +3130,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@internationalized/date@3.8.2': + dependencies: + '@swc/helpers': 0.5.17 + '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.2 @@ -3347,6 +3379,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + '@tailwindcss/node@4.1.10': dependencies: '@ampproject/remapping': 2.3.0 @@ -3695,6 +3731,17 @@ snapshots: set-cookie-parser: 2.7.1 uncrypto: 0.1.3 + bits-ui@2.8.5(@internationalized/date@3.8.2)(svelte@5.34.1): + dependencies: + '@floating-ui/core': 1.7.1 + '@floating-ui/dom': 1.7.1 + '@internationalized/date': 3.8.2 + esm-env: 1.2.2 + runed: 0.28.0(svelte@5.34.1) + svelte: 5.34.1 + svelte-toolbelt: 0.9.2(svelte@5.34.1) + tabbable: 6.2.0 + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -4072,6 +4119,10 @@ snapshots: flatted@3.3.3: {} + focus-trap@7.6.5: + dependencies: + tabbable: 6.2.0 + fsevents@2.3.2: optional: true @@ -4389,12 +4440,12 @@ snapshots: mdurl@2.0.0: {} - melt@https://pkg.vc/-/@melt-ui/melt@42e572f(@floating-ui/dom@1.7.1)(svelte@5.34.1): + melt@0.38.0(@floating-ui/dom@1.7.1)(svelte@5.34.1): dependencies: '@floating-ui/dom': 1.7.1 dequal: 2.0.3 + focus-trap: 7.6.5 jest-axe: 9.0.0 - nanoid: 5.1.5 runed: 0.23.4(svelte@5.34.1) svelte: 5.34.1 @@ -4461,8 +4512,6 @@ snapshots: nanoid@3.3.11: {} - nanoid@5.1.5: {} - nanostores@0.11.4: {} natural-compare@1.4.0: {} @@ -4481,7 +4530,7 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 - openai@5.3.0(ws@8.18.2)(zod@3.25.64): + openai@5.5.1(ws@8.18.2)(zod@3.25.64): optionalDependencies: ws: 8.18.2 zod: 3.25.64 @@ -4821,6 +4870,13 @@ snapshots: style-to-object: 1.0.9 svelte: 5.34.1 + svelte-toolbelt@0.9.2(svelte@5.34.1): + dependencies: + clsx: 2.1.1 + runed: 0.28.0(svelte@5.34.1) + style-to-object: 1.0.9 + svelte: 5.34.1 + svelte@5.34.1: dependencies: '@ampproject/remapping': 2.3.0 @@ -4840,6 +4896,8 @@ snapshots: symbol-tree@3.2.4: {} + tabbable@6.2.0: {} + tailwind-merge@3.0.2: {} tailwind-merge@3.3.1: {} @@ -4909,6 +4967,8 @@ snapshots: tslib@2.8.1: {} + tw-animate-css@1.3.4: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/src/app.css b/src/app.css index 5f5f1ca..4e47f90 100644 --- a/src/app.css +++ b/src/app.css @@ -4,6 +4,8 @@ @import '@fontsource-variable/nunito-sans'; @import '@fontsource/instrument-serif'; +@import 'tw-animate-css'; + @custom-variant dark (&:is(.dark *)); :root { diff --git a/src/lib/backend/convex/messages.ts b/src/lib/backend/convex/messages.ts index f6dc7a4..adcfc3c 100644 --- a/src/lib/backend/convex/messages.ts +++ b/src/lib/backend/convex/messages.ts @@ -2,7 +2,7 @@ import { v } from 'convex/values'; import { api } from './_generated/api'; import { type Id } from './_generated/dataModel'; import { query } from './_generated/server'; -import { messageRoleValidator, providerValidator } from './schema'; +import { messageRoleValidator, providerValidator, reasoningEffortValidator } from './schema'; import { mutation } from './functions'; export const getAllFromConversation = query({ @@ -47,6 +47,7 @@ export const create = mutation({ provider: v.optional(providerValidator), token_count: v.optional(v.number()), web_search_enabled: v.optional(v.boolean()), + reasoning_effort: v.optional(reasoningEffortValidator), // Optional image attachments images: v.optional( v.array( @@ -94,6 +95,7 @@ export const create = mutation({ provider: args.provider, token_count: args.token_count, web_search_enabled: args.web_search_enabled, + reasoning_effort: args.reasoning_effort, // Optional image attachments images: args.images, }), @@ -112,7 +114,11 @@ export const updateContent = mutation({ session_token: v.string(), message_id: v.string(), content: v.string(), + reasoning: v.optional(v.string()), content_html: v.optional(v.string()), + generation_id: v.optional(v.string()), + reasoning_effort: v.optional(reasoningEffortValidator), + annotations: v.optional(v.array(v.record(v.string(), v.any()))), }, handler: async (ctx, args) => { const session = await ctx.runQuery(api.betterAuth.publicGetSession, { @@ -131,7 +137,11 @@ export const updateContent = mutation({ await ctx.db.patch(message._id, { content: args.content, + reasoning: args.reasoning, content_html: args.content_html, + generation_id: args.generation_id, + annotations: args.annotations, + reasoning_effort: args.reasoning_effort, }); }, }); diff --git a/src/lib/backend/convex/schema.ts b/src/lib/backend/convex/schema.ts index 97aef42..8db117b 100644 --- a/src/lib/backend/convex/schema.ts +++ b/src/lib/backend/convex/schema.ts @@ -8,6 +8,11 @@ export const messageRoleValidator = v.union( v.literal('assistant'), v.literal('system') ); +export const reasoningEffortValidator = v.union( + v.literal('low'), + v.literal('medium'), + v.literal('high') +); export type MessageRole = Infer; @@ -31,7 +36,8 @@ export default defineSchema({ provider: providerValidator, /** Different providers may use different ids for the same model */ model_id: v.string(), - pinned: v.union(v.number(), v.null()), + // null is just here for compat we treat null as true + pinned: v.optional(v.union(v.boolean(), v.null())), }) .index('by_user', ['user_id']) .index('by_model_provider', ['model_id', 'provider']) @@ -61,6 +67,7 @@ export default defineSchema({ role: v.union(v.literal('user'), v.literal('assistant'), v.literal('system')), content: v.string(), content_html: v.optional(v.string()), + reasoning: v.optional(v.string()), error: v.optional(v.string()), // Optional, coming from SK API route model_id: v.optional(v.string()), @@ -79,5 +86,7 @@ export default defineSchema({ cost_usd: v.optional(v.number()), generation_id: v.optional(v.string()), web_search_enabled: v.optional(v.boolean()), + reasoning_effort: v.optional(reasoningEffortValidator), + annotations: v.optional(v.array(v.record(v.string(), v.any()))), }).index('by_conversation', ['conversation_id']), }); diff --git a/src/lib/backend/convex/user_enabled_models.ts b/src/lib/backend/convex/user_enabled_models.ts index bdb784e..173f026 100644 --- a/src/lib/backend/convex/user_enabled_models.ts +++ b/src/lib/backend/convex/user_enabled_models.ts @@ -109,12 +109,38 @@ export const set = mutation({ await ctx.db.insert('user_enabled_models', { ...object.pick(args, ['provider', 'model_id']), user_id: session.userId, - pinned: null, + pinned: false, }); } }, }); +export const toggle_pinned = mutation({ + args: { + session_token: v.string(), + enabled_model_id: v.id('user_enabled_models'), + }, + handler: async (ctx, args) => { + const session = await ctx.runQuery(internal.betterAuth.getSession, { + sessionToken: args.session_token, + }); + + if (!session) throw new Error('Invalid session token'); + + const model = await ctx.db.get(args.enabled_model_id); + + if (!model) throw new Error('Model not found'); + + await ctx.db.patch(args.enabled_model_id, { + pinned: !isPinned(model), + }); + }, +}); + +export function isPinned(model: Doc<'user_enabled_models'>) { + return model.pinned === null || model.pinned; +} + export const enable_initial = mutation({ args: { session_token: v.string(), @@ -150,7 +176,7 @@ export const enable_initial = mutation({ user_id: session.userId, provider: Provider.OpenRouter, model_id: model, - pinned: null, + pinned: true, }) ) ); diff --git a/src/lib/backend/convex/user_keys.ts b/src/lib/backend/convex/user_keys.ts index a91a1c9..9848b07 100644 --- a/src/lib/backend/convex/user_keys.ts +++ b/src/lib/backend/convex/user_keys.ts @@ -99,14 +99,26 @@ export const set = mutation({ ]; await Promise.all( - defaultModels.map((model) => - ctx.db.insert('user_enabled_models', { + defaultModels.map(async (model) => { + const existing = await ctx.db + .query('user_enabled_models') + .withIndex('by_model_provider_user', (q) => + q + .eq('model_id', model) + .eq('provider', Provider.OpenRouter) + .eq('user_id', session.userId) + ) + .first(); + + if (existing) return; + + await ctx.db.insert('user_enabled_models', { user_id: session.userId, provider: Provider.OpenRouter, model_id: model, - pinned: null, - }) - ) + pinned: true, + }); + }) ); } } diff --git a/src/lib/components/animations/shiny-text.svelte b/src/lib/components/animations/shiny-text.svelte index 5dcd114..cb0a75f 100644 --- a/src/lib/components/animations/shiny-text.svelte +++ b/src/lib/components/animations/shiny-text.svelte @@ -16,7 +16,7 @@

+ import { api } from '$lib/backend/convex/_generated/api'; + import { useCachedQuery } from '$lib/cache/cached-query.svelte'; + import Cohere from '$lib/components/icons/cohere.svelte'; + import Deepseek from '$lib/components/icons/deepseek.svelte'; + import Tooltip from '$lib/components/ui/tooltip.svelte'; + import { IsMobile } from '$lib/hooks/is-mobile.svelte'; + import { models as modelsState } from '$lib/state/models.svelte'; + import { session } from '$lib/state/session.svelte'; + import { settings } from '$lib/state/settings.svelte'; + import { Provider } from '$lib/types'; + import { fuzzysearch } from '$lib/utils/fuzzy-search'; + import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import { capitalize } from '$lib/utils/strings'; + import { cn } from '$lib/utils/utils'; + import { type Component } from 'svelte'; + import LogosClaudeIcon from '~icons/logos/claude-icon'; + import LogosMistralAiIcon from '~icons/logos/mistral-ai-icon'; + import BrainIcon from '~icons/lucide/brain'; + import ChevronDownIcon from '~icons/lucide/chevron-down'; + import CpuIcon from '~icons/lucide/cpu'; + import EyeIcon from '~icons/lucide/eye'; + import SearchIcon from '~icons/lucide/search'; + import ZapIcon from '~icons/lucide/zap'; + import MaterialIconThemeGeminiAi from '~icons/material-icon-theme/gemini-ai'; + import GoogleIcon from '~icons/simple-icons/google'; + import MetaIcon from '~icons/simple-icons/meta'; + import MicrosoftIcon from '~icons/simple-icons/microsoft'; + import OpenaiIcon from '~icons/simple-icons/openai'; + import XIcon from '~icons/simple-icons/x'; + import { Command } from 'bits-ui'; + import * as Popover from '$lib/components/ui/popover'; + import { shortcut } from '$lib/actions/shortcut.svelte'; + import { Button } from '../ui/button'; + import ChevronLeftIcon from '~icons/lucide/chevron-left'; + import { Kbd } from '../ui/kbd'; + import { cmdOrCtrl } from '$lib/hooks/is-mac.svelte'; + import { useConvexClient } from 'convex-svelte'; + import type { Id } from '$lib/backend/convex/_generated/dataModel'; + import { ResultAsync } from 'neverthrow'; + import PinIcon from '~icons/lucide/pin'; + import PinOffIcon from '~icons/lucide/pin-off'; + import { isPinned } from '$lib/backend/convex/user_enabled_models'; + + type Props = { + class?: string; + /* When images are attached, we should not select models that don't support images */ + onlyImageModels?: boolean; + }; + + let { class: className, onlyImageModels }: Props = $props(); + + const client = useConvexClient(); + + const enabledModelsQuery = useCachedQuery(api.user_enabled_models.get_enabled, { + session_token: session.current?.session.token ?? '', + }); + + const enabledArr = $derived(Object.values(enabledModelsQuery.data ?? {})); + + modelsState.init(); + + // Company icon mapping + const companyIcons: Record = { + openai: OpenaiIcon, + anthropic: LogosClaudeIcon, + google: GoogleIcon, + meta: MetaIcon, + mistral: ZapIcon, + 'x-ai': XIcon, + microsoft: MicrosoftIcon, + qwen: CpuIcon, + deepseek: Deepseek, + cohere: Cohere, + }; + + function getModelIcon(modelId: string): Component | null { + const id = modelId.toLowerCase(); + + // Model-specific icons take priority + if (id.includes('claude') || id.includes('anthropic')) return LogosClaudeIcon; + if (id.includes('gemini') || id.includes('gemma')) return MaterialIconThemeGeminiAi; + if (id.includes('mistral') || id.includes('mixtral')) return LogosMistralAiIcon; + + // Fallback to company icons + const company = getCompanyFromModelId(modelId); + return companyIcons[company] || null; + } + + function getCompanyFromModelId(modelId: string): string { + const id = modelId.toLowerCase(); + + if (id.includes('gpt') || id.includes('o1') || id.includes('openai')) return 'openai'; + + if (id.includes('claude') || id.includes('anthropic')) return 'anthropic'; + + if ( + id.includes('gemini') || + id.includes('gemma') || + id.includes('google') || + id.includes('palm') + ) + return 'google'; + + if (id.includes('llama') || id.includes('meta')) return 'meta'; + + if (id.includes('mistral') || id.includes('mixtral')) return 'mistral'; + + if (id.includes('grok') || id.includes('x-ai')) return 'x-ai'; + + if (id.includes('phi') || id.includes('microsoft')) return 'microsoft'; + + if (id.includes('qwen') || id.includes('alibaba')) return 'qwen'; + + if (id.includes('deepseek')) return 'deepseek'; + + if (id.includes('command') || id.includes('cohere')) return 'cohere'; + + // Try to extract from model path (e.g., "anthropic/claude-3") + const pathParts = modelId.split('/'); + if (pathParts.length > 1) { + const provider = pathParts[0]?.toLowerCase(); + if (provider && companyIcons[provider]) return provider; + } + + return 'other'; + } + + let search = $state(''); + + const filteredModels = $derived( + fuzzysearch({ + haystack: enabledArr, + needle: search, + property: 'model_id', + }) + ); + + // Group models by company + const groupedModels = $derived.by(() => { + const groups: Record = {}; + + filteredModels.forEach((model) => { + const company = getCompanyFromModelId(model.model_id); + if (!groups[company]) { + groups[company] = []; + } + groups[company].push(model); + }); + + // Sort companies with known icons first + const result = Object.entries(groups).sort(([a], [b]) => { + const aHasIcon = companyIcons[a] ? 0 : 1; + const bHasIcon = companyIcons[b] ? 0 : 1; + return aHasIcon - bHasIcon || a.localeCompare(b); + }); + + return result; + }); + + const currentModel = $derived(enabledArr.find((m) => m.model_id === settings.modelId)); + + $effect(() => { + if (!enabledArr.find((m) => m.model_id === settings.modelId) && enabledArr.length > 0) { + settings.modelId = enabledArr[0]!.model_id; + } + }); + + let open = $state(false); + let view = $state<'favorites' | 'enabled'>('favorites'); + let activeModel = $state(''); + + // Model name formatting utility + const termReplacements = [ + { from: 'gpt', to: 'GPT' }, + { from: 'claude', to: 'Claude' }, + { from: 'deepseek', to: 'DeepSeek' }, + { from: 'o3', to: 'o3' }, + ]; + + function formatModelName(modelId: string) { + const cleanId = modelId.replace(/^[^/]+\//, ''); + const parts = cleanId.split(/[-_,:]/); + + const formattedParts = parts.map((part) => { + let formatted = capitalize(part); + termReplacements.forEach(({ from, to }) => { + formatted = formatted.replace(new RegExp(`\\b${from}\\b`, 'gi'), to); + }); + return formatted; + }); + + return { + full: formattedParts.join(' '), + primary: formattedParts[0] || '', + secondary: formattedParts.slice(1).join(' '), + }; + } + + function modelSelected(modelId: string) { + settings.modelId = modelId; + open = false; + } + + function toggleView() { + view = view === 'favorites' ? 'enabled' : 'favorites'; + } + + let pinning = $state(false); + + async function togglePin(modelId: Id<'user_enabled_models'>) { + pinning = true; + + await ResultAsync.fromPromise( + client.mutation(api.user_enabled_models.toggle_pinned, { + session_token: session.current?.session.token ?? '', + enabled_model_id: modelId, + }), + (e) => e + ); + + pinning = false; + } + + const isMobile = new IsMobile(); + + const activeModelInfo = $derived.by(() => { + if (activeModel === '') return null; + + const model = enabledArr.find((m) => m.model_id === activeModel); + + if (!model) return null; + + return { + ...model, + formatted: formatModelName(activeModel), + }; + }); + + const pinnedModels = $derived(enabledArr.filter((m) => isPinned(m))); + + + (open = true), + }} +/> + + + {#if enabledArr.length} + +

+ {#if currentModel && getModelIcon(currentModel.model_id)} + {@const IconComponent = getModelIcon(currentModel.model_id)} + + {/if} + + {currentModel ? formatModelName(currentModel.model_id).full : 'Select model'} + +
+ + + + + + + + {#if view === 'favorites' && pinnedModels.length > 0} + {#each pinnedModels as model (model._id)} + {@const formatted = formatModelName(model.model_id)} + {@const openRouterModel = modelsState + .from(Provider.OpenRouter) + .find((m) => m.id === model.model_id)} + {@const disabled = + onlyImageModels && openRouterModel && !supportsImages(openRouterModel)} + + modelSelected(model.model_id)} + > +
+ {#if getModelIcon(model.model_id)} + {@const ModelIcon = getModelIcon(model.model_id)} + + {/if} + +

+ {formatted.full} +

+
+ +
+ {#if openRouterModel && supportsImages(openRouterModel)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports image analysis +
+ {/if} + + {#if openRouterModel && supportsReasoning(openRouterModel)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports reasoning +
+ {/if} +
+
+ {/each} + {:else if view === 'enabled'} + {#if pinnedModels.length > 0} + + + Pinned + + + {#each pinnedModels as model (model._id)} + {@render modelCard(model)} + {/each} + + + {/if} + {#each groupedModels as [company, models] (company)} + {@const filteredModels = models.filter((m) => !isPinned(m))} + {#if filteredModels.length > 0} + + + {company} + + + {#each filteredModels as model (model._id)} + {@render modelCard(model)} + {/each} + + + {/if} + {/each} + {/if} +
+
+
+ + {#if !isMobile.current && activeModelInfo && view === 'enabled'} +
+ +
+ {/if} +
+
+ {/if} + + +{#snippet modelCard(model: (typeof enabledArr)[number])} + {@const formatted = formatModelName(model.model_id)} + {@const openRouterModel = modelsState + .from(Provider.OpenRouter) + .find((m) => m.id === model.model_id)} + {@const disabled = onlyImageModels && openRouterModel && !supportsImages(openRouterModel)} + + modelSelected(model.model_id)} + > +
+ {#if getModelIcon(model.model_id)} + {@const ModelIcon = getModelIcon(model.model_id)} + + {/if} + +

+ {isMobile.current ? formatted.full : formatted.primary} +

+ + +
+ +
+ {#if openRouterModel && supportsImages(openRouterModel)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports image analysis +
+ {/if} + + {#if openRouterModel && supportsReasoning(openRouterModel)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports reasoning +
+ {/if} +
+ +
+ +
+
+{/snippet} diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..9ed6956 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,41 @@ + + + + {#snippet children({ checked, indeterminate })} + + {#if indeterminate} + + {:else} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..8990bfe --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,27 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..ccc483c --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..261ab7e --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..ba5c946 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..05c91fb --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..3e98749 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..bd2c448 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..a8ac630 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..7ce000b --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..69b490f --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..0bef89f --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..032b645 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/index.ts b/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..e7f0a79 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,49 @@ +import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; +import CheckboxItem from './dropdown-menu-checkbox-item.svelte'; +import Content from './dropdown-menu-content.svelte'; +import Group from './dropdown-menu-group.svelte'; +import Item from './dropdown-menu-item.svelte'; +import Label from './dropdown-menu-label.svelte'; +import RadioGroup from './dropdown-menu-radio-group.svelte'; +import RadioItem from './dropdown-menu-radio-item.svelte'; +import Separator from './dropdown-menu-separator.svelte'; +import Shortcut from './dropdown-menu-shortcut.svelte'; +import Trigger from './dropdown-menu-trigger.svelte'; +import SubContent from './dropdown-menu-sub-content.svelte'; +import SubTrigger from './dropdown-menu-sub-trigger.svelte'; +import GroupHeading from './dropdown-menu-group-heading.svelte'; +const Sub = DropdownMenuPrimitive.Sub; +const Root = DropdownMenuPrimitive.Root; + +export { + CheckboxItem, + Content, + Root as DropdownMenu, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger, +}; diff --git a/src/lib/components/ui/kbd/kbd.svelte b/src/lib/components/ui/kbd/kbd.svelte index 14fa701..64e193b 100644 --- a/src/lib/components/ui/kbd/kbd.svelte +++ b/src/lib/components/ui/kbd/kbd.svelte @@ -14,6 +14,7 @@ primary: 'bg-primary text-primary-foreground', }, size: { + xs: 'min-w-5 gap-1.5 p-0.5 px-0.5 text-xs', sm: 'min-w-6 gap-1.5 p-0.5 px-1 text-sm', default: 'min-w-8 gap-1.5 p-1 px-2', lg: 'min-w-9 gap-2 p-1 px-3 text-lg', diff --git a/src/lib/components/ui/popover/index.ts b/src/lib/components/ui/popover/index.ts new file mode 100644 index 0000000..c95cf83 --- /dev/null +++ b/src/lib/components/ui/popover/index.ts @@ -0,0 +1,16 @@ +import { Popover as PopoverPrimitive } from 'bits-ui'; +import Content from './popover-content.svelte'; +import Trigger from './popover-trigger.svelte'; +const Root = PopoverPrimitive.Root; +const Close = PopoverPrimitive.Close; +export { + Root, + Content, + Trigger, + Close, + // + Root as Popover, + Content as PopoverContent, + Trigger as PopoverTrigger, + Close as PopoverClose, +}; diff --git a/src/lib/components/ui/popover/popover-content.svelte b/src/lib/components/ui/popover/popover-content.svelte new file mode 100644 index 0000000..c47788b --- /dev/null +++ b/src/lib/components/ui/popover/popover-content.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/src/lib/components/ui/popover/popover-trigger.svelte b/src/lib/components/ui/popover/popover-trigger.svelte new file mode 100644 index 0000000..4700cb6 --- /dev/null +++ b/src/lib/components/ui/popover/popover-trigger.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/spells/persisted-obj.svelte.ts b/src/lib/spells/persisted-obj.svelte.ts index 510ef76..232faa0 100644 --- a/src/lib/spells/persisted-obj.svelte.ts +++ b/src/lib/spells/persisted-obj.svelte.ts @@ -26,7 +26,7 @@ type PersistedObjOptions = { syncTabs?: boolean; }; -export function createPersistedObj( +export function createPersistedObj>( key: string, initialValue: T, options: PersistedObjOptions = {} @@ -37,7 +37,7 @@ export function createPersistedObj( syncTabs = true, } = options; - let current = initialValue; + let current: Record = initialValue; let storage: Storage | undefined; let subscribe: VoidFunction | undefined; let version = $state(0); @@ -47,7 +47,18 @@ export function createPersistedObj( const existingValue = storage.getItem(key); if (existingValue !== null) { const deserialized = deserialize(existingValue); - if (deserialized) current = deserialized; + + if (deserialized) { + // handle keys that were added at a later point in time + for (const key of Object.keys(initialValue)) { + const initialKeyValue = deserialized[key]; + if (initialKeyValue === undefined) { + deserialized[key] = initialValue[key]; + } + } + + current = deserialized; + } } else { serialize(initialValue); } @@ -66,7 +77,7 @@ export function createPersistedObj( version += 1; } - function deserialize(value: string): T | undefined { + function deserialize(value: string): Record | undefined { try { return serializer.deserialize(value); } catch (error) { @@ -75,7 +86,7 @@ export function createPersistedObj( } } - function serialize(value: T | undefined): void { + function serialize(value: Record | undefined): void { try { if (value != undefined) { storage?.setItem(key, serializer.serialize(value)); diff --git a/src/lib/state/settings.svelte.ts b/src/lib/state/settings.svelte.ts index 5e3d33e..0f286b5 100644 --- a/src/lib/state/settings.svelte.ts +++ b/src/lib/state/settings.svelte.ts @@ -3,4 +3,5 @@ import { createPersistedObj } from '$lib/spells/persisted-obj.svelte'; export const settings = createPersistedObj('settings', { modelId: undefined as string | undefined, webSearchEnabled: false, + reasoningEffort: 'low' as 'low' | 'medium' | 'high', }); diff --git a/src/lib/types.ts b/src/lib/types.ts index e4dde8f..05ab240 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,3 +1,5 @@ +import { z } from 'zod'; + export const Provider = { OpenRouter: 'openrouter', HuggingFace: 'huggingface', @@ -14,3 +16,21 @@ export type ProviderMeta = { models?: string[]; placeholder?: string; }; + +export const UrlCitationSchema = z.object({ + type: z.literal('url_citation'), + url_citation: z.object({ + end_index: z.number(), + start_index: z.number(), + title: z.string(), + url: z.string(), + content: z.string(), + }), +}); + +export type UrlCitation = z.infer; + +// if there are more types do this +// export const AnnotationSchema = z.union([UrlCitationSchema, ...]); +export const AnnotationSchema = UrlCitationSchema; +export type Annotation = z.infer; diff --git a/src/lib/utils/casing.ts b/src/lib/utils/casing.ts new file mode 100644 index 0000000..816d81c --- /dev/null +++ b/src/lib/utils/casing.ts @@ -0,0 +1,333 @@ +/* + Installed from @ieedan/std +*/ + +import { isLetter } from '$lib/utils/is-letter'; + +/** Converts a `camelCase` string to a `snake_case` string + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToSnake('helloWorld'); // hello_world + * ``` + */ +export function camelToSnake(str: string): string { + let newStr = ''; + + for (let i = 0; i < str.length; i++) { + // is uppercase letter + if (isLetter(str[i]) && str[i].toUpperCase() === str[i]) { + let l = i; + + while (l < str.length && isLetter(str[l]) && str[l].toUpperCase() === str[l]) { + l++; + } + + newStr += `${str.slice(i, l - 1).toLocaleLowerCase()}_${str[l - 1].toLocaleLowerCase()}`; + + i = l - 1; + + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `PascalCase` string to a `snake_case` string + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToSnake('HelloWorld'); // hello_world + * ``` + */ +export function pascalToSnake(str: string): string { + let newStr = ''; + + let firstLetter: number | undefined; + + for (let i = 0; i < str.length; i++) { + if (firstLetter === undefined && isLetter(str[i])) { + firstLetter = i; + } + + // is uppercase letter (ignoring the first) + if ( + firstLetter !== undefined && + i > firstLetter && + isLetter(str[i]) && + str[i].toUpperCase() === str[i] + ) { + let l = i; + + while (l < str.length && isLetter(str[l]) && str[l].toUpperCase() === str[l]) { + l++; + } + + newStr += `${str.slice(i, l - 1).toLocaleLowerCase()}_${str[l - 1].toLocaleLowerCase()}`; + + i = l - 1; + + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `camelCase` string to a `kebab-case` string + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToSnake('helloWorld'); // hello-world + * ``` + */ +export function camelToKebab(str: string): string { + let newStr = ''; + + for (let i = 0; i < str.length; i++) { + // is uppercase letter + if (i > 0 && isLetter(str[i]) && str[i].toUpperCase() === str[i]) { + let l = i; + + while (l < str.length && isLetter(str[l]) && str[l].toUpperCase() === str[l]) { + l++; + } + + newStr += `${str.slice(i, l - 1).toLocaleLowerCase()}-${str[l - 1].toLocaleLowerCase()}`; + + i = l - 1; + + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `PascalCase` string to a `kebab-case` string + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToSnake('HelloWorld'); // hello-world + * ``` + */ +export function pascalToKebab(str: string): string { + let newStr = ''; + + for (let i = 0; i < str.length; i++) { + // is uppercase letter (ignoring the first) + if (i > 0 && isLetter(str[i]) && str[i].toUpperCase() === str[i]) { + let l = i; + + while (l < str.length && isLetter(str[l]) && str[l].toUpperCase() === str[l]) { + l++; + } + + newStr += `${str.slice(i, l - 1).toLocaleLowerCase()}-${str[l - 1].toLocaleLowerCase()}`; + + i = l - 1; + + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `camelCase` string to a `PascalCase` string (makes first letter lowercase) + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToPascal('helloWorld'); // HelloWorld + * ``` + */ +export function camelToPascal(str: string): string { + return `${str[0].toLocaleUpperCase()}${str.slice(1)}`; +} + +/** Converts a `PascalCase` string to a `camelCase` string (makes first letter uppercase) + * + * @param str + * @returns + * + * ## Usage + * ```ts + * camelToPascal('HelloWorld'); // helloWorld + * ``` + */ +export function pascalToCamel(str: string): string { + return `${str[0].toLocaleLowerCase()}${str.slice(1)}`; +} + +/** Converts a `snake_case` string to a `PascalCase` string + * + * + * @param str + * @returns + * + * ## Usage + * ```ts + * snakeToPascal('hello_world'); // HelloWorld + * snakeToPascal('HELLO_WORLD'); // HelloWorld + * ``` + */ +export function snakeToPascal(str: string): string { + let newStr = ''; + + let firstLetter = true; + + for (let i = 0; i < str.length; i++) { + // capitalize first letter + if (firstLetter && isLetter(str[i])) { + firstLetter = false; + newStr += str[i].toUpperCase(); + continue; + } + + // capitalize first after a _ (ignoring the first) + if (!firstLetter && str[i] === '_') { + i++; + if (i <= str.length - 1) { + newStr += str[i].toUpperCase(); + } else { + newStr += '_'; + } + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `snake_case` string to a `camelCase` string + * + * + * @param str + * @returns + * + * ## Usage + * ```ts + * snakeToCamel('hello_world'); // helloWorld + * snakeToCamel('HELLO_WORLD'); // helloWorld + * ``` + */ +export function snakeToCamel(str: string): string { + let newStr = ''; + + let firstLetter = true; + + for (let i = 0; i < str.length; i++) { + // capitalize first letter + if (firstLetter && isLetter(str[i])) { + firstLetter = false; + newStr += str[i].toLowerCase(); + continue; + } + + // capitalize first after a _ (ignoring the first) + if (!firstLetter && str[i] === '_') { + i++; + if (i <= str.length - 1) { + newStr += str[i].toUpperCase(); + } else { + newStr += '_'; + } + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `kebab-case` string to a `PascalCase` string + * + * @param str + * @returns + * + * ## Usage + * ```ts + * kebabToPascal('hello-world'); // HelloWorld + * ``` + */ +export function kebabToPascal(str: string): string { + let newStr = ''; + + for (let i = 0; i < str.length; i++) { + // capitalize first + if (i === 0) { + newStr += str[i].toUpperCase(); + continue; + } + + // capitalize first after a - + if (str[i] === '-') { + i++; + if (i <= str.length - 1) { + newStr += str[i].toUpperCase(); + } + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} + +/** Converts a `kebab-case` string to a `camelCase` string + * + * + * @param str + * @returns + * + * ## Usage + * ```ts + * kebabToCamel('hello-world'); // helloWorld + * ``` + */ +export function kebabToCamel(str: string): string { + let newStr = ''; + + for (let i = 0; i < str.length; i++) { + // capitalize first after a - + if (str[i] === '-') { + i++; + if (i <= str.length - 1) { + newStr += str[i].toUpperCase(); + } + continue; + } + + newStr += str[i].toLocaleLowerCase(); + } + + return newStr; +} diff --git a/src/lib/utils/is-letter.ts b/src/lib/utils/is-letter.ts new file mode 100644 index 0000000..c84cf51 --- /dev/null +++ b/src/lib/utils/is-letter.ts @@ -0,0 +1,25 @@ +/* + Installed from @ieedan/std +*/ + +export const LETTER_REGEX = new RegExp(/[a-zA-Z]/); + +/** Checks if the provided character is a letter in the alphabet. + * + * @param char + * @returns + * + * ## Usage + * ```ts + * isLetter('a'); + * ``` + */ +export function isLetter(char: string): boolean { + if (char.length > 1) { + throw new Error( + `You probably only meant to pass a character to this function. Instead you gave ${char}` + ); + } + + return LETTER_REGEX.test(char); +} diff --git a/src/lib/utils/model-capabilities.ts b/src/lib/utils/model-capabilities.ts index c3f4252..9f39a8b 100644 --- a/src/lib/utils/model-capabilities.ts +++ b/src/lib/utils/model-capabilities.ts @@ -4,6 +4,10 @@ export function supportsImages(model: OpenRouterModel): boolean { return model.architecture.input_modalities.includes('image'); } +export function supportsReasoning(model: OpenRouterModel): boolean { + return model.supported_parameters.includes('reasoning'); +} + export function getImageSupportedModels(models: OpenRouterModel[]): OpenRouterModel[] { return models.filter(supportsImages); } diff --git a/src/routes/account/+layout.svelte b/src/routes/account/+layout.svelte index 2cb0b4e..b2bc56d 100644 --- a/src/routes/account/+layout.svelte +++ b/src/routes/account/+layout.svelte @@ -56,6 +56,14 @@ name: 'Search Messages', keys: [cmdOrCtrl, 'K'], }, + { + name: 'Scroll to bottom', + keys: [cmdOrCtrl, 'D'], + }, + { + name: 'Open Model Picker', + keys: [cmdOrCtrl, 'Shift', 'M'], + }, ]; async function signOut() { diff --git a/src/routes/account/models/+page.svelte b/src/routes/account/models/+page.svelte index 638f175..23dfd52 100644 --- a/src/routes/account/models/+page.svelte +++ b/src/routes/account/models/+page.svelte @@ -12,6 +12,7 @@ import PlusIcon from '~icons/lucide/plus'; import XIcon from '~icons/lucide/x'; import ModelCard from './model-card.svelte'; + import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; const openRouterKeyQuery = useCachedQuery(api.user_keys.get, { provider: Provider.OpenRouter, @@ -32,7 +33,14 @@ const freeModelsToggle = new Toggle({ value: false, - disabled: false, + }); + + const reasoningModelsToggle = new Toggle({ + value: false, + }); + + const imageModelsToggle = new Toggle({ + value: false, }); let initiallyEnabled = $state([]); @@ -48,11 +56,19 @@ const openRouterModels = $derived( fuzzysearch({ haystack: models.from(Provider.OpenRouter).filter((m) => { - if (!freeModelsToggle.value) return true; + if (freeModelsToggle.value) { + if (m.pricing.prompt !== '0') return false; + } - if (m.pricing.prompt === '0') return true; + if (reasoningModelsToggle.value) { + if (!supportsReasoning(m)) return false; + } - return false; + if (imageModelsToggle.value) { + if (!supportsImages(m)) return false; + } + + return true; }), needle: search, property: 'name', @@ -96,6 +112,24 @@ + + diff --git a/src/routes/account/models/model-card.svelte b/src/routes/account/models/model-card.svelte index a76d7f4..2ad59ef 100644 --- a/src/routes/account/models/model-card.svelte +++ b/src/routes/account/models/model-card.svelte @@ -7,6 +7,11 @@ import { session } from '$lib/state/session.svelte.js'; import { ResultAsync } from 'neverthrow'; import { getFirstSentence } from '$lib/utils/strings'; + import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import type { OpenRouterModel } from '$lib/backend/models/open-router'; + import Tooltip from '$lib/components/ui/tooltip.svelte'; + import EyeIcon from '~icons/lucide/eye'; + import BrainIcon from '~icons/lucide/brain'; type Model = { id: string; @@ -15,10 +20,11 @@ }; type Props = { - provider: Provider; - model: Model; enabled?: boolean; disabled?: boolean; + } & { + provider: typeof Provider.OpenRouter; + model: OpenRouterModel; }; let { provider, model, enabled = false, disabled = false }: Props = $props(); @@ -56,9 +62,9 @@ enabled, toggleEnabled} {disabled} /> - {showMore ? fullDescription : (shortDescription ?? fullDescription)} + + {showMore ? fullDescription : (shortDescription ?? fullDescription)} + {#if shortDescription !== null} + {#if showReasoning} +
+ {message.reasoning} +
+ {/if} + + {/if}
{#if message.error}
@@ -146,6 +194,58 @@ {/if}
+ {#if annotations} +
+ + {annotations.length} + {annotations.length === 1 ? 'Citation' : 'Citations'} + +
+ {#each annotations as annotation} + {#if annotation.type === 'url_citation'} + {@const url = new URL(annotation.url_citation.url)} + + {@render siteIcon({ url })} + + {/if} + {/each} +
+
+
+ {#each annotations as annotation} + {#if annotation.type === 'url_citation'} + {@const url = new URL(annotation.url_citation.url)} +
+
+ + + {annotation.url_citation.title} + +

+ {annotation.url_citation.content} +

+
+ + {@render siteIcon({ url })} + {url.hostname} + + + +
+ {/if} + {/each} +
+ {/if}
{message.model_id} {/if} + {#if message.reasoning_effort} + + + {casing.camelToPascal(message.reasoning_effort)} + + {/if} {#if message.web_search_enabled} - Web search enabled + + + {/if} {#if message.cost_usd !== undefined} @@ -209,3 +317,14 @@ /> {/if} {/if} + +{#snippet siteIcon({ url }: { url: URL })} + + {#snippet children(avatar)} + {`${url.hostname} + + + + {/snippet} + +{/snippet} diff --git a/src/routes/chat/model-picker.svelte b/src/routes/chat/model-picker.svelte deleted file mode 100644 index ef44d06..0000000 --- a/src/routes/chat/model-picker.svelte +++ /dev/null @@ -1,345 +0,0 @@ - - -{#if enabledArr.length} - - -
-
- -
- {#each groupedModels as [company, models] (company)} -
-

- {company} -

-
- {#each models as model (model._id)} - {@const isSelected = settings.modelId === model.model_id} - {@const formatted = formatModelName(model.model_id)} - {@const openRouterModel = modelsState - .from(Provider.OpenRouter) - .find((m) => m.id === model.model_id)} - {@const disabled = - onlyImageModels && openRouterModel && !supportsImages(openRouterModel)} - -
-
- {#if getModelIcon(model.model_id)} - {@const ModelIcon = getModelIcon(model.model_id)} - - {/if} - -

- {isMobile.current ? formatted.full : formatted.primary} -

- - {#if !isMobile.current} -

- {formatted.secondary} -

- {/if} -
- - {#if openRouterModel && supportsImages(openRouterModel)} - - {#snippet trigger(tooltip)} -
- -
- {/snippet} - Supports image anaylsis -
- {/if} -
- {/each} -
-
- {/each} -
-
-
-{/if} From a77493c9ef0a0fc9bc2f96c44285b123d24536ad Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Thu, 10 Jul 2025 07:08:24 -0500 Subject: [PATCH 03/10] fix ugly FF bug --- src/lib/components/model-picker/model-picker.svelte | 3 ++- src/lib/hooks/is-firefox.svelte.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 src/lib/hooks/is-firefox.svelte.ts diff --git a/src/lib/components/model-picker/model-picker.svelte b/src/lib/components/model-picker/model-picker.svelte index 2c799a4..2bc8471 100644 --- a/src/lib/components/model-picker/model-picker.svelte +++ b/src/lib/components/model-picker/model-picker.svelte @@ -41,6 +41,7 @@ import PinIcon from '~icons/lucide/pin'; import PinOffIcon from '~icons/lucide/pin-off'; import { isPinned } from '$lib/backend/convex/user_enabled_models'; + import { isFirefox } from '$lib/hooks/is-firefox.svelte'; type Props = { class?: string; @@ -271,7 +272,7 @@ Date: Mon, 11 Aug 2025 05:51:27 -0500 Subject: [PATCH 04/10] feat: go back to last chat on back to chat button (#47) --- .../model-picker/model-picker.svelte | 4 ++-- src/lib/state/last-chat.svelte.ts | 23 +++++++++++++++++++ src/routes/+layout.svelte | 8 +++++++ src/routes/account/+layout.svelte | 7 +++++- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/lib/state/last-chat.svelte.ts diff --git a/src/lib/components/model-picker/model-picker.svelte b/src/lib/components/model-picker/model-picker.svelte index 2bc8471..40c18cb 100644 --- a/src/lib/components/model-picker/model-picker.svelte +++ b/src/lib/components/model-picker/model-picker.svelte @@ -272,7 +272,7 @@
From 071e1016b145b54fbc68cf8a73104cb13afec763 Mon Sep 17 00:00:00 2001 From: Aunali321 Date: Sat, 30 Aug 2025 20:52:03 +0530 Subject: [PATCH 05/10] feat: Preliminary support for kepler-ai-sdk --- .env.example | 9 +- .gitignore | 3 +- README.md | 40 +- bun.lock | 1496 +++++++++++++++++ package.json | 5 + src/lib/backend/convex/schema.ts | 1 - src/lib/backend/convex/user_keys.ts | 32 - src/lib/backend/convex/user_settings.ts | 39 +- src/lib/services/model-manager.ts | 120 ++ src/lib/types.ts | 90 +- src/lib/utils/providers.ts | 226 ++- src/routes/account/api-keys/+page.svelte | 51 +- .../account/api-keys/provider-card.svelte | 25 +- src/routes/api/enhance-prompt/+server.ts | 110 +- src/routes/api/generate-message/+server.ts | 542 +++--- tmp/kepler-ai-sdk | 1 + 16 files changed, 2233 insertions(+), 557 deletions(-) create mode 100644 bun.lock create mode 100644 src/lib/services/model-manager.ts create mode 160000 tmp/kepler-ai-sdk diff --git a/.env.example b/.env.example index 19ee63b..db6f961 100644 --- a/.env.example +++ b/.env.example @@ -13,4 +13,11 @@ GITHUB_CLIENT_SECRET= GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= -OPENROUTER_FREE_KEY= +# Optional: Development API keys for testing (not required for production) +# Users will provide their own API keys through the settings interface +DEV_OPENAI_API_KEY= +DEV_ANTHROPIC_API_KEY= +DEV_GEMINI_API_KEY= +DEV_MISTRAL_API_KEY= +DEV_COHERE_API_KEY= +DEV_OPENROUTER_API_KEY= diff --git a/.gitignore b/.gitignore index 326371d..8cdb3e3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ vite.config.js.timestamp-* vite.config.ts.timestamp-* .aider* -src/lib/backend/convex/_generated \ No newline at end of file +src/lib/backend/convex/_generated +tmp/ diff --git a/README.md b/README.md index 48d8d73..bed7bbd 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ While thom.chat is a clone, the featureset is not identical to T3 Chat. ### 🤖 **AI & Models** -- **400+ AI Models** via OpenRouter integration -- **Free Tier** with 10 messages using premium models -- **Unlimited Free Models** (models ending in `:free`) -- **Bring Your Own Key** for unlimited access +- **Multiple AI Providers** - OpenAI, Anthropic, Google Gemini, Mistral, Cohere, OpenRouter +- **600+ AI Models** across all providers +- **Bring Your Own API Keys** - Users must provide their own API keys +- **No Usage Limits** - Use any model without restrictions when you have the API key ### 💬 **Chat Experience** @@ -79,7 +79,7 @@ While thom.chat is a clone, the featureset is not identical to T3 Chat. - 🔧 Convex Database - 🔐 BetterAuth -- 🤖 OpenRouter API +- 🤖 Kepler AI SDK (Multi-provider support) - 🦾 Blood, sweat, and tears @@ -92,7 +92,7 @@ While thom.chat is a clone, the featureset is not identical to T3 Chat. - Node.js 18+ - pnpm (recommended) -- OpenRouter API key (optional for free tier) +- At least one AI provider API key (OpenAI, Anthropic, Gemini, etc.) ### Installation @@ -129,16 +129,28 @@ While thom.chat is a clone, the featureset is not identical to T3 Chat. ## 🎮 Usage -### Free Tier +### Getting Started -- Sign up and get **10 free messages** with premium models -- Use **unlimited free models** (ending in `:free`) -- No credit card required +1. **Sign up** for a free account +2. **Add API Keys** - Go to Settings and add API keys for the providers you want to use: + - **OpenAI** - GPT models, DALL-E, Whisper + - **Anthropic** - Claude models + - **Google Gemini** - Gemini models and vision + - **Mistral** - Mistral models and embeddings + - **Cohere** - Command models and embeddings + - **OpenRouter** - Access to 300+ models +3. **Start Chatting** - Select any model from your enabled providers -### Premium Features +### Supported Providers -- Add your own OpenRouter API key for unlimited access -- Access to all 400+ models +| Provider | Models | Streaming | Tools | Vision | Embeddings | +|----------|---------|-----------|-------|--------|------------| +| OpenAI | GPT-4, o3-mini, DALL-E, TTS | ✅ | ✅ | ✅ | ✅ | +| Anthropic | Claude 4, Claude 3.5 Sonnet | ✅ | ✅ | ✅ | ❌ | +| Google Gemini | Gemini 2.5 Pro, Imagen | ✅ | ✅ | ✅ | ✅ | +| Mistral | Mistral Large, Mistral Embed | ✅ | ✅ | ❌ | ✅ | +| Cohere | Command A, Command R+ | ✅ | ✅ | ❌ | ✅ | +| OpenRouter | 300+ models | ✅ | ✅ | ✅ | ❌ | ## 🤝 Contributing @@ -158,7 +170,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - Inspired by [T3 Chat](https://t3.chat/) - Built with [SvelteKit](https://kit.svelte.dev/) -- Powered by [OpenRouter](https://openrouter.ai/) +- Powered by [Kepler AI SDK](https://deepwiki.com/keplersystems/kepler-ai-sdk) - Database by [Convex](https://convex.dev/) --- diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..d3e6705 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1496 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "thom-chat", + "dependencies": { + "@floating-ui/dom": "^1.7.1", + "@fontsource-variable/fraunces": "^5.2.7", + "@fontsource-variable/geist-mono": "^5.2.6", + "@fontsource-variable/inter": "^5.2.6", + "@fontsource-variable/inter-tight": "^5.2.6", + "@fontsource-variable/montserrat": "^5.2.6", + "@fontsource-variable/nunito-sans": "^5.2.6", + "@fontsource-variable/open-sans": "^5.2.6", + "@fontsource/instrument-serif": "^5.2.6", + "@keplersystems/kepler-ai-sdk": "^1.0.5", + "better-auth": "^1.2.9", + "convex-helpers": "^0.1.94", + "hastscript": "^9.0.1", + "markdown-it-async": "^2.2.0", + "zod": "^3.25.64", + }, + "devDependencies": { + "@better-auth-kit/convex": "^1.2.2", + "@eslint/compat": "^1.2.5", + "@eslint/js": "^9.18.0", + "@iconify/json": "^2.2.349", + "@playwright/test": "^1.49.1", + "@shikijs/langs": "^3.6.0", + "@shikijs/markdown-it": "^3.6.0", + "@shikijs/themes": "^3.6.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/svelte": "^5.2.4", + "@vercel/functions": "^2.2.0", + "bits-ui": "^2.8.5", + "clsx": "^2.1.1", + "concurrently": "^9.1.2", + "convex": "^1.24.8", + "convex-svelte": "^0.0.11", + "dotenv": "^16.5.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-svelte": "^3.0.0", + "globals": "^16.0.0", + "isomorphic-dompurify": "^2.25.0", + "jsdom": "^26.0.0", + "melt": "^0.38.0", + "mode-watcher": "^1.0.8", + "neverthrow": "^8.2.0", + "openai": "^5.5.1", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "runed": "^0.28.0", + "shiki": "^3.6.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-meta-tags": "^4.4.0", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^1.0.0", + "tailwindcss": "^4.0.0", + "tw-animate-css": "^1.3.4", + "typescript": "^5.0.0", + "typescript-eslint": "^8.20.0", + "unplugin-icons": "^22.1.0", + "vite": "^6.2.6", + "vitest": "^3.2.3", + }, + }, + }, + "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], + + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + + "@antfu/utils": ["@antfu/utils@8.1.1", "", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.55.1", "", { "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-gjOMS4chmm8BxClKmCjNHmvf1FrO1Cn++CSX6K3YCZjz5JG4I9ZttQ/xEH4FBsz6HQyZvnUpiKlOAkmxaGmEaQ=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], + + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + + "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.879.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/credential-provider-node": "3.879.0", "@aws-sdk/middleware-host-header": "3.873.0", "@aws-sdk/middleware-logger": "3.876.0", "@aws-sdk/middleware-recursion-detection": "3.873.0", "@aws-sdk/middleware-user-agent": "3.879.0", "@aws-sdk/region-config-resolver": "3.873.0", "@aws-sdk/types": "3.862.0", "@aws-sdk/util-endpoints": "3.879.0", "@aws-sdk/util-user-agent-browser": "3.873.0", "@aws-sdk/util-user-agent-node": "3.879.0", "@smithy/config-resolver": "^4.1.5", "@smithy/core": "^3.9.0", "@smithy/fetch-http-handler": "^5.1.1", "@smithy/hash-node": "^4.0.5", "@smithy/invalid-dependency": "^4.0.5", "@smithy/middleware-content-length": "^4.0.5", "@smithy/middleware-endpoint": "^4.1.19", "@smithy/middleware-retry": "^4.1.20", "@smithy/middleware-serde": "^4.0.9", "@smithy/middleware-stack": "^4.0.5", "@smithy/node-config-provider": "^4.1.4", "@smithy/node-http-handler": "^4.1.1", "@smithy/protocol-http": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.27", "@smithy/util-defaults-mode-node": "^4.0.27", "@smithy/util-endpoints": "^3.0.7", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-uMvvNmRs5shbbS2R3ZiouILpoyHUl4t2hPzp8rzqsdmvpr43SGy+L7ZKz1VxPK71xT6ZOZPU4+qEI657H3j3Yw=="], + + "@aws-sdk/client-sagemaker": ["@aws-sdk/client-sagemaker@3.879.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/credential-provider-node": "3.879.0", "@aws-sdk/middleware-host-header": "3.873.0", "@aws-sdk/middleware-logger": "3.876.0", "@aws-sdk/middleware-recursion-detection": "3.873.0", "@aws-sdk/middleware-user-agent": "3.879.0", "@aws-sdk/region-config-resolver": "3.873.0", "@aws-sdk/types": "3.862.0", "@aws-sdk/util-endpoints": "3.879.0", "@aws-sdk/util-user-agent-browser": "3.873.0", "@aws-sdk/util-user-agent-node": "3.879.0", "@smithy/config-resolver": "^4.1.5", "@smithy/core": "^3.9.0", "@smithy/fetch-http-handler": "^5.1.1", "@smithy/hash-node": "^4.0.5", "@smithy/invalid-dependency": "^4.0.5", "@smithy/middleware-content-length": "^4.0.5", "@smithy/middleware-endpoint": "^4.1.19", "@smithy/middleware-retry": "^4.1.20", "@smithy/middleware-serde": "^4.0.9", "@smithy/middleware-stack": "^4.0.5", "@smithy/node-config-provider": "^4.1.4", "@smithy/node-http-handler": "^4.1.1", "@smithy/protocol-http": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.27", "@smithy/util-defaults-mode-node": "^4.0.27", "@smithy/util-endpoints": "^3.0.7", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.7", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-2lagdd1XHZthO6EwjVIPH6cPpjh0QUVIt0ufa+XXpgXZEvLymlF/EmkOpN+vMiSzesucHEAMr8iicPovGKDFpA=="], + + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.879.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/middleware-host-header": "3.873.0", "@aws-sdk/middleware-logger": "3.876.0", "@aws-sdk/middleware-recursion-detection": "3.873.0", "@aws-sdk/middleware-user-agent": "3.879.0", "@aws-sdk/region-config-resolver": "3.873.0", "@aws-sdk/types": "3.862.0", "@aws-sdk/util-endpoints": "3.879.0", "@aws-sdk/util-user-agent-browser": "3.873.0", "@aws-sdk/util-user-agent-node": "3.879.0", "@smithy/config-resolver": "^4.1.5", "@smithy/core": "^3.9.0", "@smithy/fetch-http-handler": "^5.1.1", "@smithy/hash-node": "^4.0.5", "@smithy/invalid-dependency": "^4.0.5", "@smithy/middleware-content-length": "^4.0.5", "@smithy/middleware-endpoint": "^4.1.19", "@smithy/middleware-retry": "^4.1.20", "@smithy/middleware-serde": "^4.0.9", "@smithy/middleware-stack": "^4.0.5", "@smithy/node-config-provider": "^4.1.4", "@smithy/node-http-handler": "^4.1.1", "@smithy/protocol-http": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.27", "@smithy/util-defaults-mode-node": "^4.0.27", "@smithy/util-endpoints": "^3.0.7", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-+Pc3OYFpRYpKLKRreovPM63FPPud1/SF9vemwIJfz6KwsBCJdvg7vYD1xLSIp5DVZLeetgf4reCyAA5ImBfZuw=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.879.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@aws-sdk/xml-builder": "3.873.0", "@smithy/core": "^3.9.0", "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/signature-v4": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-AhNmLCrx980LsK+SfPXGh7YqTyZxsK0Qmy18mWmkfY0TSq7WLaSDB5zdQbgbnQCACCHy8DUYXbi4KsjlIhv3PA=="], + + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.879.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-E1iQ4+eyDKJfWVuijIxxNZ+uhZ3LF3HXnYbkguq05jIbbazXmN/AXTfQoXreXYoGzOSJltxkje9X0H7rBJRxtg=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-JgG7A8SSbr5IiCYL8kk39Y9chdSB5GPwBorDW8V8mr19G9L+qd6ohED4fAocoNFaDnYJ5wGAHhCfSJjzcsPBVQ=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/fetch-http-handler": "^5.1.1", "@smithy/node-http-handler": "^4.1.1", "@smithy/property-provider": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/util-stream": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-2hM5ByLpyK+qORUexjtYyDZsgxVCCUiJQZRMGkNXFEGz6zTpbjfTIWoh3zRgWHEBiqyPIyfEy50eIF69WshcuA=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/credential-provider-env": "3.879.0", "@aws-sdk/credential-provider-http": "3.879.0", "@aws-sdk/credential-provider-process": "3.879.0", "@aws-sdk/credential-provider-sso": "3.879.0", "@aws-sdk/credential-provider-web-identity": "3.879.0", "@aws-sdk/nested-clients": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-07M8zfb73KmMBqVO5/V3Ea9kqDspMX0fO0kaI1bsjWI6ngnMye8jCE0/sIhmkVAI0aU709VA0g+Bzlopnw9EoQ=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.879.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.879.0", "@aws-sdk/credential-provider-http": "3.879.0", "@aws-sdk/credential-provider-ini": "3.879.0", "@aws-sdk/credential-provider-process": "3.879.0", "@aws-sdk/credential-provider-sso": "3.879.0", "@aws-sdk/credential-provider-web-identity": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-FYaAqJbnSTrVL2iZkNDj2hj5087yMv2RN2GA8DJhe7iOJjzhzRojrtlfpWeJg6IhK0sBKDH+YXbdeexCzUJvtA=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-7r360x1VyEt35Sm1JFOzww2WpnfJNBbvvnzoyLt7WRfK0S/AfsuWhu5ltJ80QvJ0R3AiSNbG+q/btG2IHhDYPQ=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.879.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.879.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/token-providers": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-gd27B0NsgtKlaPNARj4IX7F7US5NuU691rGm0EUSkDsM7TctvJULighKoHzPxDQlrDbVI11PW4WtKS/Zg5zPlQ=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/nested-clients": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-Jy4uPFfGzHk1Mxy+/Wr43vuw9yXsE2yiF4e4598vc3aJfO0YtA2nSfbKD3PNKRORwXbeKqWPfph9SCKQpWoxEg=="], + + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.879.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.879.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/credential-provider-cognito-identity": "3.879.0", "@aws-sdk/credential-provider-env": "3.879.0", "@aws-sdk/credential-provider-http": "3.879.0", "@aws-sdk/credential-provider-ini": "3.879.0", "@aws-sdk/credential-provider-node": "3.879.0", "@aws-sdk/credential-provider-process": "3.879.0", "@aws-sdk/credential-provider-sso": "3.879.0", "@aws-sdk/credential-provider-web-identity": "3.879.0", "@aws-sdk/nested-clients": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/config-resolver": "^4.1.5", "@smithy/core": "^3.9.0", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-1nOjzwjXrmpbPzFuwFKYIr1LsrBucm6J8kf5Esz9brxKWynuJAPApd0JY3cO6q58+mKls0m58W6Ab/Ol7RmCMg=="], + + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.873.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA=="], + + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.876.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA=="], + + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.873.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg=="], + + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/types": "3.862.0", "@aws-sdk/util-endpoints": "3.879.0", "@smithy/core": "^3.9.0", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-DDSV8228lQxeMAFKnigkd0fHzzn5aauZMYC3CSj6e5/qE7+9OwpkUcjHfb7HZ9KWG6L2/70aKZXHqiJ4xKhOZw=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.879.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.879.0", "@aws-sdk/middleware-host-header": "3.873.0", "@aws-sdk/middleware-logger": "3.876.0", "@aws-sdk/middleware-recursion-detection": "3.873.0", "@aws-sdk/middleware-user-agent": "3.879.0", "@aws-sdk/region-config-resolver": "3.873.0", "@aws-sdk/types": "3.862.0", "@aws-sdk/util-endpoints": "3.879.0", "@aws-sdk/util-user-agent-browser": "3.873.0", "@aws-sdk/util-user-agent-node": "3.879.0", "@smithy/config-resolver": "^4.1.5", "@smithy/core": "^3.9.0", "@smithy/fetch-http-handler": "^5.1.1", "@smithy/hash-node": "^4.0.5", "@smithy/invalid-dependency": "^4.0.5", "@smithy/middleware-content-length": "^4.0.5", "@smithy/middleware-endpoint": "^4.1.19", "@smithy/middleware-retry": "^4.1.20", "@smithy/middleware-serde": "^4.0.9", "@smithy/middleware-stack": "^4.0.5", "@smithy/node-config-provider": "^4.1.4", "@smithy/node-http-handler": "^4.1.1", "@smithy/protocol-http": "^5.1.3", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.27", "@smithy/util-defaults-mode-node": "^4.0.27", "@smithy/util-endpoints": "^3.0.7", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7+n9NpIz9QtKYnxmw1fHi9C8o0GrX8LbBR4D50c7bH6Iq5+XdSuL5AFOWWQ5cMD0JhqYYJhK/fJsVau3nUtC4g=="], + + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.873.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.879.0", "", { "dependencies": { "@aws-sdk/core": "3.879.0", "@aws-sdk/nested-clients": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-47J7sCwXdnw9plRZNAGVkNEOlSiLb/kR2slnDIHRK9NB/ECKsoqgz5OZQJ9E2f0yqOs8zSNJjn3T01KxpgW8Qw=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.862.0", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.879.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-endpoints": "^3.0.7", "tslib": "^2.6.2" } }, "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.873.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg=="], + + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.873.0", "", { "dependencies": { "@aws-sdk/types": "3.862.0", "@smithy/types": "^4.3.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA=="], + + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.879.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.879.0", "@aws-sdk/types": "3.862.0", "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-A5KGc1S+CJRzYnuxJQQmH1BtGsz46AgyHkqReKfGiNQA8ET/9y9LQ5t2ABqnSBHHIh3+MiCcQSkUZ0S3rTodrQ=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.873.0", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w=="], + + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + + "@better-auth-kit/convex": ["@better-auth-kit/convex@1.2.2", "", { "dependencies": { "prettier": "^3.4.2" }, "peerDependencies": { "better-auth": "^1.2.3", "convex": "^1.17.4", "typescript": "^5.7.2" } }, "sha512-ZXTOsHrzNTgvAScTzJLQpTO+q5lncixFhNJiRORWi0CgQJ4RlJ2vto45xPdVgP5liYAOyP9x25k+S+sToqyxsA=="], + + "@better-auth/utils": ["@better-auth/utils@0.2.6", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA=="], + + "@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/compat": ["@eslint/compat@1.3.2", "", { "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="], + + "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@fontsource-variable/fraunces": ["@fontsource-variable/fraunces@5.2.7", "", {}, "sha512-PYIcwL3+0SA2IEAcA9ma07Rz8DCbJdLy7Hb1syq/FIcCyf1zSlHRRcC0a33PnBCZ9Q7B+01kFH0cS29yqWEk3w=="], + + "@fontsource-variable/geist-mono": ["@fontsource-variable/geist-mono@5.2.6", "", {}, "sha512-vw6T9JGTrYJ980bn7W8iTPhe2jVK5ifunVs7xh9dfTVArjDSkJs03JjeZrH5LKEpGABLXSlSlNU57HRm4tmFMg=="], + + "@fontsource-variable/inter": ["@fontsource-variable/inter@5.2.6", "", {}, "sha512-jks/bficUPQ9nn7GvXvHtlQIPudW7Wx8CrlZoY8bhxgeobNxlQan8DclUJuYF2loYRrGpfrhCIZZspXYysiVGg=="], + + "@fontsource-variable/inter-tight": ["@fontsource-variable/inter-tight@5.2.6", "", {}, "sha512-umb36PJO+bwzqvpKkikmIEDs0YF3P5vUBQe+UEBgPSDffzDZyKVIU07M4/1cWcsZgU1PaXjyheUYKwlsJteopA=="], + + "@fontsource-variable/montserrat": ["@fontsource-variable/montserrat@5.2.6", "", {}, "sha512-1eHefcz9ZXSMjZPckKCWSYd8rLjUpmzQIsNO8DuyU0IdPvsqletPvSIJmztr0sCU6sX4EFKsSHxJWawjmROp2A=="], + + "@fontsource-variable/nunito-sans": ["@fontsource-variable/nunito-sans@5.2.6", "", {}, "sha512-lDToEZdfmhp+W62yZxDY64+tOzIBF7phjyTcIG+nh8utXMJo8IgS9iqVoOI18LyS/FMJO9Dx3kCsJEDQaf3s9g=="], + + "@fontsource-variable/open-sans": ["@fontsource-variable/open-sans@5.2.6", "", {}, "sha512-O596UvQZ7KkR8yDJWIP8UZZoqs0DHpYsrL0Szml0/EkrJ35HcMIM3bJ3sSAIAjWsBSqKgzV2HJEIuhvxBd1iog=="], + + "@fontsource/instrument-serif": ["@fontsource/instrument-serif@5.2.6", "", {}, "sha512-uvAmjVbzV26yaPesOQxwQcVU8ALbpgZ8E329KoAr19aH0Vr5BPHzSq7ImtmYmlla4kC3qcbhePdXUJEB20ZbpA=="], + + "@google/genai": ["@google/genai@1.16.0", "", { "dependencies": { "google-auth-library": "^9.14.2", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.11.4" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-hdTYu39QgDFxv+FB6BK2zi4UIJGWhx2iPc0pHQ0C5Q/RCi+m+4gsryIzTGO+riqWcUA8/WGYp6hpqckdOBNysw=="], + + "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@iconify/json": ["@iconify/json@2.2.379", "", { "dependencies": { "@iconify/types": "*", "pathe": "^1.1.2" } }, "sha512-PInpWLQi2C+fDIbBdVNcKOj9QKl7TT6sXqFqYMa4e34sMx206PiUlg0puWgo1Q1C/TDNQiy/raGWUbssOb1eyg=="], + + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@2.3.0", "", { "dependencies": { "@antfu/install-pkg": "^1.0.0", "@antfu/utils": "^8.1.0", "@iconify/types": "^2.0.0", "debug": "^4.4.0", "globals": "^15.14.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "mlly": "^1.7.4" } }, "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA=="], + + "@internationalized/date": ["@internationalized/date@3.9.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], + + "@keplersystems/kepler-ai-sdk": ["@keplersystems/kepler-ai-sdk@1.0.5", "", { "dependencies": { "@anthropic-ai/sdk": "^0.55.0", "@google/genai": "^1.7.0", "@mistralai/mistralai": "^1.7.2", "cohere-ai": "^7.17.1", "exa-js": "^1.8.20", "openai": "^5.7.0" }, "peerDependencies": { "typescript": "^5" } }, "sha512-aynAClqQtS1Xkt53lZ4TdOS1YbtkfaYIrWxSmNXEV5DahLzHRi8YJ3zooAq2cmHUwjaEXmpaB2AMFdwAxWWTkQ=="], + + "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], + + "@mistralai/mistralai": ["@mistralai/mistralai@1.9.18", "", { "dependencies": { "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": ">= 3" } }, "sha512-D/vNAGEvWMsg95tzgLTg7pPnW9leOPyH+nh1Os05NwxVPbUykoYgMAwOEX7J46msahWdvZ4NQQuxUXIUV2P6dg=="], + + "@noble/ciphers": ["@noble/ciphers@0.6.0", "", {}, "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-YFueREq97CLslZZBI8dKzis7jMfEHSLxM+nr0Zdx1POiXFLjqqwoY5s0F1UimdBiEw/iKlHey2m56MRDv7Jtyg=="], + + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "@peculiar/asn1-x509": "^2.4.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-fJiYUBCJBDkjh347zZe5H81BdJ0+OGIg0X9z06v8xXUoql3MFeENUX0JsjCaVaU9A0L85PefLPGYkIoGpTnXLQ=="], + + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "@peculiar/asn1-x509": "^2.4.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-6PP75voaEnOSlWR9sD25iCQyLgFZHXbmxvUfnnDcfL6Zh5h2iHW38+bve4LfH7a60x7fkhZZNmiYqAlAff9Img=="], + + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.4.0", "", { "dependencies": { "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ=="], + + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw=="], + + "@playwright/test": ["@playwright/test@1.55.0", "", { "dependencies": { "playwright": "1.55.0" }, "bin": { "playwright": "cli.js" } }, "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.49.0", "", { "os": "android", "cpu": "arm" }, "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.49.0", "", { "os": "android", "cpu": "arm64" }, "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.49.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.49.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.49.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.49.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.49.0", "", { "os": "linux", "cpu": "arm" }, "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.49.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.49.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.49.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.49.0", "", { "os": "linux", "cpu": "none" }, "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.49.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.49.0", "", { "os": "linux", "cpu": "none" }, "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.49.0", "", { "os": "linux", "cpu": "none" }, "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.49.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.49.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.49.0", "", { "os": "linux", "cpu": "x64" }, "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.49.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.49.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.49.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg=="], + + "@shikijs/core": ["@shikijs/core@3.12.0", "", { "dependencies": { "@shikijs/types": "3.12.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-rPfCBd6gHIKBPpf2hKKWn2ISPSrmRKAFi+bYDjvZHpzs3zlksWvEwaF3Z4jnvW+xHxSRef7qDooIJkY0RpA9EA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.12.0", "", { "dependencies": { "@shikijs/types": "3.12.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ni3nm4lnKxyKaDoXQQJYEayX052BL7D0ikU5laHp+ynxPpIF1WIwyhzrMU6WDN7AoAfggVR4Xqx3WN+JTS+BvA=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.12.0", "", { "dependencies": { "@shikijs/types": "3.12.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-IfDl3oXPbJ/Jr2K8mLeQVpnF+FxjAc7ZPDkgr38uEw/Bg3u638neSrpwqOTnTHXt1aU0Fk1/J+/RBdst1kVqLg=="], + + "@shikijs/langs": ["@shikijs/langs@3.12.0", "", { "dependencies": { "@shikijs/types": "3.12.0" } }, "sha512-HIca0daEySJ8zuy9bdrtcBPhcYBo8wR1dyHk1vKrOuwDsITtZuQeGhEkcEfWc6IDyTcom7LRFCH6P7ljGSCEiQ=="], + + "@shikijs/markdown-it": ["@shikijs/markdown-it@3.12.0", "", { "dependencies": { "markdown-it": "^14.1.0", "shiki": "3.12.0" }, "peerDependencies": { "markdown-it-async": "^2.2.0" }, "optionalPeers": ["markdown-it-async"] }, "sha512-jHFfAaVnPbKeWOQ8c3LrlC1443eFGKWJEwu+tHZpTP81qEXgJj41DZGKrgEdrww1tBTnucRAUR3Qd/Gy2ji4ZA=="], + + "@shikijs/themes": ["@shikijs/themes@3.12.0", "", { "dependencies": { "@shikijs/types": "3.12.0" } }, "sha512-/lxvQxSI5s4qZLV/AuFaA4Wt61t/0Oka/P9Lmpr1UV+HydNCczO3DMHOC/CsXCCpbv4Zq8sMD0cDa7mvaVoj0Q=="], + + "@shikijs/types": ["@shikijs/types@3.12.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-jsFzm8hCeTINC3OCmTZdhR9DOl/foJWplH2Px0bTi4m8z59fnsueLsweX82oGcjRQ7mfQAluQYKGoH2VzsWY4A=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@simplewebauthn/browser": ["@simplewebauthn/browser@13.1.2", "", {}, "sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw=="], + + "@simplewebauthn/server": ["@simplewebauthn/server@13.1.2", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8" } }, "sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g=="], + + "@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + + "@smithy/abort-controller": ["@smithy/abort-controller@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g=="], + + "@smithy/config-resolver": ["@smithy/config-resolver@4.1.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw=="], + + "@smithy/core": ["@smithy/core@3.9.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.9", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "@smithy/util-stream": "^4.2.4", "@smithy/util-utf8": "^4.0.0", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-B/GknvCfS3llXd/b++hcrwIuqnEozQDnRL4sBmOac5/z/dr0/yG1PURNPOyU4Lsiy1IyTj8scPxVqRs5dYWf6A=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.1.1", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/querystring-builder": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ=="], + + "@smithy/hash-node": ["@smithy/hash-node@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ=="], + + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.5", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ=="], + + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.1.19", "", { "dependencies": { "@smithy/core": "^3.9.0", "@smithy/middleware-serde": "^4.0.9", "@smithy/node-config-provider": "^4.1.4", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-EAlEPncqo03siNZJ9Tm6adKCQ+sw5fNU8ncxWwaH0zTCwMPsgmERTi6CEKaermZdgJb+4Yvh0NFm36HeO4PGgQ=="], + + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.1.20", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/protocol-http": "^5.1.3", "@smithy/service-error-classification": "^4.0.7", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-T3maNEm3Masae99eFdx1Q7PIqBBEVOvRd5hralqKZNeIivnoGNx5OFtI3DiZ5gCjUkl0mNondlzSXeVxkinh7Q=="], + + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.9", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg=="], + + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ=="], + + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.1.4", "", { "dependencies": { "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.1.1", "", { "dependencies": { "@smithy/abort-controller": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/querystring-builder": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw=="], + + "@smithy/property-provider": ["@smithy/property-provider@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ=="], + + "@smithy/protocol-http": ["@smithy/protocol-http@5.1.3", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w=="], + + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A=="], + + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w=="], + + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.7", "", { "dependencies": { "@smithy/types": "^4.3.2" } }, "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg=="], + + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.1.3", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "@smithy/util-uri-escape": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw=="], + + "@smithy/smithy-client": ["@smithy/smithy-client@4.5.0", "", { "dependencies": { "@smithy/core": "^3.9.0", "@smithy/middleware-endpoint": "^4.1.19", "@smithy/middleware-stack": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-stream": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-ZSdE3vl0MuVbEwJBxSftm0J5nL/gw76xp5WF13zW9cN18MFuFXD5/LV0QD8P+sCU5bSWGyy6CTgUupE1HhOo1A=="], + + "@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/url-parser": ["@smithy/url-parser@4.0.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw=="], + + "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], + + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], + + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], + + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], + + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.27", "", { "dependencies": { "@smithy/property-provider": "^4.0.5", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-i/Fu6AFT5014VJNgWxKomBJP/GB5uuOsM4iHdcmplLm8B1eAqnRItw4lT2qpdO+mf+6TFmf6dGcggGLAVMZJsQ=="], + + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.27", "", { "dependencies": { "@smithy/config-resolver": "^4.1.5", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/smithy-client": "^4.5.0", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-3W0qClMyxl/ELqTA39aNw1N+pN0IjpXT7lPFvZ8zTxqVFP7XCpACB9QufmN4FQtd39xbgS7/Lekn7LmDa63I5w=="], + + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ=="], + + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw=="], + + "@smithy/util-middleware": ["@smithy/util-middleware@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ=="], + + "@smithy/util-retry": ["@smithy/util-retry@4.0.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.7", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ=="], + + "@smithy/util-stream": ["@smithy/util-stream@4.2.4", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.1.1", "@smithy/node-http-handler": "^4.1.1", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ=="], + + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], + + "@smithy/util-waiter": ["@smithy/util-waiter@4.0.7", "", { "dependencies": { "@smithy/abort-controller": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], + + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.1.0", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-shOuLI5D2s+0zTv2ab5M5PqfknXqWbKi+0UwB9yLTRIdzsK1R93JOO8jNhIYSHdW+IYXIYnLniu+JZqXs7h9Wg=="], + + "@sveltejs/kit": ["@sveltejs/kit@2.37.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@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 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-xgKtpjQ6Ry4mdShd01ht5AODUsW7+K1iValPDq7QX8zI1hWOKREH9GjG8SRCN5tC4K7UXmMhuQam7gbLByVcnw=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="], + + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="], + + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.12", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.12" } }, "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.12", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.12", "@tailwindcss/oxide-darwin-arm64": "4.1.12", "@tailwindcss/oxide-darwin-x64": "4.1.12", "@tailwindcss/oxide-freebsd-x64": "4.1.12", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", "@tailwindcss/oxide-linux-x64-musl": "4.1.12", "@tailwindcss/oxide-wasm32-wasi": "4.1.12", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" } }, "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.12", "", { "os": "android", "cpu": "arm64" }, "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12", "", { "os": "linux", "cpu": "arm" }, "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.12", "", { "os": "linux", "cpu": "x64" }, "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.12", "", { "os": "linux", "cpu": "x64" }, "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.12", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@emnapi/wasi-threads": "^1.0.4", "@napi-rs/wasm-runtime": "^0.2.12", "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.12", "", { "os": "win32", "cpu": "x64" }, "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.12", "", { "dependencies": { "@tailwindcss/node": "4.1.12", "@tailwindcss/oxide": "4.1.12", "tailwindcss": "4.1.12" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.8.0", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ=="], + + "@testing-library/svelte": ["@testing-library/svelte@5.2.8", "", { "dependencies": { "@testing-library/dom": "9.x.x || 10.x.x" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vite": "*", "vitest": "*" }, "optionalPeers": ["vite", "vitest"] }, "sha512-ucQOtGsJhtawOEtUmbR4rRh53e6RbM1KUluJIXRmh6D4UzxR847iIqqjRtg9mHNFmGQ8Vkam9yVcR5d1mhIHKA=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], + + "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.41.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/type-utils": "8.41.0", "@typescript-eslint/utils": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.41.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.41.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.41.0", "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0" } }, "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.41.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.41.0", "", {}, "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.41.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.41.0", "@typescript-eslint/tsconfig-utils": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.41.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.41.0", "", { "dependencies": { "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@vercel/functions": ["@vercel/functions@2.2.13", "", { "dependencies": { "@vercel/oidc": "2.0.2" }, "peerDependencies": { "@aws-sdk/credential-provider-web-identity": "*" }, "optionalPeers": ["@aws-sdk/credential-provider-web-identity"] }, "sha512-14ArBSIIcOBx9nrEgaJb4Bw+en1gl6eSoJWh8qjifLl5G3E4dRXCFOT8HP+w66vb9Wqyd1lAQBrmRhRwOj9X9A=="], + + "@vercel/oidc": ["@vercel/oidc@2.0.2", "", { "dependencies": { "@types/ms": "2.1.0", "ms": "2.1.3" } }, "sha512-59PBFx3T+k5hLTEWa3ggiMpGRz1OVvl9eN8SUai+A43IsqiOuAe7qPBf+cray/Fj6mkgnxm/D7IAtjc8zSHi7g=="], + + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "axe-core": ["axe-core@4.9.1", "", {}, "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "better-auth": ["better-auth@1.3.7", "", { "dependencies": { "@better-auth/utils": "0.2.6", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.8.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "^1.0.13", "defu": "^6.1.4", "jose": "^5.10.0", "kysely": "^0.28.5", "nanostores": "^0.11.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-/1fEyx2SGgJQM5ujozDCh9eJksnVkNU/J7Fk/tG5Y390l8nKbrPvqiFlCjlMM+scR+UABJbQzA6An7HT50LHyQ=="], + + "better-call": ["better-call@1.0.16", "", { "dependencies": { "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-42dgJ1rOtc0anOoxjXPOWuel/Z/4aeO7EJ2SiXNwvlkySSgjXhNjAjTMWa8DL1nt6EXS3jl3VKC3mPsU/lUgVA=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "bits-ui": ["bits-ui@2.9.4", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.29.1", "svelte-toolbelt": "^0.9.3", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-Cqn685P6DDuEyBZT/CWMyS5+8JAnYbctvoEVPcmiut+HUpG3SozVgjoDaUib5VG4ZYUKEi1FPwHxiXo9c6J0PA=="], + + "bowser": ["bowser@2.12.1", "", {}, "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cohere-ai": ["cohere-ai@7.18.1", "", { "dependencies": { "@aws-sdk/client-sagemaker": "^3.583.0", "@aws-sdk/credential-providers": "^3.583.0", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", "convict": "^6.2.4", "form-data": "^4.0.0", "form-data-encoder": "^4.0.2", "formdata-node": "^6.0.3", "js-base64": "3.7.7", "node-fetch": "^2.7.0", "qs": "^6.13.1", "readable-stream": "^4.5.2", "url-join": "4.0.1" } }, "sha512-kZM8dMAJlNXshl1c0X+jPTkD5P4QSexZ2LHQ6Cu8g2v3j1eGyT6rJKk+zzJikluWJ1bAyobj+ucGIhmQtWfl4A=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "concurrently": ["concurrently@9.2.1", "", { "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", "shell-quote": "1.8.3", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" }, "bin": { "concurrently": "dist/bin/concurrently.js", "conc": "dist/bin/concurrently.js" } }, "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng=="], + + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + + "convex": ["convex@1.26.2", "", { "dependencies": { "esbuild": "0.25.4", "jwt-decode": "^4.0.0", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-QG3zvZ9GTTeeBS+N5PJj73TULHYu99eVSOI3KU2hMnB9q40fHxkMFJ+AYox+K81roTakciBvNTvGlhfz+M10fQ=="], + + "convex-helpers": ["convex-helpers@0.1.104", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.24.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5", "zod": "^3.22.4 || ^4.0.15" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig=="], + + "convex-svelte": ["convex-svelte@0.0.11", "", { "peerDependencies": { "convex": "^1.10.0", "svelte": "^5.0.0" } }, "sha512-N/29gg5Zqy72vKL4xHSLk3jGwXVKIWXPs6xzq6KxGL84y/D6hG85pG2CPOzn08EzMmByts5FTkJ5p3var6yDng=="], + + "convict": ["convict@6.2.4", "", { "dependencies": { "lodash.clonedeep": "^4.5.0", "yargs-parser": "^20.2.7" } }, "sha512-qN60BAwdMVdofckX7AlohVJ2x9UvjTNoKVXCL2LxFk1l7757EJqf1nySdMkPQer0bt8kQ5lQiyZ9/2NvrFBuwQ=="], + + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], + + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + + "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "dompurify": ["dompurify@3.2.6", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.34.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="], + + "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], + + "eslint-plugin-svelte": ["eslint-plugin-svelte@3.11.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.3.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "exa-js": ["exa-js@1.9.3", "", { "dependencies": { "cross-fetch": "~4.1.0", "dotenv": "~16.4.7", "openai": "^5.0.1", "zod": "^3.22.0", "zod-to-json-schema": "^3.20.0" } }, "sha512-4u8vO5KHstifBz6fcwcBVvU62zfwsWFpD8qomU2zQ+lLRYCwOh2Rz04xSSqEeoHrkCypGjy2VHez7elBt6ibQQ=="], + + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + + "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "focus-trap": ["focus-trap@7.6.5", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "form-data-encoder": ["form-data-encoder@4.1.0", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="], + + "formdata-node": ["formdata-node@6.0.3", "", {}, "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="], + + "gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], + + "google-auth-library": ["google-auth-library@9.15.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="], + + "google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], + + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "isomorphic-dompurify": ["isomorphic-dompurify@2.26.0", "", { "dependencies": { "dompurify": "^3.2.6", "jsdom": "^26.1.0" } }, "sha512-nZmoK4wKdzPs5USq4JHBiimjdKSVAOm2T1KyDoadtMPNXYHxiENd19ou4iU/V4juFM6LVgYQnpxCYmxqNP4Obw=="], + + "jest-axe": ["jest-axe@9.0.0", "", { "dependencies": { "axe-core": "4.9.1", "chalk": "4.1.2", "jest-matcher-utils": "29.2.2", "lodash.merge": "4.6.2" } }, "sha512-Xt7O0+wIpW31lv0SO1wQZUTyJE7DEmnDEZeTt9/S9L5WUywxrv8BrgvTuQEqujtfaQOcJ70p4wg7UUgK1E2F5g=="], + + "jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + + "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], + + "jest-matcher-utils": ["jest-matcher-utils@29.2.2", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.2.1", "jest-get-type": "^29.2.0", "pretty-format": "^29.2.1" } }, "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw=="], + + "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], + + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + + "js-base64": ["js-base64@3.7.7", "", {}, "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="], + + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="], + + "jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], + + "kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], + + "kysely": ["kysely@0.28.5", "", {}, "sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], + + "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], + + "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], + + "local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.clonedeep": ["lodash.clonedeep@4.5.0", "", {}, "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="], + + "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="], + + "markdown-it-async": ["markdown-it-async@2.2.0", "", { "dependencies": { "@types/markdown-it": "^14.1.2", "markdown-it": "^14.1.0" } }, "sha512-sITME+kf799vMeO/ww/CjH6q+c05f6TLpn6VOmmWCGNqPJzSh+uFgZoMB9s0plNtW6afy63qglNAC3MhrhP/gg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="], + + "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + + "melt": ["melt@0.38.1", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "dequal": "^2.0.3", "focus-trap": "^7.6.5", "jest-axe": "^9.0.0", "runed": "^0.23.3" }, "peerDependencies": { "svelte": "^5.30.1" } }, "sha512-mBt/W9q41ddOQa76YPQ9TD6KZqq08SE4T9e6Wa5ER0hB87w7S3IpQ0CilPr6UuZ/AnC3x2PgV5axosqN9EycOQ=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], + + "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], + + "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], + + "mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "nanostores": ["nanostores@0.11.4", "", {}, "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "neverthrow": ["neverthrow@8.2.0", "", { "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.24.0" } }, "sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "nwsapi": ["nwsapi@2.2.21", "", {}, "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], + + "openai": ["openai@5.16.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-hoEH8ZNvg1HXjU9mp88L/ZH8O082Z8r6FHCXGiWAzVRrEv443aI57qhch4snu07yQydj+AUAWLenAiBXhu89Tw=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-manager-detector": ["package-manager-detector@1.3.0", "", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], + + "playwright": ["playwright@1.55.0", "", { "dependencies": { "playwright-core": "1.55.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA=="], + + "playwright-core": ["playwright-core@1.55.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], + + "postcss-safe-parser": ["postcss-safe-parser@7.0.1", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A=="], + + "postcss-scss": ["postcss-scss@4.0.9", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + + "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="], + + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rollup": ["rollup@4.49.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.49.0", "@rollup/rollup-android-arm64": "4.49.0", "@rollup/rollup-darwin-arm64": "4.49.0", "@rollup/rollup-darwin-x64": "4.49.0", "@rollup/rollup-freebsd-arm64": "4.49.0", "@rollup/rollup-freebsd-x64": "4.49.0", "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", "@rollup/rollup-linux-arm-musleabihf": "4.49.0", "@rollup/rollup-linux-arm64-gnu": "4.49.0", "@rollup/rollup-linux-arm64-musl": "4.49.0", "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", "@rollup/rollup-linux-ppc64-gnu": "4.49.0", "@rollup/rollup-linux-riscv64-gnu": "4.49.0", "@rollup/rollup-linux-riscv64-musl": "4.49.0", "@rollup/rollup-linux-s390x-gnu": "4.49.0", "@rollup/rollup-linux-x64-gnu": "4.49.0", "@rollup/rollup-linux-x64-musl": "4.49.0", "@rollup/rollup-win32-arm64-msvc": "4.49.0", "@rollup/rollup-win32-ia32-msvc": "4.49.0", "@rollup/rollup-win32-x64-msvc": "4.49.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA=="], + + "rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="], + + "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="], + + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "schema-dts": ["schema-dts@1.1.5", "", {}, "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "shiki": ["shiki@3.12.0", "", { "dependencies": { "@shikijs/core": "3.12.0", "@shikijs/engine-javascript": "3.12.0", "@shikijs/engine-oniguruma": "3.12.0", "@shikijs/langs": "3.12.0", "@shikijs/themes": "3.12.0", "@shikijs/types": "3.12.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-E+ke51tciraTHpaXYXfqnPZFSViKHhSQ3fiugThlfs/om/EonlQ0hSldcqgzOWWqX6PcjkKKzFgrjIaiPAXoaA=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], + + "strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + + "style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="], + + "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + + "svelte": ["svelte@5.38.6", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-ltBPlkvqk3bgCK7/N323atUpP3O3Y+DrGV4dcULrsSn4fZaaNnOmdplNznwfdWclAgvSr5rxjtzn/zJhRm6TKg=="], + + "svelte-check": ["svelte-check@4.3.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg=="], + + "svelte-eslint-parser": ["svelte-eslint-parser@1.3.1", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-0Iztj5vcOVOVkhy1pbo5uA9r+d3yaVoE5XPc9eABIWDOSJZ2mOsZ4D+t45rphWCOr0uMw3jtSG2fh2e7GvKnPg=="], + + "svelte-meta-tags": ["svelte-meta-tags@4.4.0", "", { "dependencies": { "schema-dts": "^1.1.5" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-0g7sksBXdCGYcNM44uipqhVwDrtImB73iZdcpWHE0q0+k96Zg0WS6ySPAV+gX34DSqrkrvcqkG/tI2lwN1KbbA=="], + + "svelte-toolbelt": ["svelte-toolbelt@0.9.3", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.29.0", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], + + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], + + "tailwind-variants": ["tailwind-variants@1.0.0", "", { "dependencies": { "tailwind-merge": "3.0.2" }, "peerDependencies": { "tailwindcss": "*" } }, "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA=="], + + "tailwindcss": ["tailwindcss@4.1.12", "", {}, "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA=="], + + "tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="], + + "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + + "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], + + "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], + + "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.3.7", "", {}, "sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "typescript-eslint": ["typescript-eslint@8.41.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.41.0", "@typescript-eslint/parser": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw=="], + + "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], + + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + + "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + + "unplugin": ["unplugin@2.3.9", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-2dcbZq6aprwXTkzptq3k5qm5B8cvpjG9ynPd5fyM2wDJuuF7PeUK64Sxf0d+X1ZyDOeGydbNzMqBSIVlH8GIfA=="], + + "unplugin-icons": ["unplugin-icons@22.2.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/utils": "^2.3.0", "debug": "^4.4.1", "local-pkg": "^1.1.1", "unplugin": "^2.3.5" }, "peerDependencies": { "@svgr/core": ">=7.0.0", "@svgx/core": "^1.0.1", "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0", "vue-template-compiler": "^2.6.12", "vue-template-es2015-compiler": "^1.9.0" }, "optionalPeers": ["@svgr/core", "@svgx/core", "@vue/compiler-sfc", "svelte", "vue-template-compiler", "vue-template-es2015-compiler"] }, "sha512-OdrXCiXexC1rFd0QpliAgcd4cMEEEQtoCf2WIrRIGu4iW6auBPpQKMCBeWxoe55phYdRyZLUWNOtzyTX+HOFSA=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "url-join": ["url-join@4.0.1", "", {}, "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@antfu/install-pkg/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@iconify/utils/globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@vitest/runner/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "@vitest/snapshot/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "bits-ui/runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="], + + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "convict/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + + "exa-js/dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "jest-diff/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "jest-matcher-utils/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "melt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + + "mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="], + + "mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], + + "node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + + "svelte-toolbelt/runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="], + + "tailwind-variants/tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="], + + "vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "jest-diff/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-diff/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "jest-matcher-utils/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "jest-matcher-utils/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], + + "node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + } +} diff --git a/package.json b/package.json index 7c9e83e..4816f95 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,10 @@ "melt": "^0.38.0", "mode-watcher": "^1.0.8", "neverthrow": "^8.2.0", + "@anthropic-ai/sdk": "^0.29.0", + "@google/generative-ai": "^0.21.0", + "@mistralai/mistralai": "^1.1.0", + "cohere-ai": "^7.14.0", "openai": "^5.5.1", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", @@ -83,6 +87,7 @@ "@fontsource-variable/nunito-sans": "^5.2.6", "@fontsource-variable/open-sans": "^5.2.6", "@fontsource/instrument-serif": "^5.2.6", + "@keplersystems/kepler-ai-sdk": "^1.0.5", "better-auth": "^1.2.9", "convex-helpers": "^0.1.94", "hastscript": "^9.0.1", diff --git a/src/lib/backend/convex/schema.ts b/src/lib/backend/convex/schema.ts index 8db117b..9add93b 100644 --- a/src/lib/backend/convex/schema.ts +++ b/src/lib/backend/convex/schema.ts @@ -22,7 +22,6 @@ export default defineSchema({ user_settings: defineTable({ user_id: v.string(), privacy_mode: v.boolean(), - free_messages_used: v.optional(v.number()), }).index('by_user', ['user_id']), user_keys: defineTable({ user_id: v.string(), diff --git a/src/lib/backend/convex/user_keys.ts b/src/lib/backend/convex/user_keys.ts index 9848b07..f120c3d 100644 --- a/src/lib/backend/convex/user_keys.ts +++ b/src/lib/backend/convex/user_keys.ts @@ -89,38 +89,6 @@ export const set = mutation({ await ctx.db.replace(existing._id, userKey); } else { await ctx.db.insert('user_keys', userKey); - - if (args.provider === Provider.OpenRouter) { - const defaultModels = [ - 'google/gemini-2.5-flash', - 'anthropic/claude-sonnet-4', - 'openai/o3-mini', - 'deepseek/deepseek-chat-v3-0324:free', - ]; - - await Promise.all( - defaultModels.map(async (model) => { - const existing = await ctx.db - .query('user_enabled_models') - .withIndex('by_model_provider_user', (q) => - q - .eq('model_id', model) - .eq('provider', Provider.OpenRouter) - .eq('user_id', session.userId) - ) - .first(); - - if (existing) return; - - await ctx.db.insert('user_enabled_models', { - user_id: session.userId, - provider: Provider.OpenRouter, - model_id: model, - pinned: true, - }); - }) - ); - } } }, }); diff --git a/src/lib/backend/convex/user_settings.ts b/src/lib/backend/convex/user_settings.ts index bd77c72..525dc44 100644 --- a/src/lib/backend/convex/user_settings.ts +++ b/src/lib/backend/convex/user_settings.ts @@ -26,41 +26,6 @@ export const get = query({ }, }); -export const incrementFreeMessageCount = mutation({ - args: { - session_token: v.string(), - }, - handler: async (ctx, args) => { - const session = await ctx.runQuery(internal.betterAuth.getSession, { - sessionToken: args.session_token, - }); - - if (!session) { - throw new Error('Invalid session token'); - } - - const s = session as SessionObj; - - const existing = await ctx.db - .query('user_settings') - .withIndex('by_user', (q) => q.eq('user_id', s.userId)) - .first(); - - if (!existing) { - await ctx.db.insert('user_settings', { - user_id: s.userId, - privacy_mode: false, - free_messages_used: 1, - }); - } else { - const currentCount = existing.free_messages_used || 0; - await ctx.db.patch(existing._id, { - free_messages_used: currentCount + 1, - }); - } - }, -}); - export const set = mutation({ args: { privacy_mode: v.boolean(), @@ -86,7 +51,6 @@ export const set = mutation({ await ctx.db.insert('user_settings', { user_id: s.userId, privacy_mode: args.privacy_mode, - free_messages_used: 0, }); } else { await ctx.db.patch(existing._id, { @@ -105,7 +69,6 @@ export const create = mutation({ await ctx.db.insert('user_settings', { user_id: args.user_id, privacy_mode: false, - free_messages_used: 0, }); }, -}); +}); \ No newline at end of file diff --git a/src/lib/services/model-manager.ts b/src/lib/services/model-manager.ts new file mode 100644 index 0000000..34d7c72 --- /dev/null +++ b/src/lib/services/model-manager.ts @@ -0,0 +1,120 @@ +import { + ModelManager, + OpenAIProvider, + AnthropicProvider, + GeminiProvider, + MistralProvider, + CohereProvider, + OpenRouterProvider, + type ProviderAdapter, + type ModelInfo, +} from '@keplersystems/kepler-ai-sdk'; +import type { Provider } from '$lib/types'; + +export interface ProviderConfig { + apiKey: string; + baseURL?: string; +} + +export interface UserApiKeys { + openai?: string; + anthropic?: string; + gemini?: string; + mistral?: string; + cohere?: string; + openrouter?: string; +} + +export class ChatModelManager { + private modelManager: ModelManager; + private enabledProviders: Map = new Map(); + + constructor() { + this.modelManager = new ModelManager(); + } + + initializeProviders(userApiKeys: UserApiKeys): void { + this.enabledProviders.clear(); + + if (userApiKeys.openai) { + const provider = new OpenAIProvider({ + apiKey: userApiKeys.openai, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('openai', provider); + } + + if (userApiKeys.anthropic) { + const provider = new AnthropicProvider({ + apiKey: userApiKeys.anthropic, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('anthropic', provider); + } + + if (userApiKeys.gemini) { + const provider = new GeminiProvider({ + apiKey: userApiKeys.gemini, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('gemini', provider); + } + + if (userApiKeys.mistral) { + const provider = new MistralProvider({ + apiKey: userApiKeys.mistral, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('mistral', provider); + } + + if (userApiKeys.cohere) { + const provider = new CohereProvider({ + apiKey: userApiKeys.cohere, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('cohere', provider); + } + + if (userApiKeys.openrouter) { + const provider = new OpenRouterProvider({ + apiKey: userApiKeys.openrouter, + }); + this.modelManager.addProvider(provider); + this.enabledProviders.set('openrouter', provider); + } + } + + async getModel(modelId: string): Promise { + return await this.modelManager.getModel(modelId); + } + + getProvider(providerName: string): ProviderAdapter | undefined { + return this.modelManager.getProvider(providerName); + } + + async listAvailableModels(): Promise { + return await this.modelManager.listModels(); + } + + async getModelsByCapability(capability: string): Promise { + const allModels = await this.listAvailableModels(); + return allModels.filter(model => model.capabilities[capability as keyof typeof model.capabilities]); + } + + hasProviderEnabled(provider: Provider): boolean { + return this.enabledProviders.has(provider); + } + + getEnabledProviders(): Provider[] { + return Array.from(this.enabledProviders.keys()); + } + + isModelAvailable(modelId: string): Promise { + return this.getModel(modelId).then(model => model !== null); + } +} + +export const createModelManager = (): ChatModelManager => { + return new ChatModelManager(); +}; \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index 05ab240..3fdae2f 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,10 +1,12 @@ import { z } from 'zod'; export const Provider = { - OpenRouter: 'openrouter', - HuggingFace: 'huggingface', OpenAI: 'openai', Anthropic: 'anthropic', + Gemini: 'gemini', + Mistral: 'mistral', + Cohere: 'cohere', + OpenRouter: 'openrouter', } as const; export type Provider = (typeof Provider)[keyof typeof Provider]; @@ -13,8 +15,13 @@ export type ProviderMeta = { title: string; link: string; description: string; - models?: string[]; - placeholder?: string; + apiKeyName: string; + placeholder: string; + docsLink: string; + supportsStreaming: boolean; + supportsTools: boolean; + supportsVision: boolean; + supportsEmbeddings: boolean; }; export const UrlCitationSchema = z.object({ @@ -34,3 +41,78 @@ export type UrlCitation = z.infer; // export const AnnotationSchema = z.union([UrlCitationSchema, ...]); export const AnnotationSchema = UrlCitationSchema; export type Annotation = z.infer; + +export const PROVIDER_META: Record = { + [Provider.OpenAI]: { + title: 'OpenAI', + link: 'https://openai.com', + description: 'GPT models, DALL-E, and Whisper from OpenAI', + apiKeyName: 'OpenAI API Key', + placeholder: 'sk-...', + docsLink: 'https://platform.openai.com/docs', + supportsStreaming: true, + supportsTools: true, + supportsVision: true, + supportsEmbeddings: true, + }, + [Provider.Anthropic]: { + title: 'Anthropic', + link: 'https://anthropic.com', + description: 'Claude models from Anthropic', + apiKeyName: 'Anthropic API Key', + placeholder: 'sk-ant-...', + docsLink: 'https://docs.anthropic.com', + supportsStreaming: true, + supportsTools: true, + supportsVision: true, + supportsEmbeddings: false, + }, + [Provider.Gemini]: { + title: 'Google Gemini', + link: 'https://cloud.google.com/vertex-ai', + description: 'Gemini models from Google', + apiKeyName: 'Google AI API Key', + placeholder: 'AIza...', + docsLink: 'https://ai.google.dev/docs', + supportsStreaming: true, + supportsTools: true, + supportsVision: true, + supportsEmbeddings: true, + }, + [Provider.Mistral]: { + title: 'Mistral', + link: 'https://mistral.ai', + description: 'Mistral models and embeddings', + apiKeyName: 'Mistral API Key', + placeholder: 'mistral-...', + docsLink: 'https://docs.mistral.ai', + supportsStreaming: true, + supportsTools: true, + supportsVision: false, + supportsEmbeddings: true, + }, + [Provider.Cohere]: { + title: 'Cohere', + link: 'https://cohere.com', + description: 'Command models and embeddings from Cohere', + apiKeyName: 'Cohere API Key', + placeholder: 'co_...', + docsLink: 'https://docs.cohere.com', + supportsStreaming: true, + supportsTools: true, + supportsVision: false, + supportsEmbeddings: true, + }, + [Provider.OpenRouter]: { + title: 'OpenRouter', + link: 'https://openrouter.ai', + description: 'Access to 300+ models through OpenRouter', + apiKeyName: 'OpenRouter API Key', + placeholder: 'sk-or-...', + docsLink: 'https://openrouter.ai/docs', + supportsStreaming: true, + supportsTools: true, + supportsVision: true, + supportsEmbeddings: false, + }, +}; diff --git a/src/lib/utils/providers.ts b/src/lib/utils/providers.ts index 662043f..edfe692 100644 --- a/src/lib/utils/providers.ts +++ b/src/lib/utils/providers.ts @@ -1,34 +1,212 @@ import { Result, ResultAsync } from 'neverthrow'; +import { Provider, PROVIDER_META } from '$lib/types'; -export type OpenRouterApiKeyData = { +export type ProviderApiKeyData = { label: string; - usage: number; - is_free_tier: boolean; - is_provisioning_key: boolean; - limit: number; - limit_remaining: number; + usage?: number; + is_free_tier?: boolean; + is_provisioning_key?: boolean; + limit?: number; + limit_remaining?: number; + valid: boolean; }; -export const OpenRouter = { - getApiKey: async (key: string): Promise> => { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch('https://openrouter.ai/api/v1/key', { - headers: { - Authorization: `Bearer ${key}`, - 'Content-Type': 'application/json', - }, - }); +export const ProviderUtils = { + /** + * Validate an API key for a specific provider + */ + validateApiKey: async (provider: Provider, key: string): Promise> => { + switch (provider) { + case Provider.OpenRouter: + return await validateOpenRouterKey(key); + case Provider.OpenAI: + return await validateOpenAIKey(key); + case Provider.Anthropic: + return await validateAnthropicKey(key); + case Provider.Gemini: + return await validateGeminiKey(key); + case Provider.Mistral: + return await validateMistralKey(key); + case Provider.Cohere: + return await validateCohereKey(key); + default: + return Result.err(`Validation not implemented for provider: ${provider}`); + } + }, - if (!res.ok) throw new Error('Failed to get API key'); + /** + * Get provider metadata + */ + getProviderMeta: (provider: Provider) => { + return PROVIDER_META[provider]; + }, - const { data } = await res.json(); + /** + * Check if a provider is supported + */ + isProviderSupported: (provider: string): provider is Provider => { + return Object.values(Provider).includes(provider as Provider); + }, - if (!data) throw new Error('No info returned for api key'); - - return data as OpenRouterApiKeyData; - })(), - (e) => `Failed to get API key ${e}` - ); + /** + * Get all supported providers + */ + getSupportedProviders: () => { + return Object.values(Provider); }, }; + +// Provider-specific validation functions +async function validateOpenRouterKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://openrouter.ai/api/v1/auth/key', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + const { data } = await res.json(); + + if (!data) { + throw new Error('No key information returned'); + } + + return { + label: data.label || 'OpenRouter API Key', + usage: data.usage, + is_free_tier: data.is_free_tier, + is_provisioning_key: data.is_provisioning_key, + limit: data.limit, + limit_remaining: data.limit_remaining, + valid: true, + }; + })(), + (e) => `Failed to validate OpenRouter API key: ${e}` + ); +} + +async function validateOpenAIKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.openai.com/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'OpenAI API Key', + valid: true, + }; + })(), + (e) => `Failed to validate OpenAI API key: ${e}` + ); +} + +async function validateAnthropicKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + // Anthropic doesn't have a simple key validation endpoint + // We'll try a minimal request to test the key + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'x-api-key': key, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: 'claude-3-haiku-20240307', + max_tokens: 1, + messages: [{ role: 'user', content: 'test' }], + }), + }); + + // Even a 400 error means the key is valid (just bad request format) + if (res.status === 401 || res.status === 403) { + throw new Error('Invalid API key'); + } + + return { + label: 'Anthropic API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Anthropic API key: ${e}` + ); +} + +async function validateGeminiKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${key}`); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Google Gemini API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Gemini API key: ${e}` + ); +} + +async function validateMistralKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.mistral.ai/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Mistral API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Mistral API key: ${e}` + ); +} + +async function validateCohereKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.cohere.ai/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Cohere API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Cohere API key: ${e}` + ); +} \ No newline at end of file diff --git a/src/routes/account/api-keys/+page.svelte b/src/routes/account/api-keys/+page.svelte index b496fe9..78ba7cf 100644 --- a/src/routes/account/api-keys/+page.svelte +++ b/src/routes/account/api-keys/+page.svelte @@ -1,45 +1,8 @@ @@ -49,17 +12,13 @@

API Keys

- Bring your own API keys for select models. Messages sent using your API keys will not count - towards your monthly limits. + Add your API keys to access models from different AI providers. You need at least one API key to use the chat.

{#each allProviders as provider (provider)} - - {#if provider === Provider.OpenRouter} - {@const meta = providersMeta[provider]} - - {/if} + {@const meta = PROVIDER_META[provider]} + {/each} -
+
\ No newline at end of file diff --git a/src/routes/account/api-keys/provider-card.svelte b/src/routes/account/api-keys/provider-card.svelte index 1322f72..c348009 100644 --- a/src/routes/account/api-keys/provider-card.svelte +++ b/src/routes/account/api-keys/provider-card.svelte @@ -12,7 +12,7 @@ import { useConvexClient } from 'convex-svelte'; import { ResultAsync } from 'neverthrow'; import { resource } from 'runed'; - import * as providers from '$lib/utils/providers'; + import { ProviderUtils } from '$lib/utils/providers'; type Props = { provider: Provider; @@ -65,11 +65,8 @@ async (key) => { if (!key) return null; - if (provider === Provider.OpenRouter) { - return (await providers.OpenRouter.getApiKey(key)).unwrapOr(null); - } - - return null; + const result = await ProviderUtils.validateApiKey(provider, key); + return result.unwrapOr(null); } ); @@ -99,11 +96,17 @@ {#if apiKeyInfoResource.loading}
{:else if apiKeyInfoResource.current} - - ${apiKeyInfoResource.current?.usage.toFixed(3)} used ・ ${apiKeyInfoResource.current?.limit_remaining.toFixed( - 3 - )} remaining - + {#if apiKeyInfoResource.current.usage !== undefined && apiKeyInfoResource.current.limit_remaining !== undefined} + + ${apiKeyInfoResource.current.usage.toFixed(3)} used ・ ${apiKeyInfoResource.current.limit_remaining.toFixed( + 3 + )} remaining + + {:else} + + ✅ API key is valid + + {/if} {:else} { + const keysResult = await ResultAsync.fromPromise( + client.query(api.user_keys.all, { + session_token: sessionToken, + }), + (e) => `Failed to get user API keys: ${e}` + ); + + if (keysResult.isErr()) { + return null; + } + + const keys = keysResult.value; + return { + openai: keys.openai, + anthropic: keys.anthropic, + gemini: keys.gemini, + mistral: keys.mistral, + cohere: keys.cohere, + openrouter: keys.openrouter, + }; +} + export const POST: RequestHandler = async ({ request, locals }) => { const bodyResult = await ResultAsync.fromPromise( request.json(), @@ -53,42 +73,63 @@ export const POST: RequestHandler = async ({ request, locals }) => { return error(401, 'You must be logged in to enhance a prompt'); } - const [rulesResult, keyResult] = await Promise.all([ - ResultAsync.fromPromise( - client.query(api.user_rules.all, { - session_token: session.session.token, - }), - (e) => `Failed to get rules: ${e}` - ), - ResultAsync.fromPromise( - client.query(api.user_keys.get, { - provider: Provider.OpenRouter, - session_token: session.session.token, - }), - (e) => `Failed to get API key: ${e}` - ), - ]); + // Get user API keys + const userApiKeys = await getUserApiKeys(session.session.token); + if (!userApiKeys) { + return error(500, 'Failed to get user API keys'); + } + + const hasAnyKey = Object.values(userApiKeys).some((key) => key); + if (!hasAnyKey) { + return error( + 400, + 'No API keys configured. Please add at least one provider API key in settings to enhance prompts.' + ); + } + + // Get user rules for context + const rulesResult = await ResultAsync.fromPromise( + client.query(api.user_rules.all, { + session_token: session.session.token, + }), + (e) => `Failed to get rules: ${e}` + ); if (rulesResult.isErr()) { return error(500, 'Failed to get rules'); } - if (keyResult.isErr()) { - return error(500, 'Failed to get key'); - } - const mentionedRules = parseMessageForRules( args.prompt, rulesResult.value.filter((r) => r.attach === 'manual') ); - const openai = new OpenAI({ - baseURL: 'https://openrouter.ai/api/v1', - apiKey: keyResult.value ?? OPENROUTER_FREE_KEY, - }); + // Initialize model manager with user's API keys + const modelManager = createModelManager(); + modelManager.initializeProviders(userApiKeys); + + // Try to find a fast, cheap model for prompt enhancement + const availableModels = await modelManager.listAvailableModels(); + const enhanceModel = + availableModels.find( + (model) => + model.id.includes('kimi-k2') || + model.id.includes('gemini-2.5-flash-lite') || + model.id.includes('gpt-5-mini') || + model.id.includes('mistral-small') + ) || availableModels[0]; + + if (!enhanceModel) { + return error(500, 'No suitable models available for prompt enhancement'); + } + + const provider = modelManager.getProvider(enhanceModel.provider); + if (!provider) { + return error(500, `Provider ${enhanceModel.provider} not available`); + } const enhancePrompt = ` -Enhance prompt below (wrapped in tags) so that it can be better understood by LLMs You job is not to answer the prompt but simply prepare it to be answered by another LLM. +Enhance prompt below (wrapped in tags) so that it can be better understood by LLMs You job is not to answer the prompt but simply prepare it to be answered by another LLM. You can do this by fixing spelling/grammatical errors, clarifying details, and removing unnecessary wording where possible. Only return the enhanced prompt, nothing else. Do NOT wrap it in quotes, do NOT use markdown. Do NOT respond to the prompt only optimize it so that another LLM can understand it better. @@ -107,23 +148,24 @@ ${args.prompt} `; const enhancedResult = await ResultAsync.fromPromise( - openai.chat.completions.create({ - model: FREE_MODEL, + provider.generateCompletion({ + model: enhanceModel.id, messages: [{ role: 'user', content: enhancePrompt }], temperature: 0.5, + maxTokens: 1000, }), (e) => `Enhance prompt API call failed: ${e}` ); if (enhancedResult.isErr()) { - return error(500, 'error enhancing the prompt'); + return error(500, 'Error enhancing the prompt'); } const enhancedResponse = enhancedResult.value; - const enhanced = enhancedResponse.choices[0]?.message?.content; + const enhanced = enhancedResponse.content?.trim(); if (!enhanced) { - return error(500, 'error enhancing the prompt'); + return error(500, 'Error enhancing the prompt'); } return response({ diff --git a/src/routes/api/generate-message/+server.ts b/src/routes/api/generate-message/+server.ts index f255aad..3d3737e 100644 --- a/src/routes/api/generate-message/+server.ts +++ b/src/routes/api/generate-message/+server.ts @@ -1,5 +1,4 @@ import { PUBLIC_CONVEX_URL } from '$env/static/public'; -import { OPENROUTER_FREE_KEY } from '$env/static/private'; import { api } from '$lib/backend/convex/_generated/api'; import type { Doc, Id } from '$lib/backend/convex/_generated/dataModel'; import { Provider, type Annotation } from '$lib/types'; @@ -8,12 +7,13 @@ import { waitUntil } from '@vercel/functions'; import { getSessionCookie } from 'better-auth/cookies'; import { ConvexHttpClient } from 'convex/browser'; import { err, ok, Result, ResultAsync } from 'neverthrow'; -import OpenAI from 'openai'; import { z } from 'zod/v4'; import { generationAbortControllers } from './cache.js'; import { md } from '$lib/utils/markdown-it.js'; import * as array from '$lib/utils/array'; import { parseMessageForRules } from '$lib/utils/rules.js'; +import { createModelManager, type ChatModelManager } from '$lib/services/model-manager.js'; +import type { UserApiKeys } from '$lib/services/model-manager.js'; // Set to true to enable debug logging const ENABLE_LOGGING = true; @@ -22,7 +22,6 @@ const reqBodySchema = z .object({ message: z.string().optional(), model_id: z.string(), - session_token: z.string(), conversation_id: z.string().optional(), web_search_enabled: z.boolean().optional(), @@ -40,7 +39,6 @@ const reqBodySchema = z .refine( (data) => { if (data.conversation_id === undefined && data.message === undefined) return false; - return true; }, { @@ -67,34 +65,45 @@ function log(message: string, startTime: number): void { const client = new ConvexHttpClient(PUBLIC_CONVEX_URL); +async function getUserApiKeys(sessionToken: string): Promise> { + const keysResult = await ResultAsync.fromPromise( + client.query(api.user_keys.all, { + session_token: sessionToken, + }), + (e) => `Failed to get user API keys: ${e}` + ); + + if (keysResult.isErr()) { + return err(keysResult.error); + } + + const keys = keysResult.value; + return ok({ + openai: keys.openai, + anthropic: keys.anthropic, + gemini: keys.gemini, + mistral: keys.mistral, + cohere: keys.cohere, + openrouter: keys.openrouter, + }); +} + async function generateConversationTitle({ conversationId, sessionToken, startTime, - keyResultPromise, userMessage, + modelManager, }: { conversationId: string; sessionToken: string; startTime: number; - keyResultPromise: ResultAsync; userMessage: string; + modelManager: ChatModelManager; }) { log('Starting conversation title generation', startTime); - const keyResult = await keyResultPromise; - - if (keyResult.isErr()) { - log(`Title generation: API key error: ${keyResult.error}`, startTime); - return; - } - - const userKey = keyResult.value; - const actualKey = userKey || OPENROUTER_FREE_KEY; - - log(`Title generation: Using ${userKey ? 'user' : 'free tier'} API key`, startTime); - - // Only generate title if conversation currently has default title + // Check if conversation currently has default title const conversationResult = await ResultAsync.fromPromise( client.query(api.conversations.get, { session_token: sessionToken, @@ -115,12 +124,25 @@ async function generateConversationTitle({ return; } - const openai = new OpenAI({ - baseURL: 'https://openrouter.ai/api/v1', - apiKey: actualKey, - }); + // Try to find a fast, cheap model for title generation + const availableModels = await modelManager.listAvailableModels(); + const titleModel = + availableModels.find((model) => model.id.includes('kimi-k2')) || + availableModels.find((model) => model.id.includes('gemini-2.5-flash-lite')) || + availableModels.find((model) => model.id.includes('gpt-5-mini')) || + availableModels[0]; + + if (!titleModel) { + log('Title generation: No suitable model available', startTime); + return; + } + + const provider = modelManager.getProvider(titleModel.provider); + if (!provider) { + log(`Title generation: Provider ${titleModel.provider} not found`, startTime); + return; + } - // Create a prompt for title generation using only the first user message const titlePrompt = `Based on this message: """${userMessage}""" @@ -129,26 +151,25 @@ Generate only the title based on the message, nothing else. Don't name the title Also, do not interact with the message directly or answer it. Just generate the title based on the message. -If its a simple hi, just name it "Greeting" or something like that. -`; +If its a simple hi, just name it "Greeting" or something like that.`; const titleResult = await ResultAsync.fromPromise( - openai.chat.completions.create({ - model: 'mistralai/ministral-8b', + provider.generateCompletion({ + model: titleModel.id, messages: [{ role: 'user', content: titlePrompt }], - max_tokens: 20, + maxTokens: 1024, temperature: 0.5, }), (e) => `Title generation API call failed: ${e}` ); if (titleResult.isErr()) { - log(`Title generation: OpenAI call failed: ${titleResult.error}`, startTime); + log(`Title generation: API call failed: ${titleResult.error}`, startTime); return; } const titleResponse = titleResult.value; - const rawTitle = titleResponse.choices[0]?.message?.content?.trim(); + const rawTitle = titleResponse.content?.trim(); if (!rawTitle) { log('Title generation: No title generated', startTime); @@ -180,20 +201,18 @@ async function generateAIResponse({ conversationId, sessionToken, startTime, - modelResultPromise, - keyResultPromise, + modelId, + modelManager, rulesResultPromise, - userSettingsPromise, abortSignal, reasoningEffort, }: { conversationId: string; sessionToken: string; startTime: number; - keyResultPromise: ResultAsync; - modelResultPromise: ResultAsync | null, string>; + modelId: string; + modelManager: ChatModelManager; rulesResultPromise: ResultAsync[], string>; - userSettingsPromise: ResultAsync | null, string>; abortSignal?: AbortSignal; reasoningEffort?: 'low' | 'medium' | 'high'; }) { @@ -204,36 +223,11 @@ async function generateAIResponse({ return; } - const [modelResult, keyResult, messagesQueryResult, rulesResult, userSettingsResult] = - await Promise.all([ - modelResultPromise, - keyResultPromise, - ResultAsync.fromPromise( - client.query(api.messages.getAllFromConversation, { - conversation_id: conversationId as Id<'conversations'>, - session_token: sessionToken, - }), - (e) => `Failed to get messages: ${e}` - ), - rulesResultPromise, - userSettingsPromise, - ]); - - if (modelResult.isErr()) { - handleGenerationError({ - error: modelResult.error, - conversationId, - messageId: undefined, - sessionToken, - startTime, - }); - return; - } - - const model = modelResult.value; + // Get model and provider + const model = await modelManager.getModel(modelId); if (!model) { handleGenerationError({ - error: 'Model not found or not enabled', + error: `Model ${modelId} not found or not available`, conversationId, messageId: undefined, sessionToken, @@ -242,7 +236,30 @@ async function generateAIResponse({ return; } - log('Background: Model found and enabled', startTime); + const provider = modelManager.getProvider(model.provider); + if (!provider) { + handleGenerationError({ + error: `Provider ${model.provider} not available`, + conversationId, + messageId: undefined, + sessionToken, + startTime, + }); + return; + } + + log(`Background: Using model ${modelId} with provider ${model.provider}`, startTime); + + const [messagesQueryResult, rulesResult] = await Promise.all([ + ResultAsync.fromPromise( + client.query(api.messages.getAllFromConversation, { + conversation_id: conversationId as Id<'conversations'>, + session_token: sessionToken, + }), + (e) => `Failed to get messages: ${e}` + ), + rulesResultPromise, + ]); if (messagesQueryResult.isErr()) { handleGenerationError({ @@ -262,14 +279,14 @@ async function generateAIResponse({ const lastUserMessage = messages.filter((m) => m.role === 'user').pop(); const webSearchEnabled = lastUserMessage?.web_search_enabled ?? false; - const modelId = webSearchEnabled ? `${model.model_id}:online` : model.model_id; + const finalModelId = webSearchEnabled ? `${modelId}:online` : modelId; // Create assistant message const messageCreationResult = await ResultAsync.fromPromise( client.mutation(api.messages.create, { conversation_id: conversationId, - model_id: model.model_id, - provider: Provider.OpenRouter, + model_id: modelId, + provider: model.provider as Provider, content: '', role: 'assistant', session_token: sessionToken, @@ -292,84 +309,6 @@ async function generateAIResponse({ const mid = messageCreationResult.value; log('Background: Assistant message created', startTime); - if (keyResult.isErr()) { - handleGenerationError({ - error: `API key query failed: ${keyResult.error}`, - conversationId, - messageId: mid, - sessionToken, - startTime, - }); - return; - } - - if (userSettingsResult.isErr()) { - handleGenerationError({ - error: `User settings query failed: ${userSettingsResult.error}`, - conversationId, - messageId: mid, - sessionToken, - startTime, - }); - return; - } - - const userKey = keyResult.value; - const userSettings = userSettingsResult.value; - let actualKey: string; - - if (userKey) { - // User has their own API key - actualKey = userKey; - log('Background: Using user API key', startTime); - } else { - // User doesn't have API key, check if using a free model - const isFreeModel = model.model_id.endsWith(':free'); - - if (!isFreeModel) { - // For non-free models, check the 10 message limit - const freeMessagesUsed = userSettings?.free_messages_used || 0; - - if (freeMessagesUsed >= 10) { - handleGenerationError({ - error: - 'Free message limit reached (10/10). Please add your own OpenRouter API key to continue chatting, or use a free model ending in ":free".', - conversationId, - messageId: mid, - sessionToken, - startTime, - }); - return; - } - - // Increment free message count before generating (only for non-free models) - const incrementResult = await ResultAsync.fromPromise( - client.mutation(api.user_settings.incrementFreeMessageCount, { - session_token: sessionToken, - }), - (e) => `Failed to increment free message count: ${e}` - ); - - if (incrementResult.isErr()) { - handleGenerationError({ - error: `Failed to track free message usage: ${incrementResult.error}`, - conversationId, - messageId: mid, - sessionToken, - startTime, - }); - return; - } - - log(`Background: Using free tier (${freeMessagesUsed + 1}/10 messages)`, startTime); - } else { - log(`Background: Using free model (${model.model_id}) - no message count`, startTime); - } - - // Use environment OpenRouter key - actualKey = OPENROUTER_FREE_KEY; - } - if (rulesResult.isErr()) { handleGenerationError({ error: `rules query failed: ${rulesResult.error}`, @@ -405,7 +344,7 @@ async function generateAIResponse({ attachedRules.push(...parsedRules); } - // remove duplicates + // Remove duplicates attachedRules = array.fromMap( array.toMap(attachedRules, (r) => [r._id, r]), (_k, v) => v @@ -413,19 +352,14 @@ async function generateAIResponse({ log(`Background: ${attachedRules.length} rules attached`, startTime); - const openai = new OpenAI({ - baseURL: 'https://openrouter.ai/api/v1', - apiKey: actualKey, - }); - const formattedMessages = messages.map((m) => { if (m.images && m.images.length > 0 && m.role === 'user') { return { role: 'user' as const, content: [ - { type: 'text' as const, text: m.content }, + { type: 'text', text: m.content }, ...m.images.map((img) => ({ - type: 'image_url' as const, + type: 'image_url', image_url: { url: img.url }, })), ], @@ -462,20 +396,16 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, return; } + // Generate completion with streaming const streamResult = await ResultAsync.fromPromise( - openai.chat.completions.create( - { - model: modelId, - messages: messagesToSend, - temperature: 0.7, - stream: true, - reasoning_effort: reasoningEffort, - }, - { - signal: abortSignal, - } - ), - (e) => `OpenAI API call failed: ${e}` + provider.generateCompletion({ + model: finalModelId, + messages: messagesToSend, + temperature: 0.7, + stream: true, + ...(reasoningEffort && { reasoning_effort: reasoningEffort }), + }), + (e) => `API call failed: ${e}` ); if (streamResult.isErr()) { @@ -490,7 +420,7 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, } const stream = streamResult.value; - log('Background: OpenAI stream created successfully', startTime); + log('Background: Stream created successfully', startTime); let content = ''; let reasoning = ''; @@ -499,23 +429,61 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, const annotations: Annotation[] = []; try { - for await (const chunk of stream) { - if (abortSignal?.aborted) { - log('AI response generation aborted during streaming', startTime); - break; + // Handle streaming response + if (stream && typeof stream[Symbol.asyncIterator] === 'function') { + for await (const chunk of stream) { + if (abortSignal?.aborted) { + log('AI response generation aborted during streaming', startTime); + break; + } + + chunkCount++; + + // Extract content from chunk based on the stream format + if (chunk && typeof chunk === 'object') { + const chunkContent = chunk.content || chunk.text || ''; + const chunkReasoning = chunk.reasoning || ''; + const chunkAnnotations = chunk.annotations || []; + + reasoning += chunkReasoning; + content += chunkContent; + annotations.push(...chunkAnnotations); + + if (!content && !reasoning) continue; + + generationId = chunk.id || generationId; + + const updateResult = await ResultAsync.fromPromise( + client.mutation(api.messages.updateContent, { + message_id: mid, + content, + reasoning: reasoning.length > 0 ? reasoning : undefined, + session_token: sessionToken, + generation_id: generationId, + annotations, + reasoning_effort: reasoningEffort, + }), + (e) => `Failed to update message content: ${e}` + ); + + if (updateResult.isErr()) { + log( + `Background message update failed on chunk ${chunkCount}: ${updateResult.error}`, + startTime + ); + } + } } + } else { + // Handle non-streaming response + const response = stream as any; + content = response.content || response.text || ''; + reasoning = response.reasoning || ''; + generationId = response.id; - chunkCount++; - - // @ts-expect-error you're wrong - reasoning += chunk.choices[0]?.delta?.reasoning || ''; - content += chunk.choices[0]?.delta?.content || ''; - // @ts-expect-error you're wrong - annotations.push(...(chunk.choices[0]?.delta?.annotations ?? [])); - - if (!content && !reasoning) continue; - - generationId = chunk.id; + if (response.annotations) { + annotations.push(...response.annotations); + } const updateResult = await ResultAsync.fromPromise( client.mutation(api.messages.updateContent, { @@ -531,10 +499,7 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, ); if (updateResult.isErr()) { - log( - `Background message update failed on chunk ${chunkCount}: ${updateResult.error}`, - startTime - ); + log(`Background message update failed: ${updateResult.error}`, startTime); } } @@ -543,50 +508,20 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, startTime ); - if (!generationId) { - log('Background: No generation id found', startTime); - return; - } - + // Final message update with completion stats const contentHtmlResultPromise = ResultAsync.fromPromise( md.renderAsync(content), (e) => `Failed to render HTML: ${e}` ); - const generationStatsResult = await retryResult( - () => getGenerationStats(generationId!, actualKey), - { - delay: 500, - retries: 2, - startTime, - fnName: 'getGenerationStats', - } - ); - - if (generationStatsResult.isErr()) { - log(`Background: Failed to get generation stats: ${generationStatsResult.error}`, startTime); - } - - // just default so we don't blow up - const generationStats = generationStatsResult.unwrapOr({ - tokens_completion: undefined, - total_cost: undefined, - }); - - log('Background: Got generation stats', startTime); - const contentHtmlResult = await contentHtmlResultPromise; - if (contentHtmlResult.isErr()) { - log(`Background: Failed to render HTML: ${contentHtmlResult.error}`, startTime); - } - - const [updateMessageResult, updateGeneratingResult, updateCostUsdResult] = await Promise.all([ + const [updateMessageResult, updateGeneratingResult] = await Promise.all([ ResultAsync.fromPromise( client.mutation(api.messages.updateMessage, { message_id: mid, - token_count: generationStats.tokens_completion, - cost_usd: generationStats.total_cost, + token_count: undefined, // Will be calculated by provider if available + cost_usd: undefined, // Will be calculated by provider if available generation_id: generationId, session_token: sessionToken, content_html: contentHtmlResult.unwrapOr(undefined), @@ -601,14 +536,6 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, }), (e) => `Failed to update generating status: ${e}` ), - ResultAsync.fromPromise( - client.mutation(api.conversations.updateCostUsd, { - conversation_id: conversationId as Id<'conversations'>, - cost_usd: generationStats.total_cost ?? 0, - session_token: sessionToken, - }), - (e) => `Failed to update cost usd: ${e}` - ), ]); if (updateGeneratingResult.isErr()) { @@ -624,13 +551,6 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, } log('Background: Message updated', startTime); - - if (updateCostUsdResult.isErr()) { - log(`Background cost usd update failed: ${updateCostUsdResult.error}`, startTime); - return; - } - - log('Background: Cost usd updated', startTime); } catch (error) { handleGenerationError({ error: `Stream processing error: ${error}`, @@ -672,7 +592,6 @@ export const POST: RequestHandler = async ({ request }) => { log('Schema validation passed', startTime); const cookie = getSessionCookie(request.headers); - const sessionToken = cookie?.split('.')[0] ?? null; if (!sessionToken) { @@ -680,29 +599,37 @@ export const POST: RequestHandler = async ({ request }) => { return error(401, 'Unauthorized'); } - const modelResultPromise = ResultAsync.fromPromise( - client.query(api.user_enabled_models.get, { - provider: Provider.OpenRouter, - model_id: args.model_id, - session_token: sessionToken, - }), - (e) => `Failed to get model: ${e}` - ); + // Get user API keys + const userApiKeysResult = await getUserApiKeys(sessionToken); + if (userApiKeysResult.isErr()) { + log(`Failed to get user API keys: ${userApiKeysResult.error}`, startTime); + return error(500, 'Failed to get user API keys'); + } - const keyResultPromise = ResultAsync.fromPromise( - client.query(api.user_keys.get, { - provider: Provider.OpenRouter, - session_token: sessionToken, - }), - (e) => `Failed to get API key: ${e}` - ); + const userApiKeys = userApiKeysResult.value; + const hasAnyKey = Object.values(userApiKeys).some((key) => key); - const userSettingsPromise = ResultAsync.fromPromise( - client.query(api.user_settings.get, { - session_token: sessionToken, - }), - (e) => `Failed to get user settings: ${e}` - ); + if (!hasAnyKey) { + log('User has no API keys configured', startTime); + return error( + 400, + 'No API keys configured. Please add at least one provider API key in settings.' + ); + } + + // Initialize model manager with user's API keys + const modelManager = createModelManager(); + modelManager.initializeProviders(userApiKeys); + + // Check if the requested model is available + const modelAvailable = await modelManager.isModelAvailable(args.model_id); + if (!modelAvailable) { + log(`Requested model ${args.model_id} not available`, startTime); + return error( + 400, + `Model ${args.model_id} is not available. Please check your API keys and try a different model.` + ); + } const rulesResultPromise = ResultAsync.fromPromise( client.query(api.user_rules.all, { @@ -715,7 +642,7 @@ export const POST: RequestHandler = async ({ request }) => { let conversationId = args.conversation_id; if (!conversationId) { - // technically zod should catch this but just in case + // Create new conversation if (args.message === undefined) { return error(400, 'You must provide a message when creating a new conversation'); } @@ -746,8 +673,8 @@ export const POST: RequestHandler = async ({ request }) => { conversationId, sessionToken, startTime, - keyResultPromise, userMessage: args.message, + modelManager, }).catch((error) => { log(`Background title generation error: ${error}`, startTime); }) @@ -798,16 +725,15 @@ export const POST: RequestHandler = async ({ request }) => { const abortController = new AbortController(); generationAbortControllers.set(conversationId, abortController); - // Start AI response generation in background - don't await + // Start AI response generation in background waitUntil( generateAIResponse({ conversationId, sessionToken, startTime, - modelResultPromise, - keyResultPromise, + modelId: args.model_id, + modelManager, rulesResultPromise, - userSettingsPromise, abortSignal: abortController.signal, reasoningEffort: args.reasoning_effort, }) @@ -834,57 +760,6 @@ export const POST: RequestHandler = async ({ request }) => { return response({ ok: true, conversation_id: conversationId }); }; -async function getGenerationStats( - generationId: string, - token: string -): Promise> { - try { - const generation = await fetch(`https://openrouter.ai/api/v1/generation?id=${generationId}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - const { data } = await generation.json(); - - if (!data) { - return err('No data returned from OpenRouter'); - } - - return ok(data); - } catch { - return err('Failed to get generation stats'); - } -} - -async function retryResult( - fn: () => Promise>, - { - retries, - delay, - startTime, - fnName, - }: { retries: number; delay: number; startTime: number; fnName: string } -): Promise> { - let attempts = 0; - let lastResult: Result | null = null; - - while (attempts <= retries) { - lastResult = await fn(); - - if (lastResult.isOk()) return lastResult; - - log(`Retrying ${fnName} ${attempts} failed: ${lastResult.error}`, startTime); - - await new Promise((resolve) => setTimeout(resolve, delay)); - attempts++; - } - - if (!lastResult) throw new Error('This should never happen'); - - return lastResult; -} - async function handleGenerationError({ error, conversationId, @@ -917,38 +792,3 @@ async function handleGenerationError({ log('Error updated', startTime); } - -export interface ApiResponse { - data: Data; -} - -export interface Data { - created_at: string; - model: string; - app_id: string | null; - external_user: string | null; - streamed: boolean; - cancelled: boolean; - latency: number; - moderation_latency: number | null; - generation_time: number; - tokens_prompt: number; - tokens_completion: number; - native_tokens_prompt: number; - native_tokens_completion: number; - native_tokens_reasoning: number; - native_tokens_cached: number; - num_media_prompt: number | null; - num_media_completion: number | null; - num_search_results: number | null; - origin: string; - is_byok: boolean; - finish_reason: string; - native_finish_reason: string; - usage: number; - id: string; - upstream_id: string; - total_cost: number; - cache_discount: number | null; - provider_name: string; -} diff --git a/tmp/kepler-ai-sdk b/tmp/kepler-ai-sdk new file mode 160000 index 0000000..73461f9 --- /dev/null +++ b/tmp/kepler-ai-sdk @@ -0,0 +1 @@ +Subproject commit 73461f942496d91e098d2d3d61c769571a13cb11 From 31d72543b375bbb4bcd7125a22d051f5fe4f3125 Mon Sep 17 00:00:00 2001 From: Aunali321 Date: Sun, 31 Aug 2025 17:09:09 +0530 Subject: [PATCH 06/10] feat: Complete migration to kepler-ai-sdk --- .mcp.json | 8 + bun.lock | 98 +++++- package.json | 2 +- src/lib/backend/convex/user_enabled_models.ts | 22 +- .../model-picker/model-picker.svelte | 215 +++++++++----- src/lib/services/model-loader.server.ts | 160 ++++++++++ src/lib/services/model-manager.ts | 22 +- src/lib/state/models.svelte.ts | 112 ++++++- src/lib/types.ts | 2 +- src/lib/utils/model-capabilities.ts | 28 +- src/lib/utils/providers.ts | 195 ++---------- src/routes/+layout.server.ts | 16 +- .../account/api-keys/provider-card.svelte | 2 +- src/routes/account/models/+page.svelte | 278 +++++++++++------- src/routes/account/models/model-card.svelte | 197 +++++++++---- src/routes/api/enhance-prompt/+server.ts | 2 +- src/routes/api/generate-message/+server.ts | 120 +++----- src/routes/api/validate-key/+server.ts | 218 ++++++++++++++ src/routes/chat/+layout.svelte | 8 +- src/routes/chat/+page.svelte | 7 +- 20 files changed, 1162 insertions(+), 550 deletions(-) create mode 100644 .mcp.json create mode 100644 src/lib/services/model-loader.server.ts create mode 100644 src/routes/api/validate-key/+server.ts diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..fefb52c --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "svelte-llm": { + "type": "http", + "url": "https://svelte-llm.stanislav.garden/mcp/mcp" + } + } +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index d3e6705..124bee5 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ "@fontsource-variable/nunito-sans": "^5.2.6", "@fontsource-variable/open-sans": "^5.2.6", "@fontsource/instrument-serif": "^5.2.6", - "@keplersystems/kepler-ai-sdk": "^1.0.5", + "@keplersystems/kepler-ai-sdk": "file:./tmp/kepler-ai-sdk", "better-auth": "^1.2.9", "convex-helpers": "^0.1.94", "hastscript": "^9.0.1", @@ -21,10 +21,13 @@ "zod": "^3.25.64", }, "devDependencies": { + "@anthropic-ai/sdk": "^0.29.0", "@better-auth-kit/convex": "^1.2.2", "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", + "@google/generative-ai": "^0.21.0", "@iconify/json": "^2.2.349", + "@mistralai/mistralai": "^1.1.0", "@playwright/test": "^1.49.1", "@shikijs/langs": "^3.6.0", "@shikijs/markdown-it": "^3.6.0", @@ -38,6 +41,7 @@ "@vercel/functions": "^2.2.0", "bits-ui": "^2.8.5", "clsx": "^2.1.1", + "cohere-ai": "^7.14.0", "concurrently": "^9.1.2", "convex": "^1.24.8", "convex-svelte": "^0.0.11", @@ -79,7 +83,7 @@ "@antfu/utils": ["@antfu/utils@8.1.1", "", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="], - "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.55.1", "", { "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-gjOMS4chmm8BxClKmCjNHmvf1FrO1Cn++CSX6K3YCZjz5JG4I9ZttQ/xEH4FBsz6HQyZvnUpiKlOAkmxaGmEaQ=="], + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.29.2", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-5dwiOPO/AZvhY4bJIG9vjFKU9Kza3hA6VEsbIQg6L9vny2RQIpCFhV50nB9IrG2edZaHZb4HuQ9Wmsn5zgWyZg=="], "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], @@ -259,6 +263,8 @@ "@google/genai": ["@google/genai@1.16.0", "", { "dependencies": { "google-auth-library": "^9.14.2", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.11.4" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-hdTYu39QgDFxv+FB6BK2zi4UIJGWhx2iPc0pHQ0C5Q/RCi+m+4gsryIzTGO+riqWcUA8/WGYp6hpqckdOBNysw=="], + "@google/generative-ai": ["@google/generative-ai@0.21.0", "", {}, "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg=="], + "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -277,6 +283,8 @@ "@internationalized/date": ["@internationalized/date@3.9.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], @@ -291,7 +299,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], - "@keplersystems/kepler-ai-sdk": ["@keplersystems/kepler-ai-sdk@1.0.5", "", { "dependencies": { "@anthropic-ai/sdk": "^0.55.0", "@google/genai": "^1.7.0", "@mistralai/mistralai": "^1.7.2", "cohere-ai": "^7.17.1", "exa-js": "^1.8.20", "openai": "^5.7.0" }, "peerDependencies": { "typescript": "^5" } }, "sha512-aynAClqQtS1Xkt53lZ4TdOS1YbtkfaYIrWxSmNXEV5DahLzHRi8YJ3zooAq2cmHUwjaEXmpaB2AMFdwAxWWTkQ=="], + "@keplersystems/kepler-ai-sdk": ["@keplersystems/kepler-ai-sdk@file:tmp/kepler-ai-sdk", { "dependencies": { "@anthropic-ai/sdk": "^0.55.0", "@google/genai": "^1.7.0", "@mistralai/mistralai": "^1.7.2", "cohere-ai": "^7.17.1", "exa-js": "^1.8.20", "openai": "^5.7.0" }, "devDependencies": { "@types/bun": "latest", "rimraf": "^5.0.5" }, "peerDependencies": { "typescript": "^5" } }], "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], @@ -317,6 +325,8 @@ "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.4.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.4.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@playwright/test": ["@playwright/test@1.55.0", "", { "dependencies": { "playwright": "1.55.0" }, "bin": { "playwright": "cli.js" } }, "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], @@ -515,6 +525,8 @@ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + "@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="], + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -537,6 +549,12 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + "@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="], + + "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + + "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], @@ -591,6 +609,8 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -633,6 +653,8 @@ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -697,6 +719,8 @@ "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], @@ -731,6 +755,8 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -819,11 +845,13 @@ "focus-trap": ["focus-trap@7.6.5", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], - "form-data-encoder": ["form-data-encoder@4.1.0", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="], + "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], - "formdata-node": ["formdata-node@6.0.3", "", {}, "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg=="], + "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -839,6 +867,8 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], @@ -879,6 +909,8 @@ "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -911,6 +943,8 @@ "isomorphic-dompurify": ["isomorphic-dompurify@2.26.0", "", { "dependencies": { "dompurify": "^3.2.6", "jsdom": "^26.1.0" } }, "sha512-nZmoK4wKdzPs5USq4JHBiimjdKSVAOm2T1KyDoadtMPNXYHxiENd19ou4iU/V4juFM6LVgYQnpxCYmxqNP4Obw=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + "jest-axe": ["jest-axe@9.0.0", "", { "dependencies": { "axe-core": "4.9.1", "chalk": "4.1.2", "jest-matcher-utils": "29.2.2", "lodash.merge": "4.6.2" } }, "sha512-Xt7O0+wIpW31lv0SO1wQZUTyJE7DEmnDEZeTt9/S9L5WUywxrv8BrgvTuQEqujtfaQOcJ70p4wg7UUgK1E2F5g=="], "jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], @@ -1059,6 +1093,8 @@ "neverthrow": ["neverthrow@8.2.0", "", { "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.24.0" } }, "sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ=="], + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "nwsapi": ["nwsapi@2.2.21", "", {}, "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA=="], @@ -1077,6 +1113,8 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.3.0", "", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], @@ -1087,6 +1125,8 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], @@ -1159,6 +1199,8 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + "rollup": ["rollup@4.49.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.49.0", "@rollup/rollup-android-arm64": "4.49.0", "@rollup/rollup-darwin-arm64": "4.49.0", "@rollup/rollup-darwin-x64": "4.49.0", "@rollup/rollup-freebsd-arm64": "4.49.0", "@rollup/rollup-freebsd-x64": "4.49.0", "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", "@rollup/rollup-linux-arm-musleabihf": "4.49.0", "@rollup/rollup-linux-arm64-gnu": "4.49.0", "@rollup/rollup-linux-arm64-musl": "4.49.0", "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", "@rollup/rollup-linux-ppc64-gnu": "4.49.0", "@rollup/rollup-linux-riscv64-gnu": "4.49.0", "@rollup/rollup-linux-riscv64-musl": "4.49.0", "@rollup/rollup-linux-s390x-gnu": "4.49.0", "@rollup/rollup-linux-x64-gnu": "4.49.0", "@rollup/rollup-linux-x64-musl": "4.49.0", "@rollup/rollup-win32-arm64-msvc": "4.49.0", "@rollup/rollup-win32-ia32-msvc": "4.49.0", "@rollup/rollup-win32-x64-msvc": "4.49.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA=="], "rou3": ["rou3@0.5.1", "", {}, "sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ=="], @@ -1203,6 +1245,8 @@ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -1215,12 +1259,16 @@ "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], @@ -1303,6 +1351,8 @@ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], @@ -1339,6 +1389,8 @@ "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], @@ -1357,6 +1409,8 @@ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], @@ -1371,7 +1425,7 @@ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], @@ -1397,6 +1451,14 @@ "@iconify/utils/globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@keplersystems/kepler-ai-sdk/@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.55.1", "", { "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-gjOMS4chmm8BxClKmCjNHmvf1FrO1Cn++CSX6K3YCZjz5JG4I9ZttQ/xEH4FBsz6HQyZvnUpiKlOAkmxaGmEaQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], @@ -1413,6 +1475,8 @@ "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@types/node-fetch/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -1423,14 +1487,20 @@ "bits-ui/runed": ["runed@0.29.2", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA=="], + "bun-types/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "convict/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "cohere-ai/form-data-encoder": ["form-data-encoder@4.1.0", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="], + + "cohere-ai/formdata-node": ["formdata-node@6.0.3", "", {}, "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg=="], "exa-js/dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "jest-diff/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], "jest-matcher-utils/pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -1467,12 +1537,26 @@ "vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "@types/node-fetch/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "bun-types/@types/node/undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "jest-diff/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "jest-diff/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], diff --git a/package.json b/package.json index 4816f95..0bd4ca0 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "@fontsource-variable/nunito-sans": "^5.2.6", "@fontsource-variable/open-sans": "^5.2.6", "@fontsource/instrument-serif": "^5.2.6", - "@keplersystems/kepler-ai-sdk": "^1.0.5", + "@keplersystems/kepler-ai-sdk": "file:./tmp/kepler-ai-sdk", "better-auth": "^1.2.9", "convex-helpers": "^0.1.94", "hastscript": "^9.0.1", diff --git a/src/lib/backend/convex/user_enabled_models.ts b/src/lib/backend/convex/user_enabled_models.ts index 173f026..900d71f 100644 --- a/src/lib/backend/convex/user_enabled_models.ts +++ b/src/lib/backend/convex/user_enabled_models.ts @@ -101,16 +101,20 @@ export const set = mutation({ ) .first(); - if (args.enabled && existing) return; // nothing to do here - - if (existing) { - await ctx.db.delete(existing._id); + if (args.enabled) { + // Enable model: insert if not exists + if (!existing) { + await ctx.db.insert('user_enabled_models', { + ...object.pick(args, ['provider', 'model_id']), + user_id: session.userId, + pinned: false, + }); + } } else { - await ctx.db.insert('user_enabled_models', { - ...object.pick(args, ['provider', 'model_id']), - user_id: session.userId, - pinned: false, - }); + // Disable model: delete if exists + if (existing) { + await ctx.db.delete(existing._id); + } } }, }); diff --git a/src/lib/components/model-picker/model-picker.svelte b/src/lib/components/model-picker/model-picker.svelte index 40c18cb..405a97e 100644 --- a/src/lib/components/model-picker/model-picker.svelte +++ b/src/lib/components/model-picker/model-picker.svelte @@ -8,9 +8,9 @@ import { models as modelsState } from '$lib/state/models.svelte'; import { session } from '$lib/state/session.svelte'; import { settings } from '$lib/state/settings.svelte'; - import { Provider } from '$lib/types'; + import { Provider, PROVIDER_META } from '$lib/types'; import { fuzzysearch } from '$lib/utils/fuzzy-search'; - import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import { supportsImages, supportsReasoning, supportsStreaming, supportsToolCalls } from '$lib/utils/model-capabilities'; import { capitalize } from '$lib/utils/strings'; import { cn } from '$lib/utils/utils'; import { type Component } from 'svelte'; @@ -57,7 +57,14 @@ session_token: session.current?.session.token ?? '', }); - const enabledArr = $derived(Object.values(enabledModelsQuery.data ?? {})); + // Get enabled models from our models state with ModelInfo data + const enabledArr = $derived.by(() => { + const enabledModelIds = Object.keys(enabledModelsQuery.data ?? {}); + const enabledModels = modelsState.all().filter(model => + enabledModelIds.some(id => id.includes(model.id)) + ); + return enabledModels; + }); modelsState.init(); @@ -133,37 +140,38 @@ fuzzysearch({ haystack: enabledArr, needle: search, - property: 'model_id', + property: 'id', }) ); - // Group models by company + // Group models by provider const groupedModels = $derived.by(() => { - const groups: Record = {}; + const groups: Record = {} as Record; filteredModels.forEach((model) => { - const company = getCompanyFromModelId(model.model_id); - if (!groups[company]) { - groups[company] = []; + const provider = model.provider as Provider; + if (!groups[provider]) { + groups[provider] = []; } - groups[company].push(model); + groups[provider].push(model); }); - // Sort companies with known icons first - const result = Object.entries(groups).sort(([a], [b]) => { - const aHasIcon = companyIcons[a] ? 0 : 1; - const bHasIcon = companyIcons[b] ? 0 : 1; - return aHasIcon - bHasIcon || a.localeCompare(b); - }); + // Sort by provider order and name + const result = Object.entries(groups) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([provider, models]) => [ + provider, + models.sort((a, b) => a.name.localeCompare(b.name)) + ] as [Provider, typeof models]); return result; }); - const currentModel = $derived(enabledArr.find((m) => m.model_id === settings.modelId)); + const currentModel = $derived(enabledArr.find((m) => m.id === settings.modelId)); $effect(() => { - if (!enabledArr.find((m) => m.model_id === settings.modelId) && enabledArr.length > 0) { - settings.modelId = enabledArr[0]!.model_id; + if (!enabledArr.find((m) => m.id === settings.modelId) && enabledArr.length > 0) { + settings.modelId = enabledArr[0]!.id; } }); @@ -179,8 +187,10 @@ { from: 'o3', to: 'o3' }, ]; - function formatModelName(modelId: string) { - const cleanId = modelId.replace(/^[^/]+\//, ''); + function formatModelName(model: { id: string; name: string }) { + // Use the name field if available, fallback to processing ID + const displayName = model.name || model.id; + const cleanId = displayName.replace(/^[^/]+\//, ''); const parts = cleanId.split(/[-_,:]/); const formattedParts = parts.map((part) => { @@ -228,17 +238,20 @@ const activeModelInfo = $derived.by(() => { if (activeModel === '') return null; - const model = enabledArr.find((m) => m.model_id === activeModel); + const model = enabledArr.find((m) => m.id === activeModel); if (!model) return null; return { ...model, - formatted: formatModelName(activeModel), + formatted: formatModelName(model), }; }); - const pinnedModels = $derived(enabledArr.filter((m) => isPinned(m))); + // For now, we'll need to maintain pinned models using the old enabled models structure + // until we migrate the pinning system to work with the new ModelInfo structure + const enabledModelsData = $derived(Object.values(enabledModelsQuery.data ?? {})); + const pinnedModels = $derived(enabledModelsData.filter((m) => isPinned(m)));
- {#if currentModel && getModelIcon(currentModel.model_id)} - {@const IconComponent = getModelIcon(currentModel.model_id)} + {#if currentModel && getModelIcon(currentModel.id)} + {@const IconComponent = getModelIcon(currentModel.id)} {/if} - {currentModel ? formatModelName(currentModel.model_id).full : 'Select model'} + {currentModel ? formatModelName(currentModel).full : 'Select model'}
@@ -324,12 +337,9 @@ > {#if view === 'favorites' && pinnedModels.length > 0} {#each pinnedModels as model (model._id)} - {@const formatted = formatModelName(model.model_id)} - {@const openRouterModel = modelsState - .from(Provider.OpenRouter) - .find((m) => m.id === model.model_id)} - {@const disabled = - onlyImageModels && openRouterModel && !supportsImages(openRouterModel)} + {@const modelInfo = enabledArr.find((m) => m.id === model.model_id)} + {@const formatted = modelInfo ? formatModelName(modelInfo) : { full: model.model_id, primary: model.model_id, secondary: '' }} + {@const disabled = onlyImageModels && modelInfo && !supportsImages(modelInfo)}
- {#if openRouterModel && supportsImages(openRouterModel)} + {#if modelInfo && supportsImages(modelInfo)} {#snippet trigger(tooltip)}
{/snippet} - Supports image analysis + Supports vision/image analysis
{/if} - {#if openRouterModel && supportsReasoning(openRouterModel)} + {#if modelInfo && supportsReasoning(modelInfo)} {#snippet trigger(tooltip)}
{/if} + + {#if modelInfo && supportsStreaming(modelInfo)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports streaming responses +
+ {/if} + + {#if modelInfo && supportsToolCalls(modelInfo)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports tool/function calling +
+ {/if}
{/each} @@ -394,22 +432,25 @@ {#each pinnedModels as model (model._id)} - {@render modelCard(model)} + {@const modelInfo = enabledArr.find((m) => m.id === model.model_id)} + {#if modelInfo} + {@render modelCard(modelInfo)} + {/if} {/each} {/if} - {#each groupedModels as [company, models] (company)} - {@const filteredModels = models.filter((m) => !isPinned(m))} - {#if filteredModels.length > 0} + {#each groupedModels as [provider, models] (provider)} + {@const providerMeta = PROVIDER_META[provider]} + {#if models.length > 0} - {company} + {providerMeta.title} - {#each filteredModels as model (model._id)} + {#each models as model (model.id)} {@render modelCard(model)} {/each} @@ -457,14 +498,12 @@ {#snippet modelCard(model: (typeof enabledArr)[number])} - {@const formatted = formatModelName(model.model_id)} - {@const openRouterModel = modelsState - .from(Provider.OpenRouter) - .find((m) => m.id === model.model_id)} - {@const disabled = onlyImageModels && openRouterModel && !supportsImages(openRouterModel)} + {@const formatted = formatModelName(model)} + {@const disabled = onlyImageModels && !supportsImages(model)} + {@const enabledModelData = enabledModelsData.find(m => m.model_id === model.id)} modelSelected(model.model_id)} + onSelect={() => modelSelected(model.id)} >
- {#if getModelIcon(model.model_id)} - {@const ModelIcon = getModelIcon(model.model_id)} + {#if getModelIcon(model.id)} + {@const ModelIcon = getModelIcon(model.id)} {/if} @@ -494,7 +533,7 @@
- {#if openRouterModel && supportsImages(openRouterModel)} + {#if supportsImages(model)} {#snippet trigger(tooltip)}
{/snippet} - Supports image analysis + Supports vision/image analysis
{/if} - {#if openRouterModel && supportsReasoning(openRouterModel)} + {#if supportsReasoning(model)} {#snippet trigger(tooltip)}
{/if} + + {#if supportsStreaming(model)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports streaming responses +
+ {/if} + + {#if supportsToolCalls(model)} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports tool/function calling +
+ {/if}
-
- -
+ +
+ {/if}
{/snippet} diff --git a/src/lib/services/model-loader.server.ts b/src/lib/services/model-loader.server.ts new file mode 100644 index 0000000..8b73dc2 --- /dev/null +++ b/src/lib/services/model-loader.server.ts @@ -0,0 +1,160 @@ +import { ConvexHttpClient } from 'convex/browser'; +import { PUBLIC_CONVEX_URL } from '$env/static/public'; +import { api } from '$lib/backend/convex/_generated/api'; +import { Provider } from '$lib/types'; +import { createModelManager } from './model-manager'; +import type { UserApiKeys } from './model-manager'; +import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; +import { ResultAsync } from 'neverthrow'; + +export interface MultiProviderModels { + [Provider.OpenAI]?: ModelInfo[]; + [Provider.Anthropic]?: ModelInfo[]; + [Provider.Gemini]?: ModelInfo[]; + [Provider.Mistral]?: ModelInfo[]; + [Provider.Cohere]?: ModelInfo[]; + [Provider.OpenRouter]?: ModelInfo[]; +} + +const client = new ConvexHttpClient(PUBLIC_CONVEX_URL); + +// Cache models for 10 minutes to avoid excessive API calls +const MODEL_CACHE_TTL = 10 * 60 * 1000; +const modelCache = new Map(); + +/** + * Load models for a specific user based on their API keys + */ +export async function loadUserModels(sessionToken: string): Promise { + const cacheKey = sessionToken; + const cached = modelCache.get(cacheKey); + + // Return cached models if still valid + if (cached && Date.now() - cached.timestamp < MODEL_CACHE_TTL) { + console.log('Returning cached models:', Object.keys(cached.models)); + return cached.models; + } + + try { + // Get user's API keys + const userApiKeys = await getUserApiKeys(sessionToken); + console.log('getUserApiKeys result:', userApiKeys); + if (!userApiKeys) { + console.log('No user API keys found'); + return {}; + } + + // Initialize ModelManager with user's API keys + const modelManager = createModelManager(); + modelManager.initializeProviders(userApiKeys); + console.log('Enabled providers:', modelManager.getEnabledProviders()); + + // Load models from all enabled providers + const models: MultiProviderModels = {}; + const enabledProviders = modelManager.getEnabledProviders(); + console.log('Loading models from providers:', enabledProviders); + + const results = await Promise.allSettled( + enabledProviders.map(async (provider) => { + try { + console.log(`Loading models from ${provider}...`); + const providerModels = await modelManager.getModelsByProvider(provider); + console.log(`${provider} returned ${providerModels.length} models`); + if (providerModels.length > 0) { + models[provider] = providerModels; + } + return { provider, count: providerModels.length }; + } catch (error) { + console.warn(`Failed to load models from ${provider}:`, error); + return { provider, error: error.message }; + } + }) + ); + + console.log('Model loading results:', results); + console.log('Final models object:', Object.keys(models)); + + // Cache the results + modelCache.set(cacheKey, { + models, + timestamp: Date.now(), + }); + + return models; + } catch (error) { + console.error('Failed to load user models:', error); + return {}; + } +} + +/** + * Load models without authentication (fallback to empty state) + */ +export function loadGuestModels(): MultiProviderModels { + return {}; +} + +/** + * Clear model cache for a specific user or all users + */ +export function clearModelCache(sessionToken?: string): void { + if (sessionToken) { + modelCache.delete(sessionToken); + } else { + modelCache.clear(); + } +} + +/** + * Get user's API keys from Convex + */ +async function getUserApiKeys(sessionToken: string): Promise { + const keysResult = await ResultAsync.fromPromise( + client.query(api.user_keys.all, { + session_token: sessionToken, + }), + (e) => `Failed to get user API keys: ${e}` + ); + + if (keysResult.isErr()) { + console.error('Failed to get user API keys:', keysResult.error); + return null; + } + + const keys = keysResult.value; + return { + openai: keys.openai, + anthropic: keys.anthropic, + google: keys.gemini, + mistral: keys.mistral, + cohere: keys.cohere, + openrouter: keys.openrouter, + }; +} + +/** + * Get models for a specific provider (useful for partial loading) + */ +export async function loadProviderModels( + sessionToken: string, + provider: Provider +): Promise { + try { + const userApiKeys = await getUserApiKeys(sessionToken); + if (!userApiKeys) { + return []; + } + + const modelManager = createModelManager(); + modelManager.initializeProviders(userApiKeys); + + if (!modelManager.hasProviderEnabled(provider)) { + return []; + } + + return await modelManager.getModelsByProvider(provider); + } catch (error) { + console.error(`Failed to load models from ${provider}:`, error); + return []; + } +} diff --git a/src/lib/services/model-manager.ts b/src/lib/services/model-manager.ts index 34d7c72..219d217 100644 --- a/src/lib/services/model-manager.ts +++ b/src/lib/services/model-manager.ts @@ -19,7 +19,7 @@ export interface ProviderConfig { export interface UserApiKeys { openai?: string; anthropic?: string; - gemini?: string; + google?: string; mistral?: string; cohere?: string; openrouter?: string; @@ -52,9 +52,9 @@ export class ChatModelManager { this.enabledProviders.set('anthropic', provider); } - if (userApiKeys.gemini) { + if (userApiKeys.google) { const provider = new GeminiProvider({ - apiKey: userApiKeys.gemini, + apiKey: userApiKeys.google, }); this.modelManager.addProvider(provider); this.enabledProviders.set('gemini', provider); @@ -97,9 +97,19 @@ export class ChatModelManager { return await this.modelManager.listModels(); } + async getModelsByProvider(provider: Provider): Promise { + if (!this.hasProviderEnabled(provider)) { + return []; + } + const allModels = await this.listAvailableModels(); + return allModels.filter((model) => model.provider === provider); + } + async getModelsByCapability(capability: string): Promise { const allModels = await this.listAvailableModels(); - return allModels.filter(model => model.capabilities[capability as keyof typeof model.capabilities]); + return allModels.filter( + (model) => model.capabilities[capability as keyof typeof model.capabilities] + ); } hasProviderEnabled(provider: Provider): boolean { @@ -111,10 +121,10 @@ export class ChatModelManager { } isModelAvailable(modelId: string): Promise { - return this.getModel(modelId).then(model => model !== null); + return this.getModel(modelId).then((model) => model !== null); } } export const createModelManager = (): ChatModelManager => { return new ChatModelManager(); -}; \ No newline at end of file +}; diff --git a/src/lib/state/models.svelte.ts b/src/lib/state/models.svelte.ts index f301928..37163a7 100644 --- a/src/lib/state/models.svelte.ts +++ b/src/lib/state/models.svelte.ts @@ -1,13 +1,17 @@ import { page } from '$app/state'; import { api } from '$lib/backend/convex/_generated/api'; import { getModelKey } from '$lib/backend/convex/user_enabled_models'; -import type { ProviderModelMap } from '$lib/backend/models/all'; import { useCachedQuery } from '$lib/cache/cached-query.svelte'; import { createInit } from '$lib/spells/create-init.svelte'; import { Provider } from '$lib/types'; +import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; import { watch } from 'runed'; import { session } from './session.svelte'; +export interface ModelWithEnabledStatus extends ModelInfo { + enabled: boolean; +} + export class Models { enabled = $state({} as Record); @@ -23,14 +27,104 @@ export class Models { ); }); - from

(provider: Provider) { - return page.data.models[provider].map((m: { id: string }) => { - return { - ...m, - enabled: this.enabled[getModelKey({ provider, model_id: m.id })] !== undefined, - }; - }) as Array; + /** + * Get models from a specific provider with enabled status + */ + from(provider: Provider): ModelWithEnabledStatus[] { + const providerModels = page.data.models[provider] || []; + return providerModels.map((model: ModelInfo) => ({ + ...model, + enabled: this.enabled[getModelKey({ provider, model_id: model.id })] !== undefined, + })); + } + + /** + * Get all models from all providers with enabled status + */ + all(): ModelWithEnabledStatus[] { + const allModels: ModelWithEnabledStatus[] = []; + const availableProviders = Object.keys(page.data.models || {}) as Provider[]; + + for (const provider of availableProviders) { + const providerModels = this.from(provider); + allModels.push(...providerModels); + } + + return allModels; + } + + /** + * Get models that support specific capabilities + */ + withCapability(capability: keyof ModelInfo['capabilities']): ModelWithEnabledStatus[] { + return this.all().filter(model => model.capabilities[capability]); + } + + /** + * Get enabled models from all providers + */ + enabledModels(): ModelWithEnabledStatus[] { + return this.all().filter(model => model.enabled); + } + + /** + * Get enabled models from a specific provider + */ + enabledFrom(provider: Provider): ModelWithEnabledStatus[] { + return this.from(provider).filter(model => model.enabled); + } + + /** + * Get available providers (providers that have models loaded) + */ + availableProviders(): Provider[] { + const models = page.data.models || {}; + return Object.keys(models).filter(provider => + models[provider as Provider] && models[provider as Provider].length > 0 + ) as Provider[]; + } + + /** + * Check if a provider has models loaded + */ + hasProvider(provider: Provider): boolean { + const models = page.data.models[provider]; + return Array.isArray(models) && models.length > 0; + } + + /** + * Search models across all providers + */ + search(query: string): ModelWithEnabledStatus[] { + const lowerQuery = query.toLowerCase(); + return this.all().filter(model => + model.name.toLowerCase().includes(lowerQuery) || + model.id.toLowerCase().includes(lowerQuery) || + model.description?.toLowerCase().includes(lowerQuery) + ); + } + + /** + * Get models sorted by preference (enabled first, then by provider order) + */ + sorted(): ModelWithEnabledStatus[] { + return this.all().sort((a, b) => { + // Enabled models first + if (a.enabled && !b.enabled) return -1; + if (!a.enabled && b.enabled) return 1; + + // Then by provider order + const providerOrder = Object.values(Provider); + const aProviderIndex = providerOrder.indexOf(a.provider as Provider); + const bProviderIndex = providerOrder.indexOf(b.provider as Provider); + if (aProviderIndex !== bProviderIndex) { + return aProviderIndex - bProviderIndex; + } + + // Finally by name + return a.name.localeCompare(b.name); + }); } } -export const models = new Models(); +export const models = new Models(); \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index 3fdae2f..76d7f08 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -69,7 +69,7 @@ export const PROVIDER_META: Record = { }, [Provider.Gemini]: { title: 'Google Gemini', - link: 'https://cloud.google.com/vertex-ai', + link: 'https://ai.google.dev/docs', description: 'Gemini models from Google', apiKeyName: 'Google AI API Key', placeholder: 'AIza...', diff --git a/src/lib/utils/model-capabilities.ts b/src/lib/utils/model-capabilities.ts index 9f39a8b..55280f5 100644 --- a/src/lib/utils/model-capabilities.ts +++ b/src/lib/utils/model-capabilities.ts @@ -1,13 +1,29 @@ -import type { OpenRouterModel } from '$lib/backend/models/open-router'; +import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; -export function supportsImages(model: OpenRouterModel): boolean { - return model.architecture.input_modalities.includes('image'); +export function supportsImages(model: ModelInfo): boolean { + return model.capabilities.vision; } -export function supportsReasoning(model: OpenRouterModel): boolean { - return model.supported_parameters.includes('reasoning'); +export function supportsReasoning(model: ModelInfo): boolean { + return model.capabilities.reasoning; } -export function getImageSupportedModels(models: OpenRouterModel[]): OpenRouterModel[] { +export function supportsStreaming(model: ModelInfo): boolean { + return model.capabilities.streaming; +} + +export function supportsToolCalls(model: ModelInfo): boolean { + return model.capabilities.toolCalls; +} + +export function getImageSupportedModels(models: ModelInfo[]): ModelInfo[] { return models.filter(supportsImages); } + +export function getReasoningModels(models: ModelInfo[]): ModelInfo[] { + return models.filter(supportsReasoning); +} + +export function getStreamingModels(models: ModelInfo[]): ModelInfo[] { + return models.filter(supportsStreaming); +} diff --git a/src/lib/utils/providers.ts b/src/lib/utils/providers.ts index edfe692..59dcddd 100644 --- a/src/lib/utils/providers.ts +++ b/src/lib/utils/providers.ts @@ -13,25 +13,29 @@ export type ProviderApiKeyData = { export const ProviderUtils = { /** - * Validate an API key for a specific provider + * Validate an API key for a specific provider via server endpoint */ validateApiKey: async (provider: Provider, key: string): Promise> => { - switch (provider) { - case Provider.OpenRouter: - return await validateOpenRouterKey(key); - case Provider.OpenAI: - return await validateOpenAIKey(key); - case Provider.Anthropic: - return await validateAnthropicKey(key); - case Provider.Gemini: - return await validateGeminiKey(key); - case Provider.Mistral: - return await validateMistralKey(key); - case Provider.Cohere: - return await validateCohereKey(key); - default: - return Result.err(`Validation not implemented for provider: ${provider}`); - } + return await ResultAsync.fromPromise( + (async () => { + const response = await fetch('/api/validate-key', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ provider, key }), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || `HTTP ${response.status}`); + } + + const result = await response.json(); + return result.data; + })(), + (e) => `Failed to validate API key: ${e}` + ); }, /** @@ -54,159 +58,4 @@ export const ProviderUtils = { getSupportedProviders: () => { return Object.values(Provider); }, -}; - -// Provider-specific validation functions -async function validateOpenRouterKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch('https://openrouter.ai/api/v1/auth/key', { - headers: { - Authorization: `Bearer ${key}`, - 'Content-Type': 'application/json', - }, - }); - - if (!res.ok) { - throw new Error(`HTTP ${res.status}: ${res.statusText}`); - } - - const { data } = await res.json(); - - if (!data) { - throw new Error('No key information returned'); - } - - return { - label: data.label || 'OpenRouter API Key', - usage: data.usage, - is_free_tier: data.is_free_tier, - is_provisioning_key: data.is_provisioning_key, - limit: data.limit, - limit_remaining: data.limit_remaining, - valid: true, - }; - })(), - (e) => `Failed to validate OpenRouter API key: ${e}` - ); -} - -async function validateOpenAIKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch('https://api.openai.com/v1/models', { - headers: { - Authorization: `Bearer ${key}`, - 'Content-Type': 'application/json', - }, - }); - - if (!res.ok) { - throw new Error(`HTTP ${res.status}: ${res.statusText}`); - } - - return { - label: 'OpenAI API Key', - valid: true, - }; - })(), - (e) => `Failed to validate OpenAI API key: ${e}` - ); -} - -async function validateAnthropicKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - // Anthropic doesn't have a simple key validation endpoint - // We'll try a minimal request to test the key - const res = await fetch('https://api.anthropic.com/v1/messages', { - method: 'POST', - headers: { - 'x-api-key': key, - 'anthropic-version': '2023-06-01', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: 'claude-3-haiku-20240307', - max_tokens: 1, - messages: [{ role: 'user', content: 'test' }], - }), - }); - - // Even a 400 error means the key is valid (just bad request format) - if (res.status === 401 || res.status === 403) { - throw new Error('Invalid API key'); - } - - return { - label: 'Anthropic API Key', - valid: true, - }; - })(), - (e) => `Failed to validate Anthropic API key: ${e}` - ); -} - -async function validateGeminiKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${key}`); - - if (!res.ok) { - throw new Error(`HTTP ${res.status}: ${res.statusText}`); - } - - return { - label: 'Google Gemini API Key', - valid: true, - }; - })(), - (e) => `Failed to validate Gemini API key: ${e}` - ); -} - -async function validateMistralKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch('https://api.mistral.ai/v1/models', { - headers: { - Authorization: `Bearer ${key}`, - 'Content-Type': 'application/json', - }, - }); - - if (!res.ok) { - throw new Error(`HTTP ${res.status}: ${res.statusText}`); - } - - return { - label: 'Mistral API Key', - valid: true, - }; - })(), - (e) => `Failed to validate Mistral API key: ${e}` - ); -} - -async function validateCohereKey(key: string): Promise> { - return await ResultAsync.fromPromise( - (async () => { - const res = await fetch('https://api.cohere.ai/v1/models', { - headers: { - Authorization: `Bearer ${key}`, - 'Content-Type': 'application/json', - }, - }); - - if (!res.ok) { - throw new Error(`HTTP ${res.status}: ${res.statusText}`); - } - - return { - label: 'Cohere API Key', - valid: true, - }; - })(), - (e) => `Failed to validate Cohere API key: ${e}` - ); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 2370456..0d6abfa 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,17 +1,19 @@ -import { getOpenRouterModels, type OpenRouterModel } from '$lib/backend/models/open-router'; -import { Provider } from '$lib/types'; +import { loadUserModels, loadGuestModels } from '$lib/services/model-loader.server'; import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ locals }) => { - const [session, openRouterModels] = await Promise.all([locals.auth(), getOpenRouterModels()]); + const session = await locals.auth(); + + // Load models based on user's API keys + const models = session?.session?.token + ? await loadUserModels(session.session.token) + : loadGuestModels(); return { session, - models: { - [Provider.OpenRouter]: openRouterModels.unwrapOr([] as OpenRouterModel[]), - }, + models, }; }; -// Makes caching easier, and tbf, we don't need SSR anyways here +// Enable SSR for better performance export const ssr = true; diff --git a/src/routes/account/api-keys/provider-card.svelte b/src/routes/account/api-keys/provider-card.svelte index c348009..54c2619 100644 --- a/src/routes/account/api-keys/provider-card.svelte +++ b/src/routes/account/api-keys/provider-card.svelte @@ -96,7 +96,7 @@ {#if apiKeyInfoResource.loading}

{:else if apiKeyInfoResource.current} - {#if apiKeyInfoResource.current.usage !== undefined && apiKeyInfoResource.current.limit_remaining !== undefined} + {#if apiKeyInfoResource.current.usage !== undefined && apiKeyInfoResource.current.limit_remaining !== undefined && apiKeyInfoResource.current.usage !== null && apiKeyInfoResource.current.limit_remaining !== null} ${apiKeyInfoResource.current.usage.toFixed(3)} used ・ ${apiKeyInfoResource.current.limit_remaining.toFixed( 3 diff --git a/src/routes/account/models/+page.svelte b/src/routes/account/models/+page.svelte index 23dfd52..26fcfe8 100644 --- a/src/routes/account/models/+page.svelte +++ b/src/routes/account/models/+page.svelte @@ -5,36 +5,37 @@ import { Search } from '$lib/components/ui/search'; import { models } from '$lib/state/models.svelte'; import { session } from '$lib/state/session.svelte'; - import { Provider } from '$lib/types.js'; + import { page } from '$app/state'; + import { Provider, PROVIDER_META } from '$lib/types.js'; import { fuzzysearch } from '$lib/utils/fuzzy-search'; import { cn } from '$lib/utils/utils'; import { Toggle } from 'melt/builders'; import PlusIcon from '~icons/lucide/plus'; import XIcon from '~icons/lucide/x'; import ModelCard from './model-card.svelte'; - import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; - const openRouterKeyQuery = useCachedQuery(api.user_keys.get, { - provider: Provider.OpenRouter, + // Get all user's API keys to determine which providers are available + const userKeysQuery = useCachedQuery(api.user_keys.all, { session_token: session.current?.session.token ?? '', }); - const hasOpenRouterKey = $derived( - openRouterKeyQuery.data !== undefined && openRouterKeyQuery.data !== '' - ); + + // Show providers that have API keys, regardless of whether models are loaded yet + const availableProviders = $derived.by(() => { + if (!userKeysQuery.data) { + return []; + } + + return Object.entries(userKeysQuery.data) + .filter(([_, key]) => key) // Only providers with API keys + .map(([provider, _]) => provider as Provider); + }); let search = $state(''); + let selectedProvider = $state('all'); - const openRouterToggle = new Toggle({ - value: true, - // TODO: enable this if and when when we use multiple providers - disabled: true, - }); - - const freeModelsToggle = new Toggle({ - value: false, - }); - + // Filter toggles const reasoningModelsToggle = new Toggle({ value: false, }); @@ -43,43 +44,47 @@ value: false, }); - let initiallyEnabled = $state([]); - $effect(() => { - if (Object.keys(models.enabled).length && initiallyEnabled.length === 0) { - initiallyEnabled = models - .from(Provider.OpenRouter) - .filter((m) => m.enabled) - .map((m) => m.id); - } + const streamingModelsToggle = new Toggle({ + value: false, }); - const openRouterModels = $derived( - fuzzysearch({ - haystack: models.from(Provider.OpenRouter).filter((m) => { - if (freeModelsToggle.value) { - if (m.pricing.prompt !== '0') return false; - } + // Get models based on current filters + const filteredModels = $derived.by(() => { + let modelList = selectedProvider === 'all' + ? models.all() + : models.from(selectedProvider); - if (reasoningModelsToggle.value) { - if (!supportsReasoning(m)) return false; - } + // Apply capability filters + if (reasoningModelsToggle.value) { + modelList = modelList.filter(m => m.capabilities.reasoning); + } - if (imageModelsToggle.value) { - if (!supportsImages(m)) return false; - } + if (imageModelsToggle.value) { + modelList = modelList.filter(m => m.capabilities.vision); + } - return true; - }), - needle: search, - property: 'name', - }).sort((a, b) => { - const aEnabled = initiallyEnabled.includes(a.id); - const bEnabled = initiallyEnabled.includes(b.id); - if (aEnabled && !bEnabled) return -1; - if (!aEnabled && bEnabled) return 1; - return 0; - }) - ); + if (streamingModelsToggle.value) { + modelList = modelList.filter(m => m.capabilities.streaming); + } + + // Apply text search + if (search) { + modelList = fuzzysearch({ + haystack: modelList, + needle: search, + property: 'name', + }); + } + + // Sort: enabled first, then by name + return modelList.sort((a, b) => { + if (a.enabled && !b.enabled) return -1; + if (!a.enabled && b.enabled) return 1; + return a.name.localeCompare(b.name); + }); + }); + + const hasAnyApiKeys = $derived(availableProviders.length > 0); @@ -91,76 +96,125 @@ Choose which models appear in your model selector. This won't affect existing conversations. -
- -
- - - - +{#if !hasAnyApiKeys} +
+

No API Keys Configured

+

+ You need to add API keys for at least one provider to see and manage models. + Go to API Keys Settings +

-
+{:else} +
+ + -{#if openRouterModels.length > 0} -
-
-

OpenRouter

-

Easy access to over 400 models.

-
-
-
+
+ +
+ + + {#each availableProviders as provider} + {@const providerMeta = PROVIDER_META[provider]} + {@const providerModels = models.from(provider)} + {@const hasModels = models.hasProvider(provider)} + {#if providerMeta} + + {/if} + {/each} +
+ + +
+ + + + + + +
+ + + {#if filteredModels.length === 0} +
+ {#if selectedProvider !== 'all' && !models.hasProvider(selectedProvider)} +

Loading models...

+

+ Models are being loaded from {PROVIDER_META[selectedProvider]?.title || selectedProvider}. Please refresh the page in a moment. +

+ {:else} +

No models found

+

+ {#if search} + Try adjusting your search or filters. + {:else} + No models match your current filters. + {/if} +

+ {/if} +
+ {:else} +
+ {#each filteredModels as model (model.id)} {/each}
- {#if !hasOpenRouterKey} -
- -
- {/if} -
+ {/if}
-{/if} +{/if} \ No newline at end of file diff --git a/src/routes/account/models/model-card.svelte b/src/routes/account/models/model-card.svelte index 2ad59ef..f1337d0 100644 --- a/src/routes/account/models/model-card.svelte +++ b/src/routes/account/models/model-card.svelte @@ -1,5 +1,6 @@
-
- {model.name} - +
+
+ {model.name} + + {providerMeta.title} + +
+ {model.id}
- enabled, toggleEnabled} {disabled} /> +
- - {showMore ? fullDescription : (shortDescription ?? fullDescription)} - - {#if shortDescription !== null} - + + {#if model.description} + + {showMore ? fullDescription : (shortDescription ?? fullDescription)} + + {#if shortDescription !== null} + + {/if} {/if} - -
- {#if model && provider === 'openrouter' && supportsImages(model)} - - {#snippet trigger(tooltip)} -
- -
- {/snippet} - Supports image analysis -
- {/if} - {#if model && provider === 'openrouter' && supportsReasoning(model)} - - {#snippet trigger(tooltip)} -
- -
- {/snippet} - Supports reasoning -
- {/if} + +
+ +
+ {#if model.capabilities.vision} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports vision/image analysis +
+ {/if} + + {#if model.capabilities.reasoning} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports reasoning +
+ {/if} + + {#if model.capabilities.streaming} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports streaming responses +
+ {/if} + + {#if model.capabilities.toolCalls} + + {#snippet trigger(tooltip)} +
+ +
+ {/snippet} + Supports tool/function calling +
+ {/if} +
+ + +
+ {#if pricingInfo} + {pricingInfo} + {/if} + {contextInfo} +
- + \ No newline at end of file diff --git a/src/routes/api/enhance-prompt/+server.ts b/src/routes/api/enhance-prompt/+server.ts index 8e55212..b57a4ba 100644 --- a/src/routes/api/enhance-prompt/+server.ts +++ b/src/routes/api/enhance-prompt/+server.ts @@ -44,7 +44,7 @@ async function getUserApiKeys(sessionToken: string): Promise return { openai: keys.openai, anthropic: keys.anthropic, - gemini: keys.gemini, + gemini: keys.google, mistral: keys.mistral, cohere: keys.cohere, openrouter: keys.openrouter, diff --git a/src/routes/api/generate-message/+server.ts b/src/routes/api/generate-message/+server.ts index 3d3737e..90cb4ff 100644 --- a/src/routes/api/generate-message/+server.ts +++ b/src/routes/api/generate-message/+server.ts @@ -81,7 +81,7 @@ async function getUserApiKeys(sessionToken: string): Promise model.id.includes('kimi-k2')) || availableModels.find((model) => model.id.includes('gemini-2.5-flash-lite')) || + availableModels.find((model) => model.id.includes('kimi-k2')) || availableModels.find((model) => model.id.includes('gpt-5-mini')) || availableModels[0]; @@ -396,21 +396,19 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, return; } - // Generate completion with streaming - const streamResult = await ResultAsync.fromPromise( - provider.generateCompletion({ + // Generate streaming completion + let stream: AsyncIterable; + try { + stream = provider.streamCompletion({ model: finalModelId, messages: messagesToSend, temperature: 0.7, - stream: true, ...(reasoningEffort && { reasoning_effort: reasoningEffort }), - }), - (e) => `API call failed: ${e}` - ); - - if (streamResult.isErr()) { + }); + log('Background: Stream created successfully', startTime); + } catch (error) { handleGenerationError({ - error: `Failed to create stream: ${streamResult.error}`, + error: `Failed to create stream: API call failed: ${error}`, conversationId, messageId: mid, sessionToken, @@ -419,9 +417,6 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, return; } - const stream = streamResult.value; - log('Background: Stream created successfully', startTime); - let content = ''; let reasoning = ''; let chunkCount = 0; @@ -430,77 +425,48 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, try { // Handle streaming response - if (stream && typeof stream[Symbol.asyncIterator] === 'function') { - for await (const chunk of stream) { - if (abortSignal?.aborted) { - log('AI response generation aborted during streaming', startTime); - break; - } + for await (const chunk of stream) { + if (abortSignal?.aborted) { + log('AI response generation aborted during streaming', startTime); + break; + } - chunkCount++; + chunkCount++; - // Extract content from chunk based on the stream format - if (chunk && typeof chunk === 'object') { - const chunkContent = chunk.content || chunk.text || ''; - const chunkReasoning = chunk.reasoning || ''; - const chunkAnnotations = chunk.annotations || []; + // Extract content from chunk based on the kepler-ai-sdk format + if (chunk && typeof chunk === 'object') { + const chunkContent = chunk.delta || chunk.content || chunk.text || ''; + const chunkReasoning = chunk.reasoning || ''; + const chunkAnnotations = chunk.annotations || []; - reasoning += chunkReasoning; - content += chunkContent; - annotations.push(...chunkAnnotations); + reasoning += chunkReasoning; + content += chunkContent; + annotations.push(...chunkAnnotations); - if (!content && !reasoning) continue; + if (!chunkContent && !chunkReasoning) continue; - generationId = chunk.id || generationId; + generationId = chunk.id || generationId; - const updateResult = await ResultAsync.fromPromise( - client.mutation(api.messages.updateContent, { - message_id: mid, - content, - reasoning: reasoning.length > 0 ? reasoning : undefined, - session_token: sessionToken, - generation_id: generationId, - annotations, - reasoning_effort: reasoningEffort, - }), - (e) => `Failed to update message content: ${e}` + const updateResult = await ResultAsync.fromPromise( + client.mutation(api.messages.updateContent, { + message_id: mid, + content, + reasoning: reasoning.length > 0 ? reasoning : undefined, + session_token: sessionToken, + generation_id: generationId, + annotations, + reasoning_effort: reasoningEffort, + }), + (e) => `Failed to update message content: ${e}` + ); + + if (updateResult.isErr()) { + log( + `Background message update failed on chunk ${chunkCount}: ${updateResult.error}`, + startTime ); - - if (updateResult.isErr()) { - log( - `Background message update failed on chunk ${chunkCount}: ${updateResult.error}`, - startTime - ); - } } } - } else { - // Handle non-streaming response - const response = stream as any; - content = response.content || response.text || ''; - reasoning = response.reasoning || ''; - generationId = response.id; - - if (response.annotations) { - annotations.push(...response.annotations); - } - - const updateResult = await ResultAsync.fromPromise( - client.mutation(api.messages.updateContent, { - message_id: mid, - content, - reasoning: reasoning.length > 0 ? reasoning : undefined, - session_token: sessionToken, - generation_id: generationId, - annotations, - reasoning_effort: reasoningEffort, - }), - (e) => `Failed to update message content: ${e}` - ); - - if (updateResult.isErr()) { - log(`Background message update failed: ${updateResult.error}`, startTime); - } } log( diff --git a/src/routes/api/validate-key/+server.ts b/src/routes/api/validate-key/+server.ts new file mode 100644 index 0000000..32df948 --- /dev/null +++ b/src/routes/api/validate-key/+server.ts @@ -0,0 +1,218 @@ +import { json } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; +import { Provider } from '$lib/types'; +import { Result, ResultAsync } from 'neverthrow'; + +export type ProviderApiKeyData = { + label: string; + usage?: number; + is_free_tier?: boolean; + is_provisioning_key?: boolean; + limit?: number; + limit_remaining?: number; + valid: boolean; +}; + +export const POST: RequestHandler = async ({ request, locals }) => { + const session = await locals.auth(); + if (!session) { + return json({ error: 'Unauthorized' }, { status: 401 }); + } + + try { + const { provider, key } = await request.json(); + + if (!provider || !key) { + return json({ error: 'Missing provider or key' }, { status: 400 }); + } + + if (!Object.values(Provider).includes(provider)) { + return json({ error: 'Invalid provider' }, { status: 400 }); + } + + const result = await validateApiKey(provider, key); + + if (result.isErr()) { + return json({ error: result.error }, { status: 400 }); + } + + return json({ data: result.value }); + } catch (error) { + console.error('API key validation error:', error); + return json({ error: 'Internal server error' }, { status: 500 }); + } +}; + +async function validateApiKey(provider: Provider, key: string): Promise> { + switch (provider) { + case Provider.OpenRouter: + return await validateOpenRouterKey(key); + case Provider.OpenAI: + return await validateOpenAIKey(key); + case Provider.Anthropic: + return await validateAnthropicKey(key); + case Provider.Gemini: + return await validateGeminiKey(key); + case Provider.Mistral: + return await validateMistralKey(key); + case Provider.Cohere: + return await validateCohereKey(key); + default: + return Result.err(`Validation not implemented for provider: ${provider}`); + } +} + +// Provider-specific validation functions +async function validateOpenRouterKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://openrouter.ai/api/v1/auth/key', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + const { data } = await res.json(); + + if (!data) { + throw new Error('No key information returned'); + } + + return { + label: data.label || 'OpenRouter API Key', + usage: data.usage, + is_free_tier: data.is_free_tier, + is_provisioning_key: data.is_provisioning_key, + limit: data.limit, + limit_remaining: data.limit_remaining, + valid: true, + }; + })(), + (e) => `Failed to validate OpenRouter API key: ${e}` + ); +} + +async function validateOpenAIKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.openai.com/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'OpenAI API Key', + valid: true, + }; + })(), + (e) => `Failed to validate OpenAI API key: ${e}` + ); +} + +async function validateAnthropicKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + // Anthropic doesn't have a simple key validation endpoint + // We'll try a minimal request to test the key + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'x-api-key': key, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: 'claude-3-haiku-20240307', + max_tokens: 1, + messages: [{ role: 'user', content: 'test' }], + }), + }); + + // Even a 400 error means the key is valid (just bad request format) + if (res.status === 401 || res.status === 403) { + throw new Error('Invalid API key'); + } + + return { + label: 'Anthropic API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Anthropic API key: ${e}` + ); +} + +async function validateGeminiKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${key}`); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Google Gemini API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Gemini API key: ${e}` + ); +} + +async function validateMistralKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.mistral.ai/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Mistral API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Mistral API key: ${e}` + ); +} + +async function validateCohereKey(key: string): Promise> { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://api.cohere.ai/v1/models', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + + return { + label: 'Cohere API Key', + valid: true, + }; + })(), + (e) => `Failed to validate Cohere API key: ${e}` + ); +} \ No newline at end of file diff --git a/src/routes/chat/+layout.svelte b/src/routes/chat/+layout.svelte index 94a7a23..be00c11 100644 --- a/src/routes/chat/+layout.svelte +++ b/src/routes/chat/+layout.svelte @@ -213,15 +213,15 @@ const currentModelSupportsImages = $derived.by(() => { if (!settings.modelId) return false; - const openRouterModels = models.from(Provider.OpenRouter); - const currentModel = openRouterModels.find((m) => m.id === settings.modelId); + const allModels = models.all(); + const currentModel = allModels.find((m) => m.id === settings.modelId); return currentModel ? supportsImages(currentModel) : false; }); const currentModelSupportsReasoning = $derived.by(() => { if (!settings.modelId) return false; - const openRouterModels = models.from(Provider.OpenRouter); - const currentModel = openRouterModels.find((m) => m.id === settings.modelId); + const allModels = models.all(); + const currentModel = allModels.find((m) => m.id === settings.modelId); if (!currentModel) return false; return supportsReasoning(currentModel); }); diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte index e643da6..19b589c 100644 --- a/src/routes/chat/+page.svelte +++ b/src/routes/chat/+page.svelte @@ -63,8 +63,7 @@ let selectedCategory = $state(null); - const openRouterKeyQuery = useCachedQuery(api.user_keys.get, { - provider: Provider.OpenRouter, + const userKeysQuery = useCachedQuery(api.user_keys.all, { session_token: session.current?.session.token ?? '', }); @@ -76,7 +75,7 @@
- {#if prompt.current.length === 0 && openRouterKeyQuery.data} + {#if prompt.current.length === 0 && userKeysQuery.data && Object.values(userKeysQuery.data).some(key => key !== null)}

Hey there

- {:else if !openRouterKeyQuery.data && !openRouterKeyQuery.isLoading} + {:else if userKeysQuery.data && !Object.values(userKeysQuery.data).some(key => key !== null) && !userKeysQuery.isLoading}

Hey there, Date: Sun, 31 Aug 2025 23:59:15 +0530 Subject: [PATCH 07/10] feat: Add support for audio, video and document file types --- src/lib/backend/convex/conversations.ts | 9 +- src/lib/backend/convex/messages.ts | 13 +- src/lib/backend/convex/schema.ts | 9 +- .../model-picker/model-picker.svelte | 69 ++++-- src/lib/types.ts | 21 ++ src/lib/utils/attachment-manager.ts | 223 ++++++++++++++++++ src/lib/utils/model-capabilities.ts | 26 +- src/routes/api/generate-message/+server.ts | 66 +++++- src/routes/chat/+layout.svelte | 184 +++++++++++---- 9 files changed, 532 insertions(+), 88 deletions(-) create mode 100644 src/lib/utils/attachment-manager.ts diff --git a/src/lib/backend/convex/conversations.ts b/src/lib/backend/convex/conversations.ts index ec158d7..bf6c741 100644 --- a/src/lib/backend/convex/conversations.ts +++ b/src/lib/backend/convex/conversations.ts @@ -96,12 +96,15 @@ export const createAndAddMessage = mutation({ role: messageRoleValidator, session_token: v.string(), web_search_enabled: v.optional(v.boolean()), - images: v.optional( + attachments: v.optional( v.array( v.object({ + type: v.union(v.literal('image'), v.literal('video'), v.literal('audio'), v.literal('document')), url: v.string(), storage_id: v.string(), - fileName: v.optional(v.string()), + fileName: v.string(), + mimeType: v.string(), + size: v.number(), }) ) ), @@ -137,7 +140,7 @@ export const createAndAddMessage = mutation({ conversation_id: conversationId, session_token: args.session_token, web_search_enabled: args.web_search_enabled, - images: args.images, + attachments: args.attachments, }); return { diff --git a/src/lib/backend/convex/messages.ts b/src/lib/backend/convex/messages.ts index adcfc3c..b718ff1 100644 --- a/src/lib/backend/convex/messages.ts +++ b/src/lib/backend/convex/messages.ts @@ -48,13 +48,16 @@ export const create = mutation({ token_count: v.optional(v.number()), web_search_enabled: v.optional(v.boolean()), reasoning_effort: v.optional(reasoningEffortValidator), - // Optional image attachments - images: v.optional( + // Optional attachments + attachments: v.optional( v.array( v.object({ + type: v.union(v.literal('image'), v.literal('video'), v.literal('audio'), v.literal('document')), url: v.string(), storage_id: v.string(), - fileName: v.optional(v.string()), + fileName: v.string(), + mimeType: v.string(), + size: v.number(), }) ) ), @@ -96,8 +99,8 @@ export const create = mutation({ token_count: args.token_count, web_search_enabled: args.web_search_enabled, reasoning_effort: args.reasoning_effort, - // Optional image attachments - images: args.images, + // Optional attachments + attachments: args.attachments, }), ctx.db.patch(args.conversation_id as Id<'conversations'>, { generating: true, diff --git a/src/lib/backend/convex/schema.ts b/src/lib/backend/convex/schema.ts index 9add93b..e08211d 100644 --- a/src/lib/backend/convex/schema.ts +++ b/src/lib/backend/convex/schema.ts @@ -72,13 +72,16 @@ export default defineSchema({ model_id: v.optional(v.string()), provider: v.optional(providerValidator), token_count: v.optional(v.number()), - // Optional image attachments - images: v.optional( + // Optional attachments + attachments: v.optional( v.array( v.object({ + type: v.union(v.literal('image'), v.literal('video'), v.literal('audio'), v.literal('document')), url: v.string(), storage_id: v.string(), - fileName: v.optional(v.string()), + fileName: v.string(), + mimeType: v.string(), + size: v.number(), }) ) ), diff --git a/src/lib/components/model-picker/model-picker.svelte b/src/lib/components/model-picker/model-picker.svelte index 405a97e..8d0cf27 100644 --- a/src/lib/components/model-picker/model-picker.svelte +++ b/src/lib/components/model-picker/model-picker.svelte @@ -10,7 +10,15 @@ import { settings } from '$lib/state/settings.svelte'; import { Provider, PROVIDER_META } from '$lib/types'; import { fuzzysearch } from '$lib/utils/fuzzy-search'; - import { supportsImages, supportsReasoning, supportsStreaming, supportsToolCalls } from '$lib/utils/model-capabilities'; + import { + supportsImages, + supportsReasoning, + supportsStreaming, + supportsToolCalls, + supportsVideo, + supportsAudio, + supportsDocuments, + } from '$lib/utils/model-capabilities'; import { capitalize } from '$lib/utils/strings'; import { cn } from '$lib/utils/utils'; import { type Component } from 'svelte'; @@ -45,14 +53,33 @@ type Props = { class?: string; - /* When images are attached, we should not select models that don't support images */ - onlyImageModels?: boolean; + /* When attachments are present, restrict to models that support all attachment types */ + requiredCapabilities?: Array<'vision' | 'audio' | 'video' | 'documents'>; }; - let { class: className, onlyImageModels }: Props = $props(); + let { class: className, requiredCapabilities = [] }: Props = $props(); const client = useConvexClient(); + function meetsRequiredCapabilities(model: any): boolean { + if (requiredCapabilities.length === 0) return true; + + return requiredCapabilities.every((capability) => { + switch (capability) { + case 'vision': + return supportsImages(model); + case 'video': + return supportsVideo(model); + case 'audio': + return supportsAudio(model); + case 'documents': + return supportsDocuments(model); + default: + return true; + } + }); + } + const enabledModelsQuery = useCachedQuery(api.user_enabled_models.get_enabled, { session_token: session.current?.session.token ?? '', }); @@ -60,9 +87,9 @@ // Get enabled models from our models state with ModelInfo data const enabledArr = $derived.by(() => { const enabledModelIds = Object.keys(enabledModelsQuery.data ?? {}); - const enabledModels = modelsState.all().filter(model => - enabledModelIds.some(id => id.includes(model.id)) - ); + const enabledModels = modelsState + .all() + .filter((model) => enabledModelIds.some((id) => id.includes(model.id))); return enabledModels; }); @@ -146,7 +173,10 @@ // Group models by provider const groupedModels = $derived.by(() => { - const groups: Record = {} as Record; + const groups: Record = {} as Record< + Provider, + typeof filteredModels + >; filteredModels.forEach((model) => { const provider = model.provider as Provider; @@ -159,10 +189,13 @@ // Sort by provider order and name const result = Object.entries(groups) .sort(([a], [b]) => a.localeCompare(b)) - .map(([provider, models]) => [ - provider, - models.sort((a, b) => a.name.localeCompare(b.name)) - ] as [Provider, typeof models]); + .map( + ([provider, models]) => + [provider, models.sort((a, b) => a.name.localeCompare(b.name))] as [ + Provider, + typeof models, + ] + ); return result; }); @@ -338,8 +371,10 @@ {#if view === 'favorites' && pinnedModels.length > 0} {#each pinnedModels as model (model._id)} {@const modelInfo = enabledArr.find((m) => m.id === model.model_id)} - {@const formatted = modelInfo ? formatModelName(modelInfo) : { full: model.model_id, primary: model.model_id, secondary: '' }} - {@const disabled = onlyImageModels && modelInfo && !supportsImages(modelInfo)} + {@const formatted = modelInfo + ? formatModelName(modelInfo) + : { full: model.model_id, primary: model.model_id, secondary: '' }} + {@const disabled = modelInfo && !meetsRequiredCapabilities(modelInfo)} {/if} - + {#if modelInfo && supportsStreaming(modelInfo)} {#snippet trigger(tooltip)} @@ -499,8 +534,8 @@ {#snippet modelCard(model: (typeof enabledArr)[number])} {@const formatted = formatModelName(model)} - {@const disabled = onlyImageModels && !supportsImages(model)} - {@const enabledModelData = enabledModelsData.find(m => m.model_id === model.id)} + {@const disabled = !meetsRequiredCapabilities(model)} + {@const enabledModelData = enabledModelsData.find((m) => m.model_id === model.id)} = { supportsStreaming: true, supportsTools: true, supportsVision: true, + supportsVideo: false, + supportsAudio: true, + supportsDocuments: false, supportsEmbeddings: true, }, [Provider.Anthropic]: { @@ -65,6 +71,9 @@ export const PROVIDER_META: Record = { supportsStreaming: true, supportsTools: true, supportsVision: true, + supportsVideo: false, + supportsAudio: false, + supportsDocuments: true, supportsEmbeddings: false, }, [Provider.Gemini]: { @@ -77,6 +86,9 @@ export const PROVIDER_META: Record = { supportsStreaming: true, supportsTools: true, supportsVision: true, + supportsVideo: true, + supportsAudio: true, + supportsDocuments: true, supportsEmbeddings: true, }, [Provider.Mistral]: { @@ -89,6 +101,9 @@ export const PROVIDER_META: Record = { supportsStreaming: true, supportsTools: true, supportsVision: false, + supportsVideo: false, + supportsAudio: false, + supportsDocuments: false, supportsEmbeddings: true, }, [Provider.Cohere]: { @@ -101,6 +116,9 @@ export const PROVIDER_META: Record = { supportsStreaming: true, supportsTools: true, supportsVision: false, + supportsVideo: false, + supportsAudio: false, + supportsDocuments: false, supportsEmbeddings: true, }, [Provider.OpenRouter]: { @@ -113,6 +131,9 @@ export const PROVIDER_META: Record = { supportsStreaming: true, supportsTools: true, supportsVision: true, + supportsVideo: false, + supportsAudio: false, + supportsDocuments: false, supportsEmbeddings: false, }, }; diff --git a/src/lib/utils/attachment-manager.ts b/src/lib/utils/attachment-manager.ts new file mode 100644 index 0000000..cb3a4ad --- /dev/null +++ b/src/lib/utils/attachment-manager.ts @@ -0,0 +1,223 @@ +import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; +import { supportsImages, supportsVideo, supportsAudio, supportsDocuments } from './model-capabilities'; + +export type AttachmentType = 'image' | 'video' | 'audio' | 'document'; + +export interface AttachmentInfo { + type: AttachmentType; + mimeType: string; + maxSize: number; // in bytes + extensions: string[]; +} + +export interface ProcessedAttachment { + type: AttachmentType; + url: string; + storage_id: string; + fileName: string; + mimeType: string; + size: number; +} + +export class AttachmentManager { + private static readonly SUPPORTED_ATTACHMENTS: Record = { + image: { + type: 'image', + mimeType: 'image/*', + maxSize: 10 * 1024 * 1024, // 10MB + extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'] + }, + video: { + type: 'video', + mimeType: 'video/*', + maxSize: 100 * 1024 * 1024, // 100MB + extensions: ['.mp4', '.webm', '.ogg', '.avi', '.mov', '.wmv', '.flv', '.mkv'] + }, + audio: { + type: 'audio', + mimeType: 'audio/*', + maxSize: 50 * 1024 * 1024, // 50MB + extensions: ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac', '.wma'] + }, + document: { + type: 'document', + mimeType: 'application/pdf,text/*', + maxSize: 20 * 1024 * 1024, // 20MB + extensions: ['.pdf', '.txt', '.md', '.doc', '.docx', '.rtf', '.csv', '.json', '.xml', '.html'] + } + }; + + private static readonly MIME_TYPE_MAPPING: Record = { + // Images + 'image/jpeg': 'image', + 'image/jpg': 'image', + 'image/png': 'image', + 'image/gif': 'image', + 'image/webp': 'image', + 'image/bmp': 'image', + 'image/svg+xml': 'image', + + // Videos + 'video/mp4': 'video', + 'video/webm': 'video', + 'video/ogg': 'video', + 'video/avi': 'video', + 'video/quicktime': 'video', + 'video/x-msvideo': 'video', + 'video/x-flv': 'video', + 'video/x-matroska': 'video', + + // Audio + 'audio/mpeg': 'audio', + 'audio/mp3': 'audio', + 'audio/wav': 'audio', + 'audio/ogg': 'audio', + 'audio/mp4': 'audio', + 'audio/aac': 'audio', + 'audio/flac': 'audio', + 'audio/x-ms-wma': 'audio', + + // Documents + 'application/pdf': 'document', + 'text/plain': 'document', + 'text/markdown': 'document', + 'text/csv': 'document', + 'text/html': 'document', + 'text/xml': 'document', + 'application/json': 'document', + 'application/msword': 'document', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'document', + 'application/rtf': 'document' + }; + + /** + * Get supported attachment types for a given model + */ + static getSupportedAttachmentTypes(model: ModelInfo): AttachmentType[] { + const types: AttachmentType[] = []; + + if (supportsImages(model)) types.push('image'); + if (supportsVideo(model)) types.push('video'); + if (supportsAudio(model)) types.push('audio'); + if (supportsDocuments(model)) types.push('document'); + + return types; + } + + /** + * Get attachment info for supported types + */ + static getAttachmentInfo(types: AttachmentType[]): AttachmentInfo[] { + return types.map(type => this.SUPPORTED_ATTACHMENTS[type]); + } + + /** + * Validate if a file is supported by the given model + */ + static validateFile(file: File, model: ModelInfo): { valid: boolean; error?: string; type?: AttachmentType } { + const supportedTypes = this.getSupportedAttachmentTypes(model); + const fileType = this.getFileType(file); + + if (!fileType) { + return { valid: false, error: `Unsupported file type: ${file.type}` }; + } + + if (!supportedTypes.includes(fileType)) { + return { valid: false, error: `${fileType} files are not supported by this model` }; + } + + const attachmentInfo = this.SUPPORTED_ATTACHMENTS[fileType]; + if (file.size > attachmentInfo.maxSize) { + return { + valid: false, + error: `File size (${this.formatFileSize(file.size)}) exceeds maximum allowed size (${this.formatFileSize(attachmentInfo.maxSize)}) for ${fileType} files` + }; + } + + return { valid: true, type: fileType }; + } + + /** + * Get file type from File object + */ + static getFileType(file: File): AttachmentType | null { + if (this.MIME_TYPE_MAPPING[file.type]) { + return this.MIME_TYPE_MAPPING[file.type]; + } + + // Fallback to extension-based detection + const extension = this.getFileExtension(file.name); + for (const [type, info] of Object.entries(this.SUPPORTED_ATTACHMENTS)) { + if (info.extensions.includes(extension)) { + return type as AttachmentType; + } + } + + return null; + } + + /** + * Get file extension from filename + */ + private static getFileExtension(filename: string): string { + return '.' + filename.split('.').pop()?.toLowerCase() || ''; + } + + /** + * Format file size for human reading + */ + static formatFileSize(bytes: number): string { + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + if (bytes === 0) return '0 Bytes'; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i]; + } + + /** + * Get accept string for HTML input element + */ + static getAcceptString(types: AttachmentType[]): string { + const mimeTypes = types.map(type => this.SUPPORTED_ATTACHMENTS[type].mimeType); + return mimeTypes.join(','); + } + + /** + * Get icon name for attachment type + */ + static getTypeIcon(type: AttachmentType): string { + const icons = { + image: 'image', + video: 'video', + audio: 'music', + document: 'file-text' + }; + return icons[type]; + } + + /** + * Get display name for attachment type + */ + static getTypeDisplayName(type: AttachmentType): string { + const names = { + image: 'Image', + video: 'Video', + audio: 'Audio', + document: 'Document' + }; + return names[type]; + } + + /** + * Create accept string for multiple types + */ + static createFileInputAccept(supportedTypes: AttachmentType[]): string { + if (supportedTypes.length === 0) return ''; + + const extensions: string[] = []; + supportedTypes.forEach(type => { + extensions.push(...this.SUPPORTED_ATTACHMENTS[type].extensions); + }); + + return extensions.join(','); + } +} \ No newline at end of file diff --git a/src/lib/utils/model-capabilities.ts b/src/lib/utils/model-capabilities.ts index 55280f5..c7a2f2a 100644 --- a/src/lib/utils/model-capabilities.ts +++ b/src/lib/utils/model-capabilities.ts @@ -13,13 +13,37 @@ export function supportsStreaming(model: ModelInfo): boolean { } export function supportsToolCalls(model: ModelInfo): boolean { - return model.capabilities.toolCalls; + return model.capabilities.functionCalling; +} + +export function supportsVideo(model: ModelInfo): boolean { + return model.capabilities.video ?? false; +} + +export function supportsAudio(model: ModelInfo): boolean { + return model.capabilities.audio; +} + +export function supportsDocuments(model: ModelInfo): boolean { + return model.capabilities.documents ?? false; } export function getImageSupportedModels(models: ModelInfo[]): ModelInfo[] { return models.filter(supportsImages); } +export function getVideoSupportedModels(models: ModelInfo[]): ModelInfo[] { + return models.filter(supportsVideo); +} + +export function getAudioSupportedModels(models: ModelInfo[]): ModelInfo[] { + return models.filter(supportsAudio); +} + +export function getDocumentSupportedModels(models: ModelInfo[]): ModelInfo[] { + return models.filter(supportsDocuments); +} + export function getReasoningModels(models: ModelInfo[]): ModelInfo[] { return models.filter(supportsReasoning); } diff --git a/src/routes/api/generate-message/+server.ts b/src/routes/api/generate-message/+server.ts index 90cb4ff..731363b 100644 --- a/src/routes/api/generate-message/+server.ts +++ b/src/routes/api/generate-message/+server.ts @@ -25,12 +25,15 @@ const reqBodySchema = z session_token: z.string(), conversation_id: z.string().optional(), web_search_enabled: z.boolean().optional(), - images: z + attachments: z .array( z.object({ + type: z.enum(['image', 'video', 'audio', 'document']), url: z.string(), storage_id: z.string(), - fileName: z.string().optional(), + fileName: z.string(), + mimeType: z.string(), + size: z.number(), }) ) .optional(), @@ -353,18 +356,57 @@ async function generateAIResponse({ log(`Background: ${attachedRules.length} rules attached`, startTime); const formattedMessages = messages.map((m) => { - if (m.images && m.images.length > 0 && m.role === 'user') { + // Handle attachments format + if (m.attachments && m.attachments.length > 0 && m.role === 'user') { + const contentParts: Array<{ + type: string; + text?: string; + imageUrl?: string; + videoUrl?: string; + audioUrl?: string; + documentUrl?: string; + mimeType?: string; + }> = [{ type: 'text', text: m.content }]; + + for (const attachment of m.attachments) { + switch (attachment.type) { + case 'image': + contentParts.push({ + type: 'image', + imageUrl: attachment.url, + mimeType: attachment.mimeType, + }); + break; + case 'video': + contentParts.push({ + type: 'video', + videoUrl: attachment.url, + mimeType: attachment.mimeType, + }); + break; + case 'audio': + contentParts.push({ + type: 'audio', + audioUrl: attachment.url, + mimeType: attachment.mimeType, + }); + break; + case 'document': + contentParts.push({ + type: 'document', + documentUrl: attachment.url, + mimeType: attachment.mimeType, + }); + break; + } + } + return { role: 'user' as const, - content: [ - { type: 'text', text: m.content }, - ...m.images.map((img) => ({ - type: 'image_url', - image_url: { url: img.url }, - })), - ], + content: contentParts, }; } + return { role: m.role as 'user' | 'assistant' | 'system', content: m.content, @@ -618,7 +660,7 @@ export const POST: RequestHandler = async ({ request }) => { content: args.message, content_html: '', role: 'user', - images: args.images, + attachments: args.attachments, web_search_enabled: args.web_search_enabled, session_token: sessionToken, }), @@ -657,7 +699,7 @@ export const POST: RequestHandler = async ({ request }) => { model_id: args.model_id, reasoning_effort: args.reasoning_effort, role: 'user', - images: args.images, + attachments: args.attachments, web_search_enabled: args.web_search_enabled, }), (e) => `Failed to create user message: ${e}` diff --git a/src/routes/chat/+layout.svelte b/src/routes/chat/+layout.svelte index be00c11..e2de67d 100644 --- a/src/routes/chat/+layout.svelte +++ b/src/routes/chat/+layout.svelte @@ -20,7 +20,8 @@ import { settings } from '$lib/state/settings.svelte.js'; import { Provider } from '$lib/types'; import { compressImage } from '$lib/utils/image-compression'; - import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import { supportsImages, supportsReasoning, supportsVideo, supportsAudio, supportsDocuments } from '$lib/utils/model-capabilities'; + import { AttachmentManager, type AttachmentType, type ProcessedAttachment } from '$lib/utils/attachment-manager'; import { omit, pick } from '$lib/utils/object.js'; import { cn } from '$lib/utils/utils.js'; import { useConvexClient } from 'convex-svelte'; @@ -30,6 +31,10 @@ import SendIcon from '~icons/lucide/arrow-up'; import ChevronDownIcon from '~icons/lucide/chevron-down'; import ImageIcon from '~icons/lucide/image'; + import VideoIcon from '~icons/lucide/video'; + import AudioIcon from '~icons/lucide/music'; + import FileTextIcon from '~icons/lucide/file-text'; + import PaperclipIcon from '~icons/lucide/paperclip'; import PanelLeftIcon from '~icons/lucide/panel-left'; import SearchIcon from '~icons/lucide/search'; import Settings2Icon from '~icons/lucide/settings-2'; @@ -119,8 +124,8 @@ loading = true; - const imagesCopy = [...selectedImages]; - selectedImages = []; + const attachmentsCopy = [...selectedAttachments]; + selectedAttachments = []; try { const res = await callGenerateMessage({ @@ -128,7 +133,7 @@ session_token: session.current?.session.token, conversation_id: page.params.id ?? undefined, model_id: settings.modelId, - images: imagesCopy.length > 0 ? imagesCopy : undefined, + attachments: attachmentsCopy.length > 0 ? attachmentsCopy : undefined, web_search_enabled: settings.webSearchEnabled, reasoning_effort: currentModelSupportsReasoning ? settings.reasoningEffort : undefined, }); @@ -195,7 +200,7 @@ const autosize = new TextareaAutosize(); const message = new PersistedState('prompt', ''); - let selectedImages = $state<{ url: string; storage_id: string; fileName?: string }[]>([]); + let selectedAttachments = $state([]); let isUploading = $state(false); let fileInput = $state(); let imageModal = $state<{ open: boolean; imageUrl: string; fileName: string }>({ @@ -211,11 +216,20 @@ models.init(); - const currentModelSupportsImages = $derived.by(() => { - if (!settings.modelId) return false; + const currentModel = $derived.by(() => { + if (!settings.modelId) return null; const allModels = models.all(); - const currentModel = allModels.find((m) => m.id === settings.modelId); - return currentModel ? supportsImages(currentModel) : false; + return allModels.find((m) => m.id === settings.modelId) || null; + }); + + const currentModelSupportsImages = $derived(currentModel ? supportsImages(currentModel) : false); + const currentModelSupportsVideo = $derived(currentModel ? supportsVideo(currentModel) : false); + const currentModelSupportsAudio = $derived(currentModel ? supportsAudio(currentModel) : false); + const currentModelSupportsDocuments = $derived(currentModel ? supportsDocuments(currentModel) : false); + + const supportedAttachmentTypes = $derived.by(() => { + if (!currentModel) return []; + return AttachmentManager.getSupportedAttachmentTypes(currentModel); }); const currentModelSupportsReasoning = $derived.by(() => { @@ -228,36 +242,49 @@ const fileUpload = new FileUpload({ multiple: true, - accept: 'image/*', - maxSize: 10 * 1024 * 1024, // 10MB + maxSize: 100 * 1024 * 1024, // 100MB max for any file type + }); + + // Update the file input accept attribute reactively + $effect(() => { + if (fileInput) { + const acceptString = AttachmentManager.getAcceptString(supportedAttachmentTypes); + fileInput.accept = acceptString; + } }); async function handleFileChange(files: File[]) { - if (!files.length || !session.current?.session.token) return; + if (!files.length || !session.current?.session.token || !currentModel) return; isUploading = true; - const uploadedFiles: { url: string; storage_id: string; fileName?: string }[] = []; + const uploadedFiles: ProcessedAttachment[] = []; try { for (const file of files) { - // Skip non-image files - if (!file.type.startsWith('image/')) { - console.warn('Skipping non-image file:', file.name); + // Validate file against current model capabilities + const validation = AttachmentManager.validateFile(file, currentModel); + if (!validation.valid) { + console.warn(`Skipping invalid file ${file.name}: ${validation.error}`); + // TODO: Show error toast to user continue; } - // Compress image to max 1MB - const compressedFile = await compressImage(file, 1024 * 1024); + let fileToUpload = file; + + // Compress images to max 1MB for better performance + if (validation.type === 'image') { + fileToUpload = await compressImage(file, 1024 * 1024); + } // Generate upload URL const uploadUrl = await client.mutation(api.storage.generateUploadUrl, { session_token: session.current.session.token, }); - // Upload compressed file + // Upload file const result = await fetch(uploadUrl, { method: 'POST', - body: compressedFile, + body: fileToUpload, }); if (!result.ok) { @@ -273,11 +300,18 @@ }); if (url) { - uploadedFiles.push({ url, storage_id: storageId, fileName: file.name }); + uploadedFiles.push({ + type: validation.type!, + url, + storage_id: storageId, + fileName: file.name, + mimeType: file.type, + size: file.size + }); } } - selectedImages = [...selectedImages, ...uploadedFiles]; + selectedAttachments = [...selectedAttachments, ...uploadedFiles]; } catch (error) { console.error('Upload failed:', error); } finally { @@ -285,8 +319,31 @@ } } - function removeImage(index: number) { - selectedImages = selectedImages.filter((_, i) => i !== index); + function removeAttachment(index: number) { + selectedAttachments = selectedAttachments.filter((_, i) => i !== index); + } + + function getRequiredCapabilities(attachments: ProcessedAttachment[]): Array<'vision' | 'audio' | 'video' | 'documents'> { + const capabilities: Array<'vision' | 'audio' | 'video' | 'documents'> = []; + + attachments.forEach(attachment => { + switch (attachment.type) { + case 'image': + if (!capabilities.includes('vision')) capabilities.push('vision'); + break; + case 'video': + if (!capabilities.includes('video')) capabilities.push('video'); + break; + case 'audio': + if (!capabilities.includes('audio')) capabilities.push('audio'); + break; + case 'document': + if (!capabilities.includes('documents')) capabilities.push('documents'); + break; + } + }); + + return capabilities; } function openImageModal(imageUrl: string, fileName: string) { @@ -446,7 +503,7 @@ 0 ? omit(fileUpload.dropzone, ['onclick']) : {}} > @@ -608,26 +665,45 @@

{/if}
- {#if selectedImages.length > 0} + {#if selectedAttachments.length > 0}
- {#each selectedImages as image, index (image.storage_id)} + {#each selectedAttachments as attachment, index (attachment.storage_id)}
+ {#if attachment.type === 'image'} + + {:else if attachment.type === 'video'} +
+ + {attachment.fileName} +
+ {:else if attachment.type === 'audio'} +
+ + {attachment.fileName} +
+ {:else if attachment.type === 'document'} +
+ + {attachment.fileName} +
+ {/if} -
- 0} /> + 0 ? getRequiredCapabilities(selectedAttachments) : []} />
{#if currentModelSupportsReasoning} @@ -746,7 +822,7 @@ - {#if currentModelSupportsImages} + {#if supportedAttachmentTypes.length > 0}
{:else} - + {/if} - + {/if} {#if session.current !== null && message.current.trim() !== ''} @@ -811,12 +893,20 @@
- {#if fileUpload.isDragging && currentModelSupportsImages} + {#if fileUpload.isDragging && supportedAttachmentTypes.length > 0}
-

Add image

-

Drop an image here to attach it to your message.

+

+ {#if supportedAttachmentTypes.length === 1 && supportedAttachmentTypes[0]} + Add {AttachmentManager.getTypeDisplayName(supportedAttachmentTypes[0]).toLowerCase()} + {:else} + Add files + {/if} +

+

+ Drop {supportedAttachmentTypes.length === 1 ? `${supportedAttachmentTypes[0]}s` : 'supported files'} here to attach to your message. +

{/if} From bca38fa221185c05560318bc2a926cb506b470a5 Mon Sep 17 00:00:00 2001 From: Aunali321 Date: Mon, 1 Sep 2025 00:24:51 +0530 Subject: [PATCH 08/10] refactor: Clean up unused code --- .../model-picker/model-picker.svelte | 40 ++- src/lib/types.ts | 49 ---- src/lib/utils/attachment-manager.ts | 229 ++---------------- src/lib/utils/model-capabilities.ts | 24 -- src/routes/api/generate-message/+server.ts | 56 +---- src/routes/chat/+layout.svelte | 44 ++-- 6 files changed, 64 insertions(+), 378 deletions(-) diff --git a/src/lib/components/model-picker/model-picker.svelte b/src/lib/components/model-picker/model-picker.svelte index 8d0cf27..eaa30c1 100644 --- a/src/lib/components/model-picker/model-picker.svelte +++ b/src/lib/components/model-picker/model-picker.svelte @@ -15,9 +15,6 @@ supportsReasoning, supportsStreaming, supportsToolCalls, - supportsVideo, - supportsAudio, - supportsDocuments, } from '$lib/utils/model-capabilities'; import { capitalize } from '$lib/utils/strings'; import { cn } from '$lib/utils/utils'; @@ -53,7 +50,7 @@ type Props = { class?: string; - /* When attachments are present, restrict to models that support all attachment types */ + /* Required capabilities that the selected model must support */ requiredCapabilities?: Array<'vision' | 'audio' | 'video' | 'documents'>; }; @@ -61,25 +58,6 @@ const client = useConvexClient(); - function meetsRequiredCapabilities(model: any): boolean { - if (requiredCapabilities.length === 0) return true; - - return requiredCapabilities.every((capability) => { - switch (capability) { - case 'vision': - return supportsImages(model); - case 'video': - return supportsVideo(model); - case 'audio': - return supportsAudio(model); - case 'documents': - return supportsDocuments(model); - default: - return true; - } - }); - } - const enabledModelsQuery = useCachedQuery(api.user_enabled_models.get_enabled, { session_token: session.current?.session.token ?? '', }); @@ -285,6 +263,18 @@ // until we migrate the pinning system to work with the new ModelInfo structure const enabledModelsData = $derived(Object.values(enabledModelsQuery.data ?? {})); const pinnedModels = $derived(enabledModelsData.filter((m) => isPinned(m))); + + function modelSupportsRequiredCapabilities(model: typeof enabledArr[number], required: Array<'vision' | 'audio' | 'video' | 'documents'>): boolean { + return required.every(capability => { + switch (capability) { + case 'vision': return model.capabilities.vision; + case 'audio': return model.capabilities.audio; + case 'video': return model.capabilities.video; + case 'documents': return model.capabilities.documents; + default: return false; + } + }); + } 0 && modelInfo && !modelSupportsRequiredCapabilities(modelInfo, requiredCapabilities)} 0 && !modelSupportsRequiredCapabilities(model, requiredCapabilities)} {@const enabledModelData = enabledModelsData.find((m) => m.model_id === model.id)} = { apiKeyName: 'OpenAI API Key', placeholder: 'sk-...', docsLink: 'https://platform.openai.com/docs', - supportsStreaming: true, - supportsTools: true, - supportsVision: true, - supportsVideo: false, - supportsAudio: true, - supportsDocuments: false, - supportsEmbeddings: true, }, [Provider.Anthropic]: { title: 'Anthropic', @@ -68,13 +54,6 @@ export const PROVIDER_META: Record = { apiKeyName: 'Anthropic API Key', placeholder: 'sk-ant-...', docsLink: 'https://docs.anthropic.com', - supportsStreaming: true, - supportsTools: true, - supportsVision: true, - supportsVideo: false, - supportsAudio: false, - supportsDocuments: true, - supportsEmbeddings: false, }, [Provider.Gemini]: { title: 'Google Gemini', @@ -83,13 +62,6 @@ export const PROVIDER_META: Record = { apiKeyName: 'Google AI API Key', placeholder: 'AIza...', docsLink: 'https://ai.google.dev/docs', - supportsStreaming: true, - supportsTools: true, - supportsVision: true, - supportsVideo: true, - supportsAudio: true, - supportsDocuments: true, - supportsEmbeddings: true, }, [Provider.Mistral]: { title: 'Mistral', @@ -98,13 +70,6 @@ export const PROVIDER_META: Record = { apiKeyName: 'Mistral API Key', placeholder: 'mistral-...', docsLink: 'https://docs.mistral.ai', - supportsStreaming: true, - supportsTools: true, - supportsVision: false, - supportsVideo: false, - supportsAudio: false, - supportsDocuments: false, - supportsEmbeddings: true, }, [Provider.Cohere]: { title: 'Cohere', @@ -113,13 +78,6 @@ export const PROVIDER_META: Record = { apiKeyName: 'Cohere API Key', placeholder: 'co_...', docsLink: 'https://docs.cohere.com', - supportsStreaming: true, - supportsTools: true, - supportsVision: false, - supportsVideo: false, - supportsAudio: false, - supportsDocuments: false, - supportsEmbeddings: true, }, [Provider.OpenRouter]: { title: 'OpenRouter', @@ -128,12 +86,5 @@ export const PROVIDER_META: Record = { apiKeyName: 'OpenRouter API Key', placeholder: 'sk-or-...', docsLink: 'https://openrouter.ai/docs', - supportsStreaming: true, - supportsTools: true, - supportsVision: true, - supportsVideo: false, - supportsAudio: false, - supportsDocuments: false, - supportsEmbeddings: false, }, }; diff --git a/src/lib/utils/attachment-manager.ts b/src/lib/utils/attachment-manager.ts index cb3a4ad..1830887 100644 --- a/src/lib/utils/attachment-manager.ts +++ b/src/lib/utils/attachment-manager.ts @@ -1,15 +1,7 @@ import type { ModelInfo } from '@keplersystems/kepler-ai-sdk'; -import { supportsImages, supportsVideo, supportsAudio, supportsDocuments } from './model-capabilities'; export type AttachmentType = 'image' | 'video' | 'audio' | 'document'; -export interface AttachmentInfo { - type: AttachmentType; - mimeType: string; - maxSize: number; // in bytes - extensions: string[]; -} - export interface ProcessedAttachment { type: AttachmentType; url: string; @@ -19,205 +11,30 @@ export interface ProcessedAttachment { size: number; } -export class AttachmentManager { - private static readonly SUPPORTED_ATTACHMENTS: Record = { - image: { - type: 'image', - mimeType: 'image/*', - maxSize: 10 * 1024 * 1024, // 10MB - extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'] - }, - video: { - type: 'video', - mimeType: 'video/*', - maxSize: 100 * 1024 * 1024, // 100MB - extensions: ['.mp4', '.webm', '.ogg', '.avi', '.mov', '.wmv', '.flv', '.mkv'] - }, - audio: { - type: 'audio', - mimeType: 'audio/*', - maxSize: 50 * 1024 * 1024, // 50MB - extensions: ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac', '.wma'] - }, - document: { - type: 'document', - mimeType: 'application/pdf,text/*', - maxSize: 20 * 1024 * 1024, // 20MB - extensions: ['.pdf', '.txt', '.md', '.doc', '.docx', '.rtf', '.csv', '.json', '.xml', '.html'] - } - }; +const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB - private static readonly MIME_TYPE_MAPPING: Record = { - // Images - 'image/jpeg': 'image', - 'image/jpg': 'image', - 'image/png': 'image', - 'image/gif': 'image', - 'image/webp': 'image', - 'image/bmp': 'image', - 'image/svg+xml': 'image', - - // Videos - 'video/mp4': 'video', - 'video/webm': 'video', - 'video/ogg': 'video', - 'video/avi': 'video', - 'video/quicktime': 'video', - 'video/x-msvideo': 'video', - 'video/x-flv': 'video', - 'video/x-matroska': 'video', - - // Audio - 'audio/mpeg': 'audio', - 'audio/mp3': 'audio', - 'audio/wav': 'audio', - 'audio/ogg': 'audio', - 'audio/mp4': 'audio', - 'audio/aac': 'audio', - 'audio/flac': 'audio', - 'audio/x-ms-wma': 'audio', - - // Documents - 'application/pdf': 'document', - 'text/plain': 'document', - 'text/markdown': 'document', - 'text/csv': 'document', - 'text/html': 'document', - 'text/xml': 'document', - 'application/json': 'document', - 'application/msword': 'document', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'document', - 'application/rtf': 'document' - }; +export function getSupportedAttachmentTypes(model: ModelInfo): AttachmentType[] { + const types: AttachmentType[] = []; + if (model.capabilities.vision) types.push('image'); + if (model.capabilities.video) types.push('video'); + if (model.capabilities.audio) types.push('audio'); + if (model.capabilities.documents) types.push('document'); + return types; +} - /** - * Get supported attachment types for a given model - */ - static getSupportedAttachmentTypes(model: ModelInfo): AttachmentType[] { - const types: AttachmentType[] = []; - - if (supportsImages(model)) types.push('image'); - if (supportsVideo(model)) types.push('video'); - if (supportsAudio(model)) types.push('audio'); - if (supportsDocuments(model)) types.push('document'); - - return types; - } - - /** - * Get attachment info for supported types - */ - static getAttachmentInfo(types: AttachmentType[]): AttachmentInfo[] { - return types.map(type => this.SUPPORTED_ATTACHMENTS[type]); - } - - /** - * Validate if a file is supported by the given model - */ - static validateFile(file: File, model: ModelInfo): { valid: boolean; error?: string; type?: AttachmentType } { - const supportedTypes = this.getSupportedAttachmentTypes(model); - const fileType = this.getFileType(file); - - if (!fileType) { - return { valid: false, error: `Unsupported file type: ${file.type}` }; - } - - if (!supportedTypes.includes(fileType)) { - return { valid: false, error: `${fileType} files are not supported by this model` }; - } - - const attachmentInfo = this.SUPPORTED_ATTACHMENTS[fileType]; - if (file.size > attachmentInfo.maxSize) { - return { - valid: false, - error: `File size (${this.formatFileSize(file.size)}) exceeds maximum allowed size (${this.formatFileSize(attachmentInfo.maxSize)}) for ${fileType} files` - }; - } - - return { valid: true, type: fileType }; - } - - /** - * Get file type from File object - */ - static getFileType(file: File): AttachmentType | null { - if (this.MIME_TYPE_MAPPING[file.type]) { - return this.MIME_TYPE_MAPPING[file.type]; - } - - // Fallback to extension-based detection - const extension = this.getFileExtension(file.name); - for (const [type, info] of Object.entries(this.SUPPORTED_ATTACHMENTS)) { - if (info.extensions.includes(extension)) { - return type as AttachmentType; - } - } - - return null; - } - - /** - * Get file extension from filename - */ - private static getFileExtension(filename: string): string { - return '.' + filename.split('.').pop()?.toLowerCase() || ''; - } - - /** - * Format file size for human reading - */ - static formatFileSize(bytes: number): string { - const sizes = ['Bytes', 'KB', 'MB', 'GB']; - if (bytes === 0) return '0 Bytes'; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i]; - } - - /** - * Get accept string for HTML input element - */ - static getAcceptString(types: AttachmentType[]): string { - const mimeTypes = types.map(type => this.SUPPORTED_ATTACHMENTS[type].mimeType); - return mimeTypes.join(','); - } - - /** - * Get icon name for attachment type - */ - static getTypeIcon(type: AttachmentType): string { - const icons = { - image: 'image', - video: 'video', - audio: 'music', - document: 'file-text' - }; - return icons[type]; - } - - /** - * Get display name for attachment type - */ - static getTypeDisplayName(type: AttachmentType): string { - const names = { - image: 'Image', - video: 'Video', - audio: 'Audio', - document: 'Document' - }; - return names[type]; - } - - /** - * Create accept string for multiple types - */ - static createFileInputAccept(supportedTypes: AttachmentType[]): string { - if (supportedTypes.length === 0) return ''; - - const extensions: string[] = []; - supportedTypes.forEach(type => { - extensions.push(...this.SUPPORTED_ATTACHMENTS[type].extensions); - }); - - return extensions.join(','); +export function getFileType(file: File): AttachmentType | null { + const { type } = file; + if (type.startsWith('image/')) return 'image'; + if (type.startsWith('video/')) return 'video'; + if (type.startsWith('audio/')) return 'audio'; + if (type === 'application/pdf' || type.startsWith('text/')) return 'document'; + return null; +} + +export function getAcceptString(types: AttachmentType[]): string { + const mimeTypes = types.map(type => `${type}/*`); + if (types.includes('document')) { + mimeTypes.push('application/pdf', 'text/*'); } + return mimeTypes.join(','); } \ No newline at end of file diff --git a/src/lib/utils/model-capabilities.ts b/src/lib/utils/model-capabilities.ts index c7a2f2a..97b0675 100644 --- a/src/lib/utils/model-capabilities.ts +++ b/src/lib/utils/model-capabilities.ts @@ -16,34 +16,10 @@ export function supportsToolCalls(model: ModelInfo): boolean { return model.capabilities.functionCalling; } -export function supportsVideo(model: ModelInfo): boolean { - return model.capabilities.video ?? false; -} - -export function supportsAudio(model: ModelInfo): boolean { - return model.capabilities.audio; -} - -export function supportsDocuments(model: ModelInfo): boolean { - return model.capabilities.documents ?? false; -} - export function getImageSupportedModels(models: ModelInfo[]): ModelInfo[] { return models.filter(supportsImages); } -export function getVideoSupportedModels(models: ModelInfo[]): ModelInfo[] { - return models.filter(supportsVideo); -} - -export function getAudioSupportedModels(models: ModelInfo[]): ModelInfo[] { - return models.filter(supportsAudio); -} - -export function getDocumentSupportedModels(models: ModelInfo[]): ModelInfo[] { - return models.filter(supportsDocuments); -} - export function getReasoningModels(models: ModelInfo[]): ModelInfo[] { return models.filter(supportsReasoning); } diff --git a/src/routes/api/generate-message/+server.ts b/src/routes/api/generate-message/+server.ts index 731363b..8359ed0 100644 --- a/src/routes/api/generate-message/+server.ts +++ b/src/routes/api/generate-message/+server.ts @@ -356,55 +356,17 @@ async function generateAIResponse({ log(`Background: ${attachedRules.length} rules attached`, startTime); const formattedMessages = messages.map((m) => { - // Handle attachments format if (m.attachments && m.attachments.length > 0 && m.role === 'user') { - const contentParts: Array<{ - type: string; - text?: string; - imageUrl?: string; - videoUrl?: string; - audioUrl?: string; - documentUrl?: string; - mimeType?: string; - }> = [{ type: 'text', text: m.content }]; + const contentParts = [ + { type: 'text', text: m.content }, + ...m.attachments.map(attachment => ({ + type: attachment.type, + [`${attachment.type}Url`]: attachment.url, + mimeType: attachment.mimeType, + })) + ]; - for (const attachment of m.attachments) { - switch (attachment.type) { - case 'image': - contentParts.push({ - type: 'image', - imageUrl: attachment.url, - mimeType: attachment.mimeType, - }); - break; - case 'video': - contentParts.push({ - type: 'video', - videoUrl: attachment.url, - mimeType: attachment.mimeType, - }); - break; - case 'audio': - contentParts.push({ - type: 'audio', - audioUrl: attachment.url, - mimeType: attachment.mimeType, - }); - break; - case 'document': - contentParts.push({ - type: 'document', - documentUrl: attachment.url, - mimeType: attachment.mimeType, - }); - break; - } - } - - return { - role: 'user' as const, - content: contentParts, - }; + return { role: 'user' as const, content: contentParts }; } return { diff --git a/src/routes/chat/+layout.svelte b/src/routes/chat/+layout.svelte index e2de67d..e82fe62 100644 --- a/src/routes/chat/+layout.svelte +++ b/src/routes/chat/+layout.svelte @@ -20,8 +20,8 @@ import { settings } from '$lib/state/settings.svelte.js'; import { Provider } from '$lib/types'; import { compressImage } from '$lib/utils/image-compression'; - import { supportsImages, supportsReasoning, supportsVideo, supportsAudio, supportsDocuments } from '$lib/utils/model-capabilities'; - import { AttachmentManager, type AttachmentType, type ProcessedAttachment } from '$lib/utils/attachment-manager'; + import { supportsImages, supportsReasoning } from '$lib/utils/model-capabilities'; + import { getSupportedAttachmentTypes, getFileType, getAcceptString, type ProcessedAttachment } from '$lib/utils/attachment-manager'; import { omit, pick } from '$lib/utils/object.js'; import { cn } from '$lib/utils/utils.js'; import { useConvexClient } from 'convex-svelte'; @@ -218,19 +218,12 @@ const currentModel = $derived.by(() => { if (!settings.modelId) return null; - const allModels = models.all(); - return allModels.find((m) => m.id === settings.modelId) || null; + return models.all().find((m) => m.id === settings.modelId) || null; }); - const currentModelSupportsImages = $derived(currentModel ? supportsImages(currentModel) : false); - const currentModelSupportsVideo = $derived(currentModel ? supportsVideo(currentModel) : false); - const currentModelSupportsAudio = $derived(currentModel ? supportsAudio(currentModel) : false); - const currentModelSupportsDocuments = $derived(currentModel ? supportsDocuments(currentModel) : false); - - const supportedAttachmentTypes = $derived.by(() => { - if (!currentModel) return []; - return AttachmentManager.getSupportedAttachmentTypes(currentModel); - }); + const supportedAttachmentTypes = $derived( + currentModel ? getSupportedAttachmentTypes(currentModel) : [] + ); const currentModelSupportsReasoning = $derived.by(() => { if (!settings.modelId) return false; @@ -245,11 +238,10 @@ maxSize: 100 * 1024 * 1024, // 100MB max for any file type }); - // Update the file input accept attribute reactively + // Update file input accept attribute reactively $effect(() => { if (fileInput) { - const acceptString = AttachmentManager.getAcceptString(supportedAttachmentTypes); - fileInput.accept = acceptString; + fileInput.accept = getAcceptString(supportedAttachmentTypes); } }); @@ -261,18 +253,16 @@ try { for (const file of files) { - // Validate file against current model capabilities - const validation = AttachmentManager.validateFile(file, currentModel); - if (!validation.valid) { - console.warn(`Skipping invalid file ${file.name}: ${validation.error}`); - // TODO: Show error toast to user + // Simple file validation + const fileType = getFileType(file); + if (!fileType || !supportedAttachmentTypes.includes(fileType)) { + console.warn(`Unsupported file type: ${file.name}`); continue; } + // Compress images for better performance let fileToUpload = file; - - // Compress images to max 1MB for better performance - if (validation.type === 'image') { + if (fileType === 'image') { fileToUpload = await compressImage(file, 1024 * 1024); } @@ -301,7 +291,7 @@ if (url) { uploadedFiles.push({ - type: validation.type!, + type: fileType, url, storage_id: storageId, fileName: file.name, @@ -838,7 +828,7 @@ {/if}