diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..187667c3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.env +.DS_Store +.github +.vercel +.wrangler +dist +doc +node_modules +plugins +scripts +wrangler.toml +config.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index ad08e406..8729fca1 100644 --- a/.gitignore +++ b/.gitignore @@ -152,3 +152,4 @@ out /wrangler-test.toml /dist/index.cjs /dist/index.d.ts +/dist/src diff --git a/Dockerfile b/Dockerfile index 6d976034..637e0efd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,15 @@ -FROM node:alpine as DEV - +FROM node:alpine AS build WORKDIR /app -COPY package.json vite.config.ts tsconfig.json ./ -COPY src ./src -RUN npm install && npm run build:local +COPY package.json tsconfig.json vite.config.ts ./ +RUN npm install +COPY src src +RUN npm run build:local -FROM node:alpine as PROD +FROM node:alpine AS production WORKDIR /app -COPY --from=DEV /app/dist/index.js /app/dist/index.js -COPY --from=DEV /app/package.json /app/ -RUN npm install --only=production --omit=dev -RUN apk add --no-cache sqlite +COPY package.json ./ +RUN npm install --omit=dev +COPY --from=build /app/dist ./dist EXPOSE 8787 -CMD ["npm", "run", "start:dist"] +CMD ["npm", "run", "start:dist"] \ No newline at end of file diff --git a/README.md b/README.md index 309d463e..19530eb7 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ You can customize the system initialization information so that your debugged pe - Serverless deployment - Multi-platform deployment support (Cloudflare Workers, Vercel, Docker[...](doc/en/PLATFORM.md)) - Adaptation to multiple AI service providers (OpenAI, Azure OpenAI, Cloudflare AI, Cohere, Anthropic, Mistral...) +- Switching Models with InlineKeyboards - Custom commands (can achieve quick switching of models, switching of robot presets) - Support for multiple Telegram bots - Streaming output diff --git a/README_CN.md b/README_CN.md index fefab568..29578034 100644 --- a/README_CN.md +++ b/README_CN.md @@ -25,6 +25,7 @@ ChatGPT-Telegram-Workers - 无服务器部署 - 多平台部署支持(Cloudflare Workers, Vercel, Docker[...](doc/cn/PLATFORM.md)) - 适配多种AI服务商(OpenAI, Azure OpenAI, Cloudflare AI, Cohere, Anthropic, Mistral...) +- 使用 InlineKeyboards 切换模型 - 自定义指令(可以实现快速切换模型,切换机器人预设) - 支持多个Telegram机器人 - 流式输出 diff --git a/dist/buildinfo.json b/dist/buildinfo.json index 7e303aa2..0b68ae4d 100644 --- a/dist/buildinfo.json +++ b/dist/buildinfo.json @@ -1 +1 @@ -{"sha":"74ab291","timestamp":1731380657} \ No newline at end of file +{"sha":"fe9ef45","timestamp":1731464647} diff --git a/dist/index.js b/dist/index.js index 309bd0a4..b3635375 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,10 +1,10 @@ -const en = { "env": { "system_init_message": "You are a helpful assistant" }, "command": { "help": { "summary": "The following commands are currently supported:\n", "help": "Get command help", "new": "Start a new conversation", "start": "Get your ID and start a new conversation", "img": "Generate an image, the complete command format is `/img image description`, for example `/img beach at moonlight`", "version": "Get the current version number to determine whether to update", "setenv": "Set user configuration, the complete command format is /setenv KEY=VALUE", "setenvs": 'Batch set user configurations, the full format of the command is /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "Delete user configuration, the complete command format is /delenv KEY", "clearenv": "Clear all user configuration", "system": "View some system information", "redo": "Redo the last conversation, /redo with modified content or directly /redo", "echo": "Echo the message" }, "new": { "new_chat_start": "A new conversation has started" } } }; +const en = { "env": { "system_init_message": "You are a helpful assistant" }, "command": { "help": { "summary": "The following commands are currently supported:\n", "help": "Get command help", "new": "Start a new conversation", "start": "Get your ID and start a new conversation", "img": "Generate an image, the complete command format is `/img image description`, for example `/img beach at moonlight`", "version": "Get the current version number to determine whether to update", "setenv": "Set user configuration, the complete command format is /setenv KEY=VALUE", "setenvs": 'Batch set user configurations, the full format of the command is /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "Delete user configuration, the complete command format is /delenv KEY", "clearenv": "Clear all user configuration", "system": "View some system information", "redo": "Redo the last conversation, /redo with modified content or directly /redo", "echo": "Echo the message", "models": "switch chat model" }, "new": { "new_chat_start": "A new conversation has started" } }, "callback_query": { "open_model_list": "Open models list", "select_provider": "Select a provider:", "select_model": "Choose model:", "change_model": "Change model to " } }; -const pt = { "env": { "system_init_message": "Você é um assistente útil" }, "command": { "help": { "summary": "Os seguintes comandos são suportados atualmente:\n", "help": "Obter ajuda sobre comandos", "new": "Iniciar uma nova conversa", "start": "Obter seu ID e iniciar uma nova conversa", "img": "Gerar uma imagem, o formato completo do comando é `/img descrição da imagem`, por exemplo `/img praia ao luar`", "version": "Obter o número da versão atual para determinar se é necessário atualizar", "setenv": "Definir configuração do usuário, o formato completo do comando é /setenv CHAVE=VALOR", "setenvs": 'Definir configurações do usuário em lote, o formato completo do comando é /setenvs {"CHAVE1": "VALOR1", "CHAVE2": "VALOR2"}', "delenv": "Excluir configuração do usuário, o formato completo do comando é /delenv CHAVE", "clearenv": "Limpar todas as configurações do usuário", "system": "Ver algumas informações do sistema", "redo": "Refazer a última conversa, /redo com conteúdo modificado ou diretamente /redo", "echo": "Repetir a mensagem" }, "new": { "new_chat_start": "Uma nova conversa foi iniciada" } } }; +const pt = { "env": { "system_init_message": "Você é um assistente útil" }, "command": { "help": { "summary": "Os seguintes comandos são suportados atualmente:\n", "help": "Obter ajuda sobre comandos", "new": "Iniciar uma nova conversa", "start": "Obter seu ID e iniciar uma nova conversa", "img": "Gerar uma imagem, o formato completo do comando é `/img descrição da imagem`, por exemplo `/img praia ao luar`", "version": "Obter o número da versão atual para determinar se é necessário atualizar", "setenv": "Definir configuração do usuário, o formato completo do comando é /setenv CHAVE=VALOR", "setenvs": 'Definir configurações do usuário em lote, o formato completo do comando é /setenvs {"CHAVE1": "VALOR1", "CHAVE2": "VALOR2"}', "delenv": "Excluir configuração do usuário, o formato completo do comando é /delenv CHAVE", "clearenv": "Limpar todas as configurações do usuário", "system": "Ver algumas informações do sistema", "redo": "Refazer a última conversa, /redo com conteúdo modificado ou diretamente /redo", "echo": "Repetir a mensagem", "models": "Mudar o modelo de diálogo" }, "new": { "new_chat_start": "Uma nova conversa foi iniciada" } }, "callback_query": { "open_model_list": "Abra a lista de modelos", "select_provider": "Escolha um fornecedor de modelos.:", "select_model": "Escolha um modelo:", "change_model": "O modelo de diálogo já foi modificado para" } }; -const zhHans = { "env": { "system_init_message": "你是一个得力的助手" }, "command": { "help": { "summary": "当前支持以下命令:\n", "help": "获取命令帮助", "new": "发起新的对话", "start": "获取你的ID, 并发起新的对话", "img": "生成一张图片, 命令完整格式为 `/img 图片描述`, 例如`/img 月光下的沙滩`", "version": "获取当前版本号, 判断是否需要更新", "setenv": "设置用户配置,命令完整格式为 /setenv KEY=VALUE", "setenvs": '批量设置用户配置, 命令完整格式为 /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "删除用户配置,命令完整格式为 /delenv KEY", "clearenv": "清除所有用户配置", "system": "查看当前一些系统信息", "redo": "重做上一次的对话, /redo 加修改过的内容 或者 直接 /redo", "echo": "回显消息" }, "new": { "new_chat_start": "新的对话已经开始" } } }; +const zhHans = { "env": { "system_init_message": "你是一个得力的助手" }, "command": { "help": { "summary": "当前支持以下命令:\n", "help": "获取命令帮助", "new": "发起新的对话", "start": "获取你的ID, 并发起新的对话", "img": "生成一张图片, 命令完整格式为 `/img 图片描述`, 例如`/img 月光下的沙滩`", "version": "获取当前版本号, 判断是否需要更新", "setenv": "设置用户配置,命令完整格式为 /setenv KEY=VALUE", "setenvs": '批量设置用户配置, 命令完整格式为 /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "删除用户配置,命令完整格式为 /delenv KEY", "clearenv": "清除所有用户配置", "system": "查看当前一些系统信息", "redo": "重做上一次的对话, /redo 加修改过的内容 或者 直接 /redo", "echo": "回显消息", "models": "切换对话模型" }, "new": { "new_chat_start": "新的对话已经开始" } }, "callback_query": { "open_model_list": "打开模型列表", "select_provider": "选择一个模型提供商:", "select_model": "选择一个模型:", "change_model": "对话模型已修改至" } }; -const zhHant = { "env": { "system_init_message": "你是一個得力的助手" }, "command": { "help": { "summary": "當前支持的命令如下:\n", "help": "獲取命令幫助", "new": "開始一個新對話", "start": "獲取您的ID並開始一個新對話", "img": "生成圖片,完整命令格式為`/img 圖片描述`,例如`/img 海灘月光`", "version": "獲取當前版本號確認是否需要更新", "setenv": "設置用戶配置,完整命令格式為/setenv KEY=VALUE", "setenvs": '批量設置用户配置, 命令完整格式為 /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "刪除用戶配置,完整命令格式為/delenv KEY", "clearenv": "清除所有用戶配置", "system": "查看一些系統信息", "redo": "重做上一次的對話 /redo 加修改過的內容 或者 直接 /redo", "echo": "回显消息" }, "new": { "new_chat_start": "開始一個新對話" } } }; +const zhHant = { "env": { "system_init_message": "你是一個得力的助手" }, "command": { "help": { "summary": "當前支持的命令如下:\n", "help": "獲取命令幫助", "new": "開始一個新對話", "start": "獲取您的ID並開始一個新對話", "img": "生成圖片,完整命令格式為`/img 圖片描述`,例如`/img 海灘月光`", "version": "獲取當前版本號確認是否需要更新", "setenv": "設置用戶配置,完整命令格式為/setenv KEY=VALUE", "setenvs": '批量設置用户配置, 命令完整格式為 /setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}', "delenv": "刪除用戶配置,完整命令格式為/delenv KEY", "clearenv": "清除所有用戶配置", "system": "查看一些系統信息", "redo": "重做上一次的對話 /redo 加修改過的內容 或者 直接 /redo", "echo": "回显消息", "models": "切換對話模式" }, "new": { "new_chat_start": "開始一個新對話" } }, "callback_query": { "open_model_list": "打開模型清單", "select_provider": "選擇一個模型供應商:", "select_model": "選擇一個模型:", "change_model": "對話模型已經修改至" } }; function loadI18n(lang) { switch (lang?.toLowerCase()) { @@ -38,6 +38,7 @@ class EnvironmentConfig { TELEGRAM_MIN_STREAM_INTERVAL = 0; TELEGRAM_PHOTO_SIZE_OFFSET = 1; TELEGRAM_IMAGE_TRANSFER_MODE = "url"; + MODEL_LIST_COLUMNS = 1; I_AM_A_GENEROUS_PERSON = false; CHAT_WHITE_LIST = []; LOCK_USER_CONFIG_KEYS = [ @@ -69,50 +70,58 @@ class AgentShareConfig { AI_PROVIDER = "auto"; AI_IMAGE_PROVIDER = "auto"; SYSTEM_INIT_MESSAGE = null; - SYSTEM_INIT_MESSAGE_ROLE = "system"; } class OpenAIConfig { OPENAI_API_KEY = []; OPENAI_CHAT_MODEL = "gpt-4o-mini"; OPENAI_API_BASE = "https://api.openai.com/v1"; OPENAI_API_EXTRA_PARAMS = {}; + OPENAI_CHAT_MODELS_LIST = ""; } -class DalleAIConfig { - DALL_E_MODEL = "dall-e-2"; +class DallEConfig { + DALL_E_MODEL = "dall-e-3"; DALL_E_IMAGE_SIZE = "512x512"; DALL_E_IMAGE_QUALITY = "standard"; DALL_E_IMAGE_STYLE = "vivid"; } class AzureConfig { AZURE_API_KEY = null; - AZURE_COMPLETIONS_API = null; - AZURE_DALLE_API = null; + AZURE_RESOURCE_NAME = null; + AZURE_CHAT_MODEL = null; + AZURE_IMAGE_MODEL = null; + AZURE_API_VERSION = "2024-06-01"; + AZURE_CHAT_MODELS_LIST = "[]"; } class WorkersConfig { CLOUDFLARE_ACCOUNT_ID = null; CLOUDFLARE_TOKEN = null; WORKERS_CHAT_MODEL = "@cf/mistral/mistral-7b-instruct-v0.1 "; WORKERS_IMAGE_MODEL = "@cf/stabilityai/stable-diffusion-xl-base-1.0"; + WORKERS_CHAT_MODELS_LIST = ""; } class GeminiConfig { GOOGLE_API_KEY = null; - GOOGLE_COMPLETIONS_API = "https://generativelanguage.googleapis.com/v1beta/models/"; - GOOGLE_COMPLETIONS_MODEL = "gemini-pro"; + GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta"; + GOOGLE_COMPLETIONS_MODEL = "gemini-1.5-flash"; + GOOGLE_CHAT_MODELS_LIST = `["gemini-1.5-flash"]`; } class MistralConfig { MISTRAL_API_KEY = null; MISTRAL_API_BASE = "https://api.mistral.ai/v1"; MISTRAL_CHAT_MODEL = "mistral-tiny"; + MISTRAL_CHAT_MODELS_LIST = ""; } class CohereConfig { COHERE_API_KEY = null; COHERE_API_BASE = "https://api.cohere.com/v2"; COHERE_CHAT_MODEL = "command-r-plus"; + COHERE_CHAT_MODELS_LIST = ""; } class AnthropicConfig { ANTHROPIC_API_KEY = null; ANTHROPIC_API_BASE = "https://api.anthropic.com/v1"; - ANTHROPIC_CHAT_MODEL = "claude-3-haiku-20240307"; + ANTHROPIC_CHAT_MODEL = "claude-3-5-haiku-latest"; + ANTHROPIC_CHAT_MODELS_LIST = `["claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"]`; } class DefineKeys { DEFINE_KEYS = []; @@ -190,13 +199,16 @@ class ConfigMerger { } } +const BUILD_TIMESTAMP = 1731464647; +const BUILD_VERSION = "fe9ef45"; + function createAgentUserConfig() { return Object.assign( {}, new DefineKeys(), new AgentShareConfig(), new OpenAIConfig(), - new DalleAIConfig(), + new DallEConfig(), new AzureConfig(), new WorkersConfig(), new GeminiConfig(), @@ -211,8 +223,8 @@ const ENV_KEY_MAPPER = { WORKERS_AI_MODEL: "WORKERS_CHAT_MODEL" }; class Environment extends EnvironmentConfig { - BUILD_TIMESTAMP = 1731380657 ; - BUILD_VERSION = "74ab291" ; + BUILD_TIMESTAMP = BUILD_TIMESTAMP; + BUILD_VERSION = BUILD_VERSION; I18N = loadI18n(); PLUGINS_ENV = {}; USER_CONFIG = createAgentUserConfig(); @@ -298,6 +310,24 @@ class Environment extends EnvironmentConfig { if (!this.USER_CONFIG.SYSTEM_INIT_MESSAGE) { this.USER_CONFIG.SYSTEM_INIT_MESSAGE = this.I18N?.env?.system_init_message || "You are a helpful assistant"; } + if (source.GOOGLE_COMPLETIONS_API && !this.USER_CONFIG.GOOGLE_API_BASE) { + this.USER_CONFIG.GOOGLE_API_BASE = source.GOOGLE_COMPLETIONS_API.replace(/\/models\/?$/, ""); + } + if (source.GOOGLE_COMPLETIONS_MODEL && !this.USER_CONFIG.GOOGLE_CHAT_MODEL) { + this.USER_CONFIG.GOOGLE_CHAT_MODEL = source.GOOGLE_COMPLETIONS_MODEL; + } + if (source.AZURE_COMPLETIONS_API && !this.USER_CONFIG.AZURE_CHAT_MODEL) { + const url = new URL(source.AZURE_COMPLETIONS_API); + this.USER_CONFIG.AZURE_RESOURCE_NAME = url.hostname.split(".").at(0) || null; + this.USER_CONFIG.AZURE_CHAT_MODEL = url.pathname.split("/").at(3) || null; + this.USER_CONFIG.AZURE_API_VERSION = url.searchParams.get("api-version") || "2024-06-01"; + } + if (source.AZURE_DALLE_API && !this.USER_CONFIG.AZURE_IMAGE_MODEL) { + const url = new URL(source.AZURE_DALLE_API); + this.USER_CONFIG.AZURE_RESOURCE_NAME = url.hostname.split(".").at(0) || null; + this.USER_CONFIG.AZURE_IMAGE_MODEL = url.pathname.split("/").at(3) || null; + this.USER_CONFIG.AZURE_API_VERSION = url.searchParams.get("api-version") || "2024-06-01"; + } } } const ENV = new Environment(); @@ -579,18 +609,36 @@ function handleEscape(text, type = "text") { class MessageContext { chat_id; message_id = null; - reply_to_message_id; + reply_to_message_id = null; parse_mode = null; allow_sending_without_reply = null; disable_web_page_preview = null; - constructor(message) { - this.chat_id = message.chat.id; + constructor(chatID) { + this.chat_id = chatID; + } + static fromMessage(message) { + const ctx = new MessageContext(message.chat.id); if (message.chat.type === "group" || message.chat.type === "supergroup") { - this.reply_to_message_id = message.message_id; - this.allow_sending_without_reply = true; + ctx.reply_to_message_id = message.message_id; + ctx.allow_sending_without_reply = true; } else { - this.reply_to_message_id = null; + ctx.reply_to_message_id = null; } + return ctx; + } + static fromCallbackQuery(callbackQuery) { + const chat = callbackQuery.message?.chat; + if (!chat) { + throw new Error("Chat not found"); + } + const ctx = new MessageContext(chat.id); + if (chat.type === "group" || chat.type === "supergroup") { + ctx.reply_to_message_id = callbackQuery.message.message_id; + ctx.allow_sending_without_reply = true; + } else { + ctx.reply_to_message_id = null; + } + return ctx; } } class MessageSender { @@ -603,12 +651,20 @@ class MessageSender { this.sendPlainText = this.sendPlainText.bind(this); this.sendPhoto = this.sendPhoto.bind(this); } - static from(token, message) { - return new MessageSender(token, new MessageContext(message)); + static fromMessage(token, message) { + return new MessageSender(token, MessageContext.fromMessage(message)); } - with(message) { - this.context = new MessageContext(message); - return this; + static fromCallbackQuery(token, callbackQuery) { + return new MessageSender(token, MessageContext.fromCallbackQuery(callbackQuery)); + } + static fromUpdate(token, update) { + if (update.callback_query) { + return MessageSender.fromCallbackQuery(token, update.callback_query); + } + if (update.message) { + return MessageSender.fromMessage(token, update.message); + } + throw new Error("Invalid update"); } update(context) { if (!this.context) { @@ -687,6 +743,12 @@ class MessageSender { } return lastMessageResponse; } + sendRawMessage(message) { + return this.api.sendMessage(message); + } + editRawMessage(message) { + return this.api.editMessageText(message); + } sendRichText(message, parseMode = ENV.DEFAULT_PARSE_MODE) { if (!this.context) { throw new Error("Message context not set"); @@ -724,10 +786,8 @@ class MessageSender { } } -async function loadChatRoleWithContext(message, context) { +async function loadChatRoleWithContext(chatId, speakerId, context) { const { groupAdminsKey } = context.SHARE_CONTEXT; - const chatId = message.chat.id; - const speakerId = message.from?.id || chatId; if (!groupAdminsKey) { return null; } @@ -810,10 +870,9 @@ async function fetchImage(url) { }); } async function urlToBase64String(url) { - try { - const { Buffer } = await import('node:buffer'); + if (typeof Buffer !== "undefined") { return fetchImage(url).then((blob) => blob.arrayBuffer()).then((buffer) => Buffer.from(buffer).toString("base64")); - } catch { + } else { return fetchImage(url).then((blob) => blob.arrayBuffer()).then((buffer) => btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)))); } } @@ -840,9 +899,6 @@ async function imageToBase64String(url) { format: `image/${format}` }; } -function renderBase64DataURI(params) { - return `data:${params.format};base64,${params.data}`; -} class Stream { response; @@ -1049,10 +1105,10 @@ function fixOpenAICompatibleOptions(options) { return new Stream(r, c); }; options.contentExtractor = options.contentExtractor || function(d) { - return d?.choices?.[0]?.delta?.content; + return d?.choices?.at(0)?.delta?.content; }; options.fullContentExtractor = options.fullContentExtractor || function(d) { - return d.choices?.[0]?.message.content; + return d.choices?.at(0)?.message.content; }; options.errorExtractor = options.errorExtractor || function(d) { return d.error?.message; @@ -1072,11 +1128,43 @@ function isEventStreamResponse(resp) { } return false; } -async function requestChatCompletions(url, header, body, onStream, onResult = null, options = null) { +async function streamHandler(stream, contentExtractor, onStream) { + let contentFull = ""; + let lengthDelta = 0; + let updateStep = 50; + let lastUpdateTime = Date.now(); + try { + for await (const part of stream) { + const textPart = contentExtractor(part); + if (!textPart) { + continue; + } + lengthDelta += textPart.length; + contentFull = contentFull + textPart; + if (lengthDelta > updateStep) { + if (ENV.TELEGRAM_MIN_STREAM_INTERVAL > 0) { + const delta = Date.now() - lastUpdateTime; + if (delta < ENV.TELEGRAM_MIN_STREAM_INTERVAL) { + continue; + } + lastUpdateTime = Date.now(); + } + lengthDelta = 0; + updateStep += 20; + await onStream(`${contentFull} +...`); + } + } + } catch (e) { + contentFull += ` +Error: ${e.message}`; + } + return contentFull; +} +async function requestChatCompletions(url, header, body, onStream, options = null) { const controller = new AbortController(); const { signal } = controller; let timeoutID = null; - let lastUpdateTime = Date.now(); if (ENV.CHAT_COMPLETE_API_TIMEOUT > 0) { timeoutID = setTimeout(() => controller.abort(), ENV.CHAT_COMPLETE_API_TIMEOUT); } @@ -1095,36 +1183,7 @@ async function requestChatCompletions(url, header, body, onStream, onResult = nu if (!stream) { throw new Error("Stream builder error"); } - let contentFull = ""; - let lengthDelta = 0; - let updateStep = 50; - try { - for await (const data of stream) { - const c = options.contentExtractor?.(data) || ""; - if (c === "") { - continue; - } - lengthDelta += c.length; - contentFull = contentFull + c; - if (lengthDelta > updateStep) { - if (ENV.TELEGRAM_MIN_STREAM_INTERVAL > 0) { - const delta = Date.now() - lastUpdateTime; - if (delta < ENV.TELEGRAM_MIN_STREAM_INTERVAL) { - continue; - } - lastUpdateTime = Date.now(); - } - lengthDelta = 0; - updateStep += 20; - await onStream(`${contentFull} -...`); - } - } - } catch (e) { - contentFull += ` -ERROR: ${e.message}`; - } - return contentFull; + return streamHandler(stream, options.contentExtractor, onStream); } if (!isJsonResponse(resp)) { throw new Error(resp.statusText); @@ -1136,13 +1195,65 @@ ERROR: ${e.message}`; if (options.errorExtractor?.(result)) { throw new Error(options.errorExtractor?.(result) || "Unknown error"); } - try { - await onResult?.(result); - return options.fullContentExtractor?.(result) || ""; - } catch (e) { - console.error(e); - throw new Error(JSON.stringify(result)); + return options.fullContentExtractor?.(result) || ""; +} + +function extractTextContent(history) { + if (typeof history.content === "string") { + return history.content; + } + if (Array.isArray(history.content)) { + return history.content.map((item) => { + if (item.type === "text") { + return item.text; + } + return ""; + }).join(""); } + return ""; +} +function extractImageContent(imageData) { + if (imageData instanceof URL) { + return { url: imageData.href }; + } + if (typeof imageData === "string") { + if (imageData.startsWith("http")) { + return { url: imageData }; + } else { + return { base64: imageData }; + } + } + if (imageData instanceof Uint8Array) { + return { base64: Buffer.from(imageData).toString("base64") }; + } + if (Buffer.isBuffer(imageData)) { + return { base64: Buffer.from(imageData).toString("base64") }; + } + return {}; +} +async function convertStringToResponseMessages(input) { + const text = await input; + return { + text, + responses: [{ role: "assistant", content: await input }] + }; +} +async function loadModelsList(raw, remoteLoader) { + if (!raw) { + return []; + } + if (raw.startsWith("[") && raw.endsWith("]")) { + try { + return JSON.parse(raw); + } catch (e) { + console.error(e); + return []; + } + } + if (raw.startsWith("http") && remoteLoader) { + return await remoteLoader(raw); + } + return []; } class Anthropic { @@ -1156,16 +1267,30 @@ class Anthropic { role: item.role, content: item.content }; - if (item.images && item.images.length > 0) { - res.content = []; - if (item.content) { - res.content.push({ type: "text", text: item.content }); - } - for (const image of item.images) { - res.content.push(await imageToBase64String(image).then(({ format, data }) => { - return { type: "image", source: { type: "base64", media_type: format, data } }; - })); + if (item.role === "system") { + return null; + } + if (Array.isArray(item.content)) { + const contents = []; + for (const content of item.content) { + switch (content.type) { + case "text": + contents.push({ type: "text", text: content.text }); + break; + case "image": { + const data = extractImageContent(content.image); + if (data.url) { + contents.push(await imageToBase64String(data.url).then(({ format, data: data2 }) => { + return { type: "image", source: { type: "base64", media_type: format, data: data2 } }; + })); + } else if (data.base64) { + contents.push({ type: "image", source: { type: "base64", media_type: "image/jpeg", data: data.base64 } }); + } + break; + } + } } + res.content = contents; } return res; }; @@ -1192,21 +1317,20 @@ class Anthropic { } } request = async (params, context, onStream) => { - const { message, images, prompt, history } = params; + const { prompt, messages } = params; const url = `${context.ANTHROPIC_API_BASE}/messages`; const header = { "x-api-key": context.ANTHROPIC_API_KEY || "", "anthropic-version": "2023-06-01", "content-type": "application/json" }; - const messages = (history || []).concat({ role: "user", content: message, images }); if (messages.length > 0 && messages[0].role === "assistant") { messages.shift(); } const body = { system: prompt, model: context.ANTHROPIC_CHAT_MODEL, - messages: await Promise.all(messages.map((item) => this.render(item))), + messages: (await Promise.all(messages.map((item) => this.render(item)))).filter((i) => i !== null), stream: onStream != null, max_tokens: ENV.MAX_TOKEN_LENGTH > 0 ? ENV.MAX_TOKEN_LENGTH : 2048 }; @@ -1221,41 +1345,56 @@ class Anthropic { return data?.delta?.text; }; options.fullContentExtractor = function(data) { - return data?.content?.[0].text; + return data?.content?.at(0).text; }; options.errorExtractor = function(data) { return data?.error?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + modelList = async (context) => { + return loadModelsList(context.ANTHROPIC_CHAT_MODELS_LIST); }; } -async function renderOpenAIMessage(item) { +async function renderOpenAIMessage(item, supportImage) { const res = { role: item.role, content: item.content }; - if (item.images && item.images.length > 0) { - res.content = []; - if (item.content) { - res.content.push({ type: "text", text: item.content }); - } - for (const image of item.images) { - switch (ENV.TELEGRAM_IMAGE_TRANSFER_MODE) { - case "base64": - res.content.push({ type: "image_url", image_url: { - url: renderBase64DataURI(await imageToBase64String(image)) - } }); + if (Array.isArray(item.content)) { + const contents = []; + for (const content of item.content) { + switch (content.type) { + case "text": + contents.push({ type: "text", text: content.text }); break; - case "url": - default: - res.content.push({ type: "image_url", image_url: { url: image } }); + case "image": + if (supportImage) { + const data = extractImageContent(content.image); + if (data.url) { + contents.push({ type: "image_url", image_url: { url: data.url } }); + } else if (data.base64) { + contents.push({ type: "image_url", image_url: { url: data.base64 } }); + } + } break; } } + res.content = contents; } return res; } +async function renderOpenAIMessages(prompt, items, supportImage) { + const messages = await Promise.all(items.map((r) => renderOpenAIMessage(r, supportImage))); + if (prompt) { + if (messages.length > 0 && messages[0].role === "system") { + messages.shift(); + } + messages.unshift({ role: "system", content: prompt }); + } + return messages; +} class OpenAIBase { name = "openai"; apikey = (context) => { @@ -1275,23 +1414,30 @@ class OpenAI extends OpenAIBase { return renderOpenAIMessage(item); }; request = async (params, context, onStream) => { - const { message, images, prompt, history } = params; + const { prompt, messages } = params; const url = `${context.OPENAI_API_BASE}/chat/completions`; const header = { "Content-Type": "application/json", "Authorization": `Bearer ${this.apikey(context)}` }; - const messages = [...history || [], { role: "user", content: message, images }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } const body = { model: context.OPENAI_CHAT_MODEL, ...context.OPENAI_API_EXTRA_PARAMS, - messages: await Promise.all(messages.map(this.render)), + messages: await renderOpenAIMessages(prompt, messages, true), stream: onStream != null }; - return requestChatCompletions(url, header, body, onStream); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; + modelList = async (context) => { + if (context.OPENAI_CHAT_MODELS_LIST === "") { + context.OPENAI_CHAT_MODELS_LIST = `${context.OPENAI_API_BASE}/models`; + } + return loadModelsList(context.OPENAI_CHAT_MODELS_LIST, async (url) => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${this.apikey(context)}` } + }).then((res) => res.json()); + return data.data?.map((model) => model.id) || []; + }); }; } class Dalle extends OpenAIBase { @@ -1326,7 +1472,7 @@ class Dalle extends OpenAIBase { if (resp.error?.message) { throw new Error(resp.error.message); } - return resp?.data?.[0]?.url; + return resp?.data?.at(0)?.url; }; } @@ -1345,33 +1491,29 @@ class AzureBase { }; } class AzureChatAI extends AzureBase { - modelKey = "AZURE_COMPLETIONS_API"; + modelKey = "AZURE_CHAT_MODEL"; enable = (context) => { - return !!(context.AZURE_API_KEY && context.AZURE_COMPLETIONS_API); + return !!(context.AZURE_API_KEY && context.AZURE_RESOURCE_NAME); }; model = (ctx) => { - return this.modelFromURI(ctx.AZURE_COMPLETIONS_API); + return ctx.AZURE_CHAT_MODEL; }; request = async (params, context, onStream) => { - const { message, images, prompt, history } = params; - const url = context.AZURE_COMPLETIONS_API; - if (!url || !context.AZURE_API_KEY) { - throw new Error("Azure Completions API is not set"); - } + const { prompt, messages } = params; + const url = `https://${context.AZURE_RESOURCE_NAME}.openai.azure.com/openai/deployments/${context.AZURE_CHAT_MODEL}/chat/completions?api-version=${context.AZURE_API_VERSION}`; const header = { "Content-Type": "application/json", - "api-key": context.AZURE_API_KEY + "api-key": context.AZURE_API_KEY || "" }; - const messages = [...history || [], { role: "user", content: message, images }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } const body = { ...context.OPENAI_API_EXTRA_PARAMS, - messages: await Promise.all(messages.map(renderOpenAIMessage)), + messages: await renderOpenAIMessages(prompt, messages, true), stream: onStream != null }; - return requestChatCompletions(url, header, body, onStream); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; + modelList = async (context) => { + return loadModelsList(context.AZURE_CHAT_MODELS_LIST); }; } class AzureImageAI extends AzureBase { @@ -1383,13 +1525,10 @@ class AzureImageAI extends AzureBase { return this.modelFromURI(ctx.AZURE_DALLE_API); }; request = async (prompt, context) => { - const url = context.AZURE_DALLE_API; - if (!url || !context.AZURE_API_KEY) { - throw new Error("Azure DALL-E API is not set"); - } + const url = `https://${context.AZURE_RESOURCE_NAME}.openai.azure.com/openai/deployments/${context.AZURE_CHAT_MODEL}/images/generations?api-version=${context.AZURE_API_VERSION}`; const header = { "Content-Type": "application/json", - "api-key": context.AZURE_API_KEY + "api-key": context.AZURE_API_KEY || "" }; const body = { prompt, @@ -1410,7 +1549,7 @@ class AzureImageAI extends AzureBase { if (resp.error?.message) { throw new Error(resp.error.message); } - return resp?.data?.[0]?.url; + return resp?.data?.at(0)?.url; }; } @@ -1424,19 +1563,15 @@ class Cohere { return ctx.COHERE_CHAT_MODEL; }; request = async (params, context, onStream) => { - const { message, prompt, history } = params; + const { prompt, messages } = params; const url = `${context.COHERE_API_BASE}/chat`; const header = { "Authorization": `Bearer ${context.COHERE_API_KEY}`, "Content-Type": "application/json", "Accept": onStream !== null ? "text/event-stream" : "application/json" }; - const messages = [...history || [], { role: "user", content: message }]; - if (prompt) { - messages.unshift({ role: "assistant", content: prompt }); - } const body = { - messages, + messages: await renderOpenAIMessages(prompt, messages), model: context.COHERE_CHAT_MODEL, stream: onStream != null }; @@ -1445,77 +1580,53 @@ class Cohere { return data?.delta?.message?.content?.text; }; options.fullContentExtractor = function(data) { - return data?.messages[0].content; + return data?.messages?.at(0)?.content; }; options.errorExtractor = function(data) { return data?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + modelList = async (context) => { + if (context.COHERE_CHAT_MODELS_LIST === "") { + const { protocol, host } = new URL(context.COHERE_API_BASE); + context.COHERE_CHAT_MODELS_LIST = `${protocol}://${host}/v2/models`; + } + return loadModelsList(context.COHERE_CHAT_MODELS_LIST, async (url) => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${context.COHERE_API_KEY}` } + }).then((res) => res.json()); + return data.models?.filter((model) => model.endpoints?.includes("chat")).map((model) => model.name) || []; + }); }; } class Gemini { name = "gemini"; modelKey = "GOOGLE_COMPLETIONS_MODEL"; - static GEMINI_ROLE_MAP = { - assistant: "model", - system: "user", - user: "user" - }; enable = (context) => { return !!context.GOOGLE_API_KEY; }; model = (ctx) => { return ctx.GOOGLE_COMPLETIONS_MODEL; }; - render = (item) => { - return { - role: Gemini.GEMINI_ROLE_MAP[item.role], - parts: [ - { - text: item.content || "" - } - ] + request = async (params, context, onStream) => { + const { prompt, messages } = params; + const url = `${context.GOOGLE_API_BASE}/chat`; + const header = { + "Authorization": `Bearer ${context.GOOGLE_API_KEY}`, + "Content-Type": "application/json", + "Accept": onStream !== null ? "text/event-stream" : "application/json" + }; + const body = { + messages: await renderOpenAIMessages(prompt, messages), + model: context.GOOGLE_COMPLETIONS_MODEL, + stream: onStream != null }; + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); }; - request = async (params, context, onStream) => { - const { message, prompt, history } = params; - if (onStream !== null) { - console.warn("Stream mode is not supported"); - } - const mode = "generateContent"; - const url = `${context.GOOGLE_COMPLETIONS_API}${context.GOOGLE_COMPLETIONS_MODEL}:${mode}`; - const contentsTemp = [...history || [], { role: "user", content: message }]; - if (prompt) { - contentsTemp.unshift({ role: "assistant", content: prompt }); - } - const contents = []; - for (const msg of contentsTemp) { - msg.role = Gemini.GEMINI_ROLE_MAP[msg.role]; - if (contents.length === 0 || contents[contents.length - 1].role !== msg.role) { - contents.push(this.render(msg)); - } else { - contents[contents.length - 1].parts[0].text += msg.content; - } - } - const resp = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-goog-api-key": context.GOOGLE_API_KEY - }, - body: JSON.stringify({ contents }) - }); - const data = await resp.json(); - try { - return data.candidates[0].content.parts[0].text; - } catch (e) { - console.error(e); - if (!data) { - throw new Error("Empty response"); - } - throw new Error(data?.error?.message || JSON.stringify(data)); - } + modelList = async (context) => { + return loadModelsList(context.GOOGLE_CHAT_MODELS_LIST); }; } @@ -1528,29 +1639,30 @@ class Mistral { model = (ctx) => { return ctx.MISTRAL_CHAT_MODEL; }; - render = (item) => { - return { - role: item.role, - content: item.content - }; - }; request = async (params, context, onStream) => { - const { message, prompt, history } = params; + const { prompt, messages } = params; const url = `${context.MISTRAL_API_BASE}/chat/completions`; const header = { "Content-Type": "application/json", "Authorization": `Bearer ${context.MISTRAL_API_KEY}` }; - const messages = [...history || [], { role: "user", content: message }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } const body = { model: context.MISTRAL_CHAT_MODEL, - messages: messages.map(this.render), + messages: await renderOpenAIMessages(prompt, messages), stream: onStream != null }; - return requestChatCompletions(url, header, body, onStream); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; + modelList = async (context) => { + if (context.MISTRAL_CHAT_MODELS_LIST === "") { + context.MISTRAL_CHAT_MODELS_LIST = `${context.MISTRAL_API_BASE}/models`; + } + return loadModelsList(context.MISTRAL_CHAT_MODELS_LIST, async (url) => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${context.MISTRAL_API_KEY}` } + }).then((res) => res.json()); + return data.data?.map((model) => model.id) || []; + }); }; } @@ -1582,7 +1694,7 @@ class WorkersChat extends WorkerBase { }; }; request = async (params, context, onStream) => { - const { message, prompt, history } = params; + const { prompt, messages } = params; const id = context.CLOUDFLARE_ACCOUNT_ID; const token = context.CLOUDFLARE_TOKEN; const model = context.WORKERS_CHAT_MODEL; @@ -1590,12 +1702,8 @@ class WorkersChat extends WorkerBase { const header = { Authorization: `Bearer ${token}` }; - const messages = [...history || [], { role: "user", content: message }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } const body = { - messages: messages.map(this.render), + messages: await renderOpenAIMessages(prompt, messages), stream: onStream !== null }; const options = {}; @@ -1606,9 +1714,22 @@ class WorkersChat extends WorkerBase { return data?.result?.response; }; options.errorExtractor = function(data) { - return data?.errors?.[0]?.message; + return data?.errors?.at(0)?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + modelList = async (context) => { + if (context.WORKERS_CHAT_MODELS_LIST === "") { + const id = context.CLOUDFLARE_ACCOUNT_ID; + context.WORKERS_CHAT_MODELS_LIST = `https://api.cloudflare.com/client/v4/accounts/${id}/ai/models/search?task=Text%20Generation`; + } + return loadModelsList(context.WORKERS_CHAT_MODELS_LIST, async (url) => { + const header = { + Authorization: `Bearer ${context.CLOUDFLARE_TOKEN}` + }; + const data = await fetch(url, { headers: header }).then((res) => res.json()); + return data.result?.map((model) => model.name) || []; + }); }; } class WorkersImage extends WorkerBase { @@ -1635,24 +1756,23 @@ class WorkersImage extends WorkerBase { }; } async function base64StringToBlob(base64String) { - try { - const { Buffer } = await import('node:buffer'); + if (typeof Buffer !== "undefined") { const buffer = Buffer.from(base64String, "base64"); return new Blob([buffer], { type: "image/png" }); - } catch { + } else { const uint8Array = Uint8Array.from(atob(base64String), (c) => c.charCodeAt(0)); return new Blob([uint8Array], { type: "image/png" }); } } const CHAT_AGENTS = [ + new OpenAI(), new Anthropic(), new AzureChatAI(), + new WorkersChat(), new Cohere(), new Gemini(), - new Mistral(), - new OpenAI(), - new WorkersChat() + new Mistral() ]; function loadChatLLM(context) { for (const llm of CHAT_AGENTS) { @@ -1686,6 +1806,51 @@ function loadImageGen(context) { return null; } +function isTelegramChatTypeGroup(type) { + return type === "group" || type === "supergroup"; +} +async function setUserConfig(values, context) { + for (const ent of Object.entries(values || {})) { + let [key, value] = ent; + key = ENV_KEY_MAPPER[key] || key; + if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { + throw new Error(`Key ${key} is locked`); + } + const configKeys = Object.keys(context.USER_CONFIG || {}) || []; + if (!configKeys.includes(key)) { + throw new Error(`Key ${key} is not allowed`); + } + context.USER_CONFIG.DEFINE_KEYS.push(key); + ConfigMerger.merge(context.USER_CONFIG, { + [key]: value + }); + console.log("Update user config: ", key, context.USER_CONFIG[key]); + } + context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); + await ENV.DATABASE.put( + context.SHARE_CONTEXT.configStoreKey, + JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)) + ); +} + +const TELEGRAM_AUTH_CHECKER = { + default(chatType) { + if (isTelegramChatTypeGroup(chatType)) { + return ["administrator", "creator"]; + } + return null; + }, + shareModeGroup(chatType) { + if (isTelegramChatTypeGroup(chatType)) { + if (!ENV.GROUP_CHAT_BOT_SHARE_MODE) { + return null; + } + return ["administrator", "creator"]; + } + return null; + } +}; + function tokensCounter() { return (text) => { return text.length; @@ -1712,7 +1877,7 @@ async function loadHistory(key) { const historyItem = list[i]; let length = 0; if (historyItem.content) { - length = counter(historyItem.content); + length = counter(extractTextContent(historyItem)); } else { historyItem.content = ""; } @@ -1738,32 +1903,30 @@ async function requestCompletionsFromLLM(params, context, agent, modifier, onStr } let history = await loadHistory(historyKey); if (modifier) { - const modifierData = modifier(history, params.message || null); + const modifierData = modifier(history, params || null); history = modifierData.history; - params.message = modifierData.message; + params = modifierData.message; + } + if (!params) { + throw new Error("Message is empty"); } + history.push(params); const llmParams = { - ...params, - history, - prompt: context.USER_CONFIG.SYSTEM_INIT_MESSAGE + prompt: context.USER_CONFIG.SYSTEM_INIT_MESSAGE || void 0, + messages: history }; - const answer = await agent.request(llmParams, context.USER_CONFIG, onStream); + const { text, responses } = await agent.request(llmParams, context.USER_CONFIG, onStream); if (!historyDisable) { - const userMessage = { role: "user", content: params.message || "", images: params.images }; - if (ENV.HISTORY_IMAGE_PLACEHOLDER && userMessage.images && userMessage.images.length > 0) { - delete userMessage.images; - userMessage.content = `${ENV.HISTORY_IMAGE_PLACEHOLDER} -${userMessage.content}`; - } - history.push(userMessage); - history.push({ role: "assistant", content: answer }); + if (ENV.HISTORY_IMAGE_PLACEHOLDER) ; + history.push(params); + history.push(...responses); await ENV.DATABASE.put(historyKey, JSON.stringify(history)).catch(console.error); } - return answer; + return text; } async function chatWithLLM(message, params, context, modifier) { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { try { const msg = await sender.sendPlainText("...").then((r) => r.json()); @@ -1835,48 +1998,35 @@ function findPhotoFileID(photos, offset) { } class ChatHandler { handle = async (message, context) => { + const text = message.text || message.caption || ""; const params = { - message: message.text || message.caption || "" + role: "user", + content: text }; if (message.photo && message.photo.length > 0) { const id = findPhotoFileID(message.photo, ENV.TELEGRAM_PHOTO_SIZE_OFFSET); const api = createTelegramBotAPI(context.SHARE_CONTEXT.botToken); const file = await api.getFileWithReturns({ file_id: id }); - const url = file.result.file_path; - if (url) { - params.images = [`${ENV.TELEGRAM_API_DOMAIN}/file/bot${context.SHARE_CONTEXT.botToken}/${url}`]; + const filePath = file.result.file_path; + if (filePath) { + const url = URL.parse(`${ENV.TELEGRAM_API_DOMAIN}/file/bot${context.SHARE_CONTEXT.botToken}/${filePath}`); + if (url) { + params.content = [ + { type: "text", text }, + { type: "image", image: url } + ]; + } } } return chatWithLLM(message, params, context, null); }; } -function isTelegramChatTypeGroup(type) { - return type === "group" || type === "supergroup"; -} - -const COMMAND_AUTH_CHECKER = { - default(chatType) { - if (isTelegramChatTypeGroup(chatType)) { - return ["administrator", "creator"]; - } - return null; - }, - shareModeGroup(chatType) { - if (isTelegramChatTypeGroup(chatType)) { - if (!ENV.GROUP_CHAT_BOT_SHARE_MODE) { - return null; - } - return ["administrator", "creator"]; - } - return null; - } -}; class ImgCommandHandler { command = "/img"; scopes = ["all_private_chats", "all_chat_administrators"]; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); if (subcommand === "") { return sender.sendPlainText(ENV.I18N.command.help.img); } @@ -1905,7 +2055,7 @@ class HelpCommandHandler { command = "/help"; scopes = ["all_private_chats", "all_chat_administrators"]; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); let helpMsg = `${ENV.I18N.command.help.summary} `; for (const [k, v] of Object.entries(ENV.I18N.command.help)) { @@ -1969,33 +2119,17 @@ class StartCommandHandler extends BaseNewCommandHandler { } class SetEnvCommandHandler { command = "/setenv"; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const kv = subcommand.indexOf("="); if (kv === -1) { return sender.sendPlainText(ENV.I18N.command.help.setenv); } - let key = subcommand.slice(0, kv); + const key = subcommand.slice(0, kv); const value = subcommand.slice(kv + 1); - key = ENV_KEY_MAPPER[key] || key; - if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { - return sender.sendPlainText(`Key ${key} is locked`); - } - if (!Object.keys(context.USER_CONFIG).includes(key)) { - return sender.sendPlainText(`Key ${key} not found`); - } try { - context.USER_CONFIG.DEFINE_KEYS.push(key); - context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); - ConfigMerger.merge(context.USER_CONFIG, { - [key]: value - }); - console.log("Update user config: ", key, context.USER_CONFIG[key]); - await ENV.DATABASE.put( - context.SHARE_CONTEXT.configStoreKey, - JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)) - ); + await setUserConfig({ [key]: value }, context); return sender.sendPlainText("Update user config success"); } catch (e) { return sender.sendPlainText(`ERROR: ${e.message}`); @@ -2004,32 +2138,12 @@ class SetEnvCommandHandler { } class SetEnvsCommandHandler { command = "/setenvs"; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { const values = JSON.parse(subcommand); - const configKeys = Object.keys(context.USER_CONFIG); - for (const ent of Object.entries(values)) { - let [key, value] = ent; - key = ENV_KEY_MAPPER[key] || key; - if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { - return sender.sendPlainText(`Key ${key} is locked`); - } - if (!configKeys.includes(key)) { - return sender.sendPlainText(`Key ${key} not found`); - } - context.USER_CONFIG.DEFINE_KEYS.push(key); - ConfigMerger.merge(context.USER_CONFIG, { - [key]: value - }); - console.log("Update user config: ", key, context.USER_CONFIG[key]); - } - context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); - await ENV.DATABASE.put( - context.SHARE_CONTEXT.configStoreKey, - JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)) - ); + await setUserConfig(values, context); return sender.sendPlainText("Update user config success"); } catch (e) { return sender.sendPlainText(`ERROR: ${e.message}`); @@ -2038,9 +2152,9 @@ class SetEnvsCommandHandler { } class DelEnvCommandHandler { command = "/delenv"; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); if (ENV.LOCK_USER_CONFIG_KEYS.includes(subcommand)) { const msg = `Key ${subcommand} is locked`; return sender.sendPlainText(msg); @@ -2060,9 +2174,9 @@ class DelEnvCommandHandler { } class ClearEnvCommandHandler { command = "/clearenv"; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { await ENV.DATABASE.put( context.SHARE_CONTEXT.configStoreKey, @@ -2078,7 +2192,7 @@ class VersionCommandHandler { command = "/version"; scopes = ["all_private_chats", "all_chat_administrators"]; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const current = { ts: ENV.BUILD_TIMESTAMP, sha: ENV.BUILD_VERSION @@ -2106,7 +2220,7 @@ class SystemCommandHandler { command = "/system"; scopes = ["all_private_chats", "all_chat_administrators"]; handle = async (message, subcommand, context) => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const chatAgent = loadChatLLM(context.USER_CONFIG); const imageAgent = loadImageGen(context.USER_CONFIG); const agent = { @@ -2148,8 +2262,8 @@ class RedoCommandHandler { command = "/redo"; scopes = ["all_private_chats", "all_group_chats", "all_chat_administrators"]; handle = async (message, subcommand, context) => { - const mf = (history, text) => { - let nextText = text; + const mf = (history, message2) => { + let nextMessage = message2; if (!(history && Array.isArray(history) && history.length > 0)) { throw new Error("History not found"); } @@ -2159,18 +2273,43 @@ class RedoCommandHandler { if (data === void 0 || data === null) { break; } else if (data.role === "user") { - if (text === "" || text === void 0 || text === null) { - nextText = data.content || null; - } + nextMessage = data; break; } } if (subcommand) { - nextText = subcommand; + nextMessage = { + role: "user", + content: subcommand + }; } - return { history: historyCopy, message: nextText }; + if (nextMessage === null) { + throw new Error("Redo message not found"); + } + return { history: historyCopy, message: nextMessage }; }; - return chatWithLLM(message, { message: null }, context, mf); + return chatWithLLM(message, null, context, mf); + }; +} +class ModelsCommandHandler { + command = "/models"; + scopes = ["all_private_chats", "all_group_chats", "all_chat_administrators"]; + handle = async (message, subcommand, context) => { + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); + const chatAgent = loadChatLLM(context.USER_CONFIG); + const params = { + chat_id: message.chat.id, + text: `${chatAgent?.name || "Nan"} | ${chatAgent?.model(context.USER_CONFIG) || "Nan"}`, + reply_markup: { + inline_keyboard: [[ + { + text: ENV.I18N.callback_query.open_model_list, + callback_data: "al:" + } + ]] + } + }; + return sender.sendRawMessage(params); }; } class EchoCommandHandler { @@ -2179,7 +2318,7 @@ class EchoCommandHandler { let msg = "
";
     msg += JSON.stringify({ message }, null, 2);
     msg += "
"; - return MessageSender.from(context.SHARE_CONTEXT.botToken, message).sendRichText(msg, "HTML"); + return MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message).sendRichText(msg, "HTML"); }; } @@ -2194,15 +2333,19 @@ const SYSTEM_COMMANDS = [ new ClearEnvCommandHandler(), new VersionCommandHandler(), new SystemCommandHandler(), + new ModelsCommandHandler(), new HelpCommandHandler() ]; async function handleSystemCommand(message, raw, command, context) { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { + const chatId = message.chat.id; + const speakerId = message.from?.id || chatId; + const chatType = message.chat.type; if (command.needAuth) { - const roleList = command.needAuth(message.chat.type); + const roleList = command.needAuth(chatType); if (roleList) { - const chatRole = await loadChatRoleWithContext(message, context); + const chatRole = await loadChatRoleWithContext(chatId, speakerId, context); if (chatRole === null) { return sender.sendPlainText("ERROR: Get chat role failed"); } @@ -2222,7 +2365,7 @@ async function handleSystemCommand(message, raw, command, context) { } } async function handlePluginCommand(message, command, raw, template, context) { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { const subcommand = raw.substring(command.length).trim(); if (template.input?.required && !subcommand) { @@ -2290,10 +2433,13 @@ function commandsBindScope() { if (!scopeCommandMap[scope]) { scopeCommandMap[scope] = []; } - scopeCommandMap[scope].push({ - command: cmd.command, - description: ENV.I18N.command.help[cmd.command.substring(1)] || "" - }); + const desc = ENV.I18N.command.help[cmd.command.substring(1)] || ""; + if (desc) { + scopeCommandMap[scope].push({ + command: cmd.command, + description: desc + }); + } } } } @@ -2340,7 +2486,7 @@ class ShareContext { lastMessageKey; configStoreKey; groupAdminsKey; - constructor(token, message) { + constructor(token, update) { const botId = Number.parseInt(token.split(":")[0]); const telegramIndex = ENV.TELEGRAM_AVAILABLE_TOKENS.indexOf(token); if (telegramIndex === -1) { @@ -2351,7 +2497,7 @@ class ShareContext { } this.botToken = token; this.botId = botId; - const id = message?.chat?.id; + const id = update.chatID; if (id === void 0 || id === null) { throw new Error("Chat id not found"); } @@ -2361,20 +2507,20 @@ class ShareContext { historyKey += `:${botId}`; configStoreKey += `:${botId}`; } - switch (message.chat.type) { + switch (update.chatType) { case "group": case "supergroup": - if (!ENV.GROUP_CHAT_BOT_SHARE_MODE && message.from?.id) { - historyKey += `:${message.from.id}`; - configStoreKey += `:${message.from.id}`; + if (!ENV.GROUP_CHAT_BOT_SHARE_MODE && update.fromUserID) { + historyKey += `:${update.fromUserID}`; + configStoreKey += `:${update.fromUserID}`; } this.groupAdminsKey = `group_admin:${id}`; break; } - if (message?.chat.is_forum && message?.is_topic_message) { - if (message?.message_thread_id) { - historyKey += `:${message.message_thread_id}`; - configStoreKey += `:${message.message_thread_id}`; + if (update.isForum && update.isTopicMessage) { + if (update.messageThreadID) { + historyKey += `:${update.messageThreadID}`; + configStoreKey += `:${update.messageThreadID}`; } } this.chatHistoryKey = historyKey; @@ -2389,8 +2535,9 @@ class WorkerContext { this.USER_CONFIG = USER_CONFIG; this.SHARE_CONTEXT = SHARE_CONTEXT; } - static async from(token, message) { - const SHARE_CONTEXT = new ShareContext(token, message); + static async from(token, update) { + const context = new UpdateContext(update); + const SHARE_CONTEXT = new ShareContext(token, context); const USER_CONFIG = Object.assign({}, ENV.USER_CONFIG); try { const userConfig = JSON.parse(await ENV.DATABASE.get(SHARE_CONTEXT.configStoreKey)); @@ -2401,6 +2548,31 @@ class WorkerContext { return new WorkerContext(USER_CONFIG, SHARE_CONTEXT); } } +class UpdateContext { + fromUserID; + chatID; + chatType; + isForum; + isTopicMessage; + messageThreadID; + constructor(update) { + if (update.message) { + this.fromUserID = update.message.from?.id; + this.chatID = update.message.chat.id; + this.chatType = update.message.chat.type; + this.isForum = update.message.chat.is_forum; + this.isTopicMessage = update.message.is_topic_message; + this.messageThreadID = update.message.message_thread_id; + } else if (update.callback_query) { + this.fromUserID = update.callback_query.from.id; + this.chatID = update.callback_query.message?.chat.id; + this.chatType = update.callback_query.message?.chat.type; + this.isForum = update.callback_query.message?.chat.is_forum; + } else { + console.error("Unknown update type"); + } + } +} function checkMention(content, entities, botName, botId) { let isMention = false; @@ -2475,74 +2647,309 @@ ${message.text}`; }; } -class SaveLastMessage { - handle = async (message, context) => { - if (!ENV.DEBUG_MODE) { - return null; +class AgentListCallbackQueryHandler { + prefix = "al:"; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + handle = async (query, data, context) => { + if (!query.message) { + throw new Error("no message"); + } + const names = CHAT_AGENTS.filter((agent) => agent.enable(ENV.USER_CONFIG)).map((agent) => agent.name); + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const keyboards = []; + for (let i = 0; i < names.length; i += 2) { + const row = []; + for (let j = 0; j < 2; j++) { + const index = i + j; + if (index >= names.length) { + break; + } + row.push({ + text: names[index], + callback_data: `ca:${JSON.stringify([names[index], 0])}` + }); + } + keyboards.push(row); } - const lastMessageKey = `last_message:${context.SHARE_CONTEXT.chatHistoryKey}`; - await ENV.DATABASE.put(lastMessageKey, JSON.stringify(message), { expirationTtl: 3600 }); - return null; + const params = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: ENV.I18N.callback_query.select_provider, + reply_markup: { + inline_keyboard: keyboards + } + }; + return sender.editRawMessage(params); }; } -class OldMessageFilter { - handle = async (message, context) => { - if (!ENV.SAFE_MODE) { - return null; +class ModelListCallbackQueryHandler { + prefix = "ca:"; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + async handle(query, data, context) { + if (!query.message) { + throw new Error("no message"); } - let idList = []; - try { - idList = JSON.parse(await ENV.DATABASE.get(context.SHARE_CONTEXT.lastMessageKey).catch(() => "[]")) || []; - } catch (e) { - console.error(e); + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const [agent, page] = JSON.parse(data.substring(this.prefix.length)); + const conf = { + ...ENV.USER_CONFIG, + AI_PROVIDER: agent + }; + const chatAgent = loadChatLLM(conf); + if (!chatAgent) { + throw new Error(`agent not found: ${agent}`); + } + const models = await chatAgent.modelList(conf); + const keyboard = []; + const maxRow = 10; + const maxCol = Math.max(1, Math.min(5, ENV.MODEL_LIST_COLUMNS)); + const maxPage = Math.ceil(models.length / maxRow / maxCol); + let currentRow = []; + for (let i = page * maxRow * maxCol; i < models.length; i++) { + if (i % maxCol === 0) { + keyboard.push(currentRow); + currentRow = []; + } + if (keyboard.length >= maxRow) { + break; + } + currentRow.push({ + text: models[i], + callback_data: `cm:${JSON.stringify([agent, models[i]])}` + }); } - if (idList.includes(message.message_id)) { - throw new Error("Ignore old message"); - } else { - idList.push(message.message_id); - if (idList.length > 100) { - idList.shift(); + if (currentRow.length > 0) { + keyboard.push(currentRow); + currentRow = []; + } + keyboard.push([ + { + text: "<", + callback_data: `ca:${JSON.stringify([agent, Math.max(page - 1, 0)])}` + }, + { + text: `${page + 1}/${maxPage}`, + callback_data: `ca:${JSON.stringify([agent, page])}` + }, + { + text: ">", + callback_data: `ca:${JSON.stringify([agent, Math.min(page + 1, maxPage - 1)])}` + }, + { + text: "⇤", + callback_data: `al:` } - await ENV.DATABASE.put(context.SHARE_CONTEXT.lastMessageKey, JSON.stringify(idList)); + ]); + if (models.length > (page + 1) * maxRow * maxCol) { + currentRow.push(); } - return null; + keyboard.push(currentRow); + const message = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: `${agent} ${ENV.I18N.callback_query.select_model}`, + reply_markup: { + inline_keyboard: keyboard + } + }; + return sender.editRawMessage(message); + } +} +class ModelChangeCallbackQueryHandler { + prefix = "cm:"; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + async handle(query, data, context) { + if (!query.message) { + throw new Error("no message"); + } + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const [agent, model] = JSON.parse(data.substring(this.prefix.length)); + const conf = { + ...ENV.USER_CONFIG, + AI_PROVIDER: agent + }; + const chatAgent = loadChatLLM(conf); + if (!agent) { + throw new Error(`agent not found: ${agent}`); + } + if (!chatAgent?.modelKey) { + throw new Error(`modelKey not found: ${agent}`); + } + await setUserConfig({ + AI_PROVIDER: agent, + [chatAgent.modelKey]: model + }, context); + const message = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: `${ENV.I18N.callback_query.change_model} ${agent} > ${model}` + }; + return sender.editRawMessage(message); + } +} + +const QUERY_HANDLERS = [ + new AgentListCallbackQueryHandler(), + new ModelListCallbackQueryHandler(), + new ModelChangeCallbackQueryHandler() +]; +async function handleCallbackQuery(callbackQuery, context) { + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, callbackQuery); + const answerCallbackQuery = (msg) => { + return sender.api.answerCallbackQuery({ + callback_query_id: callbackQuery.id, + text: msg + }); }; + try { + if (!callbackQuery.message) { + return null; + } + const chatId = callbackQuery.message.chat.id; + const speakerId = callbackQuery.from?.id || chatId; + const chatType = callbackQuery.message.chat.type; + for (const handler of QUERY_HANDLERS) { + if (handler.needAuth) { + const roleList = handler.needAuth(chatType); + if (roleList) { + const chatRole = await loadChatRoleWithContext(chatId, speakerId, context); + if (chatRole === null) { + return answerCallbackQuery("ERROR: Get chat role failed"); + } + if (!roleList.includes(chatRole)) { + return answerCallbackQuery(`ERROR: Permission denied, need ${roleList.join(" or ")}`); + } + } + } + if (callbackQuery.data) { + if (callbackQuery.data.startsWith(handler.prefix)) { + return handler.handle(callbackQuery, callbackQuery.data, context); + } + } + } + } catch (e) { + return answerCallbackQuery(`ERROR: ${e.message}`); + } + return null; } + class EnvChecker { - handle = async (message, context) => { + handle = async (update, context) => { if (!ENV.DATABASE) { - return MessageSender.from(context.SHARE_CONTEXT.botToken, message).sendPlainText("DATABASE Not Set"); + return MessageSender.fromUpdate(context.SHARE_CONTEXT.botToken, update).sendPlainText("DATABASE Not Set"); } return null; }; } class WhiteListFilter { - handle = async (message, context) => { + handle = async (update, context) => { if (ENV.I_AM_A_GENEROUS_PERSON) { return null; } - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); - const text = `You are not in the white list, please contact the administrator to add you to the white list. Your chat_id: ${message.chat.id}`; - if (message.chat.type === "private") { - if (!ENV.CHAT_WHITE_LIST.includes(`${message.chat.id}`)) { + const sender = MessageSender.fromUpdate(context.SHARE_CONTEXT.botToken, update); + let chatType = ""; + let chatID = 0; + if (update.message) { + chatType = update.message.chat.type; + chatID = update.message.chat.id; + } else if (update.callback_query?.message) { + chatType = update.callback_query.message.chat.type; + chatID = update.callback_query.message.chat.id; + } + if (!chatType || !chatID) { + throw new Error("Invalid chat type or chat id"); + } + const text = `You are not in the white list, please contact the administrator to add you to the white list. Your chat_id: ${chatID}`; + if (chatType === "private") { + if (!ENV.CHAT_WHITE_LIST.includes(`${chatID}`)) { return sender.sendPlainText(text); } return null; } - if (isTelegramChatTypeGroup(message.chat.type)) { + if (isTelegramChatTypeGroup(chatType)) { if (!ENV.GROUP_CHAT_BOT_ENABLE) { throw new Error("Not support"); } - if (!ENV.CHAT_GROUP_WHITE_LIST.includes(`${message.chat.id}`)) { + if (!ENV.CHAT_GROUP_WHITE_LIST.includes(`${chatID}`)) { return sender.sendPlainText(text); } return null; } return sender.sendPlainText( - `Not support chat type: ${message.chat.type}` + `Not support chat type: ${chatType}` ); }; } +class Update2MessageHandler { + messageHandlers; + constructor(messageHandlers) { + this.messageHandlers = messageHandlers; + } + loadMessage(body) { + if (body.edited_message) { + throw new Error("Ignore edited message"); + } + if (body.message) { + return body?.message; + } else { + throw new Error("Invalid message"); + } + } + handle = async (update, context) => { + const message = this.loadMessage(update); + if (!message) { + return null; + } + for (const handler of this.messageHandlers) { + const result = await handler.handle(message, context); + if (result) { + return result; + } + } + return null; + }; +} +class CallbackQueryHandler { + handle = async (update, context) => { + if (update.callback_query) { + return handleCallbackQuery(update.callback_query, context); + } + return null; + }; +} +class SaveLastMessage { + handle = async (message, context) => { + if (!ENV.DEBUG_MODE) { + return null; + } + const lastMessageKey = `last_message:${context.SHARE_CONTEXT.chatHistoryKey}`; + await ENV.DATABASE.put(lastMessageKey, JSON.stringify(message), { expirationTtl: 3600 }); + return null; + }; +} +class OldMessageFilter { + handle = async (message, context) => { + if (!ENV.SAFE_MODE) { + return null; + } + let idList = []; + try { + idList = JSON.parse(await ENV.DATABASE.get(context.SHARE_CONTEXT.lastMessageKey).catch(() => "[]")) || []; + } catch (e) { + console.error(e); + } + if (idList.includes(message.message_id)) { + throw new Error("Ignore old message"); + } else { + idList.push(message.message_id); + if (idList.length > 100) { + idList.shift(); + } + await ENV.DATABASE.put(context.SHARE_CONTEXT.lastMessageKey, JSON.stringify(idList)); + } + return null; + }; +} class MessageFilter { handle = async (message, context) => { if (message.text) { @@ -2566,32 +2973,24 @@ class CommandHandler { }; } -function loadMessage(body) { - if (body.edited_message) { - throw new Error("Ignore edited message"); - } - if (body.message) { - return body?.message; - } else { - throw new Error("Invalid message"); - } -} const SHARE_HANDLER = [ new EnvChecker(), new WhiteListFilter(), - new MessageFilter(), - new GroupMention(), - new OldMessageFilter(), - new SaveLastMessage(), - new CommandHandler(), - new ChatHandler() + new CallbackQueryHandler(), + new Update2MessageHandler([ + new MessageFilter(), + new GroupMention(), + new OldMessageFilter(), + new SaveLastMessage(), + new CommandHandler(), + new ChatHandler() + ]) ]; async function handleUpdate(token, update) { - const message = loadMessage(update); - const context = await WorkerContext.from(token, message); + const context = await WorkerContext.from(token, update); for (const handler of SHARE_HANDLER) { try { - const result = await handler.handle(message, context); + const result = await handler.handle(update, context); if (result) { return result; } diff --git a/doc/cn/CHANGELOG.md b/doc/cn/CHANGELOG.md index 321332ff..4a9626c2 100644 --- a/doc/cn/CHANGELOG.md +++ b/doc/cn/CHANGELOG.md @@ -1,5 +1,8 @@ # 更新日志 +- v1.10.0 + - 使用 InlineKeyboards 切换模型 + - v1.9.0 - 添加插件系统 diff --git a/doc/cn/CONFIG.md b/doc/cn/CONFIG.md index 28cfe207..1cfdc27a 100644 --- a/doc/cn/CONFIG.md +++ b/doc/cn/CONFIG.md @@ -80,12 +80,12 @@ OPENAI_API_BASE,GOOGLE_COMPLETIONS_API,MISTRAL_API_BASE,COHERE_API_BASE,ANTHROPI ### 通用配置 -| KEY | 名称 | 默认值 | 描述 | -|--------------------------|-------------|-------------|------------------------------------------------------------------------| -| AI_PROVIDER | AI提供商 | `auto` | 可选值 `auto, openai, azure, workers, gemini, mistral, cohere, anthropic` | -| AI_IMAGE_PROVIDER | AI图片提供商 | `auto` | 可选值 `auto, openai, azure, workers` | -| SYSTEM_INIT_MESSAGE | 全局默认初始化消息 | `你是一个得力的助手` | 根据绑定的语言自动选择默认值 | -| SYSTEM_INIT_MESSAGE_ROLE | 全局默认初始化消息角色 | `system` | | +| KEY | 名称 | 默认值 | 描述 | +|------------------------------|-----------------|-------------|------------------------------------------------------------------------| +| AI_PROVIDER | AI提供商 | `auto` | 可选值 `auto, openai, azure, workers, gemini, mistral, cohere, anthropic` | +| AI_IMAGE_PROVIDER | AI图片提供商 | `auto` | 可选值 `auto, openai, azure, workers` | +| SYSTEM_INIT_MESSAGE | 全局默认初始化消息 | `你是一个得力的助手` | 根据绑定的语言自动选择默认值 | +| ~~SYSTEM_INIT_MESSAGE_ROLE~~ | ~~全局默认初始化消息角色~~ | `system` | 废弃 | ### OpenAI @@ -95,7 +95,7 @@ OPENAI_API_BASE,GOOGLE_COMPLETIONS_API,MISTRAL_API_BASE,COHERE_API_BASE,ANTHROPI | OPENAI_CHAT_MODEL | OpenAI的模型名称 | `gpt-4o-mini` | | OPENAI_API_BASE | OpenAI API BASE | `https://api.openai.com/v1` | | OPENAI_API_EXTRA_PARAMS | OpenAI API Extra Params | `{}` | -| DALL_E_MODEL | DALL-E的模型名称 | `dall-e-2` | +| DALL_E_MODEL | DALL-E的模型名称 | `dall-e-3` | | DALL_E_IMAGE_SIZE | DALL-E图片尺寸 | `512x512` | | DALL_E_IMAGE_QUALITY | DALL-E图片质量 | `standard` | | DALL_E_IMAGE_STYLE | DALL-E图片风格 | `vivid` | @@ -106,11 +106,15 @@ OPENAI_API_BASE,GOOGLE_COMPLETIONS_API,MISTRAL_API_BASE,COHERE_API_BASE,ANTHROPI > AZURE_DALLE_API `https://RESOURCE_NAME.openai.azure.com/openai/deployments/MODEL_NAME/images/generations?api-version=VERSION_NAME` -| KEY | 名称 | 默认值 | -|--------------------------|-------------------------|------------------------------------------------------------| -| AZURE_API_KEY | Azure API Key | `null` | -| AZURE_COMPLETIONS_API | Azure Completions API | `null` | -| AZURE_DALLE_API | Azure DallE API | `null` | +| KEY | 名称 | 默认值 | +|---------------------------|---------------------------|--------------| +| AZURE_API_KEY | Azure API Key | `null` | +| ~~AZURE_COMPLETIONS_API~~ | ~~Azure Completions API~~ | `null` | +| ~~AZURE_DALLE_API~~ | ~~Azure DallE API~~ | `null` | +| AZURE_RESOURCE_NAME | Azure 资源名称 | `null` | +| AZURE_CHAT_MODEL | Azure 对话模型 | `null` | +| AZURE_IMAGE_MODEL | Azure 图片模型 | `null` | +| AZURE_API_VERSION | Azure API 版本号 | `2024-06-01` | ### Workers @@ -124,13 +128,14 @@ OPENAI_API_BASE,GOOGLE_COMPLETIONS_API,MISTRAL_API_BASE,COHERE_API_BASE,ANTHROPI ### Gemini -cloudflare workers 暂时不支持访问 +> cloudflare workers 暂时不支持访问 -| KEY | 名称 | 默认值 | -|--------------------------|-------------------------|------------------------------------------------------------| -| GOOGLE_API_KEY | Google Gemini API Key | `null` | -| GOOGLE_COMPLETIONS_API | Google Gemini API | `https://generativelanguage.googleapis.com/v1beta/models/` | -| GOOGLE_COMPLETIONS_MODEL | Google Gemini Model | `gemini-pro` | +| KEY | 名称 | 默认值 | +|----------------------------|----------------------------------|------------------------------------------------------------| +| GOOGLE_API_KEY | Google Gemini API Key | `null` | +| ~~GOOGLE_COMPLETIONS_API~~ | ~~Google Gemini API~~ | `https://generativelanguage.googleapis.com/v1beta/models/` | +| GOOGLE_COMPLETIONS_MODEL | Google Gemini Model | `gemini-pro` | +| GOOGLE_API_BASE | 支持Openai API 格式的 Gemini API Base | `https://generativelanguage.googleapis.com/v1beta` | ### Mistral @@ -158,19 +163,20 @@ cloudflare workers 暂时不支持访问 ## 支持命令 -| 命令 | 说明 | 示例 | -|:-----------|:--------------------------|:------------------------------------------------| -| `/help` | 获取命令帮助 | `/help` | -| `/new` | 发起新的对话 | `/new` | -| `/start` | 获取你的ID,并发起新的对话 | `/start` | -| `/img` | 生成一张图片 | `/img 图片描述` | -| `/version` | 获取当前版本号,判断是否需要更新 | `/version` | -| `/setenv` | 设置用户配置, 详情见`用户配置` | `/setenv KEY=VALUE` | -| `/setenvs` | 批量设置用户配置, 详情见`用户配置` | `/setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}` | -| `/delenv` | 删除用户配置 | `/delenv KEY` | -| `/system` | 查看当前一些系统信息 | `/system` | -| `/redo` | 修改上一个提问或者换一个回答 | `/redo 修改过的内容` 或者 `/redo` | -| `/echo` | 回显消息,仅开发模式可用 | `/echo` | +| 命令 | 说明 | 示例 | +|:-----------|:--------------------|:------------------------------------------------| +| `/help` | 获取命令帮助 | `/help` | +| `/new` | 发起新的对话 | `/new` | +| `/start` | 获取你的ID,并发起新的对话 | `/start` | +| `/img` | 生成一张图片 | `/img 图片描述` | +| `/version` | 获取当前版本号,判断是否需要更新 | `/version` | +| `/setenv` | 设置用户配置, 详情见`用户配置` | `/setenv KEY=VALUE` | +| `/setenvs` | 批量设置用户配置, 详情见`用户配置` | `/setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}` | +| `/delenv` | 删除用户配置 | `/delenv KEY` | +| `/system` | 查看当前一些系统信息 | `/system` | +| `/redo` | 修改上一个提问或者换一个回答 | `/redo 修改过的内容` 或者 `/redo` | +| `/models` | 切换对话模型 | `/models` 后通过内置菜单选择模型 | +| `/echo` | 回显消息,仅开发模式可用 | `/echo` | ## 自定义命令 @@ -223,3 +229,21 @@ COMMAND_DESCRIPTION_cn2en = '将对话内容翻译成英文' ``` 如果你想将自定义命令绑定到telegram的菜单中,你可以添加如下环境变量`COMMAND_SCOPE_azure = "all_private_chats,all_group_chats,all_chat_administrators"`,这样插件就会在所有的私聊,群聊和群组中生效。 + + +## 模型列表 + +支持使用 `/models` 命令获取支持的模型列表,并且通过菜单选择切换。 +模型列表支持的配置项的类型为 URL 或者 json 数组。 如果是 URL,会自动请求获取模型列表,如果是 json 数组,会直接使用该数组。 +当前支持从URL获取模型列表的AI提供商有 `openai, workers, mistral, cohere`。只支持 json 数组的AI提供商有 `azure, gemini, anthropic`。 +当支持从URL获取模型列表的AI提供商的模型列表配置项为空时候,会默认根据其 base api 自动拼接获取模型列表的URL。 + +| AI提供商 | 模型列表配置项 | 默认值 | 自动拼接生成的值 | +|:----------|--------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| +| openai | OPENAI_CHAT_MODELS_LIST | `` | `${OPENAI_API_BASE}/models` | +| workers | WORKERS_CHAT_MODELS_LIST | `` | `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/models/search?task=Text%20Generation` | +| mistral | MISTRAL_CHAT_MODELS_LIST | `` | `${MISTRAL_API_BASE}/models` | +| cohere | COHERE_CHAT_MODELS_LIST | `` | `https://api.cohere.com/v1/models` | +| azure | AZURE_CHAT_MODELS_LIST | `[]` | | +| gemini | GOOGLE_COMPLETIONS_MODELS_LIST | `["gemini-1.5-flash"]` | | +| anthropic | ANTHROPIC_CHAT_MODELS_LIST | `["claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"]` | | diff --git a/doc/cn/VERCEL.md b/doc/cn/VERCEL.md index a5d2c4ee..49cba6f1 100644 --- a/doc/cn/VERCEL.md +++ b/doc/cn/VERCEL.md @@ -1,6 +1,6 @@ # 使用Vercel部署 (实验性) -`/src/adapter/vercel`中提供了示例代码,可以完成Vercel部署,和基础的功能测试。但是无法保证所有功能都能正常工作。 +`/src/entry/vercel`中提供了示例代码,可以完成Vercel部署,和基础的功能测试。但是无法保证所有功能都能正常工作。 ### 自动部署 diff --git a/doc/en/CHANGELOG.md b/doc/en/CHANGELOG.md index d3d72e98..cce3c21c 100644 --- a/doc/en/CHANGELOG.md +++ b/doc/en/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +- v1.10.0 + - Switching Models with InlineKeyboards + - v1.9.0 - Add plugin system diff --git a/doc/en/CONFIG.md b/doc/en/CONFIG.md index a9254c1a..cfd42a48 100644 --- a/doc/en/CONFIG.md +++ b/doc/en/CONFIG.md @@ -80,12 +80,12 @@ Each user's custom configuration can only be modified by sending a message throu ### General configuration -| KEY | Name | Default | Description | -|--------------------------|--------------------------------------|-------------------------------|----------------------------------------------------------------------------| -| AI_PROVIDER | AI provider | `auto` | Options `auto, openai, azure, workers, gemini, mistral, cohere, anthropic` | -| AI_IMAGE_PROVIDER | AI image provider | `auto` | Options `auto, openai, azure, workers` | -| SYSTEM_INIT_MESSAGE | Default initialization message. | `You are a helpful assistant` | Automatically select default values based on the bound language. | -| SYSTEM_INIT_MESSAGE_ROLE | Default initialization message role. | `system` | | +| KEY | Name | Default | Description | +|------------------------------|------------------------------------------|-------------------------------|----------------------------------------------------------------------------| +| AI_PROVIDER | AI provider | `auto` | Options `auto, openai, azure, workers, gemini, mistral, cohere, anthropic` | +| AI_IMAGE_PROVIDER | AI image provider | `auto` | Options `auto, openai, azure, workers` | +| SYSTEM_INIT_MESSAGE | Default initialization message. | `You are a helpful assistant` | Automatically select default values based on the bound language. | +| ~~SYSTEM_INIT_MESSAGE_ROLE~~ | ~~Default initialization message role.~~ | `system` | Deprecated | ### OpenAI @@ -95,7 +95,7 @@ Each user's custom configuration can only be modified by sending a message throu | OPENAI_CHAT_MODEL | OpenAI Model | `gpt-4o-mini` | | OPENAI_API_BASE | OpenAI API BASE | `https://api.openai.com/v1` | | OPENAI_API_EXTRA_PARAMS | OpenAI API Extra Params | `{}` | -| DALL_E_MODEL | DALL-E model name. | `dall-e-2` | +| DALL_E_MODEL | DALL-E model name. | `dall-e-3` | | DALL_E_IMAGE_SIZE | DALL-E Image size | `512x512` | | DALL_E_IMAGE_QUALITY | DALL-E Image quality | `standard` | | DALL_E_IMAGE_STYLE | DALL-E Image style | `vivid` | @@ -106,11 +106,16 @@ Each user's custom configuration can only be modified by sending a message throu > AZURE_DALLE_API `https://RESOURCE_NAME.openai.azure.com/openai/deployments/MODEL_NAME/images/generations?api-version=VERSION_NAME` -| KEY | Name | Default | -|-----------------------|-----------------------|---------| -| AZURE_API_KEY | Azure API Key | `null` | -| AZURE_COMPLETIONS_API | Azure Completions API | `null` | -| AZURE_DALLE_API | Azure DallE API | `null` | +| KEY | 名称 | 默认值 | +|---------------------------|---------------------------|--------------| +| AZURE_API_KEY | Azure API Key | `null` | +| ~~AZURE_COMPLETIONS_API~~ | ~~Azure Completions API~~ | `null` | +| ~~AZURE_DALLE_API~~ | ~~Azure DallE API~~ | `null` | +| AZURE_RESOURCE_NAME | Azure Resource Name | `null` | +| AZURE_CHAT_MODEL | Azure Chat Model | `null` | +| AZURE_IMAGE_MODEL | Azure Image Model | `null` | +| AZURE_API_VERSION | Azure API version number | `2024-06-01` | + ### Workers @@ -123,14 +128,17 @@ Each user's custom configuration can only be modified by sending a message throu ### Gemini -| KEY | Name | Default | -|--------------------------|-----------------------|------------------------------------------------------------| -| GOOGLE_API_KEY | Google Gemini API Key | `null` | -| GOOGLE_COMPLETIONS_API | Google Gemini API | `https://generativelanguage.googleapis.com/v1beta/models/` | -| GOOGLE_COMPLETIONS_MODEL | Google Gemini Model | `gemini-pro` | - > Cloudflare Workers currently do not support accessing Gemini. +| KEY | Name | Default | +|----------------------------|-----------------------------------------------|------------------------------------------------------------| +| GOOGLE_API_KEY | Google Gemini API Key | `null` | +| ~~GOOGLE_COMPLETIONS_API~~ | ~~Google Gemini API~~ | `https://generativelanguage.googleapis.com/v1beta/models/` | +| GOOGLE_COMPLETIONS_MODEL | Google Gemini Model | `gemini-pro` | +| GOOGLE_API_BASE | Supports Gemini API Base in OpenAI API format | `https://generativelanguage.googleapis.com/v1beta` | + + + ### Mistral | KEY | Name | Default | @@ -157,19 +165,20 @@ Each user's custom configuration can only be modified by sending a message throu ## Command -| Command | Description | Example | -|:-----------|:------------------------------------------------------------------------|:------------------------------------------------| -| `/help` | Get command help. | `/help` | -| `/new` | Initiate a new conversation. | `/new` | -| `/start` | Get your ID and start a new conversation. | `/start` | -| `/img` | Generate an image. | `/img Image Description` | -| `/version` | Get the current version number and determine if an update is needed. | `/version` | -| `/setenv` | Set user configuration, see `User Configuration` for details. | `/setenv KEY=VALUE` | -| `/setenvs` | Batch setting user configuration, see "User Configuration" for details. | `/setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}` | -| `/delenv` | Delete user configuration. | `/delenv KEY` | -| `/system` | View some current system information. | `/system` | -| `/redo` | Edit the previous question or provide a different answer. | `/redo Modified content.` or `/redo` | -| `/echo` | Echo message, only available in development mode. | `/echo` | +| Command | Description | Example | +|:-----------|:------------------------------------------------------------------------|:------------------------------------------------------------------| +| `/help` | Get command help. | `/help` | +| `/new` | Initiate a new conversation. | `/new` | +| `/start` | Get your ID and start a new conversation. | `/start` | +| `/img` | Generate an image. | `/img Image Description` | +| `/version` | Get the current version number and determine if an update is needed. | `/version` | +| `/setenv` | Set user configuration, see `User Configuration` for details. | `/setenv KEY=VALUE` | +| `/setenvs` | Batch setting user configuration, see "User Configuration" for details. | `/setenvs {"KEY1": "VALUE1", "KEY2": "VALUE2"}` | +| `/delenv` | Delete user configuration. | `/delenv KEY` | +| `/system` | View some current system information. | `/system` | +| `/redo` | Edit the previous question or provide a different answer. | `/redo Modified content.` or `/redo` | +| `/models` | Switch chat model | `/models` After that, select the model through the built-in menu. | +| `/echo` | Echo message, only available in development mode. | `/echo` | ## Custom command @@ -221,4 +230,22 @@ COMMAND_DESCRIPTION_gpt4 = 'Switch AI provider to OpenAI GPT-4.' COMMAND_DESCRIPTION_cn2en = 'Translate the conversation content into English.' ``` -If you want to bind custom commands to the menu of Telegram, you can add the following environment variable `COMMAND_SCOPE_azure = "all_private_chats,all_group_chats,all_chat_administrators"`, so that the plugin will take effect in all private chats, group chats and groups. \ No newline at end of file +If you want to bind custom commands to the menu of Telegram, you can add the following environment variable `COMMAND_SCOPE_azure = "all_private_chats,all_group_chats,all_chat_administrators"`, so that the plugin will take effect in all private chats, group chats and groups. + + +## Model List + +Supports using the `/models` command to get a list of supported models and switching between them via menu selections. +The supported configuration items for the models list are of type URL or json array. If it is a URL, the list of models will be requested automatically, if it is a json array, the array will be used directly. +Current AI providers that support fetching the model list from a URL are `openai, workers, mistral, cohere`. AI providers that only support json arrays are `azure, gemini, anthropic`. +When the model list configuration is empty for an AI provider that supports fetching the model list from a URL, the URL for fetching the model list will be automatically spliced according to its base api by default. + +| AI provider | Model List Configuration Key | Default value | Automatically generated value | +|:------------|--------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------| +| openai | OPENAI_CHAT_MODELS_LIST | `` | `${OPENAI_API_BASE}/models` | +| workers | WORKERS_CHAT_MODELS_LIST | `` | `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/ai/models/search?task=Text%20Generation` | +| mistral | MISTRAL_CHAT_MODELS_LIST | `` | `${MISTRAL_API_BASE}/models` | +| cohere | COHERE_CHAT_MODELS_LIST | `` | `https://api.cohere.com/v1/models` | +| azure | AZURE_CHAT_MODELS_LIST | `[]` | | +| gemini | GOOGLE_COMPLETIONS_MODELS_LIST | `["gemini-1.5-flash"]` | | +| anthropic | ANTHROPIC_CHAT_MODELS_LIST | `["claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"]` | | diff --git a/doc/en/VERCEL.md b/doc/en/VERCEL.md index cf4fda4c..1994ac8b 100644 --- a/doc/en/VERCEL.md +++ b/doc/en/VERCEL.md @@ -1,6 +1,6 @@ # Deploy using Vercel (experimental) -The `/src/adapter/vercel` provides sample code that can complete Vercel deployment and basic functional testing. However, it cannot guarantee that all functions will work properly. +The `/src/entry/vercel` provides sample code that can complete Vercel deployment and basic functional testing. However, it cannot guarantee that all functions will work properly. ### Automatic deployment diff --git a/docker-compose.yaml b/docker-compose.yaml index 24201fbe..932f99f6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,4 +6,6 @@ services: - "8787:8787" volumes: - ./config.json:/app/config.json:ro # change `./config.json` to your local path - - ./wrangler.toml:/app/config.toml:ro # change `./wrangler.toml` to your local path + - ./wrangler.toml:/app/wrangler.toml:ro # change `./wrangler.toml` to your local path + network_mode: "host" # If you access the proxy port based on the host + entrypoint: ["npm", "run", "start:dist"] \ No newline at end of file diff --git a/package.json b/package.json index 012f4cd0..65af9e31 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "chatgpt-telegram-workers", "type": "module", - "version": "1.9.4", + "version": "1.10.0", "description": "The easiest and quickest way to deploy your own ChatGPT Telegram bot is to use a single file and simply copy and paste it. There is no need for any dependencies, local development environment configuration, domain names, or servers.", "author": "tbxark ", "license": "MIT", @@ -21,41 +21,53 @@ ], "scripts": { "lint": "eslint --fix *.js *.ts src plugins scripts", + "version": "tsx src/vite/version/main.ts", "build": "vite build", - "build:local": "BUILD_MODE=local vite build", - "build:docker": "npm run build:local && cd dist && docker build -t chatgpt-telegram-workers:latest .", - "build:dockerx": "npm run build:local && cd dist && docker build --platform linux/amd64,linux/arm64 -t tbxark/chatgpt-telegram-workers:latest --push .", - "build:vercel": "BUILD_MODE=vercel vite build", - "build:pack": "BUILD_MODE=pack vite build", + "build:local": "ENTRY=src/entry/local/index.ts vite build", + "build:docker": "docker build -t chatgpt-telegram-workers:latest .", + "build:dockerx": "docker build --platform linux/amd64,linux/arm64 -t tbxark/chatgpt-telegram-workers:latest --push .", + "build:vercel": "ENTRY=src/entry/vercel/index.ts vite build", + "build:pack": "TYPES=true FORMATS=es,cjs vite build", "deploy:dist": "wrangler deploy", "deploy:build": "npm run build && wrangler deploy", "deploy:vercel": "vercel deploy --prod", - "deploy:plugin": "BUILD_MODE=plugins-page vite build && wrangler pages deploy plugins --project-name=interpolate-test --branch=main", - "start:dist": "node dist/index.js", - "start:local": "CONFIG_PATH=./config.json TOML_PATH=./wrangler.toml tsx src/adapter/local/index.ts", + "deploy:plugin": "vite build -c plugins/vite.config.ts && wrangler pages deploy plugins --project-name=interpolate-test --branch=main", + "start:dist": "CONFIG_PATH=./config.json TOML_PATH=./wrangler.toml node dist/index.js", + "start:local": "CONFIG_PATH=./config.json TOML_PATH=./wrangler.toml tsx src/entry/local/index.ts", "start:debug": "wrangler dev --local", - "prepare:vercel": "tsx ./scripts/plugins/vercel/setenv.ts", + "prepare:vercel": "tsx src/vite/vercel/setenv.ts", "wrangler": "wrangler", "test": "tsx ./src/agent/index.test.ts" }, "dependencies": { + "@ai-sdk/anthropic": "^0.0.56", + "@ai-sdk/azure": "^0.0.52", + "@ai-sdk/cohere": "^0.0.28", + "@ai-sdk/google": "^0.0.55", + "@ai-sdk/mistral": "^0.0.46", + "@ai-sdk/openai": "^0.0.72", + "ai": "^3.4.33", "cloudflare-worker-adapter": "^1.3.3" }, "devDependencies": { - "@antfu/eslint-config": "^3.6.2", + "@antfu/eslint-config": "^3.8.0", "@rollup/plugin-node-resolve": "^15.2.3", - "@types/node": "^22.5.5", - "@vercel/node": "^3.2.14", - "eslint": "^9.10.0", + "@types/node": "^22.9.0", + "@types/react": "^18.3.11", + "@vercel/node": "^3.2.24", + "eslint": "^9.14.0", "eslint-plugin-format": "^0.1.2", + "openai": "^4.68.1", + "react-dom": "^18.3.1", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-node-externals": "^7.1.3", - "telegram-bot-api-types": "^7.9.12", - "tsx": "^4.19.1", - "typescript": "^5.6.2", - "vite": "^5.4.3", + "stylelint": "^16.10.0", + "telegram-bot-api-types": "^7.11.0", + "tsx": "^4.19.2", + "typescript": "^5.6.3", + "vite": "^5.4.11", "vite-plugin-checker": "^0.8.0", - "vite-plugin-dts": "^4.2.1", - "wrangler": "^3.78.5" + "vite-plugin-dts": "^4.3.0", + "wrangler": "^3.86.1" } } diff --git a/plugins/vite.config.ts b/plugins/vite.config.ts new file mode 100644 index 00000000..aa918f8d --- /dev/null +++ b/plugins/vite.config.ts @@ -0,0 +1,32 @@ +import * as path from 'node:path'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import cleanup from 'rollup-plugin-cleanup'; +import nodeExternals from 'rollup-plugin-node-externals'; +import { defineConfig } from 'vite'; +import checker from 'vite-plugin-checker'; + +export default defineConfig({ + plugins: [ + nodeResolve({ + preferBuiltins: true, + }), + cleanup({ + comments: 'none', + extensions: ['js', 'ts'], + }), + checker({ + typescript: true, + }), + nodeExternals(), + ], + build: { + target: 'esnext', + lib: { + entry: path.resolve(__dirname, '../src/plugins/interpolate.ts'), + fileName: 'interpolate', + formats: ['es'], + }, + minify: false, + outDir: path.resolve(__dirname, 'dist'), + }, +}); diff --git a/scripts/plugins/docker/index.ts b/scripts/plugins/docker/index.ts deleted file mode 100644 index 4dae04b7..00000000 --- a/scripts/plugins/docker/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as fs from 'node:fs/promises'; -import path from 'node:path'; - -const dockerfile = ` -FROM node:alpine as PROD - -WORKDIR /app -COPY index.js package.json /app/ -RUN npm install --only=production --omit=dev -RUN apk add --no-cache sqlite -EXPOSE 8787 -CMD ["npm", "run", "start"] -`; - -const packageJson = ` -{ - "name": "chatgpt-telegram-workers", - "type": "module", - "version": "1.8.0", - "author": "TBXark", - "license": "MIT", - "module": "index.js", - "scripts": { - "start": "node index.js" - }, - "dependencies": { - "cloudflare-worker-adapter": "^1.2.3" - }, - "devDependencies": {} -} -`; - -export function createDockerPlugin(targetDir: string) { - return { - name: 'docker', - async closeBundle() { - await fs.writeFile(path.resolve(targetDir, 'Dockerfile'), dockerfile.trim()); - await fs.writeFile(path.resolve(targetDir, 'package.json'), packageJson.trim()); - }, - }; -} diff --git a/scripts/plugins/version/index.ts b/scripts/plugins/version/index.ts deleted file mode 100644 index 7656caef..00000000 --- a/scripts/plugins/version/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { execSync } from 'node:child_process'; -import * as fs from 'node:fs/promises'; -import path from 'node:path'; - -let COMMIT_HASH = 'unknown'; -const TIMESTAMP = Math.floor(Date.now() / 1000); - -try { - COMMIT_HASH = execSync('git rev-parse --short HEAD').toString().trim(); -} catch (e) { - console.warn(e); -} - -export function createVersionPlugin(targetDir: string) { - return { - name: 'buildInfo', - async closeBundle() { - await fs.writeFile(path.resolve(targetDir, 'timestamp'), TIMESTAMP.toString()); - await fs.writeFile(path.resolve(targetDir, 'buildinfo.json'), JSON.stringify({ - sha: COMMIT_HASH, - timestamp: TIMESTAMP, - })); - }, - }; -} - -export const versionDefine = { - __BUILD_VERSION__: JSON.stringify(COMMIT_HASH), - __BUILD_TIMESTAMP__: TIMESTAMP.toString(), -}; diff --git a/src/agent/anthropic.ts b/src/agent/anthropic.ts index 2c2c397e..b498c8dc 100644 --- a/src/agent/anthropic.ts +++ b/src/agent/anthropic.ts @@ -1,11 +1,18 @@ import type { AgentUserConfig } from '../config/env'; import type { SseChatCompatibleOptions } from './request'; import type { SSEMessage, SSEParserResult } from './stream'; -import type { ChatAgent, ChatStreamTextHandler, HistoryItem, LLMChatParams } from './types'; +import type { + ChatAgent, + ChatAgentResponse, + ChatStreamTextHandler, + HistoryItem, + LLMChatParams, +} from './types'; import { ENV } from '../config/env'; import { imageToBase64String } from '../utils/image'; import { requestChatCompletions } from './request'; import { Stream } from './stream'; +import { convertStringToResponseMessages, extractImageContent, loadModelsList } from './utils'; export class Anthropic implements ChatAgent { readonly name = 'anthropic'; @@ -20,22 +27,37 @@ export class Anthropic implements ChatAgent { role: item.role, content: item.content, }; - - if (item.images && item.images.length > 0) { - res.content = []; - if (item.content) { - res.content.push({ type: 'text', text: item.content }); - } - for (const image of item.images) { - res.content.push(await imageToBase64String(image).then(({ format, data }) => { - return { type: 'image', source: { type: 'base64', media_type: format, data } }; - })); + if (item.role === 'system') { + return null; + } + if (Array.isArray(item.content)) { + const contents = []; + for (const content of item.content) { + switch (content.type) { + case 'text': + contents.push({ type: 'text', text: content.text }); + break; + case 'image': { + const data = extractImageContent(content.image); + if (data.url) { + contents.push(await imageToBase64String(data.url).then(({ format, data }) => { + return { type: 'image', source: { type: 'base64', media_type: format, data } }; + })); + } else if (data.base64) { + contents.push({ type: 'image', source: { type: 'base64', media_type: 'image/jpeg', data: data.base64 } }); + } + break; + } + default: + break; + } } + res.content = contents; } return res; }; - readonly model = (ctx: AgentUserConfig): string => { + readonly model = (ctx: AgentUserConfig): string | null => { return ctx.ANTHROPIC_CHAT_MODEL; }; @@ -64,8 +86,8 @@ export class Anthropic implements ChatAgent { } } - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, images, prompt, history } = params; + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; const url = `${context.ANTHROPIC_API_BASE}/messages`; const header = { 'x-api-key': context.ANTHROPIC_API_KEY || '', @@ -73,8 +95,6 @@ export class Anthropic implements ChatAgent { 'content-type': 'application/json', }; - const messages: HistoryItem[] = (history || []).concat({ role: 'user', content: message, images }); - if (messages.length > 0 && messages[0].role === 'assistant') { messages.shift(); } @@ -82,14 +102,13 @@ export class Anthropic implements ChatAgent { const body = { system: prompt, model: context.ANTHROPIC_CHAT_MODEL, - messages: await Promise.all(messages.map(item => this.render(item))), + messages: (await Promise.all(messages.map(item => this.render(item)))).filter(i => i !== null), stream: onStream != null, max_tokens: ENV.MAX_TOKEN_LENGTH > 0 ? ENV.MAX_TOKEN_LENGTH : 2048, }; if (!body.system) { delete body.system; } - const options: SseChatCompatibleOptions = {}; options.streamBuilder = function (r, c) { return new Stream(r, c, Anthropic.parser); @@ -98,11 +117,15 @@ export class Anthropic implements ChatAgent { return data?.delta?.text; }; options.fullContentExtractor = function (data: any) { - return data?.content?.[0].text; + return data?.content?.at(0).text; }; options.errorExtractor = function (data: any) { return data?.error?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + return loadModelsList(context.ANTHROPIC_CHAT_MODELS_LIST); }; } diff --git a/src/agent/azure.ts b/src/agent/azure.ts index d53fd781..e88b41b3 100644 --- a/src/agent/azure.ts +++ b/src/agent/azure.ts @@ -1,7 +1,14 @@ import type { AgentUserConfig } from '../config/env'; -import type { ChatAgent, ChatStreamTextHandler, ImageAgent, LLMChatParams } from './types'; -import { renderOpenAIMessage } from './openai'; +import type { + ChatAgent, + ChatAgentResponse, + ChatStreamTextHandler, + ImageAgent, + LLMChatParams, +} from './types'; +import { renderOpenAIMessages } from './openai'; import { requestChatCompletions } from './request'; +import { convertStringToResponseMessages, loadModelsList } from './utils'; class AzureBase { readonly name = 'azure'; @@ -19,39 +26,33 @@ class AzureBase { } export class AzureChatAI extends AzureBase implements ChatAgent { - readonly modelKey = 'AZURE_COMPLETIONS_API'; + readonly modelKey = 'AZURE_CHAT_MODEL'; readonly enable = (context: AgentUserConfig): boolean => { - return !!(context.AZURE_API_KEY && context.AZURE_COMPLETIONS_API); + return !!(context.AZURE_API_KEY && context.AZURE_RESOURCE_NAME); }; - readonly model = (ctx: AgentUserConfig) => { - return this.modelFromURI(ctx.AZURE_COMPLETIONS_API); + readonly model = (ctx: AgentUserConfig): string | null => { + return ctx.AZURE_CHAT_MODEL; }; - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, images, prompt, history } = params; - const url = context.AZURE_COMPLETIONS_API; - if (!url || !context.AZURE_API_KEY) { - throw new Error('Azure Completions API is not set'); - } + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; + const url = `https://${context.AZURE_RESOURCE_NAME}.openai.azure.com/openai/deployments/${context.AZURE_CHAT_MODEL}/chat/completions?api-version=${context.AZURE_API_VERSION}`; const header = { 'Content-Type': 'application/json', - 'api-key': context.AZURE_API_KEY, + 'api-key': context.AZURE_API_KEY || '', }; - - const messages = [...(history || []), { role: 'user', content: message, images }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } - const body = { ...context.OPENAI_API_EXTRA_PARAMS, - messages: await Promise.all(messages.map(renderOpenAIMessage)), + messages: await renderOpenAIMessages(prompt, messages, true), stream: onStream != null, }; + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; - return requestChatCompletions(url, header, body, onStream); + readonly modelList = async (context: AgentUserConfig): Promise => { + return loadModelsList(context.AZURE_CHAT_MODELS_LIST); }; } @@ -67,13 +68,10 @@ export class AzureImageAI extends AzureBase implements ImageAgent { }; readonly request = async (prompt: string, context: AgentUserConfig): Promise => { - const url = context.AZURE_DALLE_API; - if (!url || !context.AZURE_API_KEY) { - throw new Error('Azure DALL-E API is not set'); - } + const url = `https://${context.AZURE_RESOURCE_NAME}.openai.azure.com/openai/deployments/${context.AZURE_CHAT_MODEL}/images/generations?api-version=${context.AZURE_API_VERSION}`; const header = { 'Content-Type': 'application/json', - 'api-key': context.AZURE_API_KEY, + 'api-key': context.AZURE_API_KEY || '', }; const body = { prompt, @@ -95,6 +93,6 @@ export class AzureImageAI extends AzureBase implements ImageAgent { if (resp.error?.message) { throw new Error(resp.error.message); } - return resp?.data?.[0]?.url; + return resp?.data?.at(0)?.url; }; } diff --git a/src/agent/chat.ts b/src/agent/chat.ts index df7b4210..d165bbc2 100644 --- a/src/agent/chat.ts +++ b/src/agent/chat.ts @@ -1,6 +1,7 @@ import type { WorkerContext } from '../config/context'; -import type { ChatAgent, HistoryItem, HistoryModifier, LLMChatRequestParams } from './types'; +import type { ChatAgent, HistoryItem, HistoryModifier, LLMChatParams, UserMessageItem } from './types'; import { ENV } from '../config/env'; +import { extractTextContent } from './utils'; /** * @returns {(function(string): number)} @@ -37,7 +38,7 @@ async function loadHistory(key: string): Promise { const historyItem = list[i]; let length = 0; if (historyItem.content) { - length = counter(historyItem.content); + length = counter(extractTextContent(historyItem)); } else { historyItem.content = ''; } @@ -62,7 +63,7 @@ async function loadHistory(key: string): Promise { export type StreamResultHandler = (text: string) => Promise; -export async function requestCompletionsFromLLM(params: LLMChatRequestParams, context: WorkerContext, agent: ChatAgent, modifier: HistoryModifier | null, onStream: StreamResultHandler | null): Promise { +export async function requestCompletionsFromLLM(params: UserMessageItem | null, context: WorkerContext, agent: ChatAgent, modifier: HistoryModifier | null, onStream: StreamResultHandler | null): Promise { const historyDisable = ENV.AUTO_TRIM_HISTORY && ENV.MAX_HISTORY_LENGTH <= 0; const historyKey = context.SHARE_CONTEXT.chatHistoryKey; if (!historyKey) { @@ -70,25 +71,26 @@ export async function requestCompletionsFromLLM(params: LLMChatRequestParams, co } let history = await loadHistory(historyKey); if (modifier) { - const modifierData = modifier(history, params.message || null); + const modifierData = modifier(history, params || null); history = modifierData.history; - params.message = modifierData.message; + params = modifierData.message; } - const llmParams = { - ...params, - history, - prompt: context.USER_CONFIG.SYSTEM_INIT_MESSAGE, + if (!params) { + throw new Error('Message is empty'); + } + history.push(params); + const llmParams: LLMChatParams = { + prompt: context.USER_CONFIG.SYSTEM_INIT_MESSAGE || undefined, + messages: history, }; - const answer = await agent.request(llmParams, context.USER_CONFIG, onStream); + const { text, responses } = await agent.request(llmParams, context.USER_CONFIG, onStream); if (!historyDisable) { - const userMessage = { role: 'user', content: params.message || '', images: params.images }; - if (ENV.HISTORY_IMAGE_PLACEHOLDER && userMessage.images && userMessage.images.length > 0) { - delete userMessage.images; - userMessage.content = `${ENV.HISTORY_IMAGE_PLACEHOLDER}\n${userMessage.content}`; + if (ENV.HISTORY_IMAGE_PLACEHOLDER) { + // TODO: Add image placeholder } - history.push(userMessage); - history.push({ role: 'assistant', content: answer }); + history.push(params); + history.push(...responses); await ENV.DATABASE.put(historyKey, JSON.stringify(history)).catch(console.error); } - return answer; + return text; } diff --git a/src/agent/cohere.ts b/src/agent/cohere.ts index 3085c971..6e6d2eec 100644 --- a/src/agent/cohere.ts +++ b/src/agent/cohere.ts @@ -1,7 +1,9 @@ import type { AgentUserConfig } from '../config/env'; import type { SseChatCompatibleOptions } from './request'; -import type { ChatAgent, ChatStreamTextHandler, LLMChatParams } from './types'; +import type { ChatAgent, ChatAgentResponse, ChatStreamTextHandler, LLMChatParams } from './types'; +import { renderOpenAIMessages } from './openai'; import { requestChatCompletions } from './request'; +import { convertStringToResponseMessages, loadModelsList } from './utils'; export class Cohere implements ChatAgent { readonly name = 'cohere'; @@ -11,26 +13,20 @@ export class Cohere implements ChatAgent { return !!(context.COHERE_API_KEY); }; - readonly model = (ctx: AgentUserConfig): string => { + readonly model = (ctx: AgentUserConfig): string | null => { return ctx.COHERE_CHAT_MODEL; }; - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, prompt, history } = params; + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; const url = `${context.COHERE_API_BASE}/chat`; const header = { 'Authorization': `Bearer ${context.COHERE_API_KEY}`, 'Content-Type': 'application/json', 'Accept': onStream !== null ? 'text/event-stream' : 'application/json', }; - - const messages = [...history || [], { role: 'user', content: message }]; - if (prompt) { - messages.unshift({ role: 'assistant', content: prompt }); - } - const body = { - messages, + messages: await renderOpenAIMessages(prompt, messages), model: context.COHERE_CHAT_MODEL, stream: onStream != null, }; @@ -40,11 +36,24 @@ export class Cohere implements ChatAgent { return data?.delta?.message?.content?.text; }; options.fullContentExtractor = function (data: any) { - return data?.messages[0].content; + return data?.messages?.at(0)?.content; }; options.errorExtractor = function (data: any) { return data?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + if (context.COHERE_CHAT_MODELS_LIST === '') { + const { protocol, host } = new URL(context.COHERE_API_BASE); + context.COHERE_CHAT_MODELS_LIST = `${protocol}://${host}/v2/models`; + } + return loadModelsList(context.COHERE_CHAT_MODELS_LIST, async (url): Promise => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${context.COHERE_API_KEY}` }, + }).then(res => res.json()); + return data.models?.filter((model: any) => model.endpoints?.includes('chat')).map((model: any) => model.name) || []; + }); }; } diff --git a/src/agent/gemini.ts b/src/agent/gemini.ts index 0452e025..022c8c46 100644 --- a/src/agent/gemini.ts +++ b/src/agent/gemini.ts @@ -1,16 +1,13 @@ import type { AgentUserConfig } from '../config/env'; -import type { ChatAgent, ChatStreamTextHandler, HistoryItem, LLMChatParams } from './types'; +import type { ChatAgent, ChatAgentResponse, ChatStreamTextHandler, LLMChatParams } from './types'; +import { renderOpenAIMessages } from './openai'; +import { requestChatCompletions } from './request'; +import { convertStringToResponseMessages, loadModelsList } from './utils'; export class Gemini implements ChatAgent { readonly name = 'gemini'; readonly modelKey = 'GOOGLE_COMPLETIONS_MODEL'; - static GEMINI_ROLE_MAP: Record = { - assistant: 'model', - system: 'user', - user: 'user', - }; - readonly enable = (context: AgentUserConfig): boolean => { return !!(context.GOOGLE_API_KEY); }; @@ -19,59 +16,23 @@ export class Gemini implements ChatAgent { return ctx.GOOGLE_COMPLETIONS_MODEL; }; - private render = (item: HistoryItem): object => { - return { - role: Gemini.GEMINI_ROLE_MAP[item.role], - parts: [ - { - text: item.content || '', - }, - ], + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; + const url = `${context.GOOGLE_API_BASE}/chat`; + const header = { + 'Authorization': `Bearer ${context.GOOGLE_API_KEY}`, + 'Content-Type': 'application/json', + 'Accept': onStream !== null ? 'text/event-stream' : 'application/json', + }; + const body = { + messages: await renderOpenAIMessages(prompt, messages), + model: context.GOOGLE_COMPLETIONS_MODEL, + stream: onStream != null, }; + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); }; - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, prompt, history } = params; - if (onStream !== null) { - console.warn('Stream mode is not supported'); - } - const mode = 'generateContent'; // onStream ? 'streamGenerateContent' : 'generateContent' - const url = `${context.GOOGLE_COMPLETIONS_API}${context.GOOGLE_COMPLETIONS_MODEL}:${mode}`; - - const contentsTemp = [...history || [], { role: 'user', content: message }]; - if (prompt) { - contentsTemp.unshift({ role: 'assistant', content: prompt }); - } - const contents: any[] = []; - // role必须是 model,user 而且不能连续两个一样 - for (const msg of contentsTemp) { - msg.role = Gemini.GEMINI_ROLE_MAP[msg.role]; - // 如果存在最后一个元素或role不一样则插入 - if (contents.length === 0 || contents[contents.length - 1].role !== msg.role) { - contents.push(this.render(msg)); - } else { - // 否则合并 - contents[contents.length - 1].parts[0].text += msg.content; - } - } - - const resp = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-goog-api-key': context.GOOGLE_API_KEY, - } as Record, - body: JSON.stringify({ contents }), - }); - const data = await resp.json() as any; - try { - return data.candidates[0].content.parts[0].text; - } catch (e) { - console.error(e); - if (!data) { - throw new Error('Empty response'); - } - throw new Error(data?.error?.message || JSON.stringify(data)); - } + readonly modelList = async (context: AgentUserConfig): Promise => { + return loadModelsList(context.GOOGLE_CHAT_MODELS_LIST); }; } diff --git a/src/agent/index.test.ts b/src/agent/index.test.ts index ffe7724f..1c82a067 100644 --- a/src/agent/index.test.ts +++ b/src/agent/index.test.ts @@ -10,8 +10,12 @@ import '../config/env.test'; }); const params: LLMChatParams = { prompt: 'You are a useful assistant.', - message: 'What is your name?', - history: [], + messages: [ + { + role: 'user', + content: 'What is your name?', + }, + ], }; console.log(agent?.name, agent?.model(ENV.USER_CONFIG)); agent?.request(params, ENV.USER_CONFIG, async (text) => { diff --git a/src/agent/index.ts b/src/agent/index.ts index ad0f1018..f7abaf4e 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -8,14 +8,14 @@ import { Mistral } from './mistralai'; import { Dalle, OpenAI } from './openai'; import { WorkersChat, WorkersImage } from './workersai'; -const CHAT_AGENTS: ChatAgent[] = [ +export const CHAT_AGENTS: ChatAgent[] = [ + new OpenAI(), new Anthropic(), new AzureChatAI(), + new WorkersChat(), new Cohere(), new Gemini(), new Mistral(), - new OpenAI(), - new WorkersChat(), ]; export function loadChatLLM(context: AgentUserConfig): ChatAgent | null { @@ -33,7 +33,7 @@ export function loadChatLLM(context: AgentUserConfig): ChatAgent | null { return null; } -const IMAGE_AGENTS: ImageAgent[] = [ +export const IMAGE_AGENTS: ImageAgent[] = [ new AzureImageAI(), new Dalle(), new WorkersImage(), @@ -52,4 +52,4 @@ export function loadImageGen(context: AgentUserConfig): ImageAgent | null { } } return null; -} +}; diff --git a/src/agent/mistralai.ts b/src/agent/mistralai.ts index 9e5dc8ea..5c3d69c3 100644 --- a/src/agent/mistralai.ts +++ b/src/agent/mistralai.ts @@ -1,6 +1,8 @@ import type { AgentUserConfig } from '../config/env'; -import type { ChatAgent, ChatStreamTextHandler, HistoryItem, LLMChatParams } from './types'; +import type { ChatAgent, ChatAgentResponse, ChatStreamTextHandler, LLMChatParams } from './types'; +import { renderOpenAIMessages } from './openai'; import { requestChatCompletions } from './request'; +import { convertStringToResponseMessages, loadModelsList } from './utils'; export class Mistral implements ChatAgent { readonly name = 'mistral'; @@ -10,36 +12,36 @@ export class Mistral implements ChatAgent { return !!(context.MISTRAL_API_KEY); }; - readonly model = (ctx: AgentUserConfig): string => { + readonly model = (ctx: AgentUserConfig): string | null => { return ctx.MISTRAL_CHAT_MODEL; }; - private render = (item: HistoryItem): any => { - return { - role: item.role, - content: item.content, - }; - }; - - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, prompt, history } = params; + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; const url = `${context.MISTRAL_API_BASE}/chat/completions`; const header = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${context.MISTRAL_API_KEY}`, }; - const messages = [...(history || []), { role: 'user', content: message }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } - const body = { model: context.MISTRAL_CHAT_MODEL, - messages: messages.map(this.render), + messages: await renderOpenAIMessages(prompt, messages), stream: onStream != null, }; - return requestChatCompletions(url, header, body, onStream); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + if (context.MISTRAL_CHAT_MODELS_LIST === '') { + context.MISTRAL_CHAT_MODELS_LIST = `${context.MISTRAL_API_BASE}/models`; + } + return loadModelsList(context.MISTRAL_CHAT_MODELS_LIST, async (url): Promise => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${context.MISTRAL_API_KEY}` }, + }).then(res => res.json()); + return data.data?.map((model: any) => model.id) || []; + }); }; } diff --git a/src/agent/next/next.ts b/src/agent/next/next.ts new file mode 100644 index 00000000..08498ff8 --- /dev/null +++ b/src/agent/next/next.ts @@ -0,0 +1,130 @@ +import type { ProviderV1 } from '@ai-sdk/provider'; +import type { LanguageModelV1 } from 'ai'; +import type { AgentUserConfig } from '../../config/env'; +import type { ChatAgent, ChatAgentResponse, ChatStreamTextHandler, HistoryItem, LLMChatParams } from '../types'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { createAzure } from '@ai-sdk/azure'; +import { createCohere } from '@ai-sdk/cohere'; +import { createGoogleGenerativeAI } from '@ai-sdk/google'; +import { createMistral } from '@ai-sdk/mistral'; +import { createOpenAI } from '@ai-sdk/openai'; +import { generateText, streamText } from 'ai'; +import { streamHandler } from '../request'; + +async function requestChatCompletionsV2(params: { model: LanguageModelV1; prompt?: string; messages: HistoryItem[] }, onStream: ChatStreamTextHandler | null): Promise { + if (onStream !== null) { + const stream = await streamText({ + model: params.model, + prompt: params.prompt, + messages: params.messages, + }); + await streamHandler(stream.textStream, t => t, onStream); + return { + text: await stream.text, + responses: (await stream.response).messages, + }; + } else { + const result = await generateText({ + model: params.model, + prompt: params.prompt, + messages: params.messages, + }); + return { + text: result.text, + responses: result.response.messages, + }; + } +} + +type ProviderCreator = (context: AgentUserConfig) => ProviderV1; + +class NextChatAgent implements ChatAgent { + readonly name: string; + readonly modelKey = 'NEXT_CHAT_MODEL'; + readonly adapter: ChatAgent; + readonly providerCreator: ProviderCreator; + + constructor(adapter: ChatAgent, providerCreator: ProviderCreator) { + this.name = adapter.name; + this.adapter = adapter; + this.providerCreator = providerCreator; + } + + static from(agent: ChatAgent): NextChatAgent | null { + const provider = this.newProviderCreator(agent.name); + if (!provider) { + return null; + } + return new NextChatAgent(agent, provider); + } + + readonly enable = (context: AgentUserConfig): boolean => { + return this.adapter.enable(context); + }; + + readonly model = (ctx: AgentUserConfig): string | null => { + return this.adapter.model(ctx); + }; + + static newProviderCreator = (provider: string): ProviderCreator | null => { + switch (provider) { + case 'anthropic': + return (context: AgentUserConfig) => createAnthropic({ + baseURL: context.ANTHROPIC_API_BASE, + apiKey: context.ANTHROPIC_API_KEY || undefined, + }); + case 'azure': + return (context: AgentUserConfig) => createAzure({ + resourceName: context.AZURE_RESOURCE_NAME || undefined, + apiKey: context.AZURE_API_KEY || undefined, + }); + case 'cohere': + return (context: AgentUserConfig) => createCohere({ + baseURL: context.COHERE_API_BASE, + apiKey: context.COHERE_API_KEY || undefined, + }); + case 'gemini': + return (context: AgentUserConfig) => createGoogleGenerativeAI({ + baseURL: context.GOOGLE_API_BASE, + apiKey: context.GOOGLE_API_KEY || undefined, + }); + case 'mistral': + return (context: AgentUserConfig) => createMistral({ + baseURL: context.MISTRAL_API_BASE, + apiKey: context.MISTRAL_API_KEY || undefined, + }); + case 'openai': + return (context: AgentUserConfig) => createOpenAI({ + baseURL: context.OPENAI_API_BASE, + apiKey: context.OPENAI_API_KEY.at(0) || undefined, + }); + default: + return null; + } + }; + + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const model = this.model(context); + if (!model) { + throw new Error('Model not found'); + } + return requestChatCompletionsV2({ + model: this.providerCreator(context).languageModel(model), + prompt: params.prompt, + messages: params.messages, + }, onStream); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + return this.adapter.modelList(context); + }; +} + +export function injectNextChatAgent(agents: ChatAgent[]) { + for (let i = 0; i < agents.length; i++) { + const next = NextChatAgent.from(agents[i]); + if (next) { + agents[i] = next; + } + } +} diff --git a/src/agent/openai.ts b/src/agent/openai.ts index 844d997e..b6e56475 100644 --- a/src/agent/openai.ts +++ b/src/agent/openai.ts @@ -1,36 +1,57 @@ import type { AgentUserConfig } from '../config/env'; -import type { ChatAgent, ChatStreamTextHandler, HistoryItem, ImageAgent, LLMChatParams } from './types'; -import { ENV } from '../config/env'; -import { imageToBase64String, renderBase64DataURI } from '../utils/image'; +import type { + ChatAgent, + ChatAgentResponse, + ChatStreamTextHandler, + HistoryItem, + ImageAgent, + LLMChatParams, +} from './types'; import { requestChatCompletions } from './request'; +import { convertStringToResponseMessages, extractImageContent, loadModelsList } from './utils'; -export async function renderOpenAIMessage(item: HistoryItem): Promise { +async function renderOpenAIMessage(item: HistoryItem, supportImage?: boolean): Promise { const res: any = { role: item.role, content: item.content, }; - if (item.images && item.images.length > 0) { - res.content = []; - if (item.content) { - res.content.push({ type: 'text', text: item.content }); - } - for (const image of item.images) { - switch (ENV.TELEGRAM_IMAGE_TRANSFER_MODE) { - case 'base64': - res.content.push({ type: 'image_url', image_url: { - url: renderBase64DataURI(await imageToBase64String(image)), - } }); + if (Array.isArray(item.content)) { + const contents = []; + for (const content of item.content) { + switch (content.type) { + case 'text': + contents.push({ type: 'text', text: content.text }); + break; + case 'image': + if (supportImage) { + const data = extractImageContent(content.image); + if (data.url) { + contents.push({ type: 'image_url', image_url: { url: data.url } }); + } else if (data.base64) { + contents.push({ type: 'image_url', image_url: { url: data.base64 } }); + } + } break; - case 'url': default: - res.content.push({ type: 'image_url', image_url: { url: image } }); break; } } + res.content = contents; } return res; } +export async function renderOpenAIMessages(prompt: string | undefined, items: HistoryItem[], supportImage?: boolean): Promise { + const messages = await Promise.all(items.map(r => renderOpenAIMessage(r, supportImage))); + if (prompt) { + if (messages.length > 0 && messages[0].role === 'system') { + messages.shift(); + } + messages.unshift({ role: 'system', content: prompt }); + } + return messages; +} + class OpenAIBase { readonly name = 'openai'; apikey = (context: AgentUserConfig): string => { @@ -46,7 +67,7 @@ export class OpenAI extends OpenAIBase implements ChatAgent { return context.OPENAI_API_KEY.length > 0; }; - readonly model = (ctx: AgentUserConfig): string => { + readonly model = (ctx: AgentUserConfig): string | null => { return ctx.OPENAI_CHAT_MODEL; }; @@ -54,27 +75,33 @@ export class OpenAI extends OpenAIBase implements ChatAgent { return renderOpenAIMessage(item); }; - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, images, prompt, history } = params; + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; const url = `${context.OPENAI_API_BASE}/chat/completions`; const header = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apikey(context)}`, }; - - const messages = [...(history || []), { role: 'user', content: message, images }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } - const body = { model: context.OPENAI_CHAT_MODEL, ...context.OPENAI_API_EXTRA_PARAMS, - messages: await Promise.all(messages.map(this.render)), + messages: await renderOpenAIMessages(prompt, messages, true), stream: onStream != null, }; - return requestChatCompletions(url, header, body, onStream); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream)); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + if (context.OPENAI_CHAT_MODELS_LIST === '') { + context.OPENAI_CHAT_MODELS_LIST = `${context.OPENAI_API_BASE}/models`; + } + return loadModelsList(context.OPENAI_CHAT_MODELS_LIST, async (url): Promise => { + const data = await fetch(url, { + headers: { Authorization: `Bearer ${this.apikey(context)}` }, + }).then(res => res.json()); + return data.data?.map((model: any) => model.id) || []; + }); }; } @@ -114,6 +141,6 @@ export class Dalle extends OpenAIBase implements ImageAgent { if (resp.error?.message) { throw new Error(resp.error.message); } - return resp?.data?.[0]?.url; + return resp?.data?.at(0)?.url; }; } diff --git a/src/agent/request.ts b/src/agent/request.ts index 42687443..9c2ec0d0 100644 --- a/src/agent/request.ts +++ b/src/agent/request.ts @@ -15,10 +15,10 @@ function fixOpenAICompatibleOptions(options: SseChatCompatibleOptions | null): S return new Stream(r, c); }; options.contentExtractor = options.contentExtractor || function (d: any) { - return d?.choices?.[0]?.delta?.content; + return d?.choices?.at(0)?.delta?.content; }; options.fullContentExtractor = options.fullContentExtractor || function (d: any) { - return d.choices?.[0]?.message.content; + return d.choices?.at(0)?.message.content; }; options.errorExtractor = options.errorExtractor || function (d: any) { return d.error?.message; @@ -41,12 +41,43 @@ export function isEventStreamResponse(resp: Response): boolean { return false; } -export async function requestChatCompletions(url: string, header: Record, body: any, onStream: ChatStreamTextHandler | null, onResult: ChatStreamTextHandler | null = null, options: SseChatCompatibleOptions | null = null): Promise { +export async function streamHandler(stream: AsyncIterable, contentExtractor: (data: T) => string | null, onStream: (text: string) => Promise): Promise { + let contentFull = ''; + let lengthDelta = 0; + let updateStep = 50; + let lastUpdateTime = Date.now(); + try { + for await (const part of stream) { + const textPart = contentExtractor(part); + if (!textPart) { + continue; + } + lengthDelta += textPart.length; + contentFull = contentFull + textPart; + if (lengthDelta > updateStep) { + if (ENV.TELEGRAM_MIN_STREAM_INTERVAL > 0) { + const delta = Date.now() - lastUpdateTime; + if (delta < ENV.TELEGRAM_MIN_STREAM_INTERVAL) { + continue; + } + lastUpdateTime = Date.now(); + } + lengthDelta = 0; + updateStep += 20; + await onStream(`${contentFull}\n...`); + } + } + } catch (e) { + contentFull += `\nError: ${(e as Error).message}`; + } + return contentFull; +} + +export async function requestChatCompletions(url: string, header: Record, body: any, onStream: ChatStreamTextHandler | null, options: SseChatCompatibleOptions | null = null): Promise { const controller = new AbortController(); const { signal } = controller; let timeoutID = null; - let lastUpdateTime = Date.now(); if (ENV.CHAT_COMPLETE_API_TIMEOUT > 0) { timeoutID = setTimeout(() => controller.abort(), ENV.CHAT_COMPLETE_API_TIMEOUT); } @@ -57,67 +88,29 @@ export async function requestChatCompletions(url: string, header: Record updateStep) { - if (ENV.TELEGRAM_MIN_STREAM_INTERVAL > 0) { - const delta = Date.now() - lastUpdateTime; - if (delta < ENV.TELEGRAM_MIN_STREAM_INTERVAL) { - continue; - } - lastUpdateTime = Date.now(); - } - lengthDelta = 0; - updateStep += 20; - await onStream(`${contentFull}\n...`); - } - } - } catch (e) { - contentFull += `\nERROR: ${(e as Error).message}`; - } - return contentFull; + return streamHandler(stream, options.contentExtractor!, onStream); } - if (!isJsonResponse(resp)) { throw new Error(resp.statusText); } const result = await resp.json() as any; - if (!result) { throw new Error('Empty response'); } - if (options.errorExtractor?.(result)) { throw new Error(options.errorExtractor?.(result) || 'Unknown error'); } - try { - await onResult?.(result); - return options.fullContentExtractor?.(result) || ''; - } catch (e) { - console.error(e); - throw new Error(JSON.stringify(result)); - } + return options.fullContentExtractor?.(result) || ''; } diff --git a/src/agent/types.ts b/src/agent/types.ts index af17145a..efc8d3a5 100644 --- a/src/agent/types.ts +++ b/src/agent/types.ts @@ -1,46 +1,45 @@ +import type { CoreAssistantMessage, CoreSystemMessage, CoreToolMessage, CoreUserMessage } from 'ai'; import type { AgentUserConfig } from '../config/env'; -export interface HistoryItem { - role: string; - content?: string | null; - images?: string[] | null; -} +export type ImageContent = string | Uint8Array | ArrayBuffer | Buffer | URL; +export type SystemMessageItem = CoreSystemMessage; +export type UserMessageItem = CoreUserMessage; +export type AssistantMessageItem = CoreAssistantMessage; +export type ToolMessageItem = CoreToolMessage; +export type ResponseMessage = AssistantMessageItem | ToolMessageItem; +export type HistoryItem = SystemMessageItem | UserMessageItem | AssistantMessageItem | ToolMessageItem; export interface HistoryModifierResult { history: HistoryItem[]; - message: string | null; + message: UserMessageItem; } -export type HistoryModifier = (history: HistoryItem[], message: string | null) => HistoryModifierResult; - -export type ChatStreamTextHandler = (text: string) => Promise; - -export interface LLMChatRequestParams { - message?: string | null; - images?: string[]; +export interface LLMChatParams { + prompt?: string; + messages: HistoryItem[]; } -export interface LLMChatParams extends LLMChatRequestParams { - prompt?: string | null; - history?: HistoryItem[]; +export interface ChatAgentResponse { + text: string; + responses: ResponseMessage[]; } -export type ChatAgentRequest = (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null) => Promise; - -export interface ChatAgent { - name: string; - modelKey: string; - enable: (context: AgentUserConfig) => boolean; - request: ChatAgentRequest; - model: (ctx: AgentUserConfig) => string; -} +export type ChatStreamTextHandler = (text: string) => Promise; +export type HistoryModifier = (history: HistoryItem[], message: UserMessageItem | null) => HistoryModifierResult; +export type ChatAgentRequest = (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null) => Promise; export type ImageAgentRequest = (prompt: string, context: AgentUserConfig) => Promise; -export interface ImageAgent { +export interface Agent { name: string; modelKey: string; - enable: (context: AgentUserConfig) => boolean; - request: ImageAgentRequest; - model: (ctx: AgentUserConfig) => string; + enable: (ctx: AgentUserConfig) => boolean; + request: AgentRequest; + model: (ctx: AgentUserConfig) => string | null; } + +export interface ChatAgent extends Agent { + modelList: (ctx: AgentUserConfig) => Promise; +} + +export type ImageAgent = Agent; diff --git a/src/agent/utils.ts b/src/agent/utils.ts new file mode 100644 index 00000000..08e6439c --- /dev/null +++ b/src/agent/utils.ts @@ -0,0 +1,70 @@ +import type { ChatAgentResponse, HistoryItem, ImageContent } from './types'; + +export interface ImageRealContent { + url?: string; + base64?: string; +} + +export function extractTextContent(history: HistoryItem): string { + if (typeof history.content === 'string') { + return history.content; + } + if (Array.isArray(history.content)) { + return history.content.map((item) => { + if (item.type === 'text') { + return item.text; + } + return ''; + }).join(''); + } + return ''; +} + +export function extractImageContent(imageData: ImageContent): ImageRealContent { + if (imageData instanceof URL) { + return { url: imageData.href }; + } + // 2. 判断 DataContent 的具体类型 + // 检查是否为字符串(包括 base64) + if (typeof imageData === 'string') { + if (imageData.startsWith('http')) { + return { url: imageData }; + } else { + return { base64: imageData }; + } + } + if (imageData instanceof Uint8Array) { + return { base64: Buffer.from(imageData).toString('base64') }; + } + if (Buffer.isBuffer(imageData)) { + return { base64: Buffer.from(imageData).toString('base64') }; + } + return {}; +} + +export async function convertStringToResponseMessages(input: Promise): Promise { + const text = await input; + return { + text, + responses: [{ role: 'assistant', content: await input }], + }; +} + +export type RemoteParser = (url: string) => Promise; +export async function loadModelsList(raw: string, remoteLoader?: RemoteParser): Promise { + if (!raw) { + return []; + } + if (raw.startsWith('[') && raw.endsWith(']')) { + try { + return JSON.parse(raw); + } catch (e) { + console.error(e); + return []; + } + } + if (raw.startsWith('http') && remoteLoader) { + return await remoteLoader(raw); + } + return []; +} diff --git a/src/agent/workersai.ts b/src/agent/workersai.ts index 07b1522e..f3026837 100644 --- a/src/agent/workersai.ts +++ b/src/agent/workersai.ts @@ -1,7 +1,16 @@ import type { AgentUserConfig } from '../config/env'; import type { SseChatCompatibleOptions } from './request'; -import type { ChatAgent, ChatStreamTextHandler, HistoryItem, ImageAgent, LLMChatParams } from './types'; +import type { + ChatAgent, + ChatAgentResponse, + ChatStreamTextHandler, + HistoryItem, + ImageAgent, + LLMChatParams, +} from './types'; +import { renderOpenAIMessages } from './openai'; import { isJsonResponse, requestChatCompletions } from './request'; +import { convertStringToResponseMessages, loadModelsList } from './utils'; class WorkerBase { readonly name = 'workers'; @@ -24,7 +33,7 @@ class WorkerBase { export class WorkersChat extends WorkerBase implements ChatAgent { readonly modelKey = 'WORKERS_CHAT_MODEL'; - readonly model = (ctx: AgentUserConfig): string => { + readonly model = (ctx: AgentUserConfig): string | null => { return ctx.WORKERS_CHAT_MODEL; }; @@ -35,8 +44,8 @@ export class WorkersChat extends WorkerBase implements ChatAgent { }; }; - readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { - const { message, prompt, history } = params; + readonly request = async (params: LLMChatParams, context: AgentUserConfig, onStream: ChatStreamTextHandler | null): Promise => { + const { prompt, messages } = params; const id = context.CLOUDFLARE_ACCOUNT_ID; const token = context.CLOUDFLARE_TOKEN; const model = context.WORKERS_CHAT_MODEL; @@ -44,14 +53,8 @@ export class WorkersChat extends WorkerBase implements ChatAgent { const header = { Authorization: `Bearer ${token}`, }; - - const messages = [...(history || []), { role: 'user', content: message }]; - if (prompt) { - messages.unshift({ role: context.SYSTEM_INIT_MESSAGE_ROLE, content: prompt }); - } - const body = { - messages: messages.map(this.render), + messages: await renderOpenAIMessages(prompt, messages), stream: onStream !== null, }; @@ -63,9 +66,23 @@ export class WorkersChat extends WorkerBase implements ChatAgent { return data?.result?.response; }; options.errorExtractor = function (data: any) { - return data?.errors?.[0]?.message; + return data?.errors?.at(0)?.message; }; - return requestChatCompletions(url, header, body, onStream, null, options); + return convertStringToResponseMessages(requestChatCompletions(url, header, body, onStream, options)); + }; + + readonly modelList = async (context: AgentUserConfig): Promise => { + if (context.WORKERS_CHAT_MODELS_LIST === '') { + const id = context.CLOUDFLARE_ACCOUNT_ID; + context.WORKERS_CHAT_MODELS_LIST = `https://api.cloudflare.com/client/v4/accounts/${id}/ai/models/search?task=Text%20Generation`; + } + return loadModelsList(context.WORKERS_CHAT_MODELS_LIST, async (url): Promise => { + const header = { + Authorization: `Bearer ${context.CLOUDFLARE_TOKEN}`, + }; + const data = await fetch(url, { headers: header }).then(res => res.json()); + return data.result?.map((model: any) => model.name) || []; + }); }; } @@ -96,11 +113,10 @@ export class WorkersImage extends WorkerBase implements ImageAgent { } async function base64StringToBlob(base64String: string): Promise { - try { - const { Buffer } = await import('node:buffer'); + if (typeof Buffer !== 'undefined') { const buffer = Buffer.from(base64String, 'base64'); return new Blob([buffer], { type: 'image/png' }); - } catch { + } else { const uint8Array = Uint8Array.from(atob(base64String), c => c.charCodeAt(0)); return new Blob([uint8Array], { type: 'image/png' }); } diff --git a/src/config/config.ts b/src/config/config.ts index 041f7d63..7dfc9b04 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -22,6 +22,8 @@ export class EnvironmentConfig { TELEGRAM_PHOTO_SIZE_OFFSET = 1; // 向LLM优先传递图片方式:url, base64 TELEGRAM_IMAGE_TRANSFER_MODE = 'url'; + // 模型列表列数 + MODEL_LIST_COLUMNS = 1; // -- 权限相关 -- // @@ -92,8 +94,8 @@ export class AgentShareConfig { AI_IMAGE_PROVIDER = 'auto'; // 全局默认初始化消息 SYSTEM_INIT_MESSAGE: string | null = null; - // 全局默认初始化消息角色 - SYSTEM_INIT_MESSAGE_ROLE = 'system'; + // DEPRECATED: 全局默认初始化消息角色, 废弃此选项 + // SYSTEM_INIT_MESSAGE_ROLE = 'system'; } // -- Open AI 配置 -- @@ -106,12 +108,14 @@ export class OpenAIConfig { OPENAI_API_BASE = 'https://api.openai.com/v1'; // OpenAI API Extra Params OPENAI_API_EXTRA_PARAMS: Record = {}; + // OpenAI Chat Models List + OPENAI_CHAT_MODELS_LIST = ''; } // -- DALLE 配置 -- -export class DalleAIConfig { +export class DallEConfig { // DALL-E的模型名称 - DALL_E_MODEL = 'dall-e-2'; + DALL_E_MODEL = 'dall-e-3'; // DALL-E图片尺寸 DALL_E_IMAGE_SIZE = '512x512'; // DALL-E图片质量 @@ -124,12 +128,16 @@ export class DalleAIConfig { export class AzureConfig { // Azure API Key AZURE_API_KEY: string | null = null; - // Azure Completions API - // https://RESOURCE_NAME.openai.azure.com/openai/deployments/MODEL_NAME/chat/completions?api-version=VERSION_NAME - AZURE_COMPLETIONS_API: string | null = null; - // Azure DallE API - // https://RESOURCE_NAME.openai.azure.com/openai/deployments/MODEL_NAME/images/generations?api-version=VERSION_NAME - AZURE_DALLE_API: string | null = null; + // Azure Resource Name + AZURE_RESOURCE_NAME: string | null = null; + // Azure Chat Model + AZURE_CHAT_MODEL: string | null = null; + // Azure Image Model + AZURE_IMAGE_MODEL: string | null = null; + // Azure API version + AZURE_API_VERSION = '2024-06-01'; + // Azure Chat Models List + AZURE_CHAT_MODELS_LIST = '[]'; } // -- Workers 配置 -- @@ -142,16 +150,20 @@ export class WorkersConfig { WORKERS_CHAT_MODEL = '@cf/mistral/mistral-7b-instruct-v0.1 '; // Text-to-Image Model WORKERS_IMAGE_MODEL = '@cf/stabilityai/stable-diffusion-xl-base-1.0'; + // Workers Chat Models List, When empty, will use the api to get the list + WORKERS_CHAT_MODELS_LIST = ''; } // -- Gemini 配置 -- export class GeminiConfig { // Google Gemini API Key GOOGLE_API_KEY: string | null = null; - // Google Gemini API: Cloudflare AI gateway: https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_name}/google-ai-studio/v1/models - GOOGLE_COMPLETIONS_API = 'https://generativelanguage.googleapis.com/v1beta/models/'; + // Google Gemini API: https://ai.google.dev/gemini-api/docs/openai#rest + GOOGLE_API_BASE = 'https://generativelanguage.googleapis.com/v1beta'; // Google Gemini Model - GOOGLE_COMPLETIONS_MODEL = 'gemini-pro'; + GOOGLE_COMPLETIONS_MODEL = 'gemini-1.5-flash'; + // Google Chat Models List + GOOGLE_CHAT_MODELS_LIST = `["gemini-1.5-flash"]`; } // -- Mistral 配置 -- @@ -162,6 +174,8 @@ export class MistralConfig { MISTRAL_API_BASE = 'https://api.mistral.ai/v1'; // mistral api model MISTRAL_CHAT_MODEL = 'mistral-tiny'; + // mistral api chat models list + MISTRAL_CHAT_MODELS_LIST = ''; } // -- Cohere 配置 -- @@ -172,6 +186,8 @@ export class CohereConfig { COHERE_API_BASE = 'https://api.cohere.com/v2'; // cohere api model COHERE_CHAT_MODEL = 'command-r-plus'; + // cohere api chat models list + COHERE_CHAT_MODELS_LIST = ''; } // -- Anthropic 配置 -- @@ -181,7 +197,9 @@ export class AnthropicConfig { // Anthropic api base ANTHROPIC_API_BASE = 'https://api.anthropic.com/v1'; // Anthropic api model - ANTHROPIC_CHAT_MODEL = 'claude-3-haiku-20240307'; + ANTHROPIC_CHAT_MODEL = 'claude-3-5-haiku-latest'; + // Anthropic api chat models list + ANTHROPIC_CHAT_MODELS_LIST = `["claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"]`; } export class DefineKeys { diff --git a/src/config/context.ts b/src/config/context.ts index f86aceaf..e47a1edd 100644 --- a/src/config/context.ts +++ b/src/config/context.ts @@ -14,7 +14,7 @@ export class ShareContext { configStoreKey: string; groupAdminsKey?: string; - constructor(token: string, message: Telegram.Message) { + constructor(token: string, update: UpdateContext) { const botId = Number.parseInt(token.split(':')[0]); const telegramIndex = ENV.TELEGRAM_AVAILABLE_TOKENS.indexOf(token); @@ -27,7 +27,7 @@ export class ShareContext { this.botToken = token; this.botId = botId; - const id = message?.chat?.id; + const id = update.chatID; if (id === undefined || id === null) { throw new Error('Chat id not found'); } @@ -49,12 +49,12 @@ export class ShareContext { configStoreKey += `:${botId}`; } // 标记群组消息 - switch (message.chat.type) { + switch (update.chatType) { case 'group': case 'supergroup': - if (!ENV.GROUP_CHAT_BOT_SHARE_MODE && message.from?.id) { - historyKey += `:${message.from.id}`; - configStoreKey += `:${message.from.id}`; + if (!ENV.GROUP_CHAT_BOT_SHARE_MODE && update.fromUserID) { + historyKey += `:${update.fromUserID}`; + configStoreKey += `:${update.fromUserID}`; } this.groupAdminsKey = `group_admin:${id}`; break; @@ -63,10 +63,10 @@ export class ShareContext { } // 判断是否为话题模式 - if (message?.chat.is_forum && message?.is_topic_message) { - if (message?.message_thread_id) { - historyKey += `:${message.message_thread_id}`; - configStoreKey += `:${message.message_thread_id}`; + if (update.isForum && update.isTopicMessage) { + if (update.messageThreadID) { + historyKey += `:${update.messageThreadID}`; + configStoreKey += `:${update.messageThreadID}`; } } @@ -86,8 +86,9 @@ export class WorkerContext { this.SHARE_CONTEXT = SHARE_CONTEXT; } - static async from(token: string, message: Telegram.Message): Promise { - const SHARE_CONTEXT = new ShareContext(token, message); + static async from(token: string, update: Telegram.Update): Promise { + const context = new UpdateContext(update); + const SHARE_CONTEXT = new ShareContext(token, context); const USER_CONFIG = Object.assign({}, ENV.USER_CONFIG); try { const userConfig: AgentUserConfig = JSON.parse(await ENV.DATABASE.get(SHARE_CONTEXT.configStoreKey)); @@ -98,3 +99,33 @@ export class WorkerContext { return new WorkerContext(USER_CONFIG, SHARE_CONTEXT); } } + +class UpdateContext { + fromUserID?: number; + chatID?: number; + chatType?: string; + + isForum?: boolean; + isTopicMessage?: boolean; + messageThreadID?: number; + + constructor(update: Telegram.Update) { + if (update.message) { + this.fromUserID = update.message.from?.id; + this.chatID = update.message.chat.id; + this.chatType = update.message.chat.type; + this.isForum = update.message.chat.is_forum; + this.isTopicMessage = update.message.is_topic_message; + this.messageThreadID = update.message.message_thread_id; + } else if (update.callback_query) { + this.fromUserID = update.callback_query.from.id; + this.chatID = update.callback_query.message?.chat.id; + this.chatType = update.callback_query.message?.chat.type; + this.isForum = update.callback_query.message?.chat.is_forum; + // this.isTopicMessage = update.callback_query.message?.is_topic_message; // unsupported + // this.messageThreadID = update.callback_query.message?.message_thread_id; // unsupported + } else { + console.error('Unknown update type'); + } + } +} diff --git a/src/config/env.ts b/src/config/env.ts index 0f3b2a56..f31cb8fa 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -5,7 +5,7 @@ import { AnthropicConfig, AzureConfig, CohereConfig, - DalleAIConfig, + DallEConfig, DefineKeys, EnvironmentConfig, GeminiConfig, @@ -14,12 +14,13 @@ import { WorkersConfig, } from './config'; import { ConfigMerger } from './merger'; +import { BUILD_TIMESTAMP, BUILD_VERSION } from './version'; export type AgentUserConfig = Record & DefineKeys & AgentShareConfig & OpenAIConfig & - DalleAIConfig & + DallEConfig & AzureConfig & WorkersConfig & GeminiConfig & @@ -33,7 +34,7 @@ function createAgentUserConfig(): AgentUserConfig { new DefineKeys(), new AgentShareConfig(), new OpenAIConfig(), - new DalleAIConfig(), + new DallEConfig(), new AzureConfig(), new WorkersConfig(), new GeminiConfig(), @@ -53,13 +54,9 @@ class Environment extends EnvironmentConfig { // -- 版本数据 -- // // 当前版本 - // eslint-disable-next-line ts/ban-ts-comment - // @ts-expect-error - BUILD_TIMESTAMP = typeof __BUILD_TIMESTAMP__ === 'number' ? __BUILD_TIMESTAMP__ : 0; + BUILD_TIMESTAMP = BUILD_TIMESTAMP; // 当前版本 commit id - // eslint-disable-next-line ts/ban-ts-comment - // @ts-expect-error - BUILD_VERSION = typeof __BUILD_VERSION__ === 'string' ? __BUILD_VERSION__ : 'unknown'; + BUILD_VERSION = BUILD_VERSION; // -- 基础配置 -- I18N = loadI18n(); @@ -172,6 +169,29 @@ class Environment extends EnvironmentConfig { if (!this.USER_CONFIG.SYSTEM_INIT_MESSAGE) { this.USER_CONFIG.SYSTEM_INIT_MESSAGE = this.I18N?.env?.system_init_message || 'You are a helpful assistant'; } + // 兼容旧版 GOOGLE_COMPLETIONS_API + if (source.GOOGLE_COMPLETIONS_API && !this.USER_CONFIG.GOOGLE_API_BASE) { + this.USER_CONFIG.GOOGLE_API_BASE = source.GOOGLE_COMPLETIONS_API.replace(/\/models\/?$/, ''); + } + + if (source.GOOGLE_COMPLETIONS_MODEL && !this.USER_CONFIG.GOOGLE_CHAT_MODEL) { + this.USER_CONFIG.GOOGLE_CHAT_MODEL = source.GOOGLE_COMPLETIONS_MODEL; + } + + // 兼容旧版 AZURE_COMPLETIONS_API + if (source.AZURE_COMPLETIONS_API && !this.USER_CONFIG.AZURE_CHAT_MODEL) { + const url = new URL(source.AZURE_COMPLETIONS_API); + this.USER_CONFIG.AZURE_RESOURCE_NAME = url.hostname.split('.').at(0) || null; + this.USER_CONFIG.AZURE_CHAT_MODEL = url.pathname.split('/').at(3) || null; + this.USER_CONFIG.AZURE_API_VERSION = url.searchParams.get('api-version') || '2024-06-01'; + } + // 兼容旧版 AZURE_DALLE_API + if (source.AZURE_DALLE_API && !this.USER_CONFIG.AZURE_IMAGE_MODEL) { + const url = new URL(source.AZURE_DALLE_API); + this.USER_CONFIG.AZURE_RESOURCE_NAME = url.hostname.split('.').at(0) || null; + this.USER_CONFIG.AZURE_IMAGE_MODEL = url.pathname.split('/').at(3) || null; + this.USER_CONFIG.AZURE_API_VERSION = url.searchParams.get('api-version') || '2024-06-01'; + } } } diff --git a/src/config/version.ts b/src/config/version.ts new file mode 100644 index 00000000..969fe7cf --- /dev/null +++ b/src/config/version.ts @@ -0,0 +1,2 @@ +export const BUILD_TIMESTAMP = 1731468421; +export const BUILD_VERSION = '9e1cda2'; diff --git a/src/index.ts b/src/entry/core/index.ts similarity index 83% rename from src/index.ts rename to src/entry/core/index.ts index 59fd8b33..0fd18cc7 100644 --- a/src/index.ts +++ b/src/entry/core/index.ts @@ -1,5 +1,5 @@ -import { ENV } from './config/env'; -import { createRouter } from './route'; +import { ENV } from '../../config/env'; +import { createRouter } from '../../route'; export default { async fetch(request: Request, env: any): Promise { diff --git a/src/adapter/local/index.ts b/src/entry/local/index.ts similarity index 90% rename from src/adapter/local/index.ts rename to src/entry/local/index.ts index c55974b1..c953fb85 100644 --- a/src/adapter/local/index.ts +++ b/src/entry/local/index.ts @@ -4,6 +4,8 @@ import * as fs from 'node:fs'; import { createCache } from 'cloudflare-worker-adapter/cache'; import { installFetchProxy } from 'cloudflare-worker-adapter/proxy'; import { defaultRequestBuilder, initEnv, startServerV2 } from 'cloudflare-worker-adapter/serve'; +import { CHAT_AGENTS } from '../../agent'; +import { injectNextChatAgent } from '../../agent/next/next'; import { ENV } from '../../config/env'; import { createRouter } from '../../route'; import { createTelegramBotAPI } from '../../telegram/api'; @@ -11,7 +13,8 @@ import { handleUpdate } from '../../telegram/handler'; const { CONFIG_PATH = '/app/config.json', - TOML_PATH = '/app/config.toml', + TOML_PATH = '/app/wrangler.toml', + NEXT_ENABLE = '0', } = process.env; interface Config { @@ -58,7 +61,9 @@ async function runPolling() { console.log(`@${name.result.username} Webhook deleted, If you want to use webhook, please set it up again.`); } - while (true) { + const keepRunning = true; + // eslint-disable-next-line no-unmodified-loop-condition + while (keepRunning) { for (const token of ENV.TELEGRAM_AVAILABLE_TOKENS) { try { const resp = await clients[token].getUpdates({ offset: offset[token] }); @@ -86,6 +91,10 @@ async function runPolling() { } } +if (NEXT_ENABLE) { + injectNextChatAgent(CHAT_AGENTS); +} + // 启动服务 if (config.mode === 'webhook' && config.server !== undefined) { const router = createRouter(); diff --git a/src/entry/next/README.md b/src/entry/next/README.md new file mode 100644 index 00000000..4e8aa85e --- /dev/null +++ b/src/entry/next/README.md @@ -0,0 +1,19 @@ +# ChatGPT-Telegram-Workers-Next + + +## 中文 + +ChatGPT-Telegram-Workers-Next 是一个实验性质的版本, 使用 `https://github.com/vercel/ai` 驱动。比起原始版本支持更多特性。 + +此版本暂不提供`dist`文件,如果你想部署此版本,需要将 `wrangler.toml` 中的 `main` 修改为 `./src/entry/next/index.ts` 并添加nodejs支持`compatibility_flags = [ "nodejs_compat" ]` + +然后使用`wrangler deploy`进行部署。 + + +## English + +ChatGPT-Telegram-Workers-Next is an experimental version, driven by `https://github.com/vercel/ai`. It supports more features than the original version. + +This version does not provide the `dist` files. If you want to deploy this version, you need to modify `main` in `wrangler.toml` to `./src/entry/next/index.ts` and add nodejs support `compatibility_flags = [ "nodejs_compat" ]`. + +Then use `wrangler deploy` to deploy. \ No newline at end of file diff --git a/src/entry/next/index.ts b/src/entry/next/index.ts new file mode 100644 index 00000000..c72b8342 --- /dev/null +++ b/src/entry/next/index.ts @@ -0,0 +1,5 @@ +import { CHAT_AGENTS } from '../../agent'; +import { injectNextChatAgent } from '../../agent/next/next'; + +injectNextChatAgent(CHAT_AGENTS); +export * from '../core/index'; diff --git a/src/adapter/vercel/index.ts b/src/entry/vercel/index.ts similarity index 93% rename from src/adapter/vercel/index.ts rename to src/entry/vercel/index.ts index bbd78bd8..00b8fcf8 100644 --- a/src/adapter/vercel/index.ts +++ b/src/entry/vercel/index.ts @@ -20,7 +20,6 @@ export default async function (request: VercelRequest, response: VercelResponse) return; } const cache = UpStashRedis.create(UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN); - // edge function 使用 redis 作为数据库容易出现连接数过多的问题,此处仅作为演示,请自行实现 `Cache` 接口 ENV.merge({ ...process.env, DATABASE: cache, diff --git a/src/i18n/en.ts b/src/i18n/en.ts index e97a7fea..9abea34c 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -1,2 +1,2 @@ /* eslint-disable */ -export default {"env":{"system_init_message":"You are a helpful assistant"},"command":{"help":{"summary":"The following commands are currently supported:\n","help":"Get command help","new":"Start a new conversation","start":"Get your ID and start a new conversation","img":"Generate an image, the complete command format is `/img image description`, for example `/img beach at moonlight`","version":"Get the current version number to determine whether to update","setenv":"Set user configuration, the complete command format is /setenv KEY=VALUE","setenvs":"Batch set user configurations, the full format of the command is /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"Delete user configuration, the complete command format is /delenv KEY","clearenv":"Clear all user configuration","system":"View some system information","redo":"Redo the last conversation, /redo with modified content or directly /redo","echo":"Echo the message"},"new":{"new_chat_start":"A new conversation has started"}}} +export default {"env":{"system_init_message":"You are a helpful assistant"},"command":{"help":{"summary":"The following commands are currently supported:\n","help":"Get command help","new":"Start a new conversation","start":"Get your ID and start a new conversation","img":"Generate an image, the complete command format is `/img image description`, for example `/img beach at moonlight`","version":"Get the current version number to determine whether to update","setenv":"Set user configuration, the complete command format is /setenv KEY=VALUE","setenvs":"Batch set user configurations, the full format of the command is /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"Delete user configuration, the complete command format is /delenv KEY","clearenv":"Clear all user configuration","system":"View some system information","redo":"Redo the last conversation, /redo with modified content or directly /redo","echo":"Echo the message","models":"switch chat model"},"new":{"new_chat_start":"A new conversation has started"}},"callback_query":{"open_model_list":"Open models list","select_provider":"Select a provider:","select_model":"Choose model:","change_model":"Change model to "}} \ No newline at end of file diff --git a/src/i18n/pt.ts b/src/i18n/pt.ts index 1f7e2404..76ef8521 100644 --- a/src/i18n/pt.ts +++ b/src/i18n/pt.ts @@ -1,2 +1,2 @@ /* eslint-disable */ -export default {"env":{"system_init_message":"Você é um assistente útil"},"command":{"help":{"summary":"Os seguintes comandos são suportados atualmente:\n","help":"Obter ajuda sobre comandos","new":"Iniciar uma nova conversa","start":"Obter seu ID e iniciar uma nova conversa","img":"Gerar uma imagem, o formato completo do comando é `/img descrição da imagem`, por exemplo `/img praia ao luar`","version":"Obter o número da versão atual para determinar se é necessário atualizar","setenv":"Definir configuração do usuário, o formato completo do comando é /setenv CHAVE=VALOR","setenvs":"Definir configurações do usuário em lote, o formato completo do comando é /setenvs {\"CHAVE1\": \"VALOR1\", \"CHAVE2\": \"VALOR2\"}","delenv":"Excluir configuração do usuário, o formato completo do comando é /delenv CHAVE","clearenv":"Limpar todas as configurações do usuário","system":"Ver algumas informações do sistema","redo":"Refazer a última conversa, /redo com conteúdo modificado ou diretamente /redo","echo":"Repetir a mensagem"},"new":{"new_chat_start":"Uma nova conversa foi iniciada"}}} +export default {"env":{"system_init_message":"Você é um assistente útil"},"command":{"help":{"summary":"Os seguintes comandos são suportados atualmente:\n","help":"Obter ajuda sobre comandos","new":"Iniciar uma nova conversa","start":"Obter seu ID e iniciar uma nova conversa","img":"Gerar uma imagem, o formato completo do comando é `/img descrição da imagem`, por exemplo `/img praia ao luar`","version":"Obter o número da versão atual para determinar se é necessário atualizar","setenv":"Definir configuração do usuário, o formato completo do comando é /setenv CHAVE=VALOR","setenvs":"Definir configurações do usuário em lote, o formato completo do comando é /setenvs {\"CHAVE1\": \"VALOR1\", \"CHAVE2\": \"VALOR2\"}","delenv":"Excluir configuração do usuário, o formato completo do comando é /delenv CHAVE","clearenv":"Limpar todas as configurações do usuário","system":"Ver algumas informações do sistema","redo":"Refazer a última conversa, /redo com conteúdo modificado ou diretamente /redo","echo":"Repetir a mensagem","models":"Mudar o modelo de diálogo"},"new":{"new_chat_start":"Uma nova conversa foi iniciada"}},"callback_query":{"open_model_list":"Abra a lista de modelos","select_provider":"Escolha um fornecedor de modelos.:","select_model":"Escolha um modelo:","change_model":"O modelo de diálogo já foi modificado para"}} \ No newline at end of file diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 0dbc6c2c..0d5097c5 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -10,6 +10,7 @@ interface HelpI18n { delenv: string; system: string; redo: string; + models: string; echo: string; } @@ -23,4 +24,10 @@ export interface I18n { new_chat_start: string; }; }; + callback_query: { + open_model_list: string; + select_provider: string; + select_model: string; + change_model: string; + }; } diff --git a/src/i18n/zh-hans.ts b/src/i18n/zh-hans.ts index 1060586c..ae83c9e2 100644 --- a/src/i18n/zh-hans.ts +++ b/src/i18n/zh-hans.ts @@ -1,2 +1,2 @@ /* eslint-disable */ -export default {"env":{"system_init_message":"你是一个得力的助手"},"command":{"help":{"summary":"当前支持以下命令:\n","help":"获取命令帮助","new":"发起新的对话","start":"获取你的ID, 并发起新的对话","img":"生成一张图片, 命令完整格式为 `/img 图片描述`, 例如`/img 月光下的沙滩`","version":"获取当前版本号, 判断是否需要更新","setenv":"设置用户配置,命令完整格式为 /setenv KEY=VALUE","setenvs":"批量设置用户配置, 命令完整格式为 /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"删除用户配置,命令完整格式为 /delenv KEY","clearenv":"清除所有用户配置","system":"查看当前一些系统信息","redo":"重做上一次的对话, /redo 加修改过的内容 或者 直接 /redo","echo":"回显消息"},"new":{"new_chat_start":"新的对话已经开始"}}} +export default {"env":{"system_init_message":"你是一个得力的助手"},"command":{"help":{"summary":"当前支持以下命令:\n","help":"获取命令帮助","new":"发起新的对话","start":"获取你的ID, 并发起新的对话","img":"生成一张图片, 命令完整格式为 `/img 图片描述`, 例如`/img 月光下的沙滩`","version":"获取当前版本号, 判断是否需要更新","setenv":"设置用户配置,命令完整格式为 /setenv KEY=VALUE","setenvs":"批量设置用户配置, 命令完整格式为 /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"删除用户配置,命令完整格式为 /delenv KEY","clearenv":"清除所有用户配置","system":"查看当前一些系统信息","redo":"重做上一次的对话, /redo 加修改过的内容 或者 直接 /redo","echo":"回显消息","models":"切换对话模型"},"new":{"new_chat_start":"新的对话已经开始"}},"callback_query":{"open_model_list":"打开模型列表","select_provider":"选择一个模型提供商:","select_model":"选择一个模型:","change_model":"对话模型已修改至"}} \ No newline at end of file diff --git a/src/i18n/zh-hant.ts b/src/i18n/zh-hant.ts index 1529804b..b48c83a0 100644 --- a/src/i18n/zh-hant.ts +++ b/src/i18n/zh-hant.ts @@ -1,2 +1,2 @@ /* eslint-disable */ -export default {"env":{"system_init_message":"你是一個得力的助手"},"command":{"help":{"summary":"當前支持的命令如下:\n","help":"獲取命令幫助","new":"開始一個新對話","start":"獲取您的ID並開始一個新對話","img":"生成圖片,完整命令格式為`/img 圖片描述`,例如`/img 海灘月光`","version":"獲取當前版本號確認是否需要更新","setenv":"設置用戶配置,完整命令格式為/setenv KEY=VALUE","setenvs":"批量設置用户配置, 命令完整格式為 /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"刪除用戶配置,完整命令格式為/delenv KEY","clearenv":"清除所有用戶配置","system":"查看一些系統信息","redo":"重做上一次的對話 /redo 加修改過的內容 或者 直接 /redo","echo":"回显消息"},"new":{"new_chat_start":"開始一個新對話"}}} +export default {"env":{"system_init_message":"你是一個得力的助手"},"command":{"help":{"summary":"當前支持的命令如下:\n","help":"獲取命令幫助","new":"開始一個新對話","start":"獲取您的ID並開始一個新對話","img":"生成圖片,完整命令格式為`/img 圖片描述`,例如`/img 海灘月光`","version":"獲取當前版本號確認是否需要更新","setenv":"設置用戶配置,完整命令格式為/setenv KEY=VALUE","setenvs":"批量設置用户配置, 命令完整格式為 /setenvs {\"KEY1\": \"VALUE1\", \"KEY2\": \"VALUE2\"}","delenv":"刪除用戶配置,完整命令格式為/delenv KEY","clearenv":"清除所有用戶配置","system":"查看一些系統信息","redo":"重做上一次的對話 /redo 加修改過的內容 或者 直接 /redo","echo":"回显消息","models":"切換對話模式"},"new":{"new_chat_start":"開始一個新對話"}},"callback_query":{"open_model_list":"打開模型清單","select_provider":"選擇一個模型供應商:","select_model":"選擇一個模型:","change_model":"對話模型已經修改至"}} \ No newline at end of file diff --git a/src/telegram/auth/auth.ts b/src/telegram/auth/auth.ts new file mode 100644 index 00000000..3c61bbcf --- /dev/null +++ b/src/telegram/auth/auth.ts @@ -0,0 +1,21 @@ +import { ENV } from '../../config/env'; +import { isTelegramChatTypeGroup } from '../utils/utils'; + +export const TELEGRAM_AUTH_CHECKER = { + default(chatType: string): string[] | null { + if (isTelegramChatTypeGroup(chatType)) { + return ['administrator', 'creator']; + } + return null; + }, + shareModeGroup(chatType: string): string[] | null { + if (isTelegramChatTypeGroup(chatType)) { + // 每个人在群里有上下文的时候,不限制 + if (!ENV.GROUP_CHAT_BOT_SHARE_MODE) { + return null; + } + return ['administrator', 'creator']; + } + return null; + }, +}; diff --git a/src/telegram/callback_query/index.ts b/src/telegram/callback_query/index.ts new file mode 100644 index 00000000..2b7a8eb7 --- /dev/null +++ b/src/telegram/callback_query/index.ts @@ -0,0 +1,53 @@ +import type * as Telegram from 'telegram-bot-api-types'; +import type { WorkerContext } from '../../config/context'; +import { loadChatRoleWithContext } from '../command/auth'; +import { MessageSender } from '../utils/send'; +import { AgentListCallbackQueryHandler, ModelChangeCallbackQueryHandler, ModelListCallbackQueryHandler } from './system'; + +const QUERY_HANDLERS = [ + new AgentListCallbackQueryHandler(), + new ModelListCallbackQueryHandler(), + new ModelChangeCallbackQueryHandler(), +]; + +export async function handleCallbackQuery(callbackQuery: Telegram.CallbackQuery, context: WorkerContext): Promise { + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, callbackQuery); + const answerCallbackQuery = (msg: string): Promise => { + return sender.api.answerCallbackQuery({ + callback_query_id: callbackQuery.id, + text: msg, + }); + }; + try { + if (!callbackQuery.message) { + return null; + } + const chatId = callbackQuery.message.chat.id; + const speakerId = callbackQuery.from?.id || chatId; + const chatType = callbackQuery.message.chat.type; + for (const handler of QUERY_HANDLERS) { + // 如果存在权限条件 + if (handler.needAuth) { + const roleList = handler.needAuth(chatType); + if (roleList) { + // 获取身份并判断 + const chatRole = await loadChatRoleWithContext(chatId, speakerId, context); + if (chatRole === null) { + return answerCallbackQuery('ERROR: Get chat role failed'); + } + if (!roleList.includes(chatRole)) { + return answerCallbackQuery(`ERROR: Permission denied, need ${roleList.join(' or ')}`); + } + } + } + if (callbackQuery.data) { + if (callbackQuery.data.startsWith(handler.prefix)) { + return handler.handle(callbackQuery, callbackQuery.data, context); + } + } + } + } catch (e) { + return answerCallbackQuery(`ERROR: ${(e as Error).message}`); + } + return null; +} diff --git a/src/telegram/callback_query/system.ts b/src/telegram/callback_query/system.ts new file mode 100644 index 00000000..3f9ffb53 --- /dev/null +++ b/src/telegram/callback_query/system.ts @@ -0,0 +1,159 @@ +import type * as Telegram from 'telegram-bot-api-types'; +import type { WorkerContext } from '../../config/context'; +import type { AgentUserConfig } from '../../config/env'; +import type { CallbackQueryHandler } from './types'; +import { CHAT_AGENTS, loadChatLLM } from '../../agent'; +import { ENV } from '../../config/env'; +import { TELEGRAM_AUTH_CHECKER } from '../auth/auth'; +import { MessageSender } from '../utils/send'; +import { setUserConfig } from '../utils/utils'; + +export class AgentListCallbackQueryHandler implements CallbackQueryHandler { + prefix = 'al:'; + + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + + handle = async (query: Telegram.CallbackQuery, data: string, context: WorkerContext): Promise => { + if (!query.message) { + throw new Error('no message'); + } + const names = CHAT_AGENTS.filter(agent => agent.enable(ENV.USER_CONFIG)).map(agent => agent.name); + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const keyboards: Telegram.InlineKeyboardButton[][] = []; + for (let i = 0; i < names.length; i += 2) { + const row: Telegram.InlineKeyboardButton[] = []; + for (let j = 0; j < 2; j++) { + const index = i + j; + if (index >= names.length) { + break; + } + row.push({ + text: names[index], + callback_data: `ca:${JSON.stringify([names[index], 0])}`, + }); + } + keyboards.push(row); + } + const params: Telegram.EditMessageTextParams = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: ENV.I18N.callback_query.select_provider, + reply_markup: { + inline_keyboard: keyboards, + }, + }; + return sender.editRawMessage(params); + }; +} + +export class ModelListCallbackQueryHandler implements CallbackQueryHandler { + prefix = 'ca:'; // ca:model:page + + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + + async handle(query: Telegram.CallbackQuery, data: string, context: WorkerContext): Promise { + if (!query.message) { + throw new Error('no message'); + } + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const [agent, page] = JSON.parse(data.substring(this.prefix.length)); + const conf: AgentUserConfig = { + ...ENV.USER_CONFIG, + AI_PROVIDER: agent, + }; + const chatAgent = loadChatLLM(conf); + if (!chatAgent) { + throw new Error(`agent not found: ${agent}`); + } + const models = await chatAgent.modelList(conf); + const keyboard: Telegram.InlineKeyboardButton[][] = []; + const maxRow = 10; + const maxCol = Math.max(1, Math.min(5, ENV.MODEL_LIST_COLUMNS)); + const maxPage = Math.ceil(models.length / maxRow / maxCol); + + let currentRow: Telegram.InlineKeyboardButton[] = []; + for (let i = page * maxRow * maxCol; i < models.length; i++) { + if (i % maxCol === 0) { + keyboard.push(currentRow); + currentRow = []; + } + if (keyboard.length >= maxRow) { + break; + } + currentRow.push({ + text: models[i], + callback_data: `cm:${JSON.stringify([agent, models[i]])}`, + }); + } + if (currentRow.length > 0) { + keyboard.push(currentRow); + currentRow = []; + } + keyboard.push([ + { + text: '<', + callback_data: `ca:${JSON.stringify([agent, Math.max(page - 1, 0)])}`, + }, + { + text: `${page + 1}/${maxPage}`, + callback_data: `ca:${JSON.stringify([agent, page])}`, + }, + { + text: '>', + callback_data: `ca:${JSON.stringify([agent, Math.min(page + 1, maxPage - 1)])}`, + }, + { + text: '⇤', + callback_data: `al:`, + }, + ]); + if (models.length > (page + 1) * maxRow * maxCol) { + currentRow.push(); + } + keyboard.push(currentRow); + const message: Telegram.EditMessageTextParams = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: `${agent} ${ENV.I18N.callback_query.select_model}`, + reply_markup: { + inline_keyboard: keyboard, + }, + }; + return sender.editRawMessage(message); + } +} + +export class ModelChangeCallbackQueryHandler implements CallbackQueryHandler { + prefix = 'cm:'; + + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; + + async handle(query: Telegram.CallbackQuery, data: string, context: WorkerContext): Promise { + if (!query.message) { + throw new Error('no message'); + } + const sender = MessageSender.fromCallbackQuery(context.SHARE_CONTEXT.botToken, query); + const [agent, model] = JSON.parse(data.substring(this.prefix.length)); + const conf: AgentUserConfig = { + ...ENV.USER_CONFIG, + AI_PROVIDER: agent, + }; + const chatAgent = loadChatLLM(conf); + if (!agent) { + throw new Error(`agent not found: ${agent}`); + } + if (!chatAgent?.modelKey) { + throw new Error(`modelKey not found: ${agent}`); + } + await setUserConfig({ + AI_PROVIDER: agent, + [chatAgent.modelKey]: model, + }, context); + const message: Telegram.EditMessageTextParams = { + chat_id: query.message.chat.id, + message_id: query.message.message_id, + text: `${ENV.I18N.callback_query.change_model} ${agent} > ${model}`, + }; + return sender.editRawMessage(message); + } +} diff --git a/src/telegram/callback_query/types.ts b/src/telegram/callback_query/types.ts new file mode 100644 index 00000000..d0d88dc0 --- /dev/null +++ b/src/telegram/callback_query/types.ts @@ -0,0 +1,8 @@ +import type * as Telegram from 'telegram-bot-api-types'; +import type { WorkerContext } from '../../config/context'; + +export interface CallbackQueryHandler { + prefix: string; + handle: (query: Telegram.CallbackQuery, data: string, context: WorkerContext) => Promise; + needAuth?: (chatType: string) => string[] | null; +} diff --git a/src/telegram/command/auth.ts b/src/telegram/command/auth.ts index c26540ad..e51ebc58 100644 --- a/src/telegram/command/auth.ts +++ b/src/telegram/command/auth.ts @@ -3,12 +3,8 @@ import type { WorkerContext } from '../../config/context'; import { ENV } from '../../config/env'; import { createTelegramBotAPI } from '../api'; -export async function loadChatRoleWithContext(message: Telegram.Message, context: WorkerContext): Promise { +export async function loadChatRoleWithContext(chatId: number, speakerId: number, context: WorkerContext): Promise { const { groupAdminsKey } = context.SHARE_CONTEXT; - - const chatId = message.chat.id; - const speakerId = message.from?.id || chatId; - if (!groupAdminsKey) { return null; } diff --git a/src/telegram/command/index.ts b/src/telegram/command/index.ts index 06448edc..37da45f6 100644 --- a/src/telegram/command/index.ts +++ b/src/telegram/command/index.ts @@ -12,6 +12,7 @@ import { EchoCommandHandler, HelpCommandHandler, ImgCommandHandler, + ModelsCommandHandler, NewCommandHandler, RedoCommandHandler, SetEnvCommandHandler, @@ -32,18 +33,22 @@ const SYSTEM_COMMANDS: CommandHandler[] = [ new ClearEnvCommandHandler(), new VersionCommandHandler(), new SystemCommandHandler(), + new ModelsCommandHandler(), new HelpCommandHandler(), ]; async function handleSystemCommand(message: Telegram.Message, raw: string, command: CommandHandler, context: WorkerContext): Promise { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { + const chatId = message.chat.id; + const speakerId = message.from?.id || chatId; + const chatType = message.chat.type; // 如果存在权限条件 if (command.needAuth) { - const roleList = command.needAuth(message.chat.type); + const roleList = command.needAuth(chatType); if (roleList) { // 获取身份并判断 - const chatRole = await loadChatRoleWithContext(message, context); + const chatRole = await loadChatRoleWithContext(chatId, speakerId, context); if (chatRole === null) { return sender.sendPlainText('ERROR: Get chat role failed'); } @@ -64,7 +69,7 @@ async function handleSystemCommand(message: Telegram.Message, raw: string, comma } async function handlePluginCommand(message: Telegram.Message, command: string, raw: string, template: RequestTemplate, context: WorkerContext): Promise { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { const subcommand = raw.substring(command.length).trim(); if (template.input?.required && !subcommand) { @@ -141,10 +146,13 @@ export function commandsBindScope(): Record => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); if (subcommand === '') { return sender.sendPlainText(ENV.I18N.command.help.img); } @@ -63,7 +45,7 @@ export class HelpCommandHandler implements CommandHandler { command = '/help'; scopes = ['all_private_chats', 'all_chat_administrators']; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); let helpMsg = `${ENV.I18N.command.help.summary}\n`; for (const [k, v] of Object.entries(ENV.I18N.command.help)) { if (k === 'summary') { @@ -127,33 +109,17 @@ export class StartCommandHandler extends BaseNewCommandHandler implements Comman export class SetEnvCommandHandler implements CommandHandler { command = '/setenv'; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const kv = subcommand.indexOf('='); if (kv === -1) { return sender.sendPlainText(ENV.I18N.command.help.setenv); } - let key = subcommand.slice(0, kv); + const key = subcommand.slice(0, kv); const value = subcommand.slice(kv + 1); - key = ENV_KEY_MAPPER[key] || key; - if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { - return sender.sendPlainText(`Key ${key} is locked`); - } - if (!Object.keys(context.USER_CONFIG).includes(key)) { - return sender.sendPlainText(`Key ${key} not found`); - } try { - context.USER_CONFIG.DEFINE_KEYS.push(key); - context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); - ConfigMerger.merge(context.USER_CONFIG, { - [key]: value, - }); - console.log('Update user config: ', key, context.USER_CONFIG[key]); - await ENV.DATABASE.put( - context.SHARE_CONTEXT.configStoreKey, - JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)), - ); + await setUserConfig({ [key]: value }, context); return sender.sendPlainText('Update user config success'); } catch (e) { return sender.sendPlainText(`ERROR: ${(e as Error).message}`); @@ -163,32 +129,12 @@ export class SetEnvCommandHandler implements CommandHandler { export class SetEnvsCommandHandler implements CommandHandler { command = '/setenvs'; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { const values = JSON.parse(subcommand); - const configKeys = Object.keys(context.USER_CONFIG); - for (const ent of Object.entries(values)) { - let [key, value] = ent; - key = ENV_KEY_MAPPER[key] || key; - if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { - return sender.sendPlainText(`Key ${key} is locked`); - } - if (!configKeys.includes(key)) { - return sender.sendPlainText(`Key ${key} not found`); - } - context.USER_CONFIG.DEFINE_KEYS.push(key); - ConfigMerger.merge(context.USER_CONFIG, { - [key]: value, - }); - console.log('Update user config: ', key, context.USER_CONFIG[key]); - } - context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); - await ENV.DATABASE.put( - context.SHARE_CONTEXT.configStoreKey, - JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)), - ); + await setUserConfig(values, context); return sender.sendPlainText('Update user config success'); } catch (e) { return sender.sendPlainText(`ERROR: ${(e as Error).message}`); @@ -198,9 +144,9 @@ export class SetEnvsCommandHandler implements CommandHandler { export class DelEnvCommandHandler implements CommandHandler { command = '/delenv'; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); if (ENV.LOCK_USER_CONFIG_KEYS.includes(subcommand)) { const msg = `Key ${subcommand} is locked`; return sender.sendPlainText(msg); @@ -221,9 +167,9 @@ export class DelEnvCommandHandler implements CommandHandler { export class ClearEnvCommandHandler implements CommandHandler { command = '/clearenv'; - needAuth = COMMAND_AUTH_CHECKER.shareModeGroup; + needAuth = TELEGRAM_AUTH_CHECKER.shareModeGroup; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { await ENV.DATABASE.put( context.SHARE_CONTEXT.configStoreKey, @@ -241,7 +187,7 @@ export class VersionCommandHandler implements CommandHandler { command = '/version'; scopes = ['all_private_chats', 'all_chat_administrators']; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const current = { ts: ENV.BUILD_TIMESTAMP, sha: ENV.BUILD_VERSION, @@ -269,7 +215,7 @@ export class SystemCommandHandler implements CommandHandler { command = '/system'; scopes = ['all_private_chats', 'all_chat_administrators']; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); const chatAgent = loadChatLLM(context.USER_CONFIG); const imageAgent = loadImageGen(context.USER_CONFIG); const agent = { @@ -307,8 +253,8 @@ export class RedoCommandHandler implements CommandHandler { command = '/redo'; scopes = ['all_private_chats', 'all_group_chats', 'all_chat_administrators']; handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { - const mf = (history: HistoryItem[], text: string | null): HistoryModifierResult => { - let nextText = text; + const mf = (history: HistoryItem[], message: UserMessageItem | null): HistoryModifierResult => { + let nextMessage = message; if (!(history && Array.isArray(history) && history.length > 0)) { throw new Error('History not found'); } @@ -318,18 +264,44 @@ export class RedoCommandHandler implements CommandHandler { if (data === undefined || data === null) { break; } else if (data.role === 'user') { - if (text === '' || text === undefined || text === null) { - nextText = data.content || null; - } + nextMessage = data; break; } } if (subcommand) { - nextText = subcommand; + nextMessage = { + role: 'user', + content: subcommand, + }; + } + if (nextMessage === null) { + throw new Error('Redo message not found'); } - return { history: historyCopy, message: nextText }; + return { history: historyCopy, message: nextMessage }; + }; + return chatWithLLM(message, null, context, mf); + }; +} + +export class ModelsCommandHandler implements CommandHandler { + command = '/models'; + scopes = ['all_private_chats', 'all_group_chats', 'all_chat_administrators']; + handle = async (message: Telegram.Message, subcommand: string, context: WorkerContext): Promise => { + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); + const chatAgent = loadChatLLM(context.USER_CONFIG); + const params: Telegram.SendMessageParams = { + chat_id: message.chat.id, + text: `${chatAgent?.name || 'Nan'} | ${chatAgent?.model(context.USER_CONFIG) || 'Nan'}`, + reply_markup: { + inline_keyboard: [[ + { + text: ENV.I18N.callback_query.open_model_list, + callback_data: 'al:', + }, + ]], + }, }; - return chatWithLLM(message, { message: null }, context, mf); + return sender.sendRawMessage(params); }; } @@ -339,6 +311,6 @@ export class EchoCommandHandler implements CommandHandler { let msg = '
';
         msg += JSON.stringify({ message }, null, 2);
         msg += '
'; - return MessageSender.from(context.SHARE_CONTEXT.botToken, message).sendRichText(msg, 'HTML'); + return MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message).sendRichText(msg, 'HTML'); }; } diff --git a/src/telegram/handler/chat.ts b/src/telegram/handler/chat.ts index 414c61ff..45090961 100644 --- a/src/telegram/handler/chat.ts +++ b/src/telegram/handler/chat.ts @@ -1,6 +1,6 @@ import type * as Telegram from 'telegram-bot-api-types'; import type { StreamResultHandler } from '../../agent/chat'; -import type { HistoryModifier, LLMChatRequestParams } from '../../agent/types'; +import type { HistoryModifier, UserMessageItem } from '../../agent/types'; import type { WorkerContext } from '../../config/context'; import type { MessageHandler } from './types'; import { loadChatLLM } from '../../agent'; @@ -9,8 +9,8 @@ import { ENV } from '../../config/env'; import { createTelegramBotAPI } from '../api'; import { MessageSender } from '../utils/send'; -export async function chatWithLLM(message: Telegram.Message, params: LLMChatRequestParams, context: WorkerContext, modifier: HistoryModifier | null): Promise { - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); +export async function chatWithLLM(message: Telegram.Message, params: UserMessageItem | null, context: WorkerContext, modifier: HistoryModifier | null): Promise { + const sender = MessageSender.fromMessage(context.SHARE_CONTEXT.botToken, message); try { try { const msg = await sender.sendPlainText('...').then(r => r.json()) as Telegram.ResponseWithMessage; @@ -89,17 +89,24 @@ function findPhotoFileID(photos: Telegram.PhotoSize[], offset: number): string { export class ChatHandler implements MessageHandler { handle = async (message: Telegram.Message, context: WorkerContext): Promise => { - const params: LLMChatRequestParams = { - message: message.text || message.caption || '', + const text = message.text || message.caption || ''; + const params: UserMessageItem = { + role: 'user', + content: text, }; - if (message.photo && message.photo.length > 0) { const id = findPhotoFileID(message.photo, ENV.TELEGRAM_PHOTO_SIZE_OFFSET); const api = createTelegramBotAPI(context.SHARE_CONTEXT.botToken); const file = await api.getFileWithReturns({ file_id: id }); - const url = file.result.file_path; - if (url) { - params.images = [`${ENV.TELEGRAM_API_DOMAIN}/file/bot${context.SHARE_CONTEXT.botToken}/${url}`]; + const filePath = file.result.file_path; + if (filePath) { + const url = URL.parse(`${ENV.TELEGRAM_API_DOMAIN}/file/bot${context.SHARE_CONTEXT.botToken}/${filePath}`); + if (url) { + params.content = [ + { type: 'text', text }, + { type: 'image', image: url }, + ]; + } } } return chatWithLLM(message, params, context, null); diff --git a/src/telegram/handler/handlers.ts b/src/telegram/handler/handlers.ts index c6228d5c..525177a6 100644 --- a/src/telegram/handler/handlers.ts +++ b/src/telegram/handler/handlers.ts @@ -1,11 +1,115 @@ import type * as Telegram from 'telegram-bot-api-types'; import type { WorkerContext } from '../../config/context'; -import type { MessageHandler } from './types'; +import type { MessageHandler, UpdateHandler } from './types'; import { ENV } from '../../config/env'; +import { handleCallbackQuery } from '../callback_query'; import { handleCommandMessage } from '../command'; import { MessageSender } from '../utils/send'; import { isTelegramChatTypeGroup } from '../utils/utils'; +export class EnvChecker implements UpdateHandler { + handle = async (update: Telegram.Update, context: WorkerContext): Promise => { + if (!ENV.DATABASE) { + return MessageSender + .fromUpdate(context.SHARE_CONTEXT.botToken, update) + .sendPlainText('DATABASE Not Set'); + } + return null; + }; +} + +export class WhiteListFilter implements UpdateHandler { + handle = async (update: Telegram.Update, context: WorkerContext): Promise => { + if (ENV.I_AM_A_GENEROUS_PERSON) { + return null; + } + const sender = MessageSender.fromUpdate(context.SHARE_CONTEXT.botToken, update); + + let chatType = ''; + let chatID = 0; + + if (update.message) { + chatType = update.message.chat.type; + chatID = update.message.chat.id; + } else if (update.callback_query?.message) { + chatType = update.callback_query.message.chat.type; + chatID = update.callback_query.message.chat.id; + } + + if (!chatType || !chatID) { + throw new Error('Invalid chat type or chat id'); + } + const text = `You are not in the white list, please contact the administrator to add you to the white list. Your chat_id: ${chatID}`; + + // 判断私聊消息 + if (chatType === 'private') { + // 白名单判断 + if (!ENV.CHAT_WHITE_LIST.includes(`${chatID}`)) { + return sender.sendPlainText(text); + } + return null; + } + + // 判断群组消息 + if (isTelegramChatTypeGroup(chatType)) { + // 未打开群组机器人开关,直接忽略 + if (!ENV.GROUP_CHAT_BOT_ENABLE) { + throw new Error('Not support'); + } + // 白名单判断 + if (!ENV.CHAT_GROUP_WHITE_LIST.includes(`${chatID}`)) { + return sender.sendPlainText(text); + } + return null; + } + + return sender.sendPlainText( + `Not support chat type: ${chatType}`, + ); + }; +} + +export class Update2MessageHandler implements UpdateHandler { + messageHandlers: MessageHandler[]; + constructor(messageHandlers: MessageHandler[]) { + this.messageHandlers = messageHandlers; + } + + loadMessage(body: Telegram.Update): Telegram.Message { + if (body.edited_message) { + throw new Error('Ignore edited message'); + } + if (body.message) { + return body?.message; + } else { + throw new Error('Invalid message'); + } + } + + handle = async (update: Telegram.Update, context: WorkerContext): Promise => { + const message = this.loadMessage(update); + if (!message) { + return null; + } + for (const handler of this.messageHandlers) { + const result = await handler.handle(message, context); + if (result) { + return result; + } + } + return null; + }; +} + +export class CallbackQueryHandler implements UpdateHandler { + handle = async (update: Telegram.Update, context: WorkerContext): Promise => { + if (update.callback_query) { + return handleCallbackQuery(update.callback_query, context); + } + return null; + }; +} + export class SaveLastMessage implements MessageHandler { handle = async (message: Telegram.Message, context: WorkerContext): Promise => { if (!ENV.DEBUG_MODE) { @@ -42,53 +146,6 @@ export class OldMessageFilter implements MessageHandler { }; } -export class EnvChecker implements MessageHandler { - handle = async (message: Telegram.Message, context: WorkerContext): Promise => { - if (!ENV.DATABASE) { - return MessageSender - .from(context.SHARE_CONTEXT.botToken, message) - .sendPlainText('DATABASE Not Set'); - } - return null; - }; -} - -export class WhiteListFilter implements MessageHandler { - handle = async (message: Telegram.Message, context: WorkerContext): Promise => { - if (ENV.I_AM_A_GENEROUS_PERSON) { - return null; - } - const sender = MessageSender.from(context.SHARE_CONTEXT.botToken, message); - const text = `You are not in the white list, please contact the administrator to add you to the white list. Your chat_id: ${message.chat.id}`; - - // 判断私聊消息 - if (message.chat.type === 'private') { - // 白名单判断 - if (!ENV.CHAT_WHITE_LIST.includes(`${message.chat.id}`)) { - return sender.sendPlainText(text); - } - return null; - } - - // 判断群组消息 - if (isTelegramChatTypeGroup(message.chat.type)) { - // 未打开群组机器人开关,直接忽略 - if (!ENV.GROUP_CHAT_BOT_ENABLE) { - throw new Error('Not support'); - } - // 白名单判断 - if (!ENV.CHAT_GROUP_WHITE_LIST.includes(`${message.chat.id}`)) { - return sender.sendPlainText(text); - } - return null; - } - - return sender.sendPlainText( - `Not support chat type: ${message.chat.type}`, - ); - }; -} - export class MessageFilter implements MessageHandler { // eslint-disable-next-line unused-imports/no-unused-vars handle = async (message: Telegram.Message, context: WorkerContext): Promise => { diff --git a/src/telegram/handler/index.ts b/src/telegram/handler/index.ts index 558d9bb0..6230d8fb 100644 --- a/src/telegram/handler/index.ts +++ b/src/telegram/handler/index.ts @@ -1,55 +1,50 @@ import type * as Telegram from 'telegram-bot-api-types'; -import type { MessageHandler } from './types'; +import type { UpdateHandler } from './types'; import { WorkerContext } from '../../config/context'; import { ChatHandler } from './chat'; import { GroupMention } from './group'; import { + CallbackQueryHandler, CommandHandler, EnvChecker, MessageFilter, OldMessageFilter, SaveLastMessage, + Update2MessageHandler, WhiteListFilter, } from './handlers'; -function loadMessage(body: Telegram.Update): Telegram.Message { - if (body.edited_message) { - throw new Error('Ignore edited message'); - } - if (body.message) { - return body?.message; - } else { - throw new Error('Invalid message'); - } -} - // 消息处理中间件 -const SHARE_HANDLER: MessageHandler[] = [ +const SHARE_HANDLER: UpdateHandler[] = [ // 检查环境是否准备好: DATABASE new EnvChecker(), // 过滤非白名单用户, 提前过滤减少KV消耗 new WhiteListFilter(), - // 过滤不支持的消息(抛出异常结束消息处理) - new MessageFilter(), - // 处理群消息,判断是否需要响应此条消息 - new GroupMention(), - // 忽略旧消息 - new OldMessageFilter(), - // DEBUG: 保存最后一条消息,按照需求自行调整此中间件位置 - new SaveLastMessage(), - // 处理命令消息 - new CommandHandler(), - // 与llm聊天 - new ChatHandler(), + // 回调处理 + new CallbackQueryHandler(), + // 消息处理 + new Update2MessageHandler([ + // 过滤不支持的消息(抛出异常结束消息处理) + new MessageFilter(), + // 处理群消息,判断是否需要响应此条消息 + new GroupMention(), + // 忽略旧消息 + new OldMessageFilter(), + // DEBUG: 保存最后一条消息,按照需求自行调整此中间件位置 + new SaveLastMessage(), + // 处理命令消息 + new CommandHandler(), + // 与llm聊天 + new ChatHandler(), + ]), ]; export async function handleUpdate(token: string, update: Telegram.Update): Promise { - const message = loadMessage(update); - const context = await WorkerContext.from(token, message); + const context = await WorkerContext.from(token, update); for (const handler of SHARE_HANDLER) { try { - const result = await handler.handle(message, context); + const result = await handler.handle(update, context); if (result) { return result; } diff --git a/src/telegram/handler/types.ts b/src/telegram/handler/types.ts index 3de7294b..562180ce 100644 --- a/src/telegram/handler/types.ts +++ b/src/telegram/handler/types.ts @@ -1,10 +1,15 @@ import type * as Telegram from 'telegram-bot-api-types'; import type { WorkerContext } from '../../config/context'; -// 中间件定义 function (message: TelegramMessage, context: Context): Promise +// 中间件定义 function (message: xxx, context: Context): Promise // 1. 当函数抛出异常时,结束消息处理,返回异常信息 // 2. 当函数返回 Response 对象时,结束消息处理,返回 Response 对象 // 3. 当函数返回 null 时,继续下一个中间件处理 + +export interface UpdateHandler { + handle: (update: Telegram.Update, context: WorkerContext) => Promise; +} + export interface MessageHandler { handle: (message: Telegram.Message, context: WorkerContext) => Promise; } diff --git a/src/telegram/utils/send.ts b/src/telegram/utils/send.ts index 0a556c3d..6257da11 100644 --- a/src/telegram/utils/send.ts +++ b/src/telegram/utils/send.ts @@ -7,19 +7,39 @@ import { escape } from './md2tgmd'; class MessageContext implements Record { chat_id: number; message_id: number | null = null; // 当前发生的消息,用于后续编辑 - reply_to_message_id: number | null; + reply_to_message_id: number | null = null; parse_mode: Telegram.ParseMode | null = null; allow_sending_without_reply: boolean | null = null; disable_web_page_preview: boolean | null = null; - constructor(message: Telegram.Message) { - this.chat_id = message.chat.id; + constructor(chatID: number) { + this.chat_id = chatID; + } + + static fromMessage(message: Telegram.Message): MessageContext { + const ctx = new MessageContext(message.chat.id); if (message.chat.type === 'group' || message.chat.type === 'supergroup') { - this.reply_to_message_id = message.message_id; - this.allow_sending_without_reply = true; + ctx.reply_to_message_id = message.message_id; + ctx.allow_sending_without_reply = true; + } else { + ctx.reply_to_message_id = null; + } + return ctx; + } + + static fromCallbackQuery(callbackQuery: Telegram.CallbackQuery): MessageContext { + const chat = callbackQuery.message?.chat; + if (!chat) { + throw new Error('Chat not found'); + } + const ctx = new MessageContext(chat.id); + if (chat.type === 'group' || chat.type === 'supergroup') { + ctx.reply_to_message_id = callbackQuery.message!.message_id; + ctx.allow_sending_without_reply = true; } else { - this.reply_to_message_id = null; + ctx.reply_to_message_id = null; } + return ctx; } } @@ -35,13 +55,22 @@ export class MessageSender { this.sendPhoto = this.sendPhoto.bind(this); } - static from(token: string, message: Telegram.Message): MessageSender { - return new MessageSender(token, new MessageContext(message)); + static fromMessage(token: string, message: Telegram.Message): MessageSender { + return new MessageSender(token, MessageContext.fromMessage(message)); } - with(message: Telegram.Message): MessageSender { - this.context = new MessageContext(message); - return this; + static fromCallbackQuery(token: string, callbackQuery: Telegram.CallbackQuery): MessageSender { + return new MessageSender(token, MessageContext.fromCallbackQuery(callbackQuery)); + } + + static fromUpdate(token: string, update: Telegram.Update): MessageSender { + if (update.callback_query) { + return MessageSender.fromCallbackQuery(token, update.callback_query); + } + if (update.message) { + return MessageSender.fromMessage(token, update.message); + } + throw new Error('Invalid update'); } update(context: MessageContext | Record): MessageSender { @@ -128,6 +157,14 @@ export class MessageSender { return lastMessageResponse; } + sendRawMessage(message: Telegram.SendMessageParams): Promise { + return this.api.sendMessage(message); + } + + editRawMessage(message: Telegram.EditMessageTextParams): Promise { + return this.api.editMessageText(message); + } + sendRichText(message: string, parseMode: Telegram.ParseMode | null = (ENV.DEFAULT_PARSE_MODE as Telegram.ParseMode)): Promise { if (!this.context) { throw new Error('Message context not set'); diff --git a/src/telegram/utils/utils.ts b/src/telegram/utils/utils.ts index 68383541..80f967e1 100644 --- a/src/telegram/utils/utils.ts +++ b/src/telegram/utils/utils.ts @@ -1,3 +1,31 @@ +import type { WorkerContext } from '../../config/context'; +import { ENV, ENV_KEY_MAPPER } from '../../config/env'; +import { ConfigMerger } from '../../config/merger'; + export function isTelegramChatTypeGroup(type: string): boolean { return type === 'group' || type === 'supergroup'; } + +export async function setUserConfig(values: Record, context: WorkerContext): Promise { + for (const ent of Object.entries(values || {})) { + let [key, value] = ent; + key = ENV_KEY_MAPPER[key] || key; + if (ENV.LOCK_USER_CONFIG_KEYS.includes(key)) { + throw new Error(`Key ${key} is locked`); + } + const configKeys = Object.keys(context.USER_CONFIG || {}) || []; + if (!configKeys.includes(key)) { + throw new Error(`Key ${key} is not allowed`); + } + context.USER_CONFIG.DEFINE_KEYS.push(key); + ConfigMerger.merge(context.USER_CONFIG, { + [key]: value, + }); + console.log('Update user config: ', key, context.USER_CONFIG[key]); + } + context.USER_CONFIG.DEFINE_KEYS = Array.from(new Set(context.USER_CONFIG.DEFINE_KEYS)); + await ENV.DATABASE.put( + context.SHARE_CONTEXT.configStoreKey, + JSON.stringify(ConfigMerger.trim(context.USER_CONFIG, ENV.LOCK_USER_CONFIG_KEYS)), + ); +} diff --git a/src/utils/image/index.ts b/src/utils/image/index.ts index 8006d5d4..021abdb6 100644 --- a/src/utils/image/index.ts +++ b/src/utils/image/index.ts @@ -16,12 +16,11 @@ async function fetchImage(url: string): Promise { } async function urlToBase64String(url: string): Promise { - try { - const { Buffer } = await import('node:buffer'); + if (typeof Buffer !== 'undefined') { return fetchImage(url) .then(blob => blob.arrayBuffer()) .then(buffer => Buffer.from(buffer).toString('base64')); - } catch { + } else { // 非原生base64编码速度太慢不适合在workers中使用 // 在wrangler.toml中添加 Node.js 选项启用nodejs兼容 // compatibility_flags = [ "nodejs_compat" ] diff --git a/scripts/plugins/vercel/index.ts b/src/vite/vercel/index.ts similarity index 96% rename from scripts/plugins/vercel/index.ts rename to src/vite/vercel/index.ts index 7a3f02f4..7d1421ed 100644 --- a/scripts/plugins/vercel/index.ts +++ b/src/vite/vercel/index.ts @@ -1,7 +1,7 @@ import { execSync } from 'node:child_process'; import fs from 'node:fs/promises'; import { parse } from 'toml'; -import { ENV } from '../../../src/config/env'; +import { ENV } from '../../config/env'; export function createVercelPlugin(vercelPath: string, tomlPath: string, removeUnused = true) { return { diff --git a/scripts/plugins/vercel/setenv.ts b/src/vite/vercel/setenv.ts similarity index 100% rename from scripts/plugins/vercel/setenv.ts rename to src/vite/vercel/setenv.ts diff --git a/src/vite/version/index.ts b/src/vite/version/index.ts new file mode 100644 index 00000000..873df12e --- /dev/null +++ b/src/vite/version/index.ts @@ -0,0 +1,44 @@ +import { execSync } from 'node:child_process'; +import * as fs from 'node:fs/promises'; +import path from 'node:path'; + +const TIMESTAMP = Math.floor(Date.now() / 1000); +const COMMIT_HASH = ((): string => { + try { + return execSync('git rev-parse --short HEAD').toString().trim(); + } catch (e) { + console.warn(e); + } + return 'unknown'; +})(); + +async function createVersionTs(outDir: string) { + await fs.writeFile( + path.resolve(outDir, 'src/config/version.ts'), + `export const BUILD_TIMESTAMP = ${TIMESTAMP};\nexport const BUILD_VERSION = '${COMMIT_HASH}';\n`, + ); +} + +async function createVersionJson(outDir: string) { + await fs.writeFile(path.resolve(outDir, 'dist/buildinfo.json'), JSON.stringify({ + sha: COMMIT_HASH, + timestamp: TIMESTAMP, + })); +} + +export async function createVersion(outDir: string) { + await createVersionTs(outDir); + await createVersionJson(outDir); +} + +export function createVersionPlugin(targetDir: string) { + return { + name: 'version', + async buildStart() { + await createVersionTs(targetDir); + }, + async closeBundle() { + await createVersionJson(targetDir); + }, + }; +} diff --git a/src/vite/version/main.ts b/src/vite/version/main.ts new file mode 100644 index 00000000..67f13bdd --- /dev/null +++ b/src/vite/version/main.ts @@ -0,0 +1,8 @@ +import process from 'node:process'; +import { createVersion } from './index'; + +const { + OUT_DIR = '.', +} = process.env; + +createVersion(OUT_DIR).catch(console.error); diff --git a/tsconfig.json b/tsconfig.json index 08b0be76..7601f249 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,16 @@ { "compilerOptions": { "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react", "lib": [ "ESNext", - "dom" + "dom", + "dom.iterable" ], "rootDir": ".", - "module": "ESNext", - "moduleResolution": "bundler", - "types": ["node"], + "types": ["node", "react"], "allowJs": true, "strict": true, "declaration": true, @@ -18,4 +20,4 @@ "esModuleInterop": true }, "include": ["src/**/*"] -} +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 01bc6534..3d149b4b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,16 +2,21 @@ import type { LibraryFormats, Plugin } from 'vite'; import * as path from 'node:path'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import cleanup from 'rollup-plugin-cleanup'; -import nodeExternals from 'rollup-plugin-node-externals'; +import { nodeExternals } from 'rollup-plugin-node-externals'; import { defineConfig } from 'vite'; import checker from 'vite-plugin-checker'; import dts from 'vite-plugin-dts'; -import { createDockerPlugin } from './scripts/plugins/docker'; -import { createVersionPlugin, versionDefine } from './scripts/plugins/version'; +import { createVersionPlugin } from './src/vite/version'; + +const { + TYPES = 'false', + FORMATS = 'es', + ENTRY = 'src/entry/core/index.ts', +} = process.env; -const { BUILD_MODE } = process.env; const plugins: Plugin[] = [ nodeResolve({ + browser: false, preferBuiltins: true, }), cleanup({ @@ -22,36 +27,13 @@ const plugins: Plugin[] = [ typescript: true, }), nodeExternals(), + createVersionPlugin(path.resolve(__dirname)), ]; -let entry: string; -let outDir = 'dist'; -let fileName = 'index'; -let formats: LibraryFormats[] = ['es']; -switch (BUILD_MODE) { - case 'plugins-page': - entry = 'src/plugins/interpolate.ts'; - fileName = 'interpolate'; - outDir = 'plugins/dist'; - break; - case 'local': - entry = 'src/adapter/local/index.ts'; - plugins.push(createDockerPlugin('dist')); - break; - case 'vercel': - entry = 'src/adapter/vercel/index.ts'; - break; - case 'pack': - entry = 'src/index.ts'; - formats = ['es', 'cjs']; - plugins.push(dts({ - rollupTypes: true, - })); - break; - default: - entry = 'src/index.ts'; - plugins.push(createVersionPlugin('dist')); - break; +if (TYPES === 'true') { + plugins.push( + dts(), + ); } export default defineConfig({ @@ -59,14 +41,11 @@ export default defineConfig({ build: { target: 'esnext', lib: { - entry: path.resolve(__dirname, entry), - fileName, - formats, + entry: path.resolve(__dirname, ENTRY), + fileName: 'index', + formats: FORMATS.split(',') as LibraryFormats[], }, minify: false, - outDir, - }, - define: { - ...versionDefine, + outDir: path.resolve(__dirname, 'dist'), }, }); diff --git a/wrangler-example.toml b/wrangler-example.toml index 9cd056c2..08f886d8 100644 --- a/wrangler-example.toml +++ b/wrangler-example.toml @@ -1,7 +1,10 @@ # 这里的 name 改成你自己的workers 的名字 name = 'chatgpt-telegram-workers' compatibility_date = '2023-10-07' -main = './dist/index.js' +main = './dist/index.js' # 先使用vite编译 然后再使用编译产物部署 +#main = './src/entry/core/index.ts' # 直接编译core 不使用vite编译 +#main = './src/entry/next/index.ts' # 直接编译实现性质的版本 + workers_dev = true compatibility_flags = [ "nodejs_compat" ] @@ -180,20 +183,20 @@ OPENAI_API_KEY = 'SK-1,SK-2' ## Anthropic api model #ANTHROPIC_CHAT_MODEL = 'claude-3-haiku-20240307' -CUSTOM_COMMAND_azure= '/setenvs {"AI_PROVIDER": "azure"}' -COMMAND_DESCRIPTION_azure = 'Switch to Azure AI provider' - -CUSTOM_COMMAND_workers = '/setenvs {"AI_PROVIDER": "workers"}' -COMMAND_DESCRIPTION_workers = 'Switch to Workers AI provider' - -CUSTOM_COMMAND_openai = '/setenvs {"AI_PROVIDER": "openai"}' -COMMAND_DESCRIPTION_openai = 'Switch to OpenAI AI provider' - -CUSTOM_COMMAND_cn2en = '/setenvs {"SYSTEM_INIT_MESSAGE": "You are a translator. Please translate everything I say below into English."}' -COMMAND_DESCRIPTION_cn2en = 'Switch to Chinese to English translation mode' - -PLUGIN_COMMAND_dns = "https://raw.githubusercontent.com/TBXark/ChatGPT-Telegram-Workers/dev/plugins/dns.json" -PLUGIN_DESCRIPTION_dns = "DNS查询 /dns <类型> <域名>" - -PLUGIN_COMMAND_dicten = "https://raw.githubusercontent.com/TBXark/ChatGPT-Telegram-Workers/dev/plugins/dicten.json" -PLUGIN_DESCRIPTION_dicten = "英文字典 /dicten <单词>" \ No newline at end of file +#CUSTOM_COMMAND_azure= '/setenvs {"AI_PROVIDER": "azure"}' +#COMMAND_DESCRIPTION_azure = 'Switch to Azure AI provider' +# +#CUSTOM_COMMAND_workers = '/setenvs {"AI_PROVIDER": "workers"}' +#COMMAND_DESCRIPTION_workers = 'Switch to Workers AI provider' +# +#CUSTOM_COMMAND_openai = '/setenvs {"AI_PROVIDER": "openai"}' +#COMMAND_DESCRIPTION_openai = 'Switch to OpenAI AI provider' +# +#CUSTOM_COMMAND_cn2en = '/setenvs {"SYSTEM_INIT_MESSAGE": "You are a translator. Please translate everything I say below into English."}' +#COMMAND_DESCRIPTION_cn2en = 'Switch to Chinese to English translation mode' +# +#PLUGIN_COMMAND_dns = "https://raw.githubusercontent.com/TBXark/ChatGPT-Telegram-Workers/dev/plugins/dns.json" +#PLUGIN_DESCRIPTION_dns = "DNS查询 /dns <类型> <域名>" +# +#PLUGIN_COMMAND_dicten = "https://raw.githubusercontent.com/TBXark/ChatGPT-Telegram-Workers/dev/plugins/dicten.json" +#PLUGIN_DESCRIPTION_dicten = "英文字典 /dicten <单词>" \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index fce75f59..9ecaa56b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,42 +2,155 @@ # yarn lockfile v1 -"@antfu/eslint-config@^3.6.2": - version "3.7.3" - resolved "https://registry.yarnpkg.com/@antfu/eslint-config/-/eslint-config-3.7.3.tgz#74c12acd8a7b2480ce07fe91e34472d16af779dd" - integrity sha512-vzhKtzQT+f/xBV8T5U8SFy3D7uAqL2CEcjsJVqtA7F8tdKvGuC/96uWeEKMHk5lRfijgj+xRvb+c4qQn60YlIA== +"@ai-sdk/anthropic@^0.0.56": + version "0.0.56" + resolved "https://registry.yarnpkg.com/@ai-sdk/anthropic/-/anthropic-0.0.56.tgz#6d3629f08a4025578e0daf073d13ca31a33bef71" + integrity sha512-FC/XbeFANFp8rHH+zEZF34cvRu9T42rQxw9QnUzJ1LXTi1cWjxYOx2Zo4vfg0iofxxqgOe4fT94IdT2ERQ89bA== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/azure@^0.0.52": + version "0.0.52" + resolved "https://registry.yarnpkg.com/@ai-sdk/azure/-/azure-0.0.52.tgz#0e426405297492a1bb56cc0840bc547e24e02152" + integrity sha512-l0XHiQymgQbzIe6dilTBD6wI4iom+Lo7yHGQVzEIq2o43/4zHDL+m7k5UCPoF0nrl5lFJk3u5+crOhSGprT7ZA== + dependencies: + "@ai-sdk/openai" "0.0.72" + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/cohere@^0.0.28": + version "0.0.28" + resolved "https://registry.yarnpkg.com/@ai-sdk/cohere/-/cohere-0.0.28.tgz#ab0e420039ce52bef910fc6715b6be7157c6e636" + integrity sha512-aWavC7PuC5AcDcwTBcvx2ZemudtjPaZvLDmcVjiIhzvvW2+zoHoWmERD8CtSWIMlQp/vjdTcwNhSXTOqfxm27A== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/google@^0.0.55": + version "0.0.55" + resolved "https://registry.yarnpkg.com/@ai-sdk/google/-/google-0.0.55.tgz#0b9f929e21e7850d038041d33cc759f137a8b20d" + integrity sha512-dvEMS8Ex2H0OeuFBiT4Q1Kfrxi1ckjooy/PazNLjRQ3w9o9VQq4O24eMQGCuW1Z47qgMdXjhDzsH6qD0HOX6Cw== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/mistral@^0.0.46": + version "0.0.46" + resolved "https://registry.yarnpkg.com/@ai-sdk/mistral/-/mistral-0.0.46.tgz#56f43b0295bc5fdd630ced6459868d79de5f1fa5" + integrity sha512-WP39TqxdUR3WgFnW+k1OgOTmH7FYzNWPkn+x9gJ9/JU/TEZGyEpylk11N4nakN9jLeuJSXhqbG2exooQUX2M/g== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/openai@0.0.72", "@ai-sdk/openai@^0.0.72": + version "0.0.72" + resolved "https://registry.yarnpkg.com/@ai-sdk/openai/-/openai-0.0.72.tgz#0c09652e49b67476c4e0a445a2efe5f0cddf6de0" + integrity sha512-IKsgxIt6KJGkEHyMp975xW5VPmetwhI8g9H6dDmwvemBB41IRQa78YMNttiJqPcgmrZX2QfErOICv1gQvZ1gZg== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + +"@ai-sdk/provider-utils@1.0.22": + version "1.0.22" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz#5397a193587709796d012fc04e2df9903b70852f" + integrity sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ== + dependencies: + "@ai-sdk/provider" "0.0.26" + eventsource-parser "^1.1.2" + nanoid "^3.3.7" + secure-json-parse "^2.7.0" + +"@ai-sdk/provider@0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-0.0.26.tgz#52c3d5eb65cf7592c77c78decadf77cbec978934" + integrity sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg== + dependencies: + json-schema "^0.4.0" + +"@ai-sdk/react@0.0.70": + version "0.0.70" + resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-0.0.70.tgz#25dee1755c67da2ac0ed4f207102de93d7196f44" + integrity sha512-GnwbtjW4/4z7MleLiW+TOZC2M29eCg1tOUpuEiYFMmFNZK8mkrqM0PFZMo6UsYeUYMWqEOOcPOU9OQVJMJh7IQ== + dependencies: + "@ai-sdk/provider-utils" "1.0.22" + "@ai-sdk/ui-utils" "0.0.50" + swr "^2.2.5" + throttleit "2.1.0" + +"@ai-sdk/solid@0.0.54": + version "0.0.54" + resolved "https://registry.yarnpkg.com/@ai-sdk/solid/-/solid-0.0.54.tgz#60f2007d511f153159d9e5ddc1e8b800fb472c58" + integrity sha512-96KWTVK+opdFeRubqrgaJXoNiDP89gNxFRWUp0PJOotZW816AbhUf4EnDjBjXTLjXL1n0h8tGSE9sZsRkj9wQQ== + dependencies: + "@ai-sdk/provider-utils" "1.0.22" + "@ai-sdk/ui-utils" "0.0.50" + +"@ai-sdk/svelte@0.0.57": + version "0.0.57" + resolved "https://registry.yarnpkg.com/@ai-sdk/svelte/-/svelte-0.0.57.tgz#82e97db343f2d5f8e50da055e6897e03f03c2ee6" + integrity sha512-SyF9ItIR9ALP9yDNAD+2/5Vl1IT6kchgyDH8xkmhysfJI6WrvJbtO1wdQ0nylvPLcsPoYu+cAlz1krU4lFHcYw== + dependencies: + "@ai-sdk/provider-utils" "1.0.22" + "@ai-sdk/ui-utils" "0.0.50" + sswr "^2.1.0" + +"@ai-sdk/ui-utils@0.0.50": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@ai-sdk/ui-utils/-/ui-utils-0.0.50.tgz#f396d24b5ac1e7a8090684a6d8de47282d0bad96" + integrity sha512-Z5QYJVW+5XpSaJ4jYCCAVG7zIAuKOOdikhgpksneNmKvx61ACFaf98pmOd+xnjahl0pIlc/QIe6O4yVaJ1sEaw== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + json-schema "^0.4.0" + secure-json-parse "^2.7.0" + zod-to-json-schema "^3.23.3" + +"@ai-sdk/vue@0.0.59": + version "0.0.59" + resolved "https://registry.yarnpkg.com/@ai-sdk/vue/-/vue-0.0.59.tgz#29190415a123e631bfe7cf08f6454b73b5585714" + integrity sha512-+ofYlnqdc8c4F6tM0IKF0+7NagZRAiqBJpGDJ+6EYhDW8FHLUP/JFBgu32SjxSxC6IKFZxEnl68ZoP/Z38EMlw== + dependencies: + "@ai-sdk/provider-utils" "1.0.22" + "@ai-sdk/ui-utils" "0.0.50" + swrv "^1.0.4" + +"@antfu/eslint-config@^3.8.0": + version "3.8.0" + resolved "https://registry.yarnpkg.com/@antfu/eslint-config/-/eslint-config-3.8.0.tgz#dd1d3267d5185cfab3901328e820a2ec89862d82" + integrity sha512-O5QSufPHpKTm0wk1OQ5c2mOZVzCqYV3hIDrt5zt+cOWqiG8YXLPkSOD4fFwjomATtOuUbcLUwkcgY5dErM7aIw== dependencies: "@antfu/install-pkg" "^0.4.1" "@clack/prompts" "^0.7.0" "@eslint-community/eslint-plugin-eslint-comments" "^4.4.0" - "@eslint/markdown" "^6.1.0" - "@stylistic/eslint-plugin" "^2.8.0" - "@typescript-eslint/eslint-plugin" "^8.7.0" - "@typescript-eslint/parser" "^8.7.0" - "@vitest/eslint-plugin" "^1.1.4" + "@eslint/markdown" "^6.2.0" + "@stylistic/eslint-plugin" "^2.9.0" + "@typescript-eslint/eslint-plugin" "^8.9.0" + "@typescript-eslint/parser" "^8.9.0" + "@vitest/eslint-plugin" "^1.1.7" eslint-config-flat-gitignore "^0.3.0" eslint-flat-config-utils "^0.4.0" eslint-merge-processors "^0.1.0" eslint-plugin-antfu "^2.7.0" eslint-plugin-command "^0.2.6" - eslint-plugin-import-x "^4.3.0" - eslint-plugin-jsdoc "^50.3.0" + eslint-plugin-import-x "^4.3.1" + eslint-plugin-jsdoc "^50.4.1" eslint-plugin-jsonc "^2.16.0" - eslint-plugin-n "^17.10.3" + eslint-plugin-n "^17.11.1" eslint-plugin-no-only-tests "^3.3.0" - eslint-plugin-perfectionist "^3.7.0" + eslint-plugin-perfectionist "^3.9.0" eslint-plugin-regexp "^2.6.0" eslint-plugin-toml "^0.11.1" - eslint-plugin-unicorn "^55.0.0" + eslint-plugin-unicorn "^56.0.0" eslint-plugin-unused-imports "^4.1.4" - eslint-plugin-vue "^9.28.0" + eslint-plugin-vue "^9.29.0" eslint-plugin-yml "^1.14.0" eslint-processor-vue-blocks "^0.1.2" - globals "^15.9.0" + globals "^15.11.0" jsonc-eslint-parser "^2.4.0" local-pkg "^0.5.0" parse-gitignore "^2.0.0" - picocolors "^1.1.0" + picocolors "^1.1.1" toml-eslint-parser "^0.10.0" vue-eslint-parser "^9.4.3" yaml-eslint-parser "^1.2.3" @@ -69,7 +182,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== -"@babel/helper-validator-identifier@^7.24.5", "@babel/helper-validator-identifier@^7.24.7": +"@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== @@ -124,35 +237,35 @@ dependencies: mime "^3.0.0" -"@cloudflare/workerd-darwin-64@1.20240925.0": - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240925.0.tgz#f78fe394f73540594609d8e05a2da7feb46c76c0" - integrity sha512-KdLnSXuzB65CbqZPm+qYzk+zkQ1tUNPaaRGYVd/jPYAxwwtfTUQdQ+ahDPwVVs2tmQELKy7ZjQjf2apqSWUfjw== +"@cloudflare/workerd-darwin-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241106.1.tgz#4f470f98ca12dbc3262ec8a432466e1c1525aad9" + integrity sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw== -"@cloudflare/workerd-darwin-arm64@1.20240925.0": - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240925.0.tgz#f03b17177744ad898bb12610d15cc0a9744abfe6" - integrity sha512-MiQ6uUmCXjsXgWNV+Ock2tp2/tYqNJGzjuaH6jFioeRF+//mz7Tv7J7EczOL4zq+TH8QFOh0/PUsLyazIWVGng== +"@cloudflare/workerd-darwin-arm64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241106.1.tgz#c34d6306afc50ae2eee3e538329af7192ae17dd0" + integrity sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg== -"@cloudflare/workerd-linux-64@1.20240925.0": - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240925.0.tgz#fe0366b804b957acf5012d889e94163bab806a57" - integrity sha512-Rjix8jsJMfsInmq3Hm3fmiRQ+rwzuWRPV1pg/OWhMSfNP7Qp2RCU+RGkhgeR9Z5eNAje0Sn2BMrFq4RvF9/yRA== +"@cloudflare/workerd-linux-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241106.1.tgz#42c425137c151348146a70d3f271e5f3293d3b75" + integrity sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw== -"@cloudflare/workerd-linux-arm64@1.20240925.0": - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240925.0.tgz#fcf82de06def420972c661a6021c87683cd8fbdc" - integrity sha512-VYIPeMHQRtbwQoIjUwS/zULlywPxyDvo46XkTpIW5MScEChfqHvAYviQ7TzYGx6Q+gmZmN+DUB2KOMx+MEpCxA== +"@cloudflare/workerd-linux-arm64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241106.1.tgz#f3bc7ab9424dafbf1816d8bc2e8aae24646ecad0" + integrity sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA== -"@cloudflare/workerd-windows-64@1.20240925.0": - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240925.0.tgz#0a5c82b95b03a94591cc8a1830f28d2e41ff7685" - integrity sha512-C8peGvaU5R51bIySi1VbyfRgwNSSRknqoFSnSbSBI3uTN3THTB3UnmRKy7GXJDmyjgXuT9Pcs1IgaWNubLtNtw== +"@cloudflare/workerd-windows-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241106.1.tgz#9e2f1ec0d993c8b12c4cd7f9c5e6b953a0672707" + integrity sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg== -"@cloudflare/workers-shared@0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@cloudflare/workers-shared/-/workers-shared-0.5.4.tgz#bbf8f03b79a6bc0169ad66a6015ebe579d36753a" - integrity sha512-PNL/0TjKRdUHa1kwgVdqUNJVZ9ez4kacsi8omz+gv859EvJmsVuGiMAClY2YfJnC9LVKhKCcjqmFgKNXG9/IXA== +"@cloudflare/workers-shared@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workers-shared/-/workers-shared-0.7.1.tgz#cf32caaf58808d9e36f9ebc8baa84a9699b388f2" + integrity sha512-46cP5FCrl3TrvHeoHLb5SRuiDMKH5kc9Yvo36SAfzt8dqJI/qJRoY1GP3ioHn/gP7v2QIoUOTAzIl7Ml7MnfrA== dependencies: mime "^3.0.0" zod "^3.22.3" @@ -164,6 +277,26 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@csstools/css-parser-algorithms@^3.0.1": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + +"@csstools/css-tokenizer@^3.0.1": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" + integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== + +"@csstools/media-query-list-parser@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz#9474e08e6d7767cf68c56bf1581b59d203360cb0" + integrity sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw== + +"@csstools/selector-specificity@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz#7dfccb9df5499e627e7bfdbb4021a06813a45dba" + integrity sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ== + "@dprint/formatter@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@dprint/formatter/-/formatter-0.3.0.tgz#e374398c8e8d7dbf50e8208c87af44a66de0cb2e" @@ -179,6 +312,11 @@ resolved "https://registry.yarnpkg.com/@dprint/toml/-/toml-0.6.2.tgz#54c0b8094c6a8639e62314a300e4c88831e6254d" integrity sha512-Mk5unEANsL/L+WHYU3NpDXt1ARU5bNU5k5OZELxaJodDycKG6RoRnSlZXpW6+7UN2PSnETAFVUdKrh937ZwtHA== +"@dual-bundle/import-meta-resolve@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b" + integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg== + "@edge-runtime/format@2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@edge-runtime/format/-/format-2.2.1.tgz#10dcedb0d7c2063c9ee360fbab23846c8720f986" @@ -206,7 +344,7 @@ dependencies: "@edge-runtime/primitives" "4.1.0" -"@es-joy/jsdoccomment@^0.48.0", "@es-joy/jsdoccomment@~0.48.0": +"@es-joy/jsdoccomment@^0.48.0": version "0.48.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.48.0.tgz#5d9dc1a295cf5d1ed224dffafb4800d5c7206c27" integrity sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw== @@ -215,6 +353,15 @@ esquery "^1.6.0" jsdoc-type-pratt-parser "~4.1.0" +"@es-joy/jsdoccomment@~0.49.0": + version "0.49.0" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz#e5ec1eda837c802eca67d3b29e577197f14ba1db" + integrity sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q== + dependencies: + comment-parser "1.4.1" + esquery "^1.6.0" + jsdoc-type-pratt-parser "~4.1.0" + "@esbuild-plugins/node-globals-polyfill@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf" @@ -588,11 +735,23 @@ dependencies: eslint-visitor-keys "^3.3.0" +"@eslint-community/eslint-utils@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.8.0", "@eslint-community/regexpp@^4.9.1": version "4.11.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + "@eslint/compat@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.1.1.tgz#5736523f5105c94dfae5f35e31debc38443722cd" @@ -607,10 +766,10 @@ debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.6.0.tgz#9930b5ba24c406d67a1760e94cdbac616a6eb674" - integrity sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg== +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== "@eslint/eslintrc@^3.1.0": version "3.1.0" @@ -627,15 +786,15 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.11.1": - version "9.11.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.11.1.tgz#8bcb37436f9854b3d9a561440daf916acd940986" - integrity sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA== +"@eslint/js@9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== -"@eslint/markdown@^6.1.0": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@eslint/markdown/-/markdown-6.1.1.tgz#5f496330905cebe1c964d9ccb93fe8be493ac380" - integrity sha512-Z+1js5AeqidwhNBbnIPM6Fn4eY9D5i1NleamS0UBW6BG0J4lpvhIVOKVIi22kmH5gvxDmHUp5MHkkkjda0TehA== +"@eslint/markdown@^6.2.0": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@eslint/markdown/-/markdown-6.2.1.tgz#7f3698cd5bd16c2f6cb063e78eb8118e009654c0" + integrity sha512-cKVd110hG4ICHmWhIwZJfKmmJBvbiDWyrHODJknAtudKgZtlROGoLX9UEOA0o746zC0hCY4UV4vR+aOGW9S6JQ== dependencies: "@eslint/plugin-kit" "^0.2.0" mdast-util-from-markdown "^2.0.1" @@ -664,6 +823,19 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" @@ -674,6 +846,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@humanwhocodes/retry@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + "@ioredis/commands@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" @@ -712,27 +889,27 @@ semver "^7.3.5" tar "^6.1.11" -"@microsoft/api-extractor-model@7.29.6": - version "7.29.6" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.29.6.tgz#e5941514502049b06ca9af21e2096f8f1ad5a01b" - integrity sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw== +"@microsoft/api-extractor-model@7.29.8": + version "7.29.8" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.29.8.tgz#fa6d0c48374f1105c0637f0882cfe2044d88520a" + integrity sha512-t3Z/xcO6TRbMcnKGVMs4uMzv/gd5j0NhMiJIGjD4cJMeFJ1Hf8wnLSx37vxlRlL0GWlGJhnFgxvnaL6JlS+73g== dependencies: "@microsoft/tsdoc" "~0.15.0" "@microsoft/tsdoc-config" "~0.17.0" - "@rushstack/node-core-library" "5.7.0" + "@rushstack/node-core-library" "5.9.0" -"@microsoft/api-extractor@7.47.7": - version "7.47.7" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.47.7.tgz#3bc4450fe46c265bef857ab938aa15b9fc7a85de" - integrity sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A== +"@microsoft/api-extractor@^7.47.11": + version "7.47.11" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.47.11.tgz#00450fb4f4c30f76c18d36110aa4cae1cdc2191c" + integrity sha512-lrudfbPub5wzBhymfFtgZKuBvXxoSIAdrvS2UbHjoMT2TjIEddq6Z13pcve7A03BAouw0x8sW8G4txdgfiSwpQ== dependencies: - "@microsoft/api-extractor-model" "7.29.6" + "@microsoft/api-extractor-model" "7.29.8" "@microsoft/tsdoc" "~0.15.0" "@microsoft/tsdoc-config" "~0.17.0" - "@rushstack/node-core-library" "5.7.0" + "@rushstack/node-core-library" "5.9.0" "@rushstack/rig-package" "0.5.3" - "@rushstack/terminal" "0.14.0" - "@rushstack/ts-command-line" "4.22.6" + "@rushstack/terminal" "0.14.2" + "@rushstack/ts-command-line" "4.23.0" lodash "~4.17.15" minimatch "~3.0.3" resolve "~1.22.1" @@ -768,7 +945,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -792,6 +969,11 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@opentelemetry/api@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== + "@pkgr/core@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" @@ -905,10 +1087,10 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz#f2feb149235a5dc1deb5439758f8871255e5a161" integrity sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ== -"@rushstack/node-core-library@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-5.7.0.tgz#f28699c7d0b3de0120a207f8b9d5bd7c69806e18" - integrity sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ== +"@rushstack/node-core-library@5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-5.9.0.tgz#097213d518b29a9c28b46db9c2cc7968c67168f8" + integrity sha512-MMsshEWkTbXqxqFxD4gcIUWQOCeBChlGczdZbHfqmNZQFLHB3yWxDFSMHFUdu2/OB9NUk7Awn5qRL+rws4HQNg== dependencies: ajv "~8.13.0" ajv-draft-04 "~1.0.0" @@ -927,32 +1109,32 @@ resolve "~1.22.1" strip-json-comments "~3.1.1" -"@rushstack/terminal@0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@rushstack/terminal/-/terminal-0.14.0.tgz#967ecc586d7172204353059f8fdb1760666e9381" - integrity sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw== +"@rushstack/terminal@0.14.2": + version "0.14.2" + resolved "https://registry.yarnpkg.com/@rushstack/terminal/-/terminal-0.14.2.tgz#cc34654990500e9413265e9fc03839fa3419c4c9" + integrity sha512-2fC1wqu1VCExKC0/L+0noVcFQEXEnoBOtCIex1TOjBzEDWcw8KzJjjj7aTP6mLxepG0XIyn9OufeFb6SFsa+sg== dependencies: - "@rushstack/node-core-library" "5.7.0" + "@rushstack/node-core-library" "5.9.0" supports-color "~8.1.1" -"@rushstack/ts-command-line@4.22.6": - version "4.22.6" - resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.22.6.tgz#2aee4fc98c6043c026ce278880fbffb5227de5ca" - integrity sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg== +"@rushstack/ts-command-line@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.23.0.tgz#d2517f9da17a2f7b8967cdb417c39edc25b432ba" + integrity sha512-jYREBtsxduPV6ptNq8jOKp9+yx0ld1Tb/Tkdnlj8gTjazl1sF3DwX2VbluyYrNd0meWIL0bNeer7WDf5tKFjaQ== dependencies: - "@rushstack/terminal" "0.14.0" + "@rushstack/terminal" "0.14.2" "@types/argparse" "1.0.38" argparse "~1.0.9" string-argv "~0.3.1" -"@stylistic/eslint-plugin@^2.8.0": - version "2.8.0" - resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.8.0.tgz#9fcbcf8b4b27cc3867eedce37b8c8fded1010107" - integrity sha512-Ufvk7hP+bf+pD35R/QfunF793XlSRIC7USr3/EdgduK9j13i2JjmsM0LUz3/foS+jDYp2fzyWZA9N44CPur0Ow== +"@stylistic/eslint-plugin@^2.9.0": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz#809924752a1a13ebff2b0b6d7884fd61d389a907" + integrity sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ== dependencies: - "@typescript-eslint/utils" "^8.4.0" - eslint-visitor-keys "^4.0.0" - espree "^10.1.0" + "@typescript-eslint/utils" "^8.12.2" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" estraverse "^5.3.0" picomatch "^4.0.2" @@ -1003,6 +1185,11 @@ dependencies: "@types/ms" "*" +"@types/diff-match-patch@^1.0.36": + version "1.0.36" + resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz#dcef10a69d357fe9d43ac4ff2eca6b85dbf466af" + integrity sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== + "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -1025,6 +1212,14 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== +"@types/node-fetch@^2.6.4": + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node-forge@^1.3.0": version "1.3.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" @@ -1032,7 +1227,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^22.5.5": +"@types/node@*": version "22.7.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.4.tgz#e35d6f48dca3255ce44256ddc05dee1c23353fcc" integrity sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg== @@ -1044,11 +1239,38 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== +"@types/node@^18.11.18": + version "18.19.64" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.64.tgz#122897fb79f2a9ec9c979bded01c11461b2b1478" + integrity sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@^22.9.0": + version "22.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + dependencies: + undici-types "~6.19.8" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +"@types/prop-types@*": + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== + +"@types/react@^18.3.11": + version "18.3.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" + integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" @@ -1059,32 +1281,40 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== -"@typescript-eslint/eslint-plugin@^8.7.0": - version "8.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.7.0.tgz#d0070f206daad26253bf00ca5b80f9b54f9e2dd0" - integrity sha512-RIHOoznhA3CCfSTFiB6kBGLQtB/sox+pJ6jeFu6FxJvqL8qRxq/FfGO/UhsGgQM9oGdXkV4xUgli+dt26biB6A== +"@typescript-eslint/eslint-plugin@^8.9.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz#7dc0e419c87beadc8f554bf5a42e5009ed3748dc" + integrity sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.7.0" - "@typescript-eslint/type-utils" "8.7.0" - "@typescript-eslint/utils" "8.7.0" - "@typescript-eslint/visitor-keys" "8.7.0" + "@typescript-eslint/scope-manager" "8.14.0" + "@typescript-eslint/type-utils" "8.14.0" + "@typescript-eslint/utils" "8.14.0" + "@typescript-eslint/visitor-keys" "8.14.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^8.7.0": - version "8.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.7.0.tgz#a567b0890d13db72c7348e1d88442ea8ab4e9173" - integrity sha512-lN0btVpj2unxHlNYLI//BQ7nzbMJYBVQX5+pbNXvGYazdlgYonMn4AhhHifQ+J4fGRYA/m1DjaQjx+fDetqBOQ== +"@typescript-eslint/parser@^8.9.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.14.0.tgz#0a7e9dbc11bc07716ab2d7b1226217e9f6b51fc8" + integrity sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA== dependencies: - "@typescript-eslint/scope-manager" "8.7.0" - "@typescript-eslint/types" "8.7.0" - "@typescript-eslint/typescript-estree" "8.7.0" - "@typescript-eslint/visitor-keys" "8.7.0" + "@typescript-eslint/scope-manager" "8.14.0" + "@typescript-eslint/types" "8.14.0" + "@typescript-eslint/typescript-estree" "8.14.0" + "@typescript-eslint/visitor-keys" "8.14.0" debug "^4.3.4" +"@typescript-eslint/scope-manager@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz#01f37c147a735cd78f0ff355e033b9457da1f373" + integrity sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw== + dependencies: + "@typescript-eslint/types" "8.14.0" + "@typescript-eslint/visitor-keys" "8.14.0" + "@typescript-eslint/scope-manager@8.7.0": version "8.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz#90ee7bf9bc982b9260b93347c01a8bc2b595e0b8" @@ -1093,21 +1323,40 @@ "@typescript-eslint/types" "8.7.0" "@typescript-eslint/visitor-keys" "8.7.0" -"@typescript-eslint/type-utils@8.7.0": - version "8.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.7.0.tgz#d56b104183bdcffcc434a23d1ce26cde5e42df93" - integrity sha512-tl0N0Mj3hMSkEYhLkjREp54OSb/FI6qyCzfiiclvJvOqre6hsZTGSnHtmFLDU8TIM62G7ygEa1bI08lcuRwEnQ== +"@typescript-eslint/type-utils@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz#455c6af30c336b24a1af28bc4f81b8dd5d74d94d" + integrity sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ== dependencies: - "@typescript-eslint/typescript-estree" "8.7.0" - "@typescript-eslint/utils" "8.7.0" + "@typescript-eslint/typescript-estree" "8.14.0" + "@typescript-eslint/utils" "8.14.0" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@8.7.0", "@typescript-eslint/types@^8.7.0": +"@typescript-eslint/types@8.14.0", "@typescript-eslint/types@^8.9.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.14.0.tgz#0d33d8d0b08479c424e7d654855fddf2c71e4021" + integrity sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g== + +"@typescript-eslint/types@8.7.0": version "8.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.7.0.tgz#21d987201c07b69ce7ddc03451d7196e5445ad19" integrity sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w== +"@typescript-eslint/typescript-estree@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz#a7a3a5a53a6c09313e12fb4531d4ff582ee3c312" + integrity sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ== + dependencies: + "@typescript-eslint/types" "8.14.0" + "@typescript-eslint/visitor-keys" "8.14.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 "^1.3.0" + "@typescript-eslint/typescript-estree@8.7.0": version "8.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz#6c7db6baa4380b937fa81466c546d052f362d0e8" @@ -1122,7 +1371,17 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.7.0", "@typescript-eslint/utils@^8.1.0", "@typescript-eslint/utils@^8.4.0", "@typescript-eslint/utils@^8.7.0": +"@typescript-eslint/utils@8.14.0", "@typescript-eslint/utils@^8.12.2", "@typescript-eslint/utils@^8.9.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.14.0.tgz#ac2506875e03aba24e602364e43b2dfa45529dbd" + integrity sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.14.0" + "@typescript-eslint/types" "8.14.0" + "@typescript-eslint/typescript-estree" "8.14.0" + +"@typescript-eslint/utils@^8.1.0": version "8.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.7.0.tgz#cef3f70708b5b5fd7ed8672fc14714472bd8a011" integrity sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw== @@ -1132,6 +1391,14 @@ "@typescript-eslint/types" "8.7.0" "@typescript-eslint/typescript-estree" "8.7.0" +"@typescript-eslint/visitor-keys@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz#2418d5a54669af9658986ade4e6cfb7767d815ad" + integrity sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ== + dependencies: + "@typescript-eslint/types" "8.14.0" + eslint-visitor-keys "^3.4.3" + "@typescript-eslint/visitor-keys@8.7.0": version "8.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz#5e46f1777f9d69360a883c1a56ac3c511c9659a8" @@ -1147,10 +1414,10 @@ dependencies: crypto-js "^4.2.0" -"@vercel/build-utils@8.4.5": - version "8.4.5" - resolved "https://registry.yarnpkg.com/@vercel/build-utils/-/build-utils-8.4.5.tgz#f530b4ec9a63a07b7b0a86ea26c5f73197434a1f" - integrity sha512-uqnc1OIA+CB4oIqsfQpn/7DCeUo9mO2TjoQLTdWEgYwb/PBkI25jJLB3oq4X6yKCnSFSTb1zoHdqkroWLCNsIw== +"@vercel/build-utils@8.4.12": + version "8.4.12" + resolved "https://registry.yarnpkg.com/@vercel/build-utils/-/build-utils-8.4.12.tgz#8c5dcd4269bc2347b89424805d28e2661a3aa302" + integrity sha512-pIH0b965wJhd1otROVPndfZenPKFVoYSaRjtSKVOT/oNBT13ifq86UVjb5ZjoVfqUI2TtSTP+68kBqLPeoq30g== "@vercel/error-utils@2.0.2": version "2.0.2" @@ -1175,16 +1442,16 @@ node-gyp-build "^4.2.2" resolve-from "^5.0.0" -"@vercel/node@^3.2.14": - version "3.2.17" - resolved "https://registry.yarnpkg.com/@vercel/node/-/node-3.2.17.tgz#8ebfea8750725bd57000ec459f2088a672237c91" - integrity sha512-TaT2ieOqq4dvi44g1S073MsP4+D+KRzdm2986WjmsldSp6Xpfhai/mowpBkSYGVToiQEnvdi+qb9b9lMnvr5jQ== +"@vercel/node@^3.2.24": + version "3.2.24" + resolved "https://registry.yarnpkg.com/@vercel/node/-/node-3.2.24.tgz#1c52a264e1355ee39f9bf87575f3b2442106b1ef" + integrity sha512-KEm50YBmcfRNOw5NfdcqMI4BkP4+5TD9kRwAByHHlIZXLj1NTTknvMF+69sHBYzwpK/SUZIkeo7jTrtcl4g+RQ== dependencies: "@edge-runtime/node-utils" "2.3.0" "@edge-runtime/primitives" "4.1.0" "@edge-runtime/vm" "3.2.0" "@types/node" "16.18.11" - "@vercel/build-utils" "8.4.5" + "@vercel/build-utils" "8.4.12" "@vercel/error-utils" "2.0.2" "@vercel/nft" "0.27.3" "@vercel/static-config" "3.0.0" @@ -1210,10 +1477,10 @@ json-schema-to-ts "1.6.4" ts-morph "12.0.0" -"@vitest/eslint-plugin@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@vitest/eslint-plugin/-/eslint-plugin-1.1.4.tgz#02d53b98eda8b1892a575c879c5e5e62658b95cd" - integrity sha512-kudjgefmJJ7xQ2WfbUU6pZbm7Ou4gLYRaao/8Ynide3G0QhVKHd978sDyWX4KOH0CCMH9cyrGAkFd55eGzJ48Q== +"@vitest/eslint-plugin@^1.1.7": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@vitest/eslint-plugin/-/eslint-plugin-1.1.10.tgz#db46986904493b9824057f4385cd75b49cad320f" + integrity sha512-uScH5Kz5v32vvtQYB2iodpoPg2mGASK+VKpjlc2IUgE0+16uZKqVKi2vQxjxJ6sMCQLBs4xhBFZlmZBszsmfKQ== "@volar/language-core@2.4.5", "@volar/language-core@~2.4.1": version "2.4.5" @@ -1287,6 +1554,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + acorn-import-attributes@^1.9.5: version "1.9.5" resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" @@ -1309,6 +1583,11 @@ acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -1316,7 +1595,7 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agentkeepalive@^4.1.3: +agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== @@ -1331,6 +1610,25 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ai@^3.4.33: + version "3.4.33" + resolved "https://registry.yarnpkg.com/ai/-/ai-3.4.33.tgz#b52ac6a7def6972bb5edaf1867ffc13526b8d51d" + integrity sha512-plBlrVZKwPoRTmM8+D1sJac9Bq8eaa2jiZlHLZIWekKWI1yMWYZvCCEezY9ASPwRhULYDJB2VhKOBUUeg3S5JQ== + dependencies: + "@ai-sdk/provider" "0.0.26" + "@ai-sdk/provider-utils" "1.0.22" + "@ai-sdk/react" "0.0.70" + "@ai-sdk/solid" "0.0.54" + "@ai-sdk/svelte" "0.0.57" + "@ai-sdk/ui-utils" "0.0.50" + "@ai-sdk/vue" "0.0.59" + "@opentelemetry/api" "1.9.0" + eventsource-parser "1.1.2" + json-schema "^0.4.0" + jsondiffpatch "0.6.0" + secure-json-parse "^2.7.0" + zod-to-json-schema "^3.23.3" + ajv-draft-04@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" @@ -1363,7 +1661,7 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0: +ajv@^8.0.0, ajv@^8.0.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -1470,6 +1768,11 @@ argparse@~1.0.9: dependencies: sprintf-js "~1.0.2" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + as-table@^1.0.36: version "1.0.55" resolved "https://registry.yarnpkg.com/as-table/-/as-table-1.0.55.tgz#dc984da3937745de902cea1d45843c01bdbbec4f" @@ -1477,6 +1780,11 @@ as-table@^1.0.36: dependencies: printable-characters "^1.0.42" +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-listen@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/async-listen/-/async-listen-3.0.0.tgz#2e5941390b7d8c753d4dbe94bc6aecbdde52ec5e" @@ -1492,11 +1800,21 @@ async-sema@^3.1.1: resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808" integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -1555,15 +1873,15 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.23.3: - version "4.24.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.0.tgz#a1325fe4bc80b64fda169629fc01b3d6cecd38d4" - integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== +browserslist@^4.24.2: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001663" - electron-to-chromium "^1.5.28" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" node-releases "^2.0.18" - update-browserslist-db "^1.1.0" + update-browserslist-db "^1.1.1" buffer@^5.5.0: version "5.7.1" @@ -1607,10 +1925,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001663: - version "1.0.30001664" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz#d588d75c9682d3301956b05a3749652a80677df4" - integrity sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g== +caniuse-lite@^1.0.30001669: + version "1.0.30001680" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e" + integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA== capnp-ts@^0.7.0: version "0.7.0" @@ -1642,6 +1960,11 @@ chalk@^4.0.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + character-entities@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" @@ -1694,6 +2017,11 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +client-only@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -1753,6 +2081,18 @@ color-support@^1.1.2, color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^8.0.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -1798,12 +2138,22 @@ cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -core-js-compat@^3.37.0: - version "3.38.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" - integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== +core-js-compat@^3.38.1: + version "3.39.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61" + integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw== dependencies: - browserslist "^4.23.3" + browserslist "^4.24.2" + +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" create-require@^1.1.0: version "1.1.1" @@ -1824,22 +2174,45 @@ crypto-js@^4.2.0: resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== +css-functions-list@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.3.tgz#95652b0c24f0f59b291a9fc386041a19d4f40dbe" + integrity sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA== + +css-tree@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.0.1.tgz#bea6deaea60bb5bcf416adfb1ecf607a8d9471f6" + integrity sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q== + dependencies: + mdn-data "2.12.1" + source-map-js "^1.0.1" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + data-uri-to-buffer@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz#d296973d5a4897a5dbe31716d118211921f04770" integrity sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA== +date-fns@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== + de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== -debug@4, debug@^4.0.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: +debug@4, debug@^4.0.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6, debug@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1894,6 +2267,11 @@ defu@^6.1.4: resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -1921,11 +2299,23 @@ devlop@^1.0.0, devlop@^1.1.0: dependencies: dequal "^2.0.0" +diff-match-patch@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1948,10 +2338,10 @@ edge-runtime@2.5.9: signal-exit "4.0.2" time-span "4.0.0" -electron-to-chromium@^1.5.28: - version "1.5.29" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz#aa592a3caa95d07cc26a66563accf99fa573a1ee" - integrity sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw== +electron-to-chromium@^1.5.41: + version "1.5.57" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.57.tgz#cb43af8784166bca24565b3418bf5f775a6b1c86" + integrity sha512-xS65H/tqgOwUBa5UmOuNSLuslDo7zho0y/lgQw35pnrqiZh7UOWHCeL/Bt6noJATbA6tpQJGCifsFsIRZj1Fqg== emoji-regex@^8.0.0: version "8.0.0" @@ -1972,7 +2362,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.17.0: +enhanced-resolve@^5.17.1: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== @@ -1985,7 +2375,7 @@ entities@^4.5.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -env-paths@^2.2.0: +env-paths@^2.2.0, env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== @@ -2307,7 +2697,7 @@ eslint-plugin-command@^0.2.6: dependencies: "@es-joy/jsdoccomment" "^0.48.0" -eslint-plugin-es-x@^7.5.0: +eslint-plugin-es-x@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== @@ -2329,10 +2719,10 @@ eslint-plugin-format@^0.1.2: prettier "^3.3.2" synckit "^0.9.0" -eslint-plugin-import-x@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.3.0.tgz#1b59ca3bda6a34d2eb0c09196ccd1f905fc30861" - integrity sha512-PxGzP7gAjF2DLeRnQtbYkkgZDg1intFyYr/XS1LgTYXUDrSXMHGkXx8++6i2eDv2jMs0jfeO6G6ykyeWxiFX7w== +eslint-plugin-import-x@^4.3.1: + version "4.4.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.4.2.tgz#2fef264059e2c4a25d149cf0323fe9205427a74c" + integrity sha512-mDRXPSLQ0UQZQw91QdG4/qZT6hgeW2MJTczAbgPseUZuPEtIjjdPOolXroRkulnOn3fzj6gNgvk+wchMJiHElg== dependencies: "@typescript-eslint/utils" "^8.1.0" debug "^4.3.4" @@ -2345,12 +2735,12 @@ eslint-plugin-import-x@^4.3.0: stable-hash "^0.0.4" tslib "^2.6.3" -eslint-plugin-jsdoc@^50.3.0: - version "50.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.3.0.tgz#2a4d1ac7f45b2b62de42389ba8006fd00b7f08dd" - integrity sha512-P7qDB/RckdKETpBM4CtjHRQ5qXByPmFhRi86sN3E+J+tySchq+RSOGGhI2hDIefmmKFuTi/1ACjqsnDJDDDfzg== +eslint-plugin-jsdoc@^50.4.1: + version "50.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.5.0.tgz#3b34b7846eb6c40750e68e97ae9441455fde7a75" + integrity sha512-xTkshfZrUbiSHXBwZ/9d5ulZ2OcHXxSvm/NPo494H/hadLRJwOq5PMV0EUpMqsb9V+kQo+9BAgi6Z7aJtdBp2A== dependencies: - "@es-joy/jsdoccomment" "~0.48.0" + "@es-joy/jsdoccomment" "~0.49.0" are-docs-informative "^0.0.2" comment-parser "1.4.1" debug "^4.3.6" @@ -2375,32 +2765,32 @@ eslint-plugin-jsonc@^2.16.0: natural-compare "^1.4.0" synckit "^0.6.0" -eslint-plugin-n@^17.10.3: - version "17.10.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.10.3.tgz#6c8bae69b4d3354fe25c3c844afb3f83b83a3942" - integrity sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - enhanced-resolve "^5.17.0" - eslint-plugin-es-x "^7.5.0" - get-tsconfig "^4.7.0" - globals "^15.8.0" - ignore "^5.2.4" +eslint-plugin-n@^17.11.1: + version "17.13.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.13.1.tgz#3178c87989ad23417d22c5f66a13ecb1e9c5245e" + integrity sha512-97qzhk1z3DdSJNCqT45EslwCu5+LB9GDadSyBItgKUfGsXAmN/aa7LRQ0ZxHffUxUzvgbTPJL27/pE9ZQWHy7A== + dependencies: + "@eslint-community/eslint-utils" "^4.4.1" + enhanced-resolve "^5.17.1" + eslint-plugin-es-x "^7.8.0" + get-tsconfig "^4.8.1" + globals "^15.11.0" + ignore "^5.3.2" minimatch "^9.0.5" - semver "^7.5.3" + semver "^7.6.3" eslint-plugin-no-only-tests@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz#d9d42ccd4b5d099b4872fb5046cf95441188cfb5" integrity sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q== -eslint-plugin-perfectionist@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-3.7.0.tgz#dac1253ee51e2653806cb681d2060e7195283ea0" - integrity sha512-pemhfcR3LDbYVWeveHok9u048yR7GpsnfyPvn6RsDkp/UV7iqBV0y5K0aGb9ZJMsemOyWok7akxGzPLsz+mHKQ== +eslint-plugin-perfectionist@^3.9.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-3.9.1.tgz#19e4088e161f299a458bcd22be98bc9d1fb55099" + integrity sha512-9WRzf6XaAxF4Oi5t/3TqKP5zUjERhasHmLFHin2Yw6ZAp/EP/EVA2dr3BhQrrHWCm5SzTMZf0FcjDnBkO2xFkA== dependencies: - "@typescript-eslint/types" "^8.7.0" - "@typescript-eslint/utils" "^8.7.0" + "@typescript-eslint/types" "^8.9.0" + "@typescript-eslint/utils" "^8.9.0" minimatch "^9.0.5" natural-compare-lite "^1.4.0" @@ -2427,18 +2817,18 @@ eslint-plugin-toml@^0.11.1: lodash "^4.17.19" toml-eslint-parser "^0.10.0" -eslint-plugin-unicorn@^55.0.0: - version "55.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz#e2aeb397914799895702480970e7d148df5bcc7b" - integrity sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA== +eslint-plugin-unicorn@^56.0.0: + version "56.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.0.tgz#9fd3ebe6f478571734541fa745026b743175b59e" + integrity sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw== dependencies: - "@babel/helper-validator-identifier" "^7.24.5" + "@babel/helper-validator-identifier" "^7.24.7" "@eslint-community/eslint-utils" "^4.4.0" ci-info "^4.0.0" clean-regexp "^1.0.0" - core-js-compat "^3.37.0" - esquery "^1.5.0" - globals "^15.7.0" + core-js-compat "^3.38.1" + esquery "^1.6.0" + globals "^15.9.0" indent-string "^4.0.0" is-builtin-module "^3.2.1" jsesc "^3.0.2" @@ -2446,7 +2836,7 @@ eslint-plugin-unicorn@^55.0.0: read-pkg-up "^7.0.1" regexp-tree "^0.1.27" regjsparser "^0.10.0" - semver "^7.6.1" + semver "^7.6.3" strip-indent "^3.0.0" eslint-plugin-unused-imports@^4.1.4: @@ -2454,10 +2844,10 @@ eslint-plugin-unused-imports@^4.1.4: resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738" integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ== -eslint-plugin-vue@^9.28.0: - version "9.28.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz#e4412f0c1024bafd15ffeaa6f76f4c99152e2765" - integrity sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g== +eslint-plugin-vue@^9.29.0: + version "9.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.31.0.tgz#5da53c2826f8efd8a62835aad596826053b1085e" + integrity sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" globals "^13.24.0" @@ -2492,10 +2882,10 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-scope@^8.0.2: - version "8.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.1.0.tgz#70214a174d4cbffbc3e8a26911d8bf51b9ae9d30" - integrity sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -2505,26 +2895,31 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.0.0, eslint-visitor-keys@^4.1.0: +eslint-visitor-keys@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c" integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg== -eslint@^9.10.0: - version "9.11.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.11.1.tgz#701e5fc528990153f9cef696d8427003b5206567" - integrity sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.11.0" + "@eslint-community/regexpp" "^4.12.1" "@eslint/config-array" "^0.18.0" - "@eslint/core" "^0.6.0" + "@eslint/core" "^0.7.0" "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.11.1" + "@eslint/js" "9.14.0" "@eslint/plugin-kit" "^0.2.0" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.3.0" - "@nodelib/fs.walk" "^1.2.8" + "@humanwhocodes/retry" "^0.4.0" "@types/estree" "^1.0.6" "@types/json-schema" "^7.0.15" ajv "^6.12.4" @@ -2532,9 +2927,9 @@ eslint@^9.10.0: cross-spawn "^7.0.2" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.0.2" - eslint-visitor-keys "^4.0.0" - espree "^10.1.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2544,13 +2939,11 @@ eslint@^9.10.0: ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" 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" - strip-ansi "^6.0.1" text-table "^0.2.0" espree@^10.0.1, espree@^10.1.0: @@ -2562,6 +2955,15 @@ espree@^10.0.1, espree@^10.1.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.1.0" +espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + espree@^9.0.0, espree@^9.3.1, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -2610,6 +3012,16 @@ etag@1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventsource-parser@1.1.2, eventsource-parser@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-1.1.2.tgz#ed6154a4e3dbe7cda9278e5e35d2ffc58b309f89" + integrity sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA== + exit-hook@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" @@ -2630,7 +3042,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.7, fast-glob@^3.3.2: +fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -2656,6 +3068,11 @@ fast-uri@^3.0.1: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.2.tgz#d78b298cf70fd3b752fd951175a3da6a7b48f024" integrity sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row== +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -2670,6 +3087,13 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" +file-entry-cache@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-9.1.0.tgz#2e66ad98ce93f49aed1b178c57b0b5741591e075" + integrity sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg== + dependencies: + flat-cache "^5.0.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -2711,11 +3135,41 @@ flat-cache@^4.0.0: flatted "^3.2.9" keyv "^4.5.4" -flatted@^3.2.9: +flat-cache@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-5.0.0.tgz#26c4da7b0f288b408bb2b506b2cb66c240ddf062" + integrity sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ== + dependencies: + flatted "^3.3.1" + keyv "^4.5.4" + +flatted@^3.2.9, flatted@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -2803,7 +3257,7 @@ get-source@^2.0.12: data-uri-to-buffer "^2.0.0" source-map "^0.6.1" -get-tsconfig@^4.7.0, get-tsconfig@^4.7.3: +get-tsconfig@^4.7.3, get-tsconfig@^4.8.1: version "4.8.1" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== @@ -2853,6 +3307,22 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^13.24.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -2865,11 +3335,33 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -globals@^15.7.0, globals@^15.8.0, globals@^15.9.0: +globals@^15.11.0: + version "15.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.12.0.tgz#1811872883ad8f41055b61457a130221297de5b5" + integrity sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ== + +globals@^15.9.0: version "15.9.0" resolved "https://registry.yarnpkg.com/globals/-/globals-15.9.0.tgz#e9de01771091ffbc37db5714dab484f9f69ff399" integrity sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA== +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -2912,6 +3404,11 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + http-cache-semantics@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" @@ -2953,12 +3450,17 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1, ignore@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -import-fresh@^3.2.1: +ignore@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-6.0.2.tgz#77cccb72a55796af1b6d2f9eb14fa326d24f4283" + integrity sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A== + +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -2999,7 +3501,7 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@~1.3.0: +ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -3085,21 +3587,21 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-unicode-supported@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" - integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +itty-time@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/itty-time/-/itty-time-1.0.6.tgz#a6eeda619f19d2f4c480ceddd013b93acb05714d" + integrity sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw== + jju@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" @@ -3114,7 +3616,7 @@ js-cleanup@^1.2.0: perf-regexes "^1.0.1" skip-regex "^1.0.2" -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3174,6 +3676,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -3189,6 +3696,15 @@ jsonc-eslint-parser@^2.0.4, jsonc-eslint-parser@^2.4.0: espree "^9.0.0" semver "^7.3.5" +jsondiffpatch@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz#daa6a25bedf0830974c81545568d5f671c82551f" + integrity sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ== + dependencies: + "@types/diff-match-patch" "^1.0.36" + chalk "^5.3.0" + diff-match-patch "^1.0.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -3212,6 +3728,16 @@ keyv@^4.5.4: dependencies: json-buffer "3.0.1" +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +known-css-properties@^0.34.0: + version "0.34.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.34.0.tgz#ccd7e9f4388302231b3f174a8b1d5b1f7b576cea" + integrity sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ== + kolorist@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" @@ -3267,6 +3793,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash@^4.17.19, lodash@^4.17.21, lodash@~4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -3277,6 +3808,13 @@ longest-streak@^3.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3337,6 +3875,11 @@ markdown-table@^3.0.0: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + mdast-util-find-and-replace@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0" @@ -3459,7 +4002,17 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" -merge2@^1.3.0: +mdn-data@2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.1.tgz#10cb462215c13d95c92ff60d0fb3becac1bbb924" + integrity sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q== + +meow@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-13.2.0.tgz#6b7d63f913f984063b3cc261b6e8800c4cd3474f" + integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA== + +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -3737,7 +4290,7 @@ micromark@^4.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -3745,6 +4298,18 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" @@ -3760,10 +4325,10 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -miniflare@3.20240925.0: - version "3.20240925.0" - resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20240925.0.tgz#a291998dedf90bfb4bcfdad033ba030851ff9171" - integrity sha512-2LmQbKHf0n6ertUKhT+Iltixi53giqDH7P71+wCir3OnGyXIODqYwOECx1mSDNhYThpxM2dav8UdPn6SQiMoXw== +miniflare@3.20241106.0: + version "3.20241106.0" + resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20241106.0.tgz#d69854e7267862468dfc057c23ca57bf7801e89d" + integrity sha512-PjOoJKjUUofCueQskfhXlGvvHxZj36UAJAp1DnquMK88MFF50zCULblh0KXMSNM+bXeQYA94Gj06a7kfmBGxPw== dependencies: "@cspotcode/source-map-support" "0.8.1" acorn "^8.8.0" @@ -3773,8 +4338,8 @@ miniflare@3.20240925.0: glob-to-regexp "^0.4.1" stoppable "^1.1.0" undici "^5.28.4" - workerd "1.20240925.0" - ws "^8.17.1" + workerd "1.20241106.1" + ws "^8.18.0" youch "^3.2.2" zod "^3.22.3" @@ -3945,6 +4510,11 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" @@ -4063,6 +4633,19 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +openai@^4.68.1: + version "4.71.1" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.71.1.tgz#f26bf5db00d75703676d80bf0ae7cb7674e41eac" + integrity sha512-C6JNMaQ1eijM0lrjiRUL3MgThVP5RdwNAghpbJFdW0t11LzmyqON8Eh8MuUuEZ+CeD6bgYl2Fkn2BoptVxv9Ug== + 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" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -4140,7 +4723,7 @@ parse-imports@^2.1.1: es-module-lexer "^1.5.3" slashes "^3.0.12" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -4190,6 +4773,11 @@ path-to-regexp@^6.3.0: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" @@ -4210,6 +4798,11 @@ picocolors@^1.0.0, picocolors@^1.1.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== +picocolors@^1.0.1, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -4234,7 +4827,17 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -postcss-selector-parser@^6.0.15: +postcss-resolve-nested-selector@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz#3d84dec809f34de020372c41b039956966896686" + integrity sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw== + +postcss-safe-parser@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz#36e4f7e608111a0ca940fd9712ce034718c40ec0" + integrity sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A== + +postcss-selector-parser@^6.0.15, postcss-selector-parser@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== @@ -4242,6 +4845,11 @@ postcss-selector-parser@^6.0.15: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + postcss@^8.4.43: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" @@ -4251,6 +4859,15 @@ postcss@^8.4.43: picocolors "^1.1.0" source-map-js "^1.2.1" +postcss@^8.4.47: + version "8.4.49" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prebuild-install@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" @@ -4339,6 +4956,14 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.2" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -4547,6 +5172,13 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + scslre@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/scslre/-/scslre-0.3.0.tgz#c3211e9bfc5547fc86b1eabaa34ed1a657060155" @@ -4556,6 +5188,11 @@ scslre@^0.3.0: refa "^0.12.0" regexp-ast-analysis "^0.7.0" +secure-json-parse@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + selfsigned@^2.0.1: version "2.4.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" @@ -4574,7 +5211,7 @@ semver@^6.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.1, semver@^7.6.3: +semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -4613,6 +5250,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -4637,11 +5279,25 @@ skip-regex@^1.0.2: resolved "https://registry.yarnpkg.com/skip-regex/-/skip-regex-1.0.2.tgz#ac655d77e7c771ac2b9f37585fea37bff56ad65b" integrity sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slashes@^3.0.12: version "3.0.12" resolved "https://registry.yarnpkg.com/slashes/-/slashes-3.0.12.tgz#3d664c877ad542dc1509eaf2c50f38d483a6435a" integrity sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -4664,7 +5320,7 @@ socks@^2.6.2: ip-address "^9.0.5" smart-buffer "^4.2.0" -source-map-js@^1.2.0, source-map-js@^1.2.1: +source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -4742,6 +5398,13 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +sswr@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sswr/-/sswr-2.1.0.tgz#1eb64cd647cc9e11f871e7f43554abd8c64e1103" + integrity sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ== + dependencies: + swrev "^4.0.0" + stable-hash@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.4.tgz#55ae7dadc13e4b3faed13601587cec41859b42f7" @@ -4810,6 +5473,50 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +stylelint@^16.10.0: + version "16.10.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-16.10.0.tgz#452b42a5d82f2ad910954eb2ba2b3a2ec583cd75" + integrity sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ== + dependencies: + "@csstools/css-parser-algorithms" "^3.0.1" + "@csstools/css-tokenizer" "^3.0.1" + "@csstools/media-query-list-parser" "^3.0.1" + "@csstools/selector-specificity" "^4.0.0" + "@dual-bundle/import-meta-resolve" "^4.1.0" + balanced-match "^2.0.0" + colord "^2.9.3" + cosmiconfig "^9.0.0" + css-functions-list "^3.2.3" + css-tree "^3.0.0" + debug "^4.3.7" + fast-glob "^3.3.2" + fastest-levenshtein "^1.0.16" + file-entry-cache "^9.1.0" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.3.1" + ignore "^6.0.2" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.34.0" + mathml-tag-names "^2.1.3" + meow "^13.2.0" + micromatch "^4.0.8" + normalize-path "^3.0.0" + picocolors "^1.0.1" + postcss "^8.4.47" + postcss-resolve-nested-selector "^0.1.6" + postcss-safe-parser "^7.0.1" + postcss-selector-parser "^6.1.2" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + string-width "^4.2.3" + supports-hyperlinks "^3.1.0" + svg-tags "^1.0.0" + table "^6.8.2" + write-file-atomic "^5.0.1" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -4817,7 +5524,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -4831,11 +5538,42 @@ supports-color@~8.1.1: dependencies: has-flag "^4.0.0" +supports-hyperlinks@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz#b56150ff0173baacc15f21956450b61f2b18d3ac" + integrity sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +swr@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b" + integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg== + dependencies: + client-only "^0.0.1" + use-sync-external-store "^1.2.0" + +swrev@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/swrev/-/swrev-4.0.0.tgz#83da6983c7ef9d71ac984a9b169fc197cbf18ff8" + integrity sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA== + +swrv@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/swrv/-/swrv-1.0.4.tgz#278b4811ed4acbb1ae46654972a482fd1847e480" + integrity sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g== + synckit@^0.6.0: version "0.6.2" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.6.2.tgz#e1540b97825f2855f7170b98276e8463167f33eb" @@ -4851,6 +5589,17 @@ synckit@^0.9.0, synckit@^0.9.1: "@pkgr/core" "^0.1.0" tslib "^2.6.2" +table@^6.8.2: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -4889,16 +5638,21 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -telegram-bot-api-types@^7.9.12: - version "7.9.12" - resolved "https://registry.yarnpkg.com/telegram-bot-api-types/-/telegram-bot-api-types-7.9.12.tgz#0631d9e87d5190c21283415150f7b5f16af33cec" - integrity sha512-RM69ix6NdgTIxPFjqI5qO5AEGsMsc623AvsJPWjYHMvcTV9Zvx3/VBYCyTpMoVUSYy8NmQ9zQx0xUJVGAe14Dg== +telegram-bot-api-types@^7.11.0: + version "7.11.0" + resolved "https://registry.yarnpkg.com/telegram-bot-api-types/-/telegram-bot-api-types-7.11.0.tgz#5d9384983de2511bb1852aa0bf1ef42a02f82001" + integrity sha512-YnKHyhwGWUivI8ByShKFbLUFurt5uDvYaDjn+RC9hyx74PXSmk8mB1RhUtDYSqF3B8NE8/T1v8gK3yZaw3k2iA== text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +throttleit@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" + integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== + time-span@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/time-span/-/time-span-4.0.0.tgz#fe74cd50a54e7998712f90ddfe47109040c985c4" @@ -4987,10 +5741,10 @@ tslib@^2.2.0, tslib@^2.3.1, tslib@^2.6.2, tslib@^2.6.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== -tsx@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.1.tgz#b7bffdf4b565813e4dea14b90872af279cd0090b" - integrity sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA== +tsx@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.2.tgz#2d7814783440e0ae42354d0417d9c2989a2ae92c" + integrity sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g== dependencies: esbuild "~0.23.0" get-tsconfig "^4.7.5" @@ -5041,17 +5795,22 @@ typescript@5.4.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== -typescript@^5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" - integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== +typescript@^5.6.3: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== ufo@^1.5.3, ufo@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== -undici-types@~6.19.2: +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~6.19.2, undici-types@~6.19.8: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== @@ -5068,10 +5827,10 @@ undici@^6.19.8: resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1" integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g== -"unenv@npm:unenv-nightly@2.0.0-20240919-125358-9a64854": - version "2.0.0-20240919-125358-9a64854" - resolved "https://registry.yarnpkg.com/unenv-nightly/-/unenv-nightly-2.0.0-20240919-125358-9a64854.tgz#13f6812c7b12b9521ea05c6d49259d136e093acd" - integrity sha512-XjsgUTrTHR7iw+k/SRTNjh6EQgwpC9voygnoCJo5kh4hKqsSDHUW84MhL9EsHTNfLctvVBHaSw8e2k3R2fKXsQ== +"unenv@npm:unenv-nightly@2.0.0-20241024-111401-d4156ac": + version "2.0.0-20241024-111401-d4156ac" + resolved "https://registry.yarnpkg.com/unenv-nightly/-/unenv-nightly-2.0.0-20241024-111401-d4156ac.tgz#000835e7383ace38ad31351dc13e623d20b82855" + integrity sha512-xJO1hfY+Te+/XnfCYrCbFbRcgu6XEODND1s5wnVbaBCkuQX7JXF7fHEXPrukFE2j8EOH848P8QN19VO47XN8hw== dependencies: defu "^6.1.4" ohash "^1.1.4" @@ -5133,7 +5892,7 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -update-browserslist-db@^1.1.0: +update-browserslist-db@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== @@ -5148,6 +5907,11 @@ uri-js@^4.2.2, uri-js@^4.4.1: dependencies: punycode "^2.1.0" +use-sync-external-store@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5186,12 +5950,12 @@ vite-plugin-checker@^0.8.0: vscode-languageserver-textdocument "^1.0.1" vscode-uri "^3.0.2" -vite-plugin-dts@^4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-4.2.2.tgz#96fd412827e545564a6dfe2de678feec5ddbed5c" - integrity sha512-USwTMReZFf8yXV+cKkm4WOMqmFjbReAvkyxON5xzdnZzJEBnFgax6BBDZIGGr9WMJYvhHdpaIHLrOjXDcla4OA== +vite-plugin-dts@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-4.3.0.tgz#f7365e4969f018ee47e6dda55adf051aa47083a7" + integrity sha512-LkBJh9IbLwL6/rxh0C1/bOurDrIEmRE7joC+jFdOEEciAFPbpEKOLSAr5nNh5R7CJ45cMbksTrFfy52szzC5eA== dependencies: - "@microsoft/api-extractor" "7.47.7" + "@microsoft/api-extractor" "^7.47.11" "@rollup/pluginutils" "^5.1.0" "@volar/typescript" "^2.4.4" "@vue/language-core" "2.1.6" @@ -5201,10 +5965,10 @@ vite-plugin-dts@^4.2.1: local-pkg "^0.5.0" magic-string "^0.30.11" -vite@^5.4.3: - version "5.4.8" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8" - integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ== +vite@^5.4.11: + version "5.4.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== dependencies: esbuild "^0.21.3" postcss "^8.4.43" @@ -5269,6 +6033,11 @@ vue-eslint-parser@^9.4.3: lodash "^4.17.21" semver "^7.3.6" +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -5282,6 +6051,13 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -5301,38 +6077,40 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -workerd@1.20240925.0: - version "1.20240925.0" - resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20240925.0.tgz#0a2602eabfa7e1d01d89ff2b600ed359be9b515d" - integrity sha512-/Jj6+yLwfieZGEt3Kx4+5MoufuC3g/8iFaIh4MPBNGJOGYmdSKXvgCqz09m2+tVCYnysRfbq2zcbVxJRBfOCqQ== +workerd@1.20241106.1: + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20241106.1.tgz#08f3d63f70cd750a1f2c0652cd76c4844fe06409" + integrity sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw== optionalDependencies: - "@cloudflare/workerd-darwin-64" "1.20240925.0" - "@cloudflare/workerd-darwin-arm64" "1.20240925.0" - "@cloudflare/workerd-linux-64" "1.20240925.0" - "@cloudflare/workerd-linux-arm64" "1.20240925.0" - "@cloudflare/workerd-windows-64" "1.20240925.0" + "@cloudflare/workerd-darwin-64" "1.20241106.1" + "@cloudflare/workerd-darwin-arm64" "1.20241106.1" + "@cloudflare/workerd-linux-64" "1.20241106.1" + "@cloudflare/workerd-linux-arm64" "1.20241106.1" + "@cloudflare/workerd-windows-64" "1.20241106.1" -wrangler@^3.78.5: - version "3.78.12" - resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.78.12.tgz#c3d7b605856b904ab7cce54afc223286b8454935" - integrity sha512-a/xk/N04IvOGk9J+BLkiFg42GDyPS+0BiJimbrHsbX+CDr8Iqq3HNMEyQld+6zbmq01u/gmc8S7GKVR9vDx4+g== +wrangler@^3.86.1: + version "3.86.1" + resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.86.1.tgz#69b1599397d7e26d2e5a41ddce5c385f73b2e6eb" + integrity sha512-ujN74lPQCQRsMcvf48WLLZUqOqUbJJzt5+Xm5T3tGrpRJLW+dTB0ffvT/e2DBBvHZ7R52ZzZdm1akyLPsZmZ9Q== dependencies: "@cloudflare/kv-asset-handler" "0.3.4" - "@cloudflare/workers-shared" "0.5.4" + "@cloudflare/workers-shared" "0.7.1" "@esbuild-plugins/node-globals-polyfill" "^0.2.3" "@esbuild-plugins/node-modules-polyfill" "^0.2.2" blake3-wasm "^2.1.5" chokidar "^3.5.3" + date-fns "^4.1.0" esbuild "0.17.19" - miniflare "3.20240925.0" + itty-time "^1.0.6" + miniflare "3.20241106.0" nanoid "^3.3.3" path-to-regexp "^6.3.0" resolve "^1.22.8" resolve.exports "^2.0.2" selfsigned "^2.0.1" source-map "^0.6.1" - unenv "npm:unenv-nightly@2.0.0-20240919-125358-9a64854" - workerd "1.20240925.0" + unenv "npm:unenv-nightly@2.0.0-20241024-111401-d4156ac" + workerd "1.20241106.1" xxhash-wasm "^1.0.1" optionalDependencies: fsevents "~2.3.2" @@ -5351,7 +6129,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.17.1: +write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + +ws@^8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== @@ -5427,6 +6213,11 @@ youch@^3.2.2: mustache "^4.2.0" stacktracey "^2.1.8" +zod-to-json-schema@^3.23.3: + version "3.23.5" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz#ec23def47dcafe3a4d640eba6a346b34f9a693a5" + integrity sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA== + zod@^3.22.3: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"