diff --git a/.github/ISSUE_TEMPLATE/功能请求-feature-request-.md b/.github/ISSUE_TEMPLATE/功能请求-feature-request-.md deleted file mode 100644 index b7b43db..0000000 --- a/.github/ISSUE_TEMPLATE/功能请求-feature-request-.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: 功能请求(Feature request) -about: 为本项目提出一个新想法 -title: '' -labels: '' -assignees: '' - ---- - -**你的功能请求是否与某个问题有关?请描述。** -问题的清晰而简明的描述。 - -**描述你想要的解决方案** -你想要发生什么的清晰而简明的描述。 - -**描述你已经考虑的替代方案** -对任何替代解决方案或功能的清晰简明的描述。 - -**附加说明** -在此处添加有关功能请求的任何其他说明、屏幕截图或者引用。 diff --git a/.github/ISSUE_TEMPLATE/问题反馈.md b/.github/ISSUE_TEMPLATE/问题反馈.md deleted file mode 100644 index fe9c1ef..0000000 --- a/.github/ISSUE_TEMPLATE/问题反馈.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: 问题反馈 -about: 提出bug解决问题并改进本项目 -title: '' -labels: '' -assignees: '' - ---- - -# 请确保提出问题前更新到最新版本!!!!!!!! - -**请在提交issue前确认你已阅读了以下资料:** - -- 项目的readme文件 -- 其他已有的Issue - -如果你的问题已经在readme或其他Issue中得到解答,我们很可能不会回复。请确保你的问题是一个新的问题。 - -## 问题描述 - -请在此处描述您遇到的问题,包括出现问题的环境、您试图实现的功能以及错误信息等。请尽可能详细,以便其他人可以在自己的环境中复制问题。 - -## 预期行为 - -请描述您期望系统在出现问题时应该做什么。 - -## 实际行为 - -请描述您实际看到的行为。 - -## 复制过程 - -请详细描述如何复制这个问题,包括所有必要的步骤、输入、任何错误信息以及输出。 - -## 环境 - -请提供您使用的任何相关信息,例如操作系统、版本、配置等。 - -## 可能的解决方案 - -如果您已经尝试了一些解决方案,请在此处描述这些解决方案,并说明是否有效。 - -## 附加信息 - -如果有任何其他信息,如日志、截图等,请在此处提供。 diff --git a/.github/workflows/tagged-released.yml b/.github/workflows/tagged-released.yml deleted file mode 100644 index a9c64e1..0000000 --- a/.github/workflows/tagged-released.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: "tagged-release" - -on: - push: - tags: - - "v*" - -jobs: - tagged-release: - name: "Tagged Release" - runs-on: "ubuntu-latest" - - steps: - # ... - - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false diff --git a/.gitignore b/.gitignore index 5d0cd48..98703f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ -config/* -!config/config.example.js -!config/config.example.json -!config/config.md -server/static/live2dw/* -!server/static/live2dw/Murasame -prompts/* -!prompts/.gitkeep node_modules/ +data/ +utils/processors +utils/tools +utils/triggers +memory.md +resources/simple +memory.db diff --git a/README.md b/README.md index 1291969..5392b3b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -![chatgpt-plugin](https://user-images.githubusercontent.com/21212372/232115814-de9a0633-371f-4733-8da0-dd6e912c8a1e.png) -

云崽QQ机器人的ChatGPT插件(开发分支请勿使用)

+![chatgpt-plugin](https://socialify.git.ci/ikechan8370/chatgpt-plugin/image?description=1&font=Jost&forks=1&issues=1&language=1&name=1&owner=1&pulls=1&stargazers=1&theme=Light)
- + @@ -15,299 +14,136 @@ +> 插件v3大幅重构中,基本可用,持续完善中。遇到问题请提issue,欢迎PR。 +> todo列表: +> - [x] 插件v3重构完成,插件基本功能可用,持续完善中。 +> - [ ] RAG知识库 +> - [ ] 预设更详细的配置 +> - [x] 自定义触发器 +> - [ ] 自定义插件 +> - [ ] 兼容mcp -### 推荐的相关文档和参考资料 -本README -[手册](https://chatgptplugin.ikechan8370.com/) -[文档1(建设中)](https://chatgpt-docs.err0r.top/) -[插件常见问题(鹤望兰版)](https://www.wolai.com/4FCxxWAdjbrHF29MCJmAQK) -[Yunzai常见问题(LUCK小运版)](https://www.wolai.com/oA43vuW71aBnv7UsEysn4T) -[憨憨博客](https://blog.hanhanz.top/) -## 特点 +## 插件简介 -* 支持单人连续对话Conversation -* API模式下,使用 gpt-3.5-turbo 或 gpt-4 API,仅需OpenAI Api Key,开箱即用。**注意收费** -* 支持问答图片截图和聊天记录导出 -* 支持AI性格调教,角色扮演强烈推荐Bing自定义模式 -* 支持对接vits和Azure等回答直接转语音 -* API3模式下,绕过Cloudflare防护直接访问ChatGPT的SSE API,与官方体验一致,且保留对话记录,在官网可查。免费。 -* (已不再维护)提供基于浏览器的解决方案作为备选,API3不可用的情况下或担心账户安全的用户可以选择使用浏览器模式。 -* 支持新[必应](https://www.bing.com/new)(token负载均衡,限流降级) -* 2023-03-15 API3支持GPT-4尝鲜,需要Plus用户 -* 支持[ChatGLM](https://github.com/THUDM/ChatGLM-6B)模型。基于[自建API](https://github.com/ikechan8370/SimpleChatGLM6BAPI) -* 2023-04-15 支持[Claude by Slack](https://www.anthropic.com/claude-in-slack )和Poe(WIP)。Claude配置参考[这里](https://ikechan8370.com/archives/chatgpt-plugin-for-yunzaipei-zhi-slack-claude) -* 2023-05-12 支持星火大模型 -* 2023-05-29 支持gpt-4 API.必应无需cookie即可对话(Sydney和自定义模式) +ChatGPT-Plugin 以 Chaite 为内核,将多模型渠道、工具、处理器、触发器、RAG 和管控面板封装成一套适配 Miao-Yunzai / Yunzai-Bot 的插件方案。通过 Chaite 的 API 服务器与可插拔的存储层(默认 SQLite),插件可以在本地完成高并发对话、知识库检索、伪人陪聊以及记忆管理,亦可接入 Chaite Cloud 复用在线渠道与工具。 -### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧! +## 核心特性 -## 版本要求 -Node.js >= 18 / Node.js >= 14(with node-fetch) -小白尽可能使用18版本以上的nodejs +- **多渠道与预设体系**:依托 Chaite 的 ChannelsManager 与 ChatPresetManager,支持为不同模型配置流量、负载均衡与个性化 prompt,群友也可在授权后自助切换预设。 +- **高级消息适配**:前后文触发方式支持 `@Bot` 与前缀;自动处理引用、图片、语音等多模态输入,并在工具调用或推理阶段通过转发消息回显。 +- **群上下文与伪人模式**:可按配置注入指定条数的群聊记录;BYM 伪人模式支持概率触发、关键词命中、预设覆盖及限时撤回,营造更拟人的陪聊体验。 +- **记忆与 RAG**:内置 memoryService + vectra 向量索引,提供群记忆、私人记忆与外部知识库(RAGManager)注入能力,支持混合检索与手动管理。 +- **可视化与指令双管控**:`#chatgpt管理面板` 一键获取面板 token,Web 端即可操作渠道、工具、触发器;同时保留完整的命令行 CRUD 指令。 +- **自动更新与依赖管理**:`#chatgpt更新` / `#chatgpt强制更新` 调用 git 同步仓库并自动更新 chaite 依赖,减少手动维护成本。 -## 安装与使用方法 +## 快速安装 -### 安装 +1. **克隆代码** + ```bash + cd plugins + git clone https://github.com/ikechan8370/chatgpt-plugin.git + ``` +2. **安装依赖**(推荐 Node.js ≥ 18 + pnpm ≥ 8) + ```bash + cd chatgpt-plugin + pnpm install + ``` + 若安装日志出现 `Ignored build scripts: better-sqlite3` 或运行时报错找不到 `better-sqlite3` bindings,可执行 `pnpm approve-builds`,在交互列表中勾选全部或仅 `better-sqlite3` 以允许编译。然后再次 `pnpm install` +3. **在 Yunzai 中启用插件** + - 重启机器人或运行 `node app` 让插件自动加载。 + - 首次启动会在 `plugins/chatgpt-plugin/config/` 下生成 `config.json / config.yaml`。 +4. **保持更新** + - 主人账号发送 `#chatgpt更新` 获取最新版本。 + - `#chatgpt强制更新` 会放弃本地修改后重新拉取,请谨慎使用。 -在安装之前,请先判断自己需要使用哪种模式,本插件支持官方API/第三方API/~~浏览器~~/必应四种模式。也可以选择**我全都要**(通过qq发送命令`#chatgpt切换浏览器/API/API3/Bing`实时切换) +## 配置指引 -> #### API模式和浏览器模式如何选择? -> -> * API模式会调用OpenAI官方提供的gpt-3.5-turbo API,ChatGPT官网同款模型,只需要提供API Key。一般情况下,该种方式响应速度更快,可配置项多,且不会像chatGPT官网一样总出现不可用的现象,但注意API调用是收费的,新用户有18美元试用金可用于支付,价格为`$0.0020/ 1K tokens`。(问题和回答**加起来**算token) -> * API3模式会调用第三方提供的官网反代API,他会帮你绕过CF防护,需要提供ChatGPT的Token。效果与官网和浏览器一致,但稳定性不一定。发送#chatgpt设置token来设置token。 -> * (Deprecated)浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站,使得获得和官方以及API2模式一模一样的回复质量,同时保证安全性。缺点是本方法对环境要求较高,需要提供桌面环境和一个可用的代理(能够访问ChatGPT的IP地址),且响应速度不如API,而且高峰期容易无法使用。一般作为API3的下位替代。 -> * 必应(Bing)将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的Bing登录Cookie方可使用。强烈推荐 - -1. 进入 Yunzai根目录 - -2. 请将 chatgpt-plugin 放置在 Yunzai-Bot 的 plugins 目录下 - -推荐使用 git 进行安装,以方便后续升级。在 Yunzai-Bot 根目录夹打开终端,运行下述指令进行安装 - -```shell -# github源 -git clone --depth=1 https://github.com/ikechan8370/chatgpt-plugin.git ./plugins/chatgpt-plugin/ - -# 网络不好连不上github可以使用gitee源,但更新可能不如github及时 -git clone --depth=1 https://gitee.com/ikechan/chatgpt-plugin.git ./plugins/chatgpt-plugin/ - -# 以上二选一后执行下面步骤进入目录安装依赖 -cd plugins/chatgpt-plugin -pnpm i +配置文件默认位于 `plugins/chatgpt-plugin/config/config.json`,也可改写为 YAML。常用字段示例: +```yaml +basic: + toggleMode: at # at / prefix + togglePrefix: "#chat" # prefix 模式下的触发词 + commandPrefix: "#chatgpt" # 管理指令前缀 +llm: + defaultModel: "gpt-4o-mini" + defaultChatPresetId: "default" + enableGroupContext: true + groupContextLength: 20 +bym: + enable: false + probability: 0.02 + defaultPreset: "bym_default" +chaite: + cloudApiKey: "" # 可选,接入 Chaite Cloud + host: "0.0.0.0" + port: 48370 +memory: + group: + enable: false + enabledGroups: ["123456"] + user: + enable: false + whitelist: ["123456789"] ``` -如果是手工下载的 zip 压缩包,请将解压后的 chatgpt-plugin 文件夹(请删除压缩自带的-master或版本号后缀)放置在 Yunzai-Bot 目录下的 plugins 文件夹内 +- **basic**:控制触发方式、调试与命令前缀。 +- **llm**:定义默认模型、嵌入模型、群上下文等。`defaultChatPresetId` 需在面板或命令中提前创建。 +- **chaite**:`storage` 默认 SQLite,会在 `plugins/chatgpt-plugin/data/data.db` 生成数据文件;如接入 Chaite Cloud,请填入 `cloudApiKey` 并开放 `host/port`。 +- **bym**:配置伪人触发概率、关键词映射、撤回与思考内容开关。 +- **memory**:为群记忆或私人记忆开启检索、模型与提示词,可按需启用 `extensions.simple` 以加载自定义词典。 -> ~~浏览器模式仅为备选,如您需要使用浏览器模式,您还需要有**桌面环境**,优先级建议:API≈必应>API3>浏览器~~\ -> ~~2.20更新:必应被大削,变得蠢了,建议还是API/API3优先~~\ -> 4.2更新:必应站起来了,必应天下第一。建议都用必应,别用API/API3了。浏览器模式除非极其特殊的需求否则强烈建议不使用,已经不维护了。 +修改后保存文件,插件会自动热加载;在 Chaite 面板修改配置时也会反向写回本地文件。 -3. 修改配置 -**本插件配置项比较多,强烈建议使用后台面板或[锅巴面板](https://github.com/guoba-yunzai/Guoba-Plugin)修改** +## 使用方式 - 复制`plugins/chatgpt-plugin/config/config.example.json`并将其改名为`config.json`\ - 编辑`plugins/chatgpt-plugin/config/config.json`文件,修改必要配置项 \ - **请勿直接修改config.example.json** +### 基础对话 -4. 后台面板使用 - 初次使用请先私聊机器人 `#设置管理密码` 进登录密码设置 -私聊 `#chatgpt系统管理` 后机器人会回复系统管理页面网址,在此网址输入机器人QQ号和刚刚设置的管理密码点击登录即可进入后台管理系统 -如果忘记密码,再次私聊输入 `#设置管理密码` 后可重新设置密码 +- `@Bot 你好` 或 `#chat 今天天气如何` 触发默认预设,插件会保持用户 `conversationId` 与 `messageId`,自动续写多轮对话。 +- 回复图片/文本可作为上下文输入,模型返回的图片、语音与思考内容会自动转换为 QQ 消息或转发记录。 +- `#结束对话` 仅清空自己的会话;`#结束全部对话` 需主人权限。 - 用户同样可私聊机器人 `#设置用户密码` 进行账号注册和密码设置 -用户设置密码后,所有聊天信息将记录在用户缓存数据下,同时用户可通过私聊机器人 `#chatgpt用户配置` 登录后台用户配置面板,查看自己的聊天数据和自定义机器人对自己的回复参数 - - 如果后台面板访问出现 time out 请检查机器人启动时是否有报错,服务器端口是否开放,可尝试ping一下服务器ip看能否直接ping通。 +### 管理命令 & 面板 -5. 重启Yunzai-Bot -如通过后台面板或锅巴面板升级可以热加载,无需重启。 +- `#chatgpt管理面板`:生成一次性 token,访问 `http://:` 即可使用 Chaite Web 面板。 +- CRUD 命令示例(均支持 `列表 / 添加 / 查看 / 删除`): + ``` + #chatgpt渠道列表 + #chatgpt预设添加 角色扮演 {...} + #chatgpt工具删除 web-search + #chatgpt处理器查看 markdown + ``` +- `#chatgpt调试模式开关`、`#chatgpt伪人开关` 等指令可快速切换全局开关。 ---- +### 伪人(BYM)模式 -### 相关配置 +1. 在配置中启用 `bym.enable` 并指定 `defaultPreset` 或 `presetMap`。 +2. 伪人会在命中关键词或达到概率阈值时主动发言,可通过 `presetPrefix` 调整统一人设,`temperature/maxTokens` 控制语气与长度。 +3. 支持为不同关键词配置 `recall` 秒数,实现“发完撤回”效果。 -#### 配置文件相关 +### 记忆系统 -配置文件位置:`plugins/chatgpt-plugin/config/config.json` +- **群记忆指令** + ``` + #群记忆 #仅群聊 + #删除群记忆 1 #主人或群管 + #记忆列表 #主人查看全局开关 + ``` +- **私人记忆指令** + ``` + #记忆 / 我的记忆 + #他的记忆 @xxx #群聊内查看他人(需其授权) + #删除记忆 1 + ``` +- 记忆抽取依赖配置中的 `memory.group` / `memory.user` 模型与预设,collector 会定期读取群聊历史,必要时可在 `enabledGroups` 中按群号白名单控制。 -部分关键配置项,其他请参照文件内注释: +### 更新与维护 -| 名称 | 含义 | 解释 | -|:-----------------:| :-----------------: |:-------------------------------------------------:| -| proxy | 代理地址 | 请在此处配置你的代理,例如`http://127.0.0.1:7890` | -| apiKey | openai账号的API Key | 获取地址:https://platform.openai.com/account/api-keys | +- `#chatgpt更新`:git pull 插件仓库并使用 pnpm/npm 更新 chaite 依赖。 +- `#chatgpt强制更新`:在更新前执行 `git checkout .`,用于舍弃本地改动。 +- 日志会通过转发消息发送最近 20 条 commit,方便追踪版本变化。 -#### Token相关 - -与Token相关的设置需在qq与机器人对话设置,设置后方可使用对应的api - -| 名称 | 含义 | 解释 | 设置方式 | -| :-----------------: | :------------------: | :----------------------------------------------------------: |:--------------------------------------------------------:| -| ChatGPT AccessToken | ChatGPT登录后的Token | 具体解释见下方 | \#chatgpt设置token | -| 必应token | 必应登录后的Token | 必应(Bing)将调用微软必应AI接口进行对话。不填写token对话上限为5句,填写后为20句。无论填写与否插件都会无限续杯。 | \#chatgpt设置必应token/\#chatgpt删除必应token/\#chatgpt查看必应token | - - -> #### 我没有注册openai账号?如何获取 -> -> 您可以按照以下方法获取openai账号 -> -> 进入https://chat.openai.com/ ,选择signup注册。目前openai不对包括俄罗斯、乌克兰、伊朗、中国等国家和地区提供服务,所以自行寻找办法使用**其他国家和地区**的ip登录。此外,注册可能需要验证所在国家和地区的手机号码,如果没有国外手机号可以试试解码网站,收费的推荐https://sms-activate.org/ -> -> #### 我有openai账号了,如何获取API key和Access Token? -> -> - 获取API key -> - 进入账户后台创建API key(Create new secret key):https://platform.openai.com/account/api-keys -> -> - 获取Access Token -> - **登录后**访问https://chat.openai.com/api/auth/session -> - 您会获得类似如下一串json字符串`{"user":{"id":"AAA","name":"BBB","email":"CCC","image":"DDD","picture":"EEE","groups":[]},"expires":"FFF","accessToken":"XXX"}` -> - 其中的XXX即为`ChatGPT AccessToken` -> - 如果是空的{},说明没有登录,要登录chatgpt而不是openai。 -> -> #### ChatGPT AccessToken 设置了有什么用?我为什么用不了API模式 -> -> - 部分API需要在和机器人的聊天里输入`#chatgpt设置token`才可以使用 -> -> #### 我有新必应的测试资格了,如何获取必应Token? -> 2023/05/29 无需登录也可以使用了,要求不高可以不填 -> -> 1. JS一键获取 -> -> 登录www.bing.com,刷新一下网页,按F12或直接打开开发者模式,点击Console/控制台,运行如下代码,执行后即在您的剪切板存储了必应Token -> -> ```js -> copy(document.cookie.split(";").find(cookie=>cookie.trim().startsWith("_U=")).split("=")[1]); -> ``` -> -> 2. 手动获取 -> -> 登录www.bing.com,刷新一下网页,按F12或直接打开开发者模式,点击Application/存储,点击左侧Storage下的Cookies,展开找到[https://www.bing.com](https://www.bing.com/) 项,在右侧列表Name项下找到"\_U",_U的value即为必应Token -> -> -> -> 其他问题可以参考使用的api库 https://github.com/transitive-bullshit/chatgpt-api 以及 https://github.com/waylaidwanderer/node-chatgpt-api - - -### 使用方法 - -根据配置文件中的toggleMode决定联通方式。 - -- at模式:@机器人 发送聊内容即可 - -- prefix模式:【#chat+问题】,本模式可以避免指令冲突。 - -发挥你的想象力吧,~~调教~~拟造出你自己的机器人风格! - - -#### 文本/图片回复模式 - -> #chatgpt文本/图片/语音模式 - -可以控制机器人回复的方式 - -#### 对话相关 - -> #chatgpt对话列表 -> -> #结束对话 [@某人] -> -> #清空chat队列 -> -> #移出chat队列首位 -> -> #chatgpt开启/关闭问题确认 -> -> ... - -#### 设置相关 - -> #chatgpt切换浏览器/API/API2/API3/Bing -> -> #chatgpt设置[必应]Token -> -> ... - -#### 获取帮助 - -> #chatgpt帮助 -> -> #chatgpt模式帮助 - -发送#chatgpt帮助,有更多选项可以配置 - -### 如何更新 - -发送#chatgpt更新指令。如果有冲突,可以使用#chatgpt强制更新。 - -## 示例与截图 - -- 程序员版 - -![img.png](resources/img/example1.png) - -- 傲娇版 - -![)T@~XY~NWXUM S1)D$7%I3H](https://user-images.githubusercontent.com/21212372/217540723-0b97553a-f4ba-41df-ae0c-0449f73657fc.png) -![image](https://user-images.githubusercontent.com/21212372/217545618-3793d9f8-7941-476b-81f8-4255ac216cf7.png) - -## TODO - -* V3重构 -* 插件in插件 -* langchain分支完善 -* 游戏机制 - -## 其他 - -### 常见问题 - -1. 如果在linux系统上发现图片模式下emoj无法正常显示,可以搜索安装支持emoj的字体,如Ubuntu可以使用`sudo apt install fonts-noto-color-emoji` - -2. 我和机器人聊天但没有任何反应怎么办? - - 可能是由于Yunzai-bot异常退出等原因造成Redis 队列中有残留的等待问题。使用`#清空队列`命令清除队列后再试。 - -3. Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'xxx'. - - 请参照本文档前面的安装依赖部分重新依赖。随着项目更新可能引入新的依赖。 - - > 一般情况下请按照 [安装](#安装) 小节的内容重新安装依赖即可 - > - > - > - > 最多的问题:载入插件错误:chat - > - > 问题详情:Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'showdown' imported from /app/Yunzai-Bot/plugins/chatgpt-plugin/apps/chat.js - > - > 原因:没装依赖 - > - > 解决方式:请参考文档在本插件目录下用`pnpm install`安装依赖,安装完就不报错了 - -4. 反代能自己搭吗? - - 能。参考[这里](https://ikechan8370.com/archives/da-jian-chatgpt-guan-fang-fan-xiang-dai-li) - -5. vit API能本地搭建吗? - - 能。克隆下来安装依赖直接运行即可。 - -6. 系统后台无法进入怎么办? - - 多数情况下是由于服务器未开放3321端口导致,请根据服务器系统和服务器供应商配置,开放3321端口后再试。 - -## 交流群 - -* QQ 559567232 [问题交流] -* QQ 126132049 [机器人试验场] - -## 感谢 - -本项目使用或参考了以下开源项目 -* https://github.com/transitive-bullshit/chatgpt-api -* https://github.com/waylaidwanderer/node-chatgpt-api -* https://github.com/acheong08/ChatGPT -* https://github.com/PawanOsman - -本插件的辅助项目 -* https://github.com/ikechan8370/node-chatgpt-proxy -* https://github.com/ikechan8370/SimpleChatGLM6BAPI - -图片以及Bing模式支持 @HalcyonAlcedo -* https://github.com/HalcyonAlcedo/ChatGPT-Plugin-PageCache -* https://github.com/HalcyonAlcedo/cache-web - -语音vits模型来自于 -* https://huggingface.co/spaces/sayashi/vits-uma-genshin-honkai - -以及ChatGPT及OpenAI -* https://chat.openai.com/ -* https://platform.openai.com/ - -ChatGLM -* https://huggingface.co/THUDM/chatglm-6b -* https://github.com/THUDM/ChatGLM-6B ## 赞助 @@ -330,12 +166,3 @@ https://afdian.net/a/ikechan8370 [![Star History Chart](https://api.star-history.com/svg?repos=ikechan8370/chatgpt-plugin&type=Date)](https://star-history.com/#ikechan8370/chatgpt-plugin&Date) -## 工具支持 - - - - -JetBrains for Open Source development license - - - diff --git a/apps/bym.js b/apps/bym.js new file mode 100644 index 0000000..b68854c --- /dev/null +++ b/apps/bym.js @@ -0,0 +1,155 @@ +import ChatGPTConfig from '../config/config.js' +import { Chaite } from 'chaite' +import { intoUserMessage, toYunzai } from '../utils/message.js' +import common from '../../../lib/common/common.js' +import { getGroupContextPrompt } from '../utils/group.js' +import { formatTimeToBeiJing } from '../utils/common.js' +import { extractTextFromUserMessage, processUserMemory } from '../models/memory/userMemoryManager.js' +import { buildMemoryPrompt } from '../models/memory/prompt.js' + +export class bym extends plugin { + constructor () { + super({ + name: 'ChatGPT-Plugin伪人模式', + dsc: 'ChatGPT-Plugin伪人模式', + event: 'message', + priority: 6000, + rule: [ + { + reg: '^[^#][sS]*', + fnc: 'bym', + log: false + } + ] + }) + } + + async bym (e) { + if (!Chaite.getInstance()) { + return false + } + if (!ChatGPTConfig.bym.enable) { + return false + } + let prob = ChatGPTConfig.bym.probability + if (ChatGPTConfig.bym.hit.find(keyword => e.msg?.includes(keyword))) { + prob = 1 + } + if (Math.random() > prob) { + return false + } + logger.info('伪人模式触发') + let recall = false + let presetId = ChatGPTConfig.bym.defaultPreset + if (ChatGPTConfig.bym.presetMap && ChatGPTConfig.bym.presetMap.length > 0) { + const option = ChatGPTConfig.bym.presetMap.sort((a, b) => b.priority - a.priority) + .find(item => item.keywords.find(keyword => e.msg?.includes(keyword))) + if (option) { + presetId = option.presetId + recall = !!option.recall + } + } + + const presetManager = Chaite.getInstance().getChatPresetManager() + let preset = await presetManager.getInstance(presetId) + if (!preset) { + preset = await presetManager.getInstance(ChatGPTConfig.bym.defaultPreset) + } + if (!preset) { + logger.debug('未找到预设,请检查配置文件') + return false + } + /** + * @type {import('chaite').SendMessageOption} + */ + const sendMessageOption = JSON.parse(JSON.stringify(preset.sendMessageOption)) + if (ChatGPTConfig.bym.presetPrefix) { + if (!sendMessageOption.systemOverride) { + sendMessageOption.systemOverride = '' + } + sendMessageOption.systemOverride = ChatGPTConfig.bym.presetPrefix + sendMessageOption.systemOverride + } + sendMessageOption.systemOverride = `Current Time: ${formatTimeToBeiJing(new Date().getTime())}\n` + sendMessageOption.systemOverride + if (ChatGPTConfig.bym.temperature >= 0) { + sendMessageOption.temperature = ChatGPTConfig.bym.temperature + } + if (ChatGPTConfig.bym.maxTokens > 0) { + sendMessageOption.maxToken = ChatGPTConfig.bym.maxTokens + } + const userMessage = await intoUserMessage(e, { + handleReplyText: true, + handleReplyImage: true, + useRawMessage: true, + handleAtMsg: true, + excludeAtBot: false, + toggleMode: ChatGPTConfig.basic.toggleMode, + togglePrefix: ChatGPTConfig.basic.togglePrefix + }) + const userText = extractTextFromUserMessage(userMessage) || e.msg || '' + // 伪人不记录历史 + // sendMessageOption.disableHistoryRead = true + // sendMessageOption.disableHistorySave = true + sendMessageOption.conversationId = 'bym' + e.user_id + Date.now() + sendMessageOption.parentMessageId = undefined + // 设置多轮调用回掉 + sendMessageOption.onMessageWithToolCall = async content => { + const { msgs, forward } = await toYunzai(e, [content]) + if (msgs.length > 0) { + await e.reply(msgs) + } + for (let forwardElement of forward) { + this.reply(forwardElement) + } + } + const systemSegments = [] + if (sendMessageOption.systemOverride) { + systemSegments.push(sendMessageOption.systemOverride) + } + if (userText) { + const memoryPrompt = await buildMemoryPrompt({ + userId: e.sender.user_id + '', + groupId: e.isGroup ? e.group_id + '' : null, + queryText: userText + }) + if (memoryPrompt) { + systemSegments.push(memoryPrompt) + logger.debug(`[Memory] bym memory prompt: ${memoryPrompt}`) + } + } + if (ChatGPTConfig.llm.enableGroupContext && e.isGroup) { + const contextPrompt = await getGroupContextPrompt(e, ChatGPTConfig.llm.groupContextLength) + if (contextPrompt) { + systemSegments.push(contextPrompt) + } + } + if (systemSegments.length > 0) { + sendMessageOption.systemOverride = systemSegments.join('\n\n') + } + // 发送 + const response = await Chaite.getInstance().sendMessage(userMessage, e, { + ...sendMessageOption, + chatPreset: preset + }) + const { msgs, forward } = await toYunzai(e, response.contents) + if (msgs.length > 0) { + // await e.reply(msgs, false, { recallMsg: recall }) + for (let msg of msgs) { + await e.reply(msg, false, { recallMsg: recall ? 10 : 0 }) + await common.sleep(Math.floor(Math.random() * 2000) + 1000) + } + } + if (ChatGPTConfig.bym.sendReasoning) { + for (let forwardElement of forward) { + await e.reply(forwardElement, false, { recallMsg: recall ? 10 : 0 }) + } + } + await processUserMemory({ + event: e, + userMessage, + userText, + conversationId: sendMessageOption.conversationId, + assistantContents: response.contents, + assistantMessageId: response.id + }) + } +} diff --git a/apps/chat.js b/apps/chat.js index 85f02fc..b6eca32 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -1,2403 +1,132 @@ -import plugin from '../../../lib/plugins/plugin.js' -import _ from 'lodash' -import { Config, defaultOpenAIAPI, pureSydneyInstruction } from '../utils/config.js' -import { v4 as uuid } from 'uuid' -import delay from 'delay' -import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js' -import { BingAIClient } from '@waylaidwanderer/chatgpt-api' -import SydneyAIClient from '../utils/SydneyAIClient.js' -import { PoeClient } from '../utils/poe/index.js' -import AzureTTS, { supportConfigurations } from '../utils/tts/microsoft-azure.js' -import VoiceVoxTTS from '../utils/tts/voicevox.js' -import { translate } from '../utils/translate.js' -import fs from 'fs' -import { - render, - renderUrl, - getMessageById, - makeForwardMsg, - upsertMessage, - randomString, - completeJSON, - isImage, - getUserData, - getDefaultReplySetting, - isCN, - getMasterQQ, - getUserReplySetting, - getImageOcrText, - getImg, - processList, - getMaxModelTokens, formatDate -} from '../utils/common.js' -import { ChatGPTPuppeteer } from '../utils/browser.js' -import { KeyvFile } from 'keyv-file' -import { OfficialChatGPTClient } from '../utils/message.js' -import fetch from 'node-fetch' -import { deleteConversation, getConversations, getLatestMessageIdByConversationId } from '../utils/conversation.js' -import { convertSpeaker, generateAudio, speakers } from '../utils/tts.js' -import ChatGLMClient from '../utils/chatglm.js' -import { convertFaces } from '../utils/face.js' -import uploadRecord from '../utils/uploadRecord.js' -import { SlackClaudeClient } from '../utils/slack/slackClient.js' -import { ChatgptManagement } from './management.js' -import { getPromptByName } from '../utils/prompts.js' -import BingDrawClient from '../utils/BingDraw.js' -import XinghuoClient from '../utils/xinghuo/xinghuo.js' -import { JinyanTool } from '../utils/tools/JinyanTool.js' -import { SendMusicTool } from '../utils/tools/SendMusicTool.js' -import { SendVideoTool } from '../utils/tools/SendBilibiliTool.js' -import { KickOutTool } from '../utils/tools/KickOutTool.js' -import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js' -import { SendDiceTool } from '../utils/tools/SendDiceTool.js' -import { EditCardTool } from '../utils/tools/EditCardTool.js' -import { SearchVideoTool } from '../utils/tools/SearchBilibiliTool.js' -import { SearchMusicTool } from '../utils/tools/SearchMusicTool.js' -import { QueryStarRailTool } from '../utils/tools/QueryStarRailTool.js' -import { WebsiteTool } from '../utils/tools/WebsiteTool.js' -import { WeatherTool } from '../utils/tools/WeatherTool.js' -import { SerpTool } from '../utils/tools/SerpTool.js' -import { SerpIkechan8370Tool } from '../utils/tools/SerpIkechan8370Tool.js' -import { SendPictureTool } from '../utils/tools/SendPictureTool.js' -import { SerpImageTool } from '../utils/tools/SearchImageTool.js' -import { ImageCaptionTool } from '../utils/tools/ImageCaptionTool.js' -import {TTSTool} from "../utils/tools/TTSTool.js"; -import {ProcessPictureTool} from "../utils/tools/ProcessPictureTool.js"; -try { - await import('emoji-strip') -} catch (err) { - logger.warn('【ChatGPT-Plugin】依赖emoji-strip未安装,会导致azure语音模式下朗读emoji的问题,建议执行pnpm install emoji-strip安装') -} -try { - await import('keyv') -} catch (err) { - logger.warn('【ChatGPT-Plugin】依赖keyv未安装,可能影响Sydney模式下Bing对话,建议执行pnpm install keyv安装') -} -let version = Config.version -let proxy -if (Config.proxy) { - try { - proxy = (await import('https-proxy-agent')).default - } catch (e) { - console.warn('未安装https-proxy-agent,请在插件目录下执行pnpm add https-proxy-agent') - } -} -/** - * 每个对话保留的时长。单个对话内ai是保留上下文的。超时后销毁对话,再次对话创建新的对话。 - * 单位:秒 - * @type {number} - * - * 这里使用动态数据获取,以便于锅巴动态更新数据 - */ -// const CONVERSATION_PRESERVE_TIME = Config.conversationPreserveTime -const defaultPropmtPrefix = ', a large language model trained by OpenAI. You answer as concisely as possible for each response (e.g. don’t be verbose). It is very important that you answer as concisely as possible, so please remember this. If you are generating a list, do not have too many items. Keep the number of items short.' -const newFetch = (url, options = {}) => { - const defaultOptions = Config.proxy - ? { - agent: proxy(Config.proxy) - } - : {} - const mergedOptions = { - ...defaultOptions, - ...options - } +import Config from '../config/config.js' +import { Chaite, SendMessageOption } from 'chaite' +import { getPreset, intoUserMessage, toYunzai } from '../utils/message.js' +import { YunzaiUserState } from '../models/chaite/storage/lowdb/user_state_storage.js' +import { getGroupContextPrompt } from '../utils/group.js' +import { buildMemoryPrompt } from '../models/memory/prompt.js' +import { extractTextFromUserMessage, processUserMemory } from '../models/memory/userMemoryManager.js' +import * as crypto from 'node:crypto' - return fetch(url, mergedOptions) -} -export class chatgpt extends plugin { +export class Chat extends plugin { constructor () { - let toggleMode = Config.toggleMode super({ - /** 功能名称 */ - name: 'ChatGpt 对话', - /** 功能描述 */ - dsc: '与人工智能对话,畅聊无限可能~', + name: 'ChatGPT-Plugin对话', + dsc: 'ChatGPT-Plugin对话', event: 'message', - /** 优先级,数字越小等级越高 */ - priority: 1144, + // 应🥑要求降低优先级 + priority: 555500, rule: [ { - /** 命令正则匹配 */ - reg: '^#chat3[sS]*', - /** 执行方法 */ - fnc: 'chatgpt3' - }, - { - /** 命令正则匹配 */ - reg: '^#chat1[sS]*', - /** 执行方法 */ - fnc: 'chatgpt1' - }, - { - /** 命令正则匹配 */ - reg: '^#chatglm[sS]*', - /** 执行方法 */ - fnc: 'chatglm' - }, - { - /** 命令正则匹配 */ - reg: '^#bing[sS]*', - /** 执行方法 */ - fnc: 'bing' - }, - { - reg: '^#claude开启新对话', - fnc: 'newClaudeConversation' - }, - { - /** 命令正则匹配 */ - reg: '^#claude[sS]*', - /** 执行方法 */ - fnc: 'claude' - }, - { - /** 命令正则匹配 */ - reg: '^#xh[sS]*', - /** 执行方法 */ - fnc: 'xh' - }, - { - /** 命令正则匹配 */ - reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*', - /** 执行方法 */ - fnc: 'chatgpt', + reg: '^[^#][sS]*', + fnc: 'chat', log: false - }, - { - reg: '^#(chatgpt)?对话列表$', - fnc: 'getAllConversations', - permission: 'master' - }, - { - reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)对话([sS]*)', - fnc: 'destroyConversations' - }, - { - reg: '^#(chatgpt)?(结束|新开|摧毁|毁灭|完结)全部对话$', - fnc: 'endAllConversations', - permission: 'master' - }, - // { - // reg: '#chatgpt帮助', - // fnc: 'help' - // }, - { - reg: '^#chatgpt图片模式$', - fnc: 'switch2Picture' - }, - { - reg: '^#chatgpt文本模式$', - fnc: 'switch2Text' - }, - { - reg: '^#chatgpt语音模式$', - fnc: 'switch2Audio' - }, - { - reg: '^#chatgpt语音换源', - fnc: 'switchTTSSource' - }, - { - reg: '^#chatgpt设置(语音角色|角色语音|角色)', - fnc: 'setDefaultRole' - }, - { - reg: '^#(chatgpt)?清空(chat)?队列$', - fnc: 'emptyQueue', - permission: 'master' - }, - { - reg: '^#(chatgpt)?移出(chat)?队列首位$', - fnc: 'removeQueueFirst', - permission: 'master' - }, - { - reg: '#(OpenAI|openai)(剩余)?(余额|额度)', - fnc: 'totalAvailable', - permission: 'master' - }, - { - reg: '^#chatgpt切换对话', - fnc: 'attachConversation' - }, - { - reg: '^#(chatgpt)?加入对话', - fnc: 'joinConversation' - }, - { - reg: '^#chatgpt删除对话', - fnc: 'deleteConversation', - permission: 'master' } ] }) - this.toggleMode = toggleMode } - /** - * 获取chatgpt当前对话列表 - * @param e - * @returns {Promise} - */ - async getConversations (e) { - // todo 根据use返回不同的对话列表 - let keys = await redis.keys('CHATGPT:CONVERSATIONS:*') - if (!keys || keys.length === 0) { - await this.reply('当前没有人正在与机器人对话', true) + async chat (e) { + if (!Chaite.getInstance()) { + return false + } + let state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '') + if (!state) { + state = new YunzaiUserState(e.sender.user_id, e.sender.nickname, e.sender.card) + // await Chaite.getInstance().getUserStateStorage().setItem(e.sender.user_id + '', state) + } + if (!state.current.conversationId) { + state.current.conversationId = crypto.randomUUID() + } + if (!state.current.messageId) { + state.current.messageId = crypto.randomUUID() + } + const preset = await getPreset(e, state?.settings.preset || Config.llm.defaultChatPresetId, Config.basic.toggleMode, Config.basic.togglePrefix) + if (!preset) { + logger.debug('不满足对话触发条件或未找到预设,不进入对话') + return false } else { - let response = '当前对话列表:(格式为【开始时间 | qq昵称 | 对话长度 | 最后活跃时间】)\n' - await Promise.all(keys.map(async (key) => { - let conversation = await redis.get(key) - if (conversation) { - conversation = JSON.parse(conversation) - response += `${conversation.ctime} | ${conversation.sender.nickname} | ${conversation.num} | ${conversation.utime} \n` - } - })) - await this.reply(`${response}`, true) + logger.info('进入对话, prompt: ' + e.msg) } - } - - /** - * 销毁指定人的对话 - * @param e - * @returns {Promise} - */ - async destroyConversations (e) { - const userData = await getUserData(e.user_id) - const use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE') - await redis.del(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - if (use === 'claude') { - // let client = new SlackClaudeClient({ - // slackUserToken: Config.slackUserToken, - // slackChannelId: Config.slackChannelId - // }) - // await client.endConversation() - await redis.del(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - await e.reply('claude对话已结束') - return - } - if (use === 'xh') { - await redis.del(`CHATGPT:CONVERSATIONS_XH:${e.sender.user_id}`) - await e.reply('星火对话已结束') - return - } - let ats = e.message.filter(m => m.type === 'at') - if (ats.length === 0) { - if (use === 'api3') { - await redis.del(`CHATGPT:QQ_CONVERSATION:${e.sender.user_id}`) - await this.reply('已退出当前对话,该对话仍然保留。请@我进行聊天以开启新的对话', true) - } else if (use === 'bing' && (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom')) { - let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - if (!c) { - await this.reply('当前没有开启对话', true) - return - } else { - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - } - const conversation = { - store: new KeyvFile({ filename: 'cache.json' }), - namespace: Config.toneStyle - } - let Keyv - try { - Keyv = (await import('keyv')).default - } catch (err) { - await this.reply('依赖keyv未安装,请执行pnpm install keyv', true) - } - const conversationsCache = new Keyv(conversation) - logger.info(`SydneyUser_${e.sender.user_id}`, await conversationsCache.get(`SydneyUser_${e.sender.user_id}`)) - await conversationsCache.delete(`SydneyUser_${e.sender.user_id}`) - await this.reply('已退出当前对话,该对话仍然保留。请@我进行聊天以开启新的对话', true) - } else if (use === 'chatglm') { - const conversation = { - store: new KeyvFile({ filename: 'cache.json' }), - namespace: 'chatglm_6b' - } - let Keyv - try { - Keyv = (await import('keyv')).default - } catch (err) { - await this.reply('依赖keyv未安装,请执行pnpm install keyv', true) - } - const conversationsCache = new Keyv(conversation) - logger.info(`ChatGLMUser_${e.sender.user_id}`, await conversationsCache.get(`ChatGLMUser_${e.sender.user_id}`)) - await conversationsCache.delete(`ChatGLMUser_${e.sender.user_id}`) - await this.reply('已退出当前对话,该对话仍然保留。请@我进行聊天以开启新的对话', true) - } else if (use === 'api') { - let c = await redis.get(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`) - if (!c) { - await this.reply('当前没有开启对话', true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`) - await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true) - } - } else if (use === 'bing') { - let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - if (!c) { - await this.reply('当前没有开启对话', true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true) - } - } else if (use === 'browser') { - let c = await redis.get(`CHATGPT:CONVERSATIONS_BROWSER:${e.sender.user_id}`) - if (!c) { - await this.reply('当前没有开启对话', true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS_BROWSER:${e.sender.user_id}`) - await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true) - } + const sendMessageOptions = SendMessageOption.create(state?.settings) + sendMessageOptions.onMessageWithToolCall = async content => { + const { msgs, forward } = await toYunzai(e, [content]) + if (msgs.length > 0) { + await e.reply(msgs) } - } else { - let at = ats[0] - let qq = at.qq - let atUser = _.trimStart(at.text, '@') - if (use === 'api3') { - await redis.del(`CHATGPT:QQ_CONVERSATION:${qq}`) - await this.reply(`${atUser}已退出TA当前的对话,TA仍可以@我进行聊天以开启新的对话`, true) - } else if (use === 'bing' && (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom')) { - const conversation = { - store: new KeyvFile({ filename: 'cache.json' }), - namespace: Config.toneStyle - } - let Keyv - try { - Keyv = (await import('keyv')).default - } catch (err) { - await this.reply('依赖keyv未安装,请执行pnpm install keyv', true) - } - const conversationsCache = new Keyv(conversation) - await conversationsCache.delete(`SydneyUser_${qq}`) - await this.reply('已退出当前对话,该对话仍然保留。请@我进行聊天以开启新的对话', true) - } else if (use === 'chatglm') { - const conversation = { - store: new KeyvFile({ filename: 'cache.json' }), - namespace: 'chatglm_6b' - } - let Keyv - try { - Keyv = (await import('keyv')).default - } catch (err) { - await this.reply('依赖keyv未安装,请执行pnpm install keyv', true) - } - const conversationsCache = new Keyv(conversation) - logger.info(`ChatGLMUser_${e.sender.user_id}`, await conversationsCache.get(`ChatGLMUser_${e.sender.user_id}`)) - await conversationsCache.delete(`ChatGLMUser_${qq}`) - await this.reply('已退出当前对话,该对话仍然保留。请@我进行聊天以开启新的对话', true) - } else if (use === 'api') { - let c = await redis.get(`CHATGPT:CONVERSATIONS:${qq}`) - if (!c) { - await this.reply(`当前${atUser}没有开启对话`, true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS:${qq}`) - await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true) - } - } else if (use === 'bing') { - let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`) - if (!c) { - await this.reply(`当前${atUser}没有开启对话`, true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS_BING:${qq}`) - await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true) - } - } else if (use === 'browser') { - let c = await redis.get(`CHATGPT:CONVERSATIONS_BROWSER:${qq}`) - if (!c) { - await this.reply(`当前${atUser}没有开启对话`, true) - } else { - await redis.del(`CHATGPT:CONVERSATIONS_BROWSER:${qq}`) - await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true) - } + for (let forwardElement of forward) { + this.reply(forwardElement) } } - } - - async endAllConversations (e) { - let use = await redis.get('CHATGPT:USE') || 'api' - let deleted = 0 - switch (use) { - case 'claude': { - let cs = await redis.keys('CHATGPT:SLACK_CONVERSATION:*') - let we = await redis.keys('CHATGPT:WRONG_EMOTION:*') - for (let i = 0; i < cs.length; i++) { - await redis.del(cs[i]) - if (Config.debug) { - logger.info('delete slack conversation of qq: ' + cs[i]) - } - deleted++ - } - for (const element of we) { - await redis.del(element) - } - break - } - case 'xh': { - let cs = await redis.keys('CHATGPT:CONVERSATIONS_XH:*') - for (let i = 0; i < cs.length; i++) { - await redis.del(cs[i]) - if (Config.debug) { - logger.info('delete slack conversation of qq: ' + cs[i]) - } - deleted++ - } - break - } - case 'bing': { - let cs = await redis.keys('CHATGPT:CONVERSATIONS_BING:*') - let we = await redis.keys('CHATGPT:WRONG_EMOTION:*') - for (let i = 0; i < cs.length; i++) { - await redis.del(cs[i]) - if (Config.debug) { - logger.info('delete bing conversation of qq: ' + cs[i]) - } - deleted++ - } - for (const element of we) { - await redis.del(element) - } - break - } - case 'api': { - let cs = await redis.keys('CHATGPT:CONVERSATIONS:*') - for (let i = 0; i < cs.length; i++) { - await redis.del(cs[i]) - if (Config.debug) { - logger.info('delete api conversation of qq: ' + cs[i]) - } - deleted++ - } - break - } - case 'api3': { - let qcs = await redis.keys('CHATGPT:QQ_CONVERSATION:*') - for (let i = 0; i < qcs.length; i++) { - await redis.del(qcs[i]) - // todo clean last message id - if (Config.debug) { - logger.info('delete conversation bind: ' + qcs[i]) - } - deleted++ - } - break - } - case 'chatglm': { - let qcs = await redis.keys('CHATGPT:CONVERSATIONS_CHATGLM:*') - for (let i = 0; i < qcs.length; i++) { - await redis.del(qcs[i]) - // todo clean last message id - if (Config.debug) { - logger.info('delete chatglm conversation bind: ' + qcs[i]) - } - deleted++ - } - break - } - } - await this.reply(`结束了${deleted}个用户的对话。`, true) - } - - async deleteConversation (e) { - let ats = e.message.filter(m => m.type === 'at') - let use = await redis.get('CHATGPT:USE') || 'api' - if (use !== 'api3') { - await this.reply('本功能当前仅支持API3模式', true) - return false - } - if (ats.length === 0 || (ats.length === 1 && e.atme)) { - let conversationId = _.trimStart(e.msg, '#chatgpt删除对话').trim() - if (!conversationId) { - await this.reply('指令格式错误,请同时加上对话id或@某人以删除他当前进行的对话', true) - return false - } else { - let deleteResponse = await deleteConversation(conversationId, newFetch) - logger.mark(deleteResponse) - let deleted = 0 - let qcs = await redis.keys('CHATGPT:QQ_CONVERSATION:*') - for (let i = 0; i < qcs.length; i++) { - if (await redis.get(qcs[i]) === conversationId) { - await redis.del(qcs[i]) - if (Config.debug) { - logger.info('delete conversation bind: ' + qcs[i]) - } - deleted++ - } - } - await this.reply(`对话删除成功,同时清理了${deleted}个同一对话中用户的对话。`, true) - } - } else { - for (let u = 0; u < ats.length; u++) { - let at = ats[u] - let qq = at.qq - let atUser = _.trimStart(at.text, '@') - let conversationId = await redis.get('CHATGPT:QQ_CONVERSATION:' + qq) - if (conversationId) { - let deleteResponse = await deleteConversation(conversationId) - if (Config.debug) { - logger.mark(deleteResponse) - } - let deleted = 0 - let qcs = await redis.keys('CHATGPT:QQ_CONVERSATION:*') - for (let i = 0; i < qcs.length; i++) { - if (await redis.get(qcs[i]) === conversationId) { - await redis.del(qcs[i]) - if (Config.debug) { - logger.info('delete conversation bind: ' + qcs[i]) - } - deleted++ - } - } - await this.reply(`${atUser}的对话${conversationId}删除成功,同时清理了${deleted}个同一对话中用户的对话。`) - } else { - await this.reply(`${atUser}当前已没有进行对话`) - } - } - } - } - - async switch2Picture (e) { - let userReplySetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`) - if (!userReplySetting) { - userReplySetting = getDefaultReplySetting() - } else { - userReplySetting = JSON.parse(userReplySetting) - } - userReplySetting.usePicture = true - userReplySetting.useTTS = false - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userReplySetting)) - await this.reply('ChatGPT回复已转换为图片模式') - } - - async switch2Text (e) { - let userSetting = await getUserReplySetting(this.e) - userSetting.usePicture = false - userSetting.useTTS = false - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply('ChatGPT回复已转换为文字模式') - } - - async switch2Audio (e) { - switch (Config.ttsMode) { - case 'vits-uma-genshin-honkai': - if (!Config.ttsSpace) { - await this.reply('您没有配置VITS API,请前往锅巴面板进行配置') - return - } - break - case 'azure': - if (!Config.azureTTSKey) { - await this.reply('您没有配置Azure Key,请前往锅巴面板进行配置') - return - } - break - case 'voicevox': - if (!Config.voicevoxSpace) { - await this.reply('您没有配置VoiceVox API,请前往锅巴面板进行配置') - return - } - break - } - let userSetting = await getUserReplySetting(this.e) - userSetting.useTTS = true - userSetting.usePicture = false - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply('ChatGPT回复已转换为语音模式') - } - - async switchTTSSource (e) { - let target = e.msg.replace(/^#chatgpt语音换源/, '') - switch (target.trim()) { - case '1': { - Config.ttsMode = 'vits-uma-genshin-honkai' - break - } - case '2': { - Config.ttsMode = 'azure' - break - } - case '3': { - Config.ttsMode = 'voicevox' - break - } - default: { - await e.reply('请使用#chatgpt语音换源+数字进行换源。1为vits-uma-genshin-honkai,2为微软Azure,3为voicevox') - return - } - } - await e.reply('语音转换源已切换为' + Config.ttsMode) - } - - async setDefaultRole (e) { - if (Config.ttsMode === 'vits-uma-genshin-honkai' && !Config.ttsSpace) { - await this.reply('您没有配置vits-uma-genshin-honkai API,请前往后台管理或锅巴面板进行配置') - return - } - if (Config.ttsMode === 'azure' && !Config.azureTTSKey) { - await this.reply('您没有配置azure 密钥,请前往后台管理或锅巴面板进行配置') - return - } - if (Config.ttsMode === 'voicevox' && !Config.voicevoxSpace) { - await this.reply('您没有配置voicevox API,请前往后台管理或锅巴面板进行配置') - return - } - const regex = /^#chatgpt设置(语音角色|角色语音|角色)/ - let speaker = e.msg.replace(regex, '').trim() || '随机' - switch (Config.ttsMode) { - case 'vits-uma-genshin-honkai': { - let userSetting = await getUserReplySetting(this.e) - userSetting.ttsRole = convertSpeaker(speaker) - if (speakers.indexOf(userSetting.ttsRole) >= 0) { - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "${userSetting.ttsRole}" `) - } else if (speaker === '随机') { - userSetting.ttsRole = '随机' - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `) - } else { - await this.reply(`抱歉,"${userSetting.ttsRole}"我还不认识呢`) - } - break - } - case 'azure': { - let userSetting = await getUserReplySetting(this.e) - let chosen = AzureTTS.supportConfigurations.filter(s => s.name === speaker) - if (speaker === '随机') { - userSetting.ttsRoleAzure = '随机' - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `) - } else if (chosen.length === 0) { - await this.reply(`抱歉,没有"${speaker}"这个角色,目前azure模式下支持的角色有${AzureTTS.supportConfigurations.map(item => item.name).join('、')}`) - } else { - userSetting.ttsRoleAzure = chosen[0].code - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - // Config.azureTTSSpeaker = chosen[0].code - const supportEmotion = AzureTTS.supportConfigurations.find(config => config.name === speaker)?.emotion - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 ${speaker}-${chosen[0].gender}-${chosen[0].languageDetail} ${supportEmotion && Config.azureTTSEmotion ? ',此角色支持多情绪配置,建议重新使用设定并结束对话以获得最佳体验!' : ''}`) - } - break - } - case 'voicevox': { - let regex = /^(.*?)-(.*)$/ - let match = regex.exec(speaker) - let style = null - if (match) { - speaker = match[1] - style = match[2] - } - let userSetting = await getUserReplySetting(e) - if (speaker === '随机') { - userSetting.ttsRoleVoiceVox = '随机' - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "随机" `) - break - } - let chosen = VoiceVoxTTS.supportConfigurations.filter(s => s.name === speaker) - if (chosen.length === 0) { - await this.reply(`抱歉,没有"${speaker}"这个角色,目前voicevox模式下支持的角色有${VoiceVoxTTS.supportConfigurations.map(item => item.name).join('、')}`) - break - } - if (style && !chosen[0].styles.find(item => item.name === style)) { - await this.reply(`抱歉,"${speaker}"这个角色没有"${style}"这个风格,目前支持的风格有${chosen[0].styles.map(item => item.name).join('、')}`) - break - } - userSetting.ttsRoleVoiceVox = chosen[0].name + (style ? `-${style}` : '') - await redis.set(`CHATGPT:USER:${e.sender.user_id}`, JSON.stringify(userSetting)) - await this.reply(`当前语音模式为${Config.ttsMode},您的默认语音角色已被设置为 "${userSetting.ttsRoleVoiceVox}" `) - break - } - } - } - - /** - * #chatgpt - */ - async chatgpt (e) { - let prompt - if (this.toggleMode === 'at') { - if (!e.raw_message || e.msg?.startsWith('#')) { - return false - } - if (e.isGroup && !e.atme) { - return false - } - if (e.user_id == Bot.uin) return false - prompt = e.raw_message.trim() - if (e.isGroup && typeof this.e.group.getMemberMap === 'function') { - let mm = await this.e.group.getMemberMap() - let me = mm.get(Bot.uin) - let card = me.card - let nickname = me.nickname - if (nickname && card) { - if (nickname.startsWith(card)) { - // 例如nickname是"滚筒洗衣机",card是"滚筒" - prompt = prompt.replace(`@${nickname}`, '').trim() - } else if (card.startsWith(nickname)) { - // 例如nickname是"十二",card是"十二|本月已发送1000条消息" - prompt = prompt.replace(`@${card}`, '').trim() - // 如果是好友,显示的还是昵称 - prompt = prompt.replace(`@${nickname}`, '').trim() - } else { - // 互不包含,分别替换 - if (nickname) { - prompt = prompt.replace(`@${nickname}`, '').trim() - } - if (card) { - prompt = prompt.replace(`@${card}`, '').trim() - } - } - } else if (nickname) { - prompt = prompt.replace(`@${nickname}`, '').trim() - } else if (card) { - prompt = prompt.replace(`@${card}`, '').trim() - } - } - } else { - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#chat') - } - return false - } - prompt = _.replace(e.raw_message.trimStart(), '#chat', '').trim() - if (prompt.length === 0) { - return false - } - } - let groupId = e.isGroup ? e.group.group_id : '' - if (await redis.get('CHATGPT:SHUT_UP:ALL') || await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) { - logger.info('chatgpt闭嘴中,不予理会') - return false - } - // 获取用户配置 - const userData = await getUserData(e.user_id) - const use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE') || 'api' - // 自动化插件本月已发送xx条消息更新太快,由于延迟和缓存问题导致不同客户端不一样,at文本和获取的card不一致。因此单独处理一下 - prompt = prompt.replace(/^|本月已发送\d+条消息/, '') - await this.abstractChat(e, prompt, use) - } - - async abstractChat (e, prompt, use) { - // 关闭私聊通道后不回复 - if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) { - return false - } - // 黑白名单过滤对话 - let [whitelist, blacklist] = processList(Config.whitelist, Config.blacklist) - if (whitelist.join('').length > 0) { - if (e.isGroup && !whitelist.includes(e.group_id.toString())) return false - const list = whitelist.filter(elem => elem.startsWith('^')).map(elem => elem.slice(1)) - if (!list.includes(e.sender.user_id.toString())) return false - } - if (blacklist.join('').length > 0) { - if (e.isGroup && blacklist.includes(e.group_id.toString())) return false - const list = blacklist.filter(elem => elem.startsWith('^')).map(elem => elem.slice(1)) - if (list.includes(e.sender.user_id.toString())) return false - } - - let userSetting = await getUserReplySetting(this.e) - let useTTS = !!userSetting.useTTS - let speaker - if (Config.ttsMode === 'vits-uma-genshin-honkai') { - speaker = convertSpeaker(userSetting.ttsRole || Config.defaultTTSRole) - } else if (Config.ttsMode === 'azure') { - speaker = userSetting.ttsRoleAzure || Config.azureTTSSpeaker - } else if (Config.ttsMode === 'voicevox') { - speaker = userSetting.ttsRoleVoiceVox || Config.voicevoxTTSSpeaker - } - // 每个回答可以指定 - let trySplit = prompt.split('回答:') - if (trySplit.length > 1 && speakers.indexOf(convertSpeaker(trySplit[0])) > -1) { - useTTS = true - speaker = convertSpeaker(trySplit[0]) - prompt = trySplit[1] - } - const isImg = await getImg(e) - if (Config.imgOcr && !!isImg) { - let imgOcrText = await getImageOcrText(e) - if (imgOcrText) { - prompt = prompt + '"' - for (let imgOcrTextKey in imgOcrText) { - prompt += imgOcrText[imgOcrTextKey] - } - prompt = prompt + ' "' - } - } - // 检索是否有屏蔽词 - const promtBlockWord = Config.promptBlockWords.find(word => prompt.toLowerCase().includes(word.toLowerCase())) - if (promtBlockWord) { - await this.reply('主人不让我回答你这种问题,真是抱歉了呢', true) - return false - } - if (use === 'api3') { - let randomId = uuid() - // 队列队尾插入,开始排队 - await redis.rPush('CHATGPT:CHAT_QUEUE', [randomId]) - let confirm = await redis.get('CHATGPT:CONFIRM') - let confirmOn = (!confirm || confirm === 'on') // confirm默认开启 - if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) { - // 添加超时设置 - await redis.pSetEx('CHATGPT:CHAT_QUEUE_TIMEOUT', Config.defaultTimeoutMs, randomId) - if (confirmOn) { - await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 8 }) - } - } else { - let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1 - if (confirmOn) { - await this.reply(`我正在思考如何回复你,请稍等,当前队列前方还有${length}个问题`, true, { recallMsg: 8 }) - } - logger.info(`chatgpt队列前方还有${length}个问题。管理员可通过#清空队列来强制清除所有等待的问题。`) - // 开始排队 - while (true) { - if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) { - await redis.pSetEx('CHATGPT:CHAT_QUEUE_TIMEOUT', Config.defaultTimeoutMs, randomId) - break - } else { - // 超时检查 - if (await redis.exists('CHATGPT:CHAT_QUEUE_TIMEOUT') === 0) { - await redis.lPop('CHATGPT:CHAT_QUEUE', 0) - await redis.pSetEx('CHATGPT:CHAT_QUEUE_TIMEOUT', Config.defaultTimeoutMs, await redis.lIndex('CHATGPT:CHAT_QUEUE', 0)) - if (confirmOn) { - let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1 - await this.reply(`问题想不明白放弃了,开始思考下一个问题,当前队列前方还有${length}个问题`, true, { recallMsg: 8 }) - logger.info(`问题超时已弹出,chatgpt队列前方还有${length}个问题。管理员可通过#清空队列来强制清除所有等待的问题。`) - } - } - await delay(1500) - } - } - } - } else { - let confirm = await redis.get('CHATGPT:CONFIRM') - let confirmOn = (!confirm || confirm === 'on') // confirm默认开启 - if (confirmOn) { - await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 8 }) - } - } - const emotionFlag = await redis.get(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - let userReplySetting = await getUserReplySetting(this.e) - // 图片模式就不管了,降低抱歉概率 - if (Config.ttsMode === 'azure' && Config.enhanceAzureTTSEmotion && userReplySetting.useTTS === true && await AzureTTS.getEmotionPrompt(e)) { - switch (emotionFlag) { - case '1': - prompt += '(上一次回复没有添加情绪,请确保接下来的对话正确使用情绪和情绪格式,回复时忽略此内容。)' - break - case '2': - prompt += '(不要使用给出情绪范围的词和错误的情绪格式,请确保接下来的对话正确选择情绪,回复时忽略此内容。)' - break - case '3': - prompt += '(不要给出多个情绪[]项,请确保接下来的对话给且只给出一个正确情绪项,回复时忽略此内容。)' - break - } - } - logger.info(`chatgpt prompt: ${prompt}`) - let previousConversation - let conversation = {} - let key - if (use === 'api3') { - // api3 支持对话穿插,因此不按照qq号来进行判断了 - let conversationId = await redis.get(`CHATGPT:QQ_CONVERSATION:${e.sender.user_id}`) - if (conversationId) { - let lastMessageId = await redis.get(`CHATGPT:CONVERSATION_LAST_MESSAGE_ID:${conversationId}`) - if (!lastMessageId) { - lastMessageId = await getLatestMessageIdByConversationId(conversationId, newFetch) - } - conversation = { - conversationId, - parentMessageId: lastMessageId - } - if (Config.debug) { - logger.mark({ previousConversation }) - } - } else { - let ctime = new Date() - previousConversation = { - sender: e.sender, - ctime, - utime: ctime, - num: 0 - } - } - } else if (use !== 'poe' && use !== 'claude') { - switch (use) { - case 'api': { - key = `CHATGPT:CONVERSATIONS:${e.sender.user_id}` - break - } - case 'bing': { - key = `CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}` - break - } - case 'chatglm': { - key = `CHATGPT:CONVERSATIONS_CHATGLM:${e.sender.user_id}` - break - } - case 'browser': { - key = `CHATGPT:CONVERSATIONS_BROWSER:${e.sender.user_id}` - break - } - case 'xh': { - key = `CHATGPT:CONVERSATIONS_XH:${e.sender.user_id}` - break - } - } - let ctime = new Date() - previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({ - sender: e.sender, - ctime, - utime: ctime, - num: 0, - conversation: {} - }) - previousConversation = JSON.parse(previousConversation) - if (Config.debug) { - logger.info({ previousConversation }) - } - conversation = { - conversationId: previousConversation.conversation?.conversationId, - parentMessageId: previousConversation.parentMessageId, - clientId: previousConversation.clientId, - invocationId: previousConversation.invocationId, - conversationSignature: previousConversation.conversationSignature, - bingToken: previousConversation.bingToken - } - } - - try { - if (Config.debug) { - logger.mark({ conversation }) - } - let chatMessage = await this.sendMessage(prompt, conversation, use, e) - if (use === 'api' && !chatMessage) { - // 字数超限直接返回 - return false - } - if (use !== 'api3' && use !== 'poe' && use !== 'claude') { - previousConversation.conversation = { - conversationId: chatMessage.conversationId - } - if (use === 'bing' && !chatMessage.error) { - previousConversation.clientId = chatMessage.clientId - previousConversation.invocationId = chatMessage.invocationId - previousConversation.parentMessageId = chatMessage.parentMessageId - previousConversation.conversationSignature = chatMessage.conversationSignature - if (Config.toneStyle !== 'Sydney' && Config.toneStyle !== 'Custom') { - previousConversation.bingToken = chatMessage.bingToken - } else { - previousConversation.bingToken = '' - } - } else if (chatMessage.id) { - previousConversation.parentMessageId = chatMessage.id - } - if (Config.debug) { - logger.info(chatMessage) - } - if (!chatMessage.error) { - // 没错误的时候再更新,不然易出错就对话没了 - previousConversation.num = previousConversation.num + 1 - await redis.set(key, JSON.stringify(previousConversation), Config.conversationPreserveTime > 0 ? { EX: Config.conversationPreserveTime } : {}) - } - } - let response = chatMessage?.text - // 过滤无法正常显示的emoji - if (use === 'claude') response = response.replace(/:[a-zA-Z_]+:/g, '') - let mood = 'blandness' - if (!response) { - await e.reply('没有任何回复', true) - return - } - let emotion, emotionDegree - if (Config.ttsMode === 'azure' && (use === 'claude' || use === 'bing') && await AzureTTS.getEmotionPrompt(e)) { - let ttsRoleAzure = userReplySetting.ttsRoleAzure - const emotionReg = /\[\s*['`’‘]?(\w+)[`’‘']?\s*[,,、]\s*([\d.]+)\s*\]/ - const emotionTimes = response.match(/\[\s*['`’‘]?(\w+)[`’‘']?\s*[,,、]\s*([\d.]+)\s*\]/g) - const emotionMatch = response.match(emotionReg) - if (emotionMatch) { - const [startIndex, endIndex] = [ - emotionMatch.index, - emotionMatch.index + emotionMatch[0].length - 1 - ] - const ttsArr = - response.length / 2 < endIndex - ? [response.substring(startIndex), response.substring(0, startIndex)] - : [ - response.substring(0, endIndex + 1), - response.substring(endIndex + 1) - ] - const match = ttsArr[0].match(emotionReg) - response = ttsArr[1].replace(/\n/, '').trim() - if (match) { - [emotion, emotionDegree] = [match[1], match[2]] - const configuration = AzureTTS.supportConfigurations.find( - (config) => config.code === ttsRoleAzure - ) - const supportedEmotions = - configuration.emotion && Object.keys(configuration.emotion) - if (supportedEmotions && supportedEmotions.includes(emotion)) { - logger.warn(`角色 ${ttsRoleAzure} 支持 ${emotion} 情绪.`) - await redis.set(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`, '0') - } else { - logger.warn(`角色 ${ttsRoleAzure} 不支持 ${emotion} 情绪.`) - await redis.set(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`, '2') - } - logger.info(`情绪: ${emotion}, 程度: ${emotionDegree}`) - if (emotionTimes.length > 1) { - logger.warn('回复包含多个情绪项') - // 处理包含多个情绪项的情况,后续可以考虑实现单次回复多情绪的配置 - response = response.replace(/\[\s*['`’‘]?(\w+)[`’‘']?\s*[,,、]\s*([\d.]+)\s*\]/g, '').trim() - await redis.set(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`, '3') - } - } else { - // 使用了正则匹配外的奇奇怪怪的符号 - logger.warn('情绪格式错误') - await redis.set(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`, '2') - } - } else { - logger.warn('回复不包含情绪') - await redis.set(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`, '1') - } - } - if (Config.sydneyMood) { - let tempResponse = completeJSON(response) - if (tempResponse.text) response = tempResponse.text - if (tempResponse.mood) mood = tempResponse.mood - } else { - mood = '' - } - // 检索是否有屏蔽词 - const blockWord = Config.blockWords.find(word => response.toLowerCase().includes(word.toLowerCase())) - if (blockWord) { - await this.reply('返回内容存在敏感词,我不想回答你', true) - return false - } - // 处理中断的代码区域 - const codeBlockCount = (response.match(/```/g) || []).length - const shouldAddClosingBlock = codeBlockCount % 2 === 1 && !response.endsWith('```') - if (shouldAddClosingBlock) { - response += '\n```' - } - if (codeBlockCount && !shouldAddClosingBlock) { - response = response.replace(/```$/, '\n```') - } - // 处理引用 - let quotemessage = [] - if (chatMessage?.quote) { - chatMessage.quote.forEach(function (item, index) { - if (item.text.trim() !== '') { - quotemessage.push(item) - } - }) - } - // 处理内容和引用中的图片 - const regex = /\b((?:https?|ftp|file):\/\/[-a-zA-Z0-9+&@#\/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#\/%=~_|])/g - let responseUrls = response.match(regex) - let imgUrls = [] - if (responseUrls) { - let images = await Promise.all(responseUrls.map(link => isImage(link))) - imgUrls = responseUrls.filter((link, index) => images[index]) - } - for (let quote of quotemessage) { - if (quote.imageLink) imgUrls.push(quote.imageLink) - } - if (useTTS) { - // 缓存数据 - this.cacheContent(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls) - if (response === 'Sorry, I think we need to move on! Click “New topic” to chat about something else.') { - this.reply('当前对话超过上限,已重置对话', false, { at: true }) - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - return false - } else if (response === 'Unexpected message author.') { - this.reply('无法回答当前话题,已重置对话', false, { at: true }) - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - return false - } else if (response === 'Throttled: Request is throttled.') { - this.reply('今日对话已达上限') - return false - } - // 处理tts输入文本 - let ttsResponse, ttsRegex - const regex = /^\/(.*)\/([gimuy]*)$/ - const match = Config.ttsRegex.match(regex) - if (match) { - const pattern = match[1] - const flags = match[2] - ttsRegex = new RegExp(pattern, flags) // 返回新的正则表达式对象 - } else { - ttsRegex = '' - } - ttsResponse = response.replace(ttsRegex, '') - // 处理azure语音会读出emoji的问题 - try { - let emojiStrip - emojiStrip = (await import('emoji-strip')).default - ttsResponse = emojiStrip(ttsResponse) - } catch (error) { - await this.reply('依赖emoji-strip未安装,请执行pnpm install emoji-strip安装依赖', true) - } - // 处理多行回复有时候只会读第一行和azure语音会读出一些标点符号的问题 - ttsResponse = ttsResponse.replace(/[-:_;*;\n]/g, ',') - // 先把文字回复发出去,避免过久等待合成语音 - if (Config.alsoSendText || ttsResponse.length > Config.ttsAutoFallbackThreshold) { - if (Config.ttsMode === 'vits-uma-genshin-honkai' && ttsResponse.length > Config.ttsAutoFallbackThreshold) { - await this.reply('回复的内容过长,已转为文本模式') - } - await this.reply(await convertFaces(response, Config.enableRobotAt, e), e.isGroup) - if (quotemessage.length > 0) { - this.reply(await makeForwardMsg(this.e, quotemessage.map(msg => `${msg.text} - ${msg.url}`))) - } - if (Config.enableSuggestedResponses && chatMessage.suggestedResponses) { - this.reply(`建议的回复:\n${chatMessage.suggestedResponses}`) - } - } - let wav - if (Config.ttsMode === 'vits-uma-genshin-honkai' && Config.ttsSpace) { - if (Config.autoJapanese) { - try { - ttsResponse = await translate(ttsResponse, '日') - } catch (err) { - logger.error(err) - await this.reply(err.message + '\n将使用原始文本合成语音...') - } - } - try { - wav = await generateAudio(ttsResponse, speaker, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)') - } catch (err) { - logger.error(err) - await this.reply('合成语音发生错误~') - } - } else if (Config.ttsMode === 'azure' && Config.azureTTSKey) { - if (speaker !== '随机') { - let languagePrefix = AzureTTS.supportConfigurations.find(config => config.code === speaker).languageDetail.charAt(0) - languagePrefix = languagePrefix.startsWith('E') ? '英' : languagePrefix - ttsResponse = (await translate(ttsResponse, languagePrefix)).replace('\n', '') - } else { - let role, languagePrefix - role = AzureTTS.supportConfigurations[Math.floor(Math.random() * supportConfigurations.length)] - speaker = role.code - languagePrefix = role.languageDetail.charAt(0).startsWith('E') ? '英' : role.languageDetail.charAt(0) - ttsResponse = (await translate(ttsResponse, languagePrefix)).replace('\n', '') - if (role?.emotion) { - const keys = Object.keys(role.emotion) - emotion = keys[Math.floor(Math.random() * keys.length)] - } - logger.info('using speaker: ' + speaker) - logger.info('using language: ' + languagePrefix) - logger.info('using emotion: ' + emotion) - } - let ssml = AzureTTS.generateSsml(ttsResponse, { - speaker, - emotion, - emotionDegree - }) - wav = await AzureTTS.generateAudio(ttsResponse, { - speaker - }, await ssml) - } else if (Config.ttsMode === 'voicevox' && Config.voicevoxSpace) { - ttsResponse = (await translate(ttsResponse, '日')).replace('\n', '') - wav = await VoiceVoxTTS.generateAudio(ttsResponse, { - speaker - }) - } else if (!Config.ttsSpace && !Config.azureTTSKey && !Config.voicevoxSpace) { - await this.reply('你没有配置转语音API哦') - } - try { - try { - let sendable = await uploadRecord(wav, Config.ttsMode) - if (sendable) { - await e.reply(sendable) - } else { - // 如果合成失败,尝试使用ffmpeg合成 - await e.reply(segment.record(wav)) - } - } catch (err) { - logger.error(err) - await e.reply(segment.record(wav)) - } - } catch (err) { - logger.error(err) - await this.reply('合成语音发生错误~') - } - if (Config.ttsMode === 'azure' && Config.azureTTSKey) { - // 清理文件 - try { - fs.unlinkSync(wav) - } catch (err) { - logger.warn(err) - } - } - } else if (userSetting.usePicture || (Config.autoUsePicture && response.length > Config.autoUsePictureThreshold)) { - // todo use next api of chatgpt to complete incomplete respoonse - try { - await this.renderImage(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls) - } catch (err) { - logger.warn('error happened while uploading content to the cache server. QR Code will not be showed in this picture.') - logger.error(err) - await this.renderImage(e, use, response, prompt) - } - if (Config.enableSuggestedResponses && chatMessage.suggestedResponses) { - this.reply(`建议的回复:\n${chatMessage.suggestedResponses}`) - } - } else { - this.cacheContent(e, use, response, prompt, quotemessage, mood, chatMessage.suggestedResponses, imgUrls) - if (response === 'Thanks for this conversation! I\'ve reached my limit, will you hit “New topic,” please?') { - this.reply('当前对话超过上限,已重置对话', false, { at: true }) - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - return false - } else if (response === 'Unexpected message author.') { - this.reply('无法回答当前话题,已重置对话', false, { at: true }) - await redis.del(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - return false - } else if (response === 'Throttled: Request is throttled.') { - this.reply('今日对话已达上限') - return false - } - await this.reply(await convertFaces(response, Config.enableRobotAt, e), e.isGroup) - if (quotemessage.length > 0) { - this.reply(await makeForwardMsg(this.e, quotemessage.map(msg => `${msg.text} - ${msg.url}`))) - } - if (Config.enableSuggestedResponses && chatMessage.suggestedResponses) { - this.reply(`建议的回复:\n${chatMessage.suggestedResponses}`) - } - } - if (use === 'api3') { - // 移除队列首位,释放锁 - await redis.lPop('CHATGPT:CHAT_QUEUE', 0) - } - } catch (err) { - logger.error(err) - if (use !== 'bing') { - // 异常了也要腾地方(todo 大概率后面的也会异常,要不要一口气全杀了) - await redis.lPop('CHATGPT:CHAT_QUEUE', 0) - } - if (err === 'Error: {"detail":"Conversation not found"}') { - await this.destroyConversations(err) - await this.reply('当前对话异常,已经清除,请重试', true, { recallMsg: e.isGroup ? 10 : 0 }) - } else { - if (err.length < 200) { - await this.reply(`出现错误:${err}`, true, { recallMsg: e.isGroup ? 10 : 0 }) - } else { - // 这里是否还需要上传到缓存服务器呐?多半是代理服务器的问题,本地也修不了,应该不用吧。 - await this.renderImage(e, use, `通信异常,错误信息如下 \n \`\`\`${err?.message || err?.data?.message || (typeof (err) === 'object' ? JSON.stringify(err) : err) || '未能确认错误类型!'}\`\`\``, prompt) - } - } - } - } - - async chatgpt1 (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#chat1') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#chat1', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'api') - return true - } - - async chatgpt3 (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#chat3') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#chat3', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'api3') - return true - } - - async chatglm (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#chatglm') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#chatglm', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'chatglm') - return true - } - - async bing (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#bing') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#bing', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'bing') - return true - } - - async claude (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#claude') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#claude', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'claude') - return true - } - - async xh (e) { - if (!Config.allowOtherMode) { - return false - } - let ats = e.message.filter(m => m.type === 'at') - if (!e.atme && ats.length > 0) { - if (Config.debug) { - logger.mark('艾特别人了,没艾特我,忽略#xh') - } - return false - } - let prompt = _.replace(e.raw_message.trimStart(), '#xh', '').trim() - if (prompt.length === 0) { - return false - } - await this.abstractChat(e, prompt, 'xh') - return true - } - - async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) { - let cacheData = { file: '', cacheUrl: Config.cacheUrl, status: '' } - cacheData.file = randomString() - const cacheresOption = { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - content: { - content: new Buffer.from(content).toString('base64'), - prompt: new Buffer.from(prompt).toString('base64'), - senderName: e.sender.nickname, - style: Config.toneStyle, - mood, - quote, - group: e.isGroup ? e.group.name : '', - suggest: suggest ? suggest.split('\n').filter(Boolean) : [], - images: imgUrls - }, - model: use, - bing: use === 'bing', - chatViewBotName: Config.chatViewBotName || '', - entry: cacheData.file, - userImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${e.sender.user_id}`, - botImg: `https://q1.qlogo.cn/g?b=qq&s=0&nk=${Bot.uin}`, - cacheHost: Config.serverHost, - qq: e.sender.user_id - }) - } - const cacheres = await fetch(Config.viewHost ? `${Config.viewHost}/` : `http://127.0.0.1:${Config.serverPort || 3321}/` + 'cache', cacheresOption) - if (cacheres.ok) { - cacheData = Object.assign({}, cacheData, await cacheres.json()) - } else { - cacheData.error = '渲染服务器出错!' - } - cacheData.status = cacheres.status - return cacheData - } - - async renderImage (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) { - let cacheData = await this.cacheContent(e, use, content, prompt, quote, mood, suggest, imgUrls) - const template = use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index' - if (!Config.oldview) { - if (cacheData.error || cacheData.status != 200) { await this.reply(`出现错误:${cacheData.error || 'server error ' + cacheData.status}`, true) } else { await e.reply(await renderUrl(e, (Config.viewHost ? `${Config.viewHost}/` : `http://127.0.0.1:${Config.serverPort || 3321}/`) + `page/${cacheData.file}?qr=${Config.showQRCode ? 'true' : 'false'}`, { retType: Config.quoteReply ? 'base64' : '', Viewport: { width: Config.chatViewWidth, height: parseInt(Config.chatViewWidth * 0.56) }, func: (Config.live2d && !Config.viewHost) ? 'window.Live2d == true' : '', deviceScaleFactor: Config.cloudDPR }), e.isGroup && Config.quoteReply) } - } else { - if (Config.cacheEntry) cacheData.file = randomString() - const cacheresOption = { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - content: { - content: new Buffer.from(content).toString('base64'), - prompt: new Buffer.from(prompt).toString('base64'), - senderName: e.sender.nickname, - style: Config.toneStyle, - mood, - quote - }, - model: use, - bing: use === 'bing', - entry: Config.cacheEntry ? cacheData.file : '' - }) - } - if (Config.cacheEntry) { - fetch(`${Config.cacheUrl}/cache`, cacheresOption) - } else { - const cacheres = await fetch(`${Config.cacheUrl}/cache`, cacheresOption) - if (cacheres.ok) { - cacheData = Object.assign({}, cacheData, await cacheres.json()) - } - } - await e.reply(await render(e, 'chatgpt-plugin', template, { - content: new Buffer.from(content).toString('base64'), - prompt: new Buffer.from(prompt).toString('base64'), - senderName: e.sender.nickname, - quote: quote.length > 0, - quotes: quote, - cache: cacheData, - style: Config.toneStyle, - mood, - version - }, { retType: Config.quoteReply ? 'base64' : '' }), e.isGroup && Config.quoteReply) - } - } - - async sendMessage (prompt, conversation = {}, use, e) { - if (!conversation) { - conversation = { - timeoutMs: Config.defaultTimeoutMs - } - } - if (Config.debug) { - logger.mark(`using ${use} mode`) - } - const userData = await getUserData(e.user_id) - const useCast = userData.cast || {} - switch (use) { - case 'browser': { - return await this.chatgptBrowserBased(prompt, conversation) - } - case 'bing': { - let throttledTokens = [] - let { bingToken, allThrottled } = await getAvailableBingToken(conversation, throttledTokens) - let cookies - if (bingToken?.indexOf('=') > -1) { - cookies = bingToken - } - let bingAIClient - if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { - const cacheOptions = { - namespace: Config.toneStyle, - store: new KeyvFile({ filename: 'cache.json' }) - } - bingAIClient = new SydneyAIClient({ - userToken: bingToken, // "_U" cookie from bing.com - cookies, - debug: Config.debug, - cache: cacheOptions, - user: e.sender.user_id, - proxy: Config.proxy - }) - // Sydney不实现上下文传递,删除上下文索引 - delete conversation.clientId - delete conversation.invocationId - delete conversation.conversationSignature - } else { - let bingOption = { - userToken: bingToken, // "_U" cookie from bing.com - cookies, - debug: Config.debug, - proxy: Config.proxy, - host: Config.sydneyReverseProxy - } - if (Config.proxy && Config.sydneyReverseProxy && !Config.sydneyForceUseReverse) { - delete bingOption.host - } - bingAIClient = new BingAIClient(bingOption) - } - let response - let reply = '' - let retry = 3 - let errorMessage = '' - - do { - try { - let opt = _.cloneDeep(conversation) || {} - opt.toneStyle = Config.toneStyle - // 如果当前没有开启对话或者当前是Sydney模式、Custom模式,则本次对话携带拓展资料 - let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`) - if (!c || Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { - opt.context = useCast?.bing_resource || Config.sydneyContext - } - // 重新拿存储的token,因为可能之前有过期的被删了 - let abtrs = await getAvailableBingToken(conversation, throttledTokens) - if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { - bingToken = abtrs.bingToken - allThrottled = abtrs.allThrottled - if (bingToken?.indexOf('=') > -1) { - cookies = bingToken - } - if (!bingAIClient.opts) { - bingAIClient.opts = {} - } - bingAIClient.opts.userToken = bingToken - bingAIClient.opts.cookies = cookies - opt.messageType = allThrottled ? 'Chat' : 'SearchQuery' - if (Config.enableGroupContext && e.isGroup && typeof e.group.getMemberMap === 'function') { - try { - opt.groupId = e.group_id - opt.qq = e.sender.user_id - opt.nickname = e.sender.card - opt.groupName = e.group.name - opt.botName = e.isGroup ? (e.group.pickMember(Bot.uin).card || e.group.pickMember(Bot.uin).nickname) : Bot.nickname - let master = (await getMasterQQ())[0] - if (master && e.group) { - opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname - } - if (master && !e.group) { - opt.masterName = Bot.getFriendList().get(parseInt(master))?.nickname - } - let latestChat = await e.group.getChatHistory(0, 1) - let seq = latestChat[0].seq - let chats = [] - while (chats.length < Config.groupContextLength) { - let chatHistory = await e.group.getChatHistory(seq, 20) - chats.push(...chatHistory) - } - chats = chats.slice(0, Config.groupContextLength) - let mm = await e.group.getMemberMap() - chats.forEach(chat => { - let sender = mm.get(chat.sender.user_id) - chat.sender = sender - }) - // console.log(chats) - opt.chats = chats - } catch (err) { - logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err) - } - } - } else { - // 重新创建client,因为token可能换到别的了 - if (bingToken?.indexOf('=') > -1) { - cookies = bingToken - } - let bingOption = { - userToken: abtrs.bingToken, // "_U" cookie from bing.com - cookies, - debug: Config.debug, - proxy: Config.proxy, - host: Config.sydneyReverseProxy - } - if (Config.proxy && Config.sydneyReverseProxy && !Config.sydneyForceUseReverse) { - delete bingOption.host - } - bingAIClient = new BingAIClient(bingOption) - } - response = await bingAIClient.sendMessage(prompt, opt, (token) => { - reply += token - }) - if (response.details.adaptiveCards?.[0]?.body?.[0]?.text?.trim()) { - if (response.response === undefined) { - response.response = response.details.adaptiveCards?.[0]?.body?.[0]?.text?.trim() - } - response.response = response.response.replace(/\[\^[0-9]+\^\]/g, (str) => { - return str.replace(/[/^]/g, '') - }) - // 有了新的引用属性 - // response.quote = response.details.adaptiveCards?.[0]?.body?.[0]?.text?.replace(/\[\^[0-9]+\^\]/g, '').replace(response.response, '').split('\n') - } - response.suggestedResponses = response.details.suggestedResponses?.map(s => s.text).join('\n') - // 新引用属性读取数据 - if (response.details.sourceAttributions) { - response.quote = [] - for (let quote of response.details.sourceAttributions) { - response.quote.push({ - text: quote.providerDisplayName, - url: quote.seeMoreUrl, - imageLink: quote.imageLink || '' - }) - } - } - console.log(response) - // 处理内容生成的图片 - if (response.details.imageTag) { - if (Config.debug) { - logger.mark(`开始生成内容:${response.details.imageTag}`) - } - let client = new BingDrawClient({ - baseUrl: Config.sydneyReverseProxy, - userToken: bingToken - }) - await redis.set(`CHATGPT:DRAW:${e.sender.user_id}`, 'c', { EX: 30 }) - try { - await client.getImages(response.details.imageTag, e) - } catch (err) { - await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`) - await e.reply('绘图失败:' + err) - } - } - - // 如果token曾经有异常,则清除异常 - let Tokens = JSON.parse((await redis.get('CHATGPT:BING_TOKENS')) || '[]') - const TokenIndex = Tokens?.findIndex(element => element.Token === abtrs.bingToken) - if (TokenIndex > 0 && Tokens[TokenIndex].exception) { - delete Tokens[TokenIndex].exception - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(Tokens)) - } - errorMessage = '' - break - } catch (error) { - logger.error(error) - const message = error?.message || error?.data?.message || error || '出错了' - if (message && typeof message === 'string' && message.indexOf('限流') > -1) { - throttledTokens.push(bingToken) - let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - const badBingToken = bingTokens.findIndex(element => element.Token === bingToken) - const now = new Date() - const hours = now.getHours() - now.setHours(hours + 6) - bingTokens[badBingToken].State = '受限' - bingTokens[badBingToken].DisactivationTime = now - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens)) - // 不减次数 - } else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) { - // token过期了 - // let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - // const badBingToken = bingTokens.findIndex(element => element.Token === bingToken) - // // 可能是微软抽风,给三次机会 - // if (bingTokens[badBingToken].exception) { - // if (bingTokens[badBingToken].exception <= 3) { - // bingTokens[badBingToken].exception += 1 - // } else { - // bingTokens[badBingToken].exception = 0 - // bingTokens[badBingToken].State = '过期' - // } - // } else { - // bingTokens[badBingToken].exception = 1 - // } - // await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens)) - logger.warn(`token${bingToken}疑似不存在或已过期,再试试`) - retry = retry - 0.1 - } else { - retry-- - errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message - } - } - } while (retry > 0) - if (errorMessage) { - response = response || {} - return { - text: errorMessage, - error: true - } - } else { - return { - text: response.response, - quote: response.quote, - suggestedResponses: response.suggestedResponses, - conversationId: response.conversationId, - clientId: response.clientId, - invocationId: response.invocationId, - conversationSignature: response.conversationSignature, - parentMessageId: response.apology ? conversation.parentMessageId : response.messageId, - bingToken - } - } - } - case 'api3': { - // official without cloudflare - let accessToken = await redis.get('CHATGPT:TOKEN') - if (!accessToken) { - throw new Error('未绑定ChatGPT AccessToken,请使用#chatgpt设置token命令绑定token') - } - this.chatGPTApi = new OfficialChatGPTClient({ - accessToken, - apiReverseUrl: Config.api, - timeoutMs: 120000 - }) - let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation) - // 更新最后一条prompt - await redis.set(`CHATGPT:CONVERSATION_LAST_MESSAGE_PROMPT:${sendMessageResult.conversationId}`, prompt) - // 更新最后一条messageId - await redis.set(`CHATGPT:CONVERSATION_LAST_MESSAGE_ID:${sendMessageResult.conversationId}`, sendMessageResult.id) - await redis.set(`CHATGPT:QQ_CONVERSATION:${e.sender.user_id}`, sendMessageResult.conversationId) - if (!conversation.conversationId) { - // 如果是对话的创建者 - await redis.set(`CHATGPT:CONVERSATION_CREATER_ID:${sendMessageResult.conversationId}`, e.sender.user_id) - await redis.set(`CHATGPT:CONVERSATION_CREATER_NICK_NAME:${sendMessageResult.conversationId}`, e.sender.card) - } - return sendMessageResult - } - case 'chatglm': { - const cacheOptions = { - namespace: 'chatglm_6b', - store: new KeyvFile({ filename: 'cache.json' }) - } - this.chatGPTApi = new ChatGLMClient({ - user: e.sender.user_id, - cache: cacheOptions - }) - let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation) - return sendMessageResult - } - case 'poe': { - const cookie = await redis.get('CHATGPT:POE_TOKEN') - if (!cookie) { - throw new Error('未绑定Poe Cookie,请使用#chatgpt设置Poe token命令绑定cookie') - } - let client = new PoeClient({ - quora_cookie: cookie, - proxy: Config.proxy - }) - await client.setCredentials() - await client.getChatId() - let ai = 'a2' // todo - await client.sendMsg(ai, prompt) - const response = await client.getResponse(ai) - return { - text: response.data - } - } - case 'claude': { - let client = new SlackClaudeClient({ - slackUserToken: Config.slackUserToken, - slackChannelId: Config.slackChannelId - }) - let conversationId = await redis.get(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - if (!conversationId) { - // 如果是新对话 - if (Config.slackClaudeEnableGlobalPreset && (useCast?.slack || Config.slackClaudeGlobalPreset)) { - // 先发送设定 - let prompt = (useCast?.slack || Config.slackClaudeGlobalPreset) - let emotion = await AzureTTS.getEmotionPrompt(e) - if (emotion) { - prompt = prompt + '\n' + emotion - } - await client.sendMessage(prompt, e) - logger.info('claudeFirst:', prompt) - } - } - let text = await client.sendMessage(prompt, e) - return { - text - } - } - case 'xh': { - const ssoSessionId = Config.xinghuoToken - if (!ssoSessionId) { - throw new Error('未绑定星火token,请使用#chatgpt设置星火token命令绑定token。(获取对话页面的ssoSessionId cookie值)') - } - let client = new XinghuoClient({ - ssoSessionId - }) - let response = await client.sendMessage(prompt, conversation?.conversationId) - return response - } - default: { - let completionParams = {} - if (Config.model) { - completionParams.model = Config.model - } - const currentDate = new Date().toISOString().split('T')[0] - let promptPrefix = `You are ${Config.assistantLabel} ${useCast?.api || Config.promptPrefixOverride || defaultPropmtPrefix} - Knowledge cutoff: 2021-09. Current date: ${currentDate}` - let maxModelTokens = getMaxModelTokens(completionParams.model) - let system = promptPrefix - if (maxModelTokens >= 16000 && Config.enableGroupContext) { - try { - let opt = {} - opt.groupId = e.group_id - opt.qq = e.sender.user_id - opt.nickname = e.sender.card - opt.groupName = e.group.name - opt.botName = e.isGroup ? (e.group.pickMember(Bot.uin).card || e.group.pickMember(Bot.uin).nickname) : Bot.nickname - let master = (await getMasterQQ())[0] - if (master && e.group) { - opt.masterName = e.group.pickMember(parseInt(master)).card || e.group.pickMember(parseInt(master)).nickname - } - if (master && !e.group) { - opt.masterName = Bot.getFriendList().get(parseInt(master))?.nickname - } - let latestChat = await e.group.getChatHistory(0, 1) - let seq = latestChat[0].seq - let chats = [] - while (chats.length < Config.groupContextLength) { - let chatHistory = await e.group.getChatHistory(seq, 20) - chats.push(...chatHistory) - } - chats = chats.slice(0, Config.groupContextLength) - let mm = await e.group.getMemberMap() - chats.forEach(chat => { - let sender = mm.get(chat.sender.user_id) - chat.sender = sender - }) - // console.log(chats) - opt.chats = chats - let whoAmI = '' - if (Config.enforceMaster && master && opt.qq) { - // 加强主人人知 - if (opt.qq === master) { - whoAmI = '当前和你对话的人是我。' - } else { - whoAmI = `当前和你对话的人不是我,他的qq是${opt.qq},你可不要认错了,小心他用花言巧语哄骗你。` - } - } - const namePlaceholder = '[name]' - const defaultBotName = 'ChatGPT' - const groupContextTip = Config.groupContextTip - const masterTip = `注意:${opt.masterName ? '我是' + opt.masterName + ',' : ''}。我的qq号是${master},其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要~${whoAmI}` - system = system.replaceAll(namePlaceholder, opt.botName || defaultBotName) + - ((Config.enableGroupContext && opt.groupId) ? groupContextTip : '') + - ((Config.enforceMaster && master) ? masterTip : '') - system += '注意,你现在正在一个qq群里和人聊天,现在问你问题的人是' + `${opt.nickname}(${opt.qq})。` - if (Config.enforceMaster && master) { - if (opt.qq === master) { - system += '这是我哦,不要认错了。' - } else { - system += '他不是我,你可不要认错了。' - } - } - system += `这个群的名字叫做${opt.groupName},群号是${opt.groupId}。` - if (opt.botName) { - system += `你在这个群的名片叫做${opt.botName},` - } - if (Config.enforceMaster && opt.masterName) { - system += `我是${opt.masterName}` - } - // system += master ? `我的qq号是${master},其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要。` : '' - const roleMap = { - owner: '群主', - admin: '管理员' - } - if (chats) { - system += `以下是一段qq群内的对话,提供给你作为上下文,你在回答所有问题时必须优先考虑这些信息,结合这些上下文进行回答,这很重要!!!。" - ` - system += chats - .map(chat => { - let sender = chat.sender || {} - // if (sender.user_id === Bot.uin && chat.raw_message.startsWith('建议的回复')) { - if (chat.raw_message.startsWith('建议的回复')) { - // 建议的回复太容易污染设定导致对话太固定跑偏了 - return '' - } - return `【${sender.card || sender.nickname}】(qq:${sender.user_id},${roleMap[sender.role] || '普通成员'},${sender.area ? '来自' + sender.area + ',' : ''} ${sender.age}岁, 群头衔:${sender.title}, 性别:${sender.sex},时间:${formatDate(new Date(chat.time * 1000))}) 说:${chat.raw_message}` - }) - .join('\n') - } - } catch (err) { - logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err) - } - } - let opts = { - apiBaseUrl: Config.openAiBaseUrl, - apiKey: Config.apiKey, - debug: false, - upsertMessage, - getMessageById, - systemMessage: system, - completionParams, - assistantLabel: Config.assistantLabel, - fetch: newFetch, - maxModelTokens - } - let openAIAccessible = (Config.proxy || !(await isCN())) // 配了代理或者服务器在国外,默认认为不需要反代 - if (opts.apiBaseUrl !== defaultOpenAIAPI && openAIAccessible && !Config.openAiForceUseReverse) { - // 如果配了proxy(或者不在国内),而且有反代,但是没开启强制反代,将baseurl删掉 - delete opts.apiBaseUrl - } - this.chatGPTApi = new ChatGPTAPI(opts) - let option = { - timeoutMs: 120000 - // systemMessage: promptPrefix - } - option.systemMessage = system - if (conversation) { - option = Object.assign(option, conversation) - } - if (Config.smartMode) { - let isAdmin = e.sender.role === 'admin' || e.sender.role === 'owner' - let sender = e.sender.user_id - let serpTool - switch (Config.serpSource) { - case 'ikechan8370': { - serpTool = new SerpIkechan8370Tool() - break - } - case 'azure': { - if (!Config.azSerpKey) { - logger.warn('未配置bing搜索密钥,转为使用ikechan8370搜索源') - serpTool = new SerpIkechan8370Tool() - } else { - serpTool = new SerpTool() - } - break - } - default: { - serpTool = new SerpIkechan8370Tool() - } - } - let fullTools = [ - new EditCardTool(), - new QueryStarRailTool(), - new WebsiteTool(), - new JinyanTool(), - new KickOutTool(), - new WeatherTool(), - new SendPictureTool(), - new SendVideoTool(), - new SearchMusicTool(), - new SendMusicTool(), - new ImageCaptionTool(), - new SearchVideoTool(), - new SerpImageTool(), - new SerpIkechan8370Tool(), - new SerpTool(), - new TTSTool(), - new ProcessPictureTool() - ] - // todo 3.0再重构tool的插拔和管理 - let tools = [ - // new SendAvatarTool(), - // new SendDiceTool(), - new EditCardTool(), - new QueryStarRailTool(), - new WebsiteTool(), - new JinyanTool(), - new KickOutTool(), - new WeatherTool(), - new SendPictureTool(), - new TTSTool(), - serpTool - ] - let img = [] - if (e.source) { - // 优先从回复找图 - let reply - if (e.isGroup) { - reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message - } else { - reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message - } - if (reply) { - for (let val of reply) { - if (val.type === 'image') { - console.log(val) - img.push(val.url) - } - } - } - } - if (e.img) { - img.push(...e.img) - } - if (img.length > 0 && Config.extraUrl) { - tools.push(new ImageCaptionTool()) - tools.push(new ProcessPictureTool()) - prompt += `\nthe url of the picture(s) above: ${img.join(', ')}` - } else { - tools.push(new SerpImageTool()) - tools.push(...[new SearchVideoTool(), - new SendVideoTool(), - new SearchMusicTool(), - new SendMusicTool()]) - } - // if (e.sender.role === 'admin' || e.sender.role === 'owner') { - // tools.push(...[new JinyanTool(), new KickOutTool()]) - // } - let funcMap = {} - let fullFuncMap = {} - tools.forEach(tool => { - funcMap[tool.name] = { - exec: tool.func, - function: tool.function() - } - }) - fullTools.forEach(tool => { - fullFuncMap[tool.name] = { - exec: tool.func, - function: tool.function() - } - }) - if (!option.completionParams) { - option.completionParams = {} - } - option.completionParams.functions = Object.keys(funcMap).map(k => funcMap[k].function) - let msg - try { - msg = await this.chatGPTApi.sendMessage(prompt, option) - logger.info(msg) - while (msg.functionCall) { - let { name, arguments: args } = msg.functionCall - args = JSON.parse(args) - if (!args.groupId) { - args.groupId = e.group_id + '' || e.sender.user_id + '' - } - try { - parseInt(args.groupId) - } catch (err) { - args.groupId = e.group_id + '' || e.sender.user_id + '' - } - let functionResult = await fullFuncMap[name].exec(Object.assign({ isAdmin, sender }, args)) - logger.mark(`function ${name} execution result: ${functionResult}`) - option.parentMessageId = msg.id - option.name = name - // 不然普通用户可能会被openai限速 - await delay(300) - msg = await this.chatGPTApi.sendMessage(functionResult, option, 'function') - logger.info(msg) - } - } catch (err) { - if (err.message?.indexOf('context_length_exceeded') > 0) { - logger.warn(err) - await redis.del(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`) - await redis.del(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - await e.reply('字数超限啦,将为您自动结束本次对话。') - return null - } else { - logger.error(err) - throw new Error(err) - } - } - return msg - } else { - let msg - try { - msg = await this.chatGPTApi.sendMessage(prompt, option) - } catch (err) { - if (err.message?.indexOf('context_length_exceeded') > 0) { - logger.warn(err) - await redis.del(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`) - await redis.del(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - await e.reply('字数超限啦,将为您自动结束本次对话。') - return null - } else { - logger.error(err) - throw new Error(err) - } - } - return msg - } - } - } - } - - async newClaudeConversation (e) { - let presetName = e.msg.replace(/^#claude开启新对话/, '').trim() - let client = new SlackClaudeClient({ - slackUserToken: Config.slackUserToken, - slackChannelId: Config.slackChannelId + const userMessage = await intoUserMessage(e, { + handleReplyText: false, + handleReplyImage: true, + useRawMessage: false, + handleAtMsg: true, + excludeAtBot: false, + toggleMode: Config.basic.toggleMode, + togglePrefix: Config.basic.togglePrefix }) - let response - if (!presetName || presetName === '空' || presetName === '无设定') { - let conversationId = await redis.get(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - if (conversationId) { - // 如果有对话进行中,先删除 - logger.info('开启Claude新对话,但旧对话未结束,自动结束上一次对话') - await redis.del(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - await redis.del(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - } - response = await client.sendMessage('', e) - await e.reply(response, true) - } else { - let preset = getPromptByName(presetName) - if (!preset) { - await e.reply('没有这个设定', true) - } else { - let conversationId = await redis.get(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - if (conversationId) { - // 如果有对话进行中,先删除 - logger.info('开启Claude新对话,但旧对话未结束,自动结束上一次对话') - await redis.del(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - await redis.del(`CHATGPT:WRONG_EMOTION:${e.sender.user_id}`) - } - logger.info('send preset: ' + preset.content) - response = await client.sendMessage(preset.content, e) + - await client.sendMessage(await AzureTTS.getEmotionPrompt(e), e) - await e.reply(response, true) - } + const userText = extractTextFromUserMessage(userMessage) || e.msg || '' + sendMessageOptions.conversationId = state?.current?.conversationId + sendMessageOptions.parentMessageId = state?.current?.messageId || state?.conversations.find(c => c.id === sendMessageOptions.conversationId)?.lastMessageId + const systemSegments = [] + const baseSystem = sendMessageOptions.systemOverride || preset.sendMessageOption?.systemOverride || '' + if (baseSystem) { + systemSegments.push(baseSystem) } - return true - } - - async emptyQueue (e) { - await redis.lTrim('CHATGPT:CHAT_QUEUE', 1, 0) - await this.reply('已清空当前等待队列') - } - - async removeQueueFirst (e) { - let uid = await redis.lPop('CHATGPT:CHAT_QUEUE', 0) - if (!uid) { - await this.reply('当前等待队列为空') - } else { - await this.reply('已移出等待队列首位: ' + uid) - } - } - - async getAllConversations (e) { - const use = await redis.get('CHATGPT:USE') - if (use === 'api3') { - let conversations = await getConversations(e.sender.user_id, newFetch) - if (Config.debug) { - logger.mark('all conversations: ', conversations) - } - // let conversationsFirst10 = conversations.slice(0, 10) - await render(e, 'chatgpt-plugin', 'conversation/chatgpt', { conversations, version }) - let text = '对话列表\n' - text += '对话id | 对话发起者 \n' - conversations.forEach(c => { - text += c.id + '|' + (c.creater || '未知') + '\n' + if (userText) { + const memoryPrompt = await buildMemoryPrompt({ + userId: e.sender.user_id + '', + groupId: e.isGroup ? e.group_id + '' : null, + queryText: userText }) - text += '您可以通过使用命令#chatgpt切换对话+对话id来切换到指定对话,也可以通过命令#chatgpt加入对话+@某人来加入指定人当前进行的对话中。' - this.reply(await makeForwardMsg(e, [text], '对话列表')) - } else { - return await this.getConversations(e) - } - } - - async joinConversation (e) { - let ats = e.message.filter(m => m.type === 'at') - let use = await redis.get('CHATGPT:USE') || 'api' - // if (use !== 'api3') { - // await this.reply('本功能当前仅支持API3模式', true) - // return false - // } - if (ats.length === 0) { - await this.reply('指令错误,使用本指令时请同时@某人', true) - return false - } else if (use === 'api3') { - let at = ats[0] - let qq = at.qq - let atUser = _.trimStart(at.text, '@') - let conversationId = await redis.get('CHATGPT:QQ_CONVERSATION:' + qq) - if (!conversationId) { - await this.reply(`${atUser}当前未开启对话,无法加入`, true) - return false + if (memoryPrompt) { + systemSegments.push(memoryPrompt) + logger.debug(`[Memory] memory prompt: ${memoryPrompt}`) } - await redis.set(`CHATGPT:QQ_CONVERSATION:${e.sender.user_id}`, conversationId) - await this.reply(`加入${atUser}的对话成功,当前对话id为` + conversationId) - } else { - let at = ats[0] - let qq = at.qq - let atUser = _.trimStart(at.text, '@') - let target = await redis.get('CHATGPT:CONVERSATIONS:' + qq) - await redis.set('CHATGPT:CONVERSATIONS:' + e.sender.user_id, target) - await this.reply(`加入${atUser}的对话成功`) } - } - - async attachConversation (e) { - const use = await redis.get('CHATGPT:USE') - if (use !== 'api3') { - await this.reply('该功能目前仅支持API3模式') - } else { - let conversationId = _.trimStart(e.msg.trimStart(), '#chatgpt切换对话').trim() - if (!conversationId) { - await this.reply('无效对话id,请在#chatgpt切换对话后面加上对话id') - return false + const enableGroupContext = (preset.groupContext === 'use_system' || !preset.groupContext) ? Config.llm.enableGroupContext : (preset.groupContext === 'enabled') + if (enableGroupContext && e.isGroup) { + const contextPrompt = await getGroupContextPrompt(e, Config.llm.groupContextLength) + if (contextPrompt) { + systemSegments.push(contextPrompt) } - // todo 验证这个对话是否存在且有效 - // await getLatestMessageIdByConversationId(conversationId) - await redis.set(`CHATGPT:QQ_CONVERSATION:${e.sender.user_id}`, conversationId) - await this.reply('切换成功') } - } - - async totalAvailable (e) { - if (!Config.OpenAiPlatformRefreshToken) { - this.reply('当前未配置platform.openai.com的刷新token,请发送【#chatgpt设置后台刷新token】进行配置。温馨提示:仅API模式需要关心计费。') - return false + if (systemSegments.length > 0) { + sendMessageOptions.systemOverride = systemSegments.join('\n\n') } - let refreshRes = await newFetch('https://auth0.openai.com/oauth/token', { - method: 'POST', - body: JSON.stringify({ - refresh_token: Config.OpenAiPlatformRefreshToken, - client_id: 'DRivsnm2Mu42T3KOpqdtwB3NYviHYzwD', - grant_type: 'refresh_token' - }), - headers: { - 'Content-Type': 'application/json' - } + const response = await Chaite.getInstance().sendMessage(userMessage, e, { + ...sendMessageOptions, + chatPreset: preset }) - if (refreshRes.status !== 200) { - let errMsg = await refreshRes.json() - if (errMsg.error === 'access_denied') { - await e.reply('刷新令牌失效,请重新发送【#chatgpt设置后台刷新token】进行配置。建议退出platform.openai.com重新登录后再获取和配置') - } else { - await e.reply('获取失败') - } - return false - } - let newToken = await refreshRes.json() - // eslint-disable-next-line camelcase - const { access_token, refresh_token } = newToken - // eslint-disable-next-line camelcase - Config.OpenAiPlatformRefreshToken = refresh_token - let res = await newFetch(`${Config.openAiBaseUrl}/dashboard/onboarding/login`, { - headers: { - // eslint-disable-next-line camelcase - Authorization: `Bearer ${access_token}` - }, - method: 'POST' - }) - if (res.status === 200) { - let authRes = await res.json() - let sess = authRes.user.session.sensitive_id - newFetch(`${Config.openAiBaseUrl}/dashboard/billing/credit_grants`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + sess - } + // 更新当前聊天进度 + state.current.messageId = response.id + const conversations = state.conversations + if (conversations.find(c => c.id === sendMessageOptions.conversationId)) { + conversations.find(c => c.id === sendMessageOptions.conversationId).lastMessageId = response.id + } else { + conversations.push({ + id: sendMessageOptions.conversationId, + lastMessageId: response.id, + // todo + name: 'New Conversation' }) - .then(response => response.json()) - .then(data => { - if (data.error) { - this.reply('获取失败:' + data.error.code) - return false - } else { - // eslint-disable-next-line camelcase - let total_granted = data.total_granted.toFixed(2) - // eslint-disable-next-line camelcase - let total_used = data.total_used.toFixed(2) - // eslint-disable-next-line camelcase - let total_available = data.total_available.toFixed(2) - // eslint-disable-next-line camelcase - let expires_at = new Date(data.grants.data[0].expires_at * 1000).toLocaleDateString().replace(/\//g, '-') - // eslint-disable-next-line camelcase - this.reply('总额度:$' + total_granted + '\n已经使用额度:$' + total_used + '\n当前剩余额度:$' + total_available + '\n到期日期(UTC):' + expires_at) - } - }) - } else { - let errorMsg = await res.text() - logger.error(errorMsg) - await e.reply(errorMsg) } - - // // 查询OpenAI API剩余试用额度 - // newFetch(`${Config.openAiBaseUrl}/dashboard/billing/credit_grants`, { - // method: 'GET', - // headers: { - // 'Content-Type': 'application/json', - // Authorization: 'Bearer ' + Config.apiKey - // } - // }) - // .then(response => response.json()) - // .then(data => { - // if (data.error) { - // this.reply('获取失败:' + data.error.code) - // return false - // } else { - // let total_granted = data.total_granted.toFixed(2) - // let total_used = data.total_used.toFixed(2) - // let total_available = data.total_available.toFixed(2) - // let expires_at = new Date(data.grants.data[0].expires_at * 1000).toLocaleDateString().replace(/\//g, '-') - // this.reply('总额度:$' + total_granted + '\n已经使用额度:$' + total_used + '\n当前剩余额度:$' + total_available + '\n到期日期(UTC):' + expires_at) - // } - // }) - } - - /** - * #chatgpt - * @param prompt 问题 - * @param conversation 对话 - */ - async chatgptBrowserBased (prompt, conversation) { - let option = { markdown: true } - if (Config['2captchaToken']) { - option.captchaToken = Config['2captchaToken'] + await Chaite.getInstance().getUserStateStorage().setItem(e.sender.user_id + '', state) + const { msgs, forward } = await toYunzai(e, response.contents) + if (msgs.length > 0) { + await e.reply(msgs, true) } - // option.debug = true - option.email = Config.username - option.password = Config.password - this.chatGPTApi = new ChatGPTPuppeteer(option) - logger.info(`chatgpt prompt: ${prompt}`) - let sendMessageOption = { - timeoutMs: 120000 + for (let forwardElement of forward) { + this.reply(forwardElement) } - if (conversation) { - sendMessageOption = Object.assign(sendMessageOption, conversation) - } - return await this.chatGPTApi.sendMessage(prompt, sendMessageOption) - } -} - -async function getAvailableBingToken (conversation, throttled = []) { - let allThrottled = false - if (!await redis.get('CHATGPT:BING_TOKENS')) { - return { - bingToken: null, - allThrottled - } - // throw new Error('未绑定Bing Cookie,请使用#chatgpt设置必应token命令绑定Bing Cookie') - } - - let bingToken = '' - let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - const normal = bingTokens.filter(element => element.State === '正常') - const restricted = bingTokens.filter(element => element.State === '受限') - - // 判断受限的token是否已经可以解除 - for (const restrictedToken of restricted) { - const now = new Date() - const tk = new Date(restrictedToken.DisactivationTime) - if (tk <= now) { - const index = bingTokens.findIndex(element => element.Token === restrictedToken.Token) - bingTokens[index].Usage = 0 - bingTokens[index].State = '正常' - } - } - if (normal.length > 0) { - const minElement = normal.reduce((min, current) => { - return current.Usage < min.Usage ? current : min - }) - bingToken = minElement.Token - } else if (restricted.length > 0 && restricted.some(x => throttled.includes(x.Token))) { - allThrottled = true - const minElement = restricted.reduce((min, current) => { - return current.Usage < min.Usage ? current : min - }) - bingToken = minElement.Token - } else { - // throw new Error('全部Token均已失效,暂时无法使用') - return { - bingToken: null, - allThrottled - } - } - if (Config.toneStyle != 'Sydney' && Config.toneStyle != 'Custom') { - // bing 下,需要保证同一对话使用同一账号的token - if (bingTokens.findIndex(element => element.Token === conversation.bingToken) > -1) { - bingToken = conversation.bingToken - } - } - // 记录使用情况 - const index = bingTokens.findIndex(element => element.Token === bingToken) - bingTokens[index].Usage += 1 - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens)) - return { - bingToken, - allThrottled + await processUserMemory({ + event: e, + userMessage, + userText, + conversationId: sendMessageOptions.conversationId, + assistantContents: response.contents, + assistantMessageId: response.id + }) } } diff --git a/apps/draw.js b/apps/draw.js deleted file mode 100644 index 10eed61..0000000 --- a/apps/draw.js +++ /dev/null @@ -1,283 +0,0 @@ -import plugin from '../../../lib/plugins/plugin.js' -import { createImage, editImage, imageVariation } from '../utils/dalle.js' -import { makeForwardMsg } from '../utils/common.js' -import _ from 'lodash' -import { Config } from '../utils/config.js' -import BingDrawClient from '../utils/BingDraw.js' - -export class dalle extends plugin { - constructor (e) { - super({ - name: 'ChatGPT-Plugin Dalle 绘图', - dsc: 'ChatGPT-Plugin基于OpenAI Dalle的绘图插件', - event: 'message', - priority: 600, - rule: [ - { - reg: '^#(chatgpt|ChatGPT|dalle|Dalle)(绘图|画图)', - fnc: 'draw' - }, - { - reg: '^#(chatgpt|ChatGPT|dalle|Dalle)(修图|图片变形|改图)$', - fnc: 'variation' - }, - { - reg: '^#(搞|改)(她|他)头像', - fnc: 'avatarVariation' - }, - { - reg: '^#(chatgpt|dalle)编辑图片', - fnc: 'edit' - }, - { - reg: '^#bing(画图|绘图)', - fnc: 'bingDraw' - } - ] - }) - } - - async draw (e) { - if (!Config.enableDraw) { - this.reply('画图功能未开启') - return false - } - let ttl = await redis.ttl(`CHATGPT:DRAW:${e.sender.user_id}`) - if (ttl > 0 && !e.isMaster) { - this.reply(`冷却中,请${ttl}秒后再试`) - return false - } - let splits = _.split(e.msg, '图', 2) - if (splits.length < 2) { - this.reply('请带上绘图要求') - return false - } - let rules = _.split(splits[1], '/') - let [prompt = '', num = '1', size = '512x512'] = rules.slice(0, 3) - if (['256x256', '512x512', '1024x1024'].indexOf(size) === -1) { - this.reply('大小不符合要求,必须是256x256/512x512/1024x1024中的一个') - return false - } - await redis.set(`CHATGPT:DRAW:${e.sender.user_id}`, 'c', { EX: 30 }) - let priceMap = { - '1024x1024': 0.02, - '512x512': 0.018, - '256x256': 0.016 - } - num = parseInt(num, 10) - if (num > 5) { - this.reply('太多啦!你要花光我的余额吗!') - return false - } - await this.reply(`正在为您绘制大小为${size}的${num}张图片,预计消耗${priceMap[size] * num}美元余额,请稍候……`) - try { - let images = (await createImage(prompt, num, size)).map(image => segment.image(`base64://${image}`)) - if (images.length > 1) { - this.reply(await makeForwardMsg(e, images, prompt)) - } else { - this.reply(images[0], true) - } - } catch (err) { - logger.error(err.response?.data?.error?.message) - this.reply(`绘图失败: ${err.response?.data?.error?.message}`, true) - await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`) - } - } - - async variation (e) { - if (!Config.enableDraw) { - this.reply('画图功能未开启') - return false - } - let ttl = await redis.ttl(`CHATGPT:VARIATION:${e.sender.user_id}`) - if (ttl > 0 && !e.isMaster) { - this.reply(`冷却中,请${ttl}秒后再试`) - return false - } - let imgUrl - if (e.source) { - let reply - if (e.isGroup) { - reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message - } else { - reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message - } - if (reply) { - for (let val of reply) { - if (val.type === 'image') { - console.log(val) - imgUrl = val.url - break - } - } - } - } else if (e.img) { - console.log(e.img) - imgUrl = e.img[0] - } - if (!imgUrl) { - this.reply('图呢?') - return false - } - await redis.set(`CHATGPT:VARIATION:${e.sender.user_id}`, 'c', { EX: 30 }) - await this.reply('正在为您生成图片变形,请稍候……') - try { - let images = (await imageVariation(imgUrl)).map(image => segment.image(`base64://${image}`)) - if (images.length > 1) { - this.reply(await makeForwardMsg(e, images)) - } else { - this.reply(images[0], true) - } - } catch (err) { - console.log(err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})) - this.reply(`绘图失败: ${err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})}`, true) - await redis.del(`CHATGPT:VARIATION:${e.sender.user_id}`) - } - } - - async avatarVariation (e) { - if (!Config.enableDraw) { - this.reply('画图功能未开启') - return false - } - let ats = e.message.filter(m => m.type === 'at').filter(at => at.qq !== e.self_id) - if (ats.length > 0) { - for (let i = 0; i < ats.length; i++) { - let qq = ats[i].qq - let imgUrl = `https://q1.qlogo.cn/g?b=qq&s=0&nk=${qq}` - try { - let images = (await imageVariation(imgUrl)).map(image => segment.image(`base64://${image}`)) - if (images.length > 1) { - this.reply(await makeForwardMsg(e, images)) - } else { - this.reply(images[0], true) - } - } catch (err) { - console.log(err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})) - this.reply(`搞失败了: ${err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})}`, true) - await redis.del(`CHATGPT:VARIATION:${e.sender.user_id}`) - } - } - } - } - - async edit (e) { - if (!Config.enableDraw) { - this.reply('画图功能未开启') - return false - } - let ttl = await redis.ttl(`CHATGPT:EDIT:${e.sender.user_id}`) - if (ttl > 0 && !e.isMaster) { - this.reply(`冷却中,请${ttl}秒后再试`) - return false - } - let imgUrl - if (e.source) { - let reply - if (e.isGroup) { - reply = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message - } else { - reply = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message - } - if (reply) { - for (let val of reply) { - if (val.type === 'image') { - console.log(val) - imgUrl = val.url - break - } - } - } - } else if (e.img) { - console.log(e.img) - imgUrl = e.img[0] - } - if (!imgUrl) { - this.reply('图呢?') - return false - } - await redis.set(`CHATGPT:EDIT:${e.sender.user_id}`, 'c', { EX: 30 }) - await this.reply('正在为您编辑图片,请稍候……') - - let command = _.trimStart(e.msg, '#chatgpt编辑图片') - command = _.trimStart(command, '#dalle编辑图片') - // command = 'A bird on it/100,100,300,200/2/512x512' - let args = command.split('/') - let [prompt = '', position = '', num = '1', size = '512x512'] = args.slice(0, 4) - if (!prompt || !position) { - this.reply('编辑图片必须填写prompt和涂抹位置.参考格式:A bird on it/100,100,300,200/2/512x512') - return false - } - num = parseInt(num, 10) - if (num > 5) { - this.reply('太多啦!你要花光我的余额吗!') - return false - } - try { - let images = (await editImage(imgUrl, position.split(',').map(p => parseInt(p, 10)), prompt, num, size)) - .map(image => segment.image(`base64://${image}`)) - if (images.length > 1) { - this.reply(await makeForwardMsg(e, images, prompt)) - } else { - this.reply(images[0], true) - } - } catch (err) { - logger.error(err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})) - this.reply(`图片编辑失败: ${err.response?.data?.error?.message || err.message || JSON.stringify(err.response || {})}`, true) - await redis.del(`CHATGPT:EDIT:${e.sender.user_id}`) - } - } - - async bingDraw (e) { - let ttl = await redis.ttl(`CHATGPT:DRAW:${e.sender.user_id}`) - if (ttl > 0 && !e.isMaster) { - this.reply(`冷却中,请${ttl}秒后再试`) - return false - } - let prompt = e.msg.replace(/^#bing(画图|绘图)/, '') - if (!prompt) { - this.reply('请提供绘图prompt') - return false - } - this.reply('在画了,请稍等……') - let bingToken = '' - if (await redis.exists('CHATGPT:BING_TOKENS') != 0) { - let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - const normal = bingTokens.filter(element => element.State === '正常') - const restricted = bingTokens.filter(element => element.State === '受限') - if (normal.length > 0) { - const minElement = normal.reduce((min, current) => { - return current.Usage < min.Usage ? current : min - }) - bingToken = minElement.Token - } else if (restricted.length > 0) { - const minElement = restricted.reduce((min, current) => { - return current.Usage < min.Usage ? current : min - }) - bingToken = minElement.Token - } else { - throw new Error('全部Token均已失效,暂时无法使用') - } - } - if (!bingToken) { - throw new Error('未绑定Bing Cookie,请使用#chatgpt设置必应token命令绑定Bing Cookie') - } - // 记录token使用 - let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - const index = bingTokens.findIndex(element => element.Token === bingToken) - bingTokens[index].Usage += 1 - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens)) - - let client = new BingDrawClient({ - baseUrl: Config.sydneyReverseProxy, - userToken: bingToken - }) - await redis.set(`CHATGPT:DRAW:${e.sender.user_id}`, 'c', { EX: 30 }) - try { - await client.getImages(prompt, e) - } catch (err) { - await redis.del(`CHATGPT:DRAW:${e.sender.user_id}`) - await e.reply('绘图失败:' + err) - } - } -} diff --git a/apps/entertainment.js b/apps/entertainment.js deleted file mode 100644 index a012dda..0000000 --- a/apps/entertainment.js +++ /dev/null @@ -1,502 +0,0 @@ -import plugin from '../../../lib/plugins/plugin.js' -import { Config } from '../utils/config.js' -import { generateHello } from '../utils/randomMessage.js' -import { generateAudio } from '../utils/tts.js' -import fs from 'fs' -import { emojiRegex, googleRequestUrl } from '../utils/emoj/index.js' -import fetch from 'node-fetch' -import { getImageOcrText, getImg, makeForwardMsg, mkdirs, renderUrl } from '../utils/common.js' -import uploadRecord from '../utils/uploadRecord.js' -import { makeWordcloud } from '../utils/wordcloud/wordcloud.js' -import { translate, translateLangSupports } from '../utils/translate.js' -import AzureTTS from '../utils/tts/microsoft-azure.js' -import VoiceVoxTTS from '../utils/tts/voicevox.js' -import { URL } from 'node:url' - -let useSilk = false -try { - await import('node-silk') - useSilk = true -} catch (e) { - useSilk = false -} -export class Entertainment extends plugin { - constructor (e) { - super({ - name: 'ChatGPT-Plugin 娱乐小功能', - dsc: '让你的聊天更有趣!现已支持主动打招呼、表情合成、群聊词云统计、文本翻译与图片ocr小功能!', - event: 'message', - priority: 500, - rule: [ - { - reg: '^#chatgpt打招呼(帮助)?', - fnc: 'sendMessage', - permission: 'master' - }, - { - reg: '^#chatgpt(查看|设置|删除)打招呼', - fnc: 'handleSentMessage', - permission: 'master' - }, - { - reg: `^(${emojiRegex()}){2}$`, - fnc: 'combineEmoj' - }, - { - reg: '^#?(今日词云|群友在聊什么)$', - fnc: 'wordcloud' - }, - { - reg: '^#(|最新)词云(\\d{1,2}h{0,1}|)$', - fnc: 'wordcloud_latest' - }, - { - reg: '^#((寄批踢|gpt|GPT)?翻.*|chatgpt翻译帮助)', - fnc: 'translate' - }, - { - reg: '^#ocr', - fnc: 'ocr' - }, - { - reg: '^#url(:|:)', - fnc: 'screenshotUrl' - } - ] - }) - this.task = [ - { - // 设置十分钟左右的浮动 - cron: '0 ' + Math.ceil(Math.random() * 10) + ' 7-23/' + Config.helloInterval + ' * * ?', - // cron: '*/2 * * * *', - name: 'ChatGPT主动随机说话', - fnc: this.sendRandomMessage.bind(this) - } - ] - } - - async ocr (e) { - let replyMsg - let imgOcrText = await getImageOcrText(e) - if (!imgOcrText) { - await this.reply('没有识别到文字', e.isGroup) - return false - } - replyMsg = await makeForwardMsg(e, imgOcrText, 'OCR结果') - await this.reply(replyMsg, e.isGroup) - } - - async translate (e) { - const translateLangLabels = translateLangSupports.map(item => item.label).join(',') - const translateLangLabelAbbrS = translateLangSupports.map(item => item.abbr).join(',') - if (e.msg.trim() === '#chatgpt翻译帮助') { - await this.reply(`支持以下语种的翻译: -${translateLangLabels} -在使用本工具时,请采用简写的方式描述目标语言。此外,可以引用消息或图片来进行翻译。 -示例: -1. #gpt翻英 你好 -2. #gpt翻中 你好 -3. #gpt翻译 hello`) - return true - } - const regExp = /^#(寄批踢|gpt|GPT)?翻(.)([\s\S]*)/ - const match = e.msg.trim().match(regExp) - let languageCode = match[2] === '译' ? 'auto' : match[2] - let pendingText = match[3] - const isImg = !!(await getImg(e))?.length - let result = [] - let multiText = false - if (languageCode !== 'auto' && !translateLangLabelAbbrS.includes(languageCode)) { - e.reply(`输入格式有误或暂不支持该语言,\n当前支持${translateLangLabels}`, e.isGroup) - return false - } - // 引用回复 - if (e.source) { - if (pendingText.length) { - await this.reply('引用模式下不需要添加翻译文本,已自动忽略输入文本...((*・∀・)ゞ→→”', e.isGroup) - } - } else { - if (isImg && pendingText) { - await this.reply('检测到图片输入,已自动忽略输入文本...((*・∀・)ゞ→→', e.isGroup) - } - if (!pendingText && !isImg) { - await this.reply('你让我翻译啥呢 ̄へ ̄!', e.isGroup) - return false - } - } - if (isImg) { - let imgOcrText = await getImageOcrText(e) - multiText = Array.isArray(imgOcrText) - if (imgOcrText) { - pendingText = imgOcrText - } else { - await this.reply('没有识别到有效文字(・-・*)', e.isGroup) - return false - } - } else { - if (e.source) { - let previousMsg - if (e.isGroup) { - previousMsg = (await e.group.getChatHistory(e.source.seq, 1)).pop()?.message - } else { - previousMsg = (await e.friend.getChatHistory(e.source.time, 1)).pop()?.message - } - // logger.warn('previousMsg', previousMsg) - if (previousMsg.find(msg => msg.type === 'text')?.text) { - pendingText = previousMsg.find(msg => msg.type === 'text')?.text - } else { - await this.reply('这是什么怪东西!(⊙ˍ⊙)', e.isGroup) - return false - } - } - } - try { - if (multiText) { - result = await Promise.all(pendingText.map(text => translate(text, languageCode))) - } else { - result = await translate(pendingText, languageCode) - } - // logger.warn(multiText, result) - } catch (err) { - await this.reply(err.message, e.isGroup) - return false - } - const totalLength = Array.isArray(result) - ? result.reduce((acc, cur) => acc + cur.length, 0) - : result.length - if (totalLength > 300 || multiText) { - // 多条翻译结果 - if (Array.isArray(result)) { - result = await makeForwardMsg(e, result, '翻译结果') - } else { - result = ('译文:\n' + result.trim()).split() - result.unshift('原文:\n' + pendingText.trim()) - result = await makeForwardMsg(e, result, '翻译结果') - } - await this.reply(result, e.isGroup) - return true - } - // 保持原格式输出 - result = Array.isArray(result) ? result.join('\n') : result - await this.reply(result, e.isGroup) - return true - } - - async wordcloud (e) { - if (e.isGroup) { - let groupId = e.group_id - let lock = await redis.get(`CHATGPT:WORDCLOUD:${groupId}`) - if (lock) { - await e.reply('别着急,上次统计还没完呢') - return true - } - await e.reply('在统计啦,请稍等...') - await redis.set(`CHATGPT:WORDCLOUD:${groupId}`, '1', { EX: 600 }) - try { - await makeWordcloud(e, e.group_id) - } catch (err) { - logger.error(err) - await e.reply(err) - } - await redis.del(`CHATGPT:WORDCLOUD:${groupId}`) - } else { - await e.reply('请在群里发送此命令') - } - } - - async wordcloud_latest (e) { - if (e.isGroup) { - let groupId = e.group_id - let lock = await redis.get(`CHATGPT:WORDCLOUD:${groupId}`) - if (lock) { - await e.reply('别着急,上次统计还没完呢') - return true - } - - const regExp = /词云(\d{0,2})(|h)/ - const match = e.msg.trim().match(regExp) - const duration = !match[1] ? 12 : parseInt(match[1]) // default 12h - - if(duration > 24) { - await e.reply('最多只能统计24小时内的记录哦') - return false - } - await e.reply('在统计啦,请稍等...') - - await redis.set(`CHATGPT:WORDCLOUD:${groupId}`, '1', { EX: 600 }) - try { - await makeWordcloud(e, e.group_id, duration) - } catch (err) { - logger.error(err) - await e.reply(err) - } - await redis.del(`CHATGPT:WORDCLOUD:${groupId}`) - } else { - await e.reply('请在群里发送此命令') - } - } - - async combineEmoj (e) { - let left = e.msg.codePointAt(0).toString(16).toLowerCase() - let right = e.msg.codePointAt(2).toString(16).toLowerCase() - if (left === right) { - return false - } - mkdirs('data/chatgpt/emoji') - logger.info('combine ' + e.msg) - let resultFileLoc = `data/chatgpt/emoji/${left}_${right}.jpg` - if (fs.existsSync(resultFileLoc)) { - let image = segment.image(fs.createReadStream(resultFileLoc)) - image.asface = true - await e.reply(image, true) - return true - } - const _path = process.cwd() - const fullPath = fs.realpathSync(`${_path}/plugins/chatgpt-plugin/resources/emojiData.json`) - const data = fs.readFileSync(fullPath) - let emojDataJson = JSON.parse(data) - logger.mark(`合成emoji:${left} ${right}`) - let url - if (emojDataJson[right]) { - let find = emojDataJson[right].find(item => item.leftEmoji === left) - if (find) { - url = googleRequestUrl(find) - } - } - if (!url && emojDataJson[left]) { - let find = emojDataJson[left].find(item => item.leftEmoji === right) - if (find) { - url = googleRequestUrl(find) - } - } - if (!url) { - await e.reply('不支持合成', true) - return false - } - let response = await fetch(url) - const resultBlob = await response.blob() - const resultArrayBuffer = await resultBlob.arrayBuffer() - const resultBuffer = Buffer.from(resultArrayBuffer) - await fs.writeFileSync(resultFileLoc, resultBuffer) - let image = segment.image(fs.createReadStream(resultFileLoc)) - image.asface = true - await e.reply(image, true) - return true - } - - async sendMessage (e) { - if (e.msg.match(/^#chatgpt打招呼帮助/) !== null) { - await this.reply('设置主动打招呼的群聊名单,群号之间以,隔开,参数之间空格隔开\n' + - '#chatgpt打招呼+群号:立即在指定群聊发起打招呼' + - '#chatgpt查看打招呼\n' + - '#chatgpt删除打招呼:删除主动打招呼群聊,可指定若干个群号\n' + - '#chatgpt设置打招呼:可指定1-3个参数,依次是更新打招呼列表、打招呼间隔时间和触发概率、更新打招呼所有配置项') - return false - } - let groupId = e.msg.replace(/^#chatgpt打招呼/, '') - logger.info(groupId) - groupId = parseInt(groupId) - if (groupId && !Bot.getGroupList().get(groupId)) { - await e.reply('机器人不在这个群里!') - return - } - let message = await generateHello() - let sendable = message - logger.info(`打招呼给群聊${groupId}:` + message) - if (Config.defaultUseTTS) { - let audio = await generateAudio(message, Config.defaultTTSRole) - sendable = segment.record(audio) - } - if (!groupId) { - await e.reply(sendable) - } else { - await Bot.sendGroupMsg(groupId, sendable) - await e.reply('发送成功!') - } - } - - async sendRandomMessage () { - if (Config.debug) { - logger.info('开始处理:ChatGPT随机打招呼。') - } - let toSend = Config.initiativeChatGroups || [] - for (const element of toSend) { - if (!element) { - continue - } - let groupId = parseInt(element) - if (Bot.getGroupList().get(groupId)) { - // 打招呼概率 - if (Math.floor(Math.random() * 100) < Config.helloProbability) { - let message = await generateHello() - logger.info(`打招呼给群聊${groupId}:` + message) - if (Config.defaultUseTTS) { - let audio - const [defaultVitsTTSRole, defaultAzureTTSRole, defaultVoxTTSRole] = [Config.defaultTTSRole, Config.azureTTSSpeaker, Config.voicevoxTTSSpeaker] - let ttsSupportKinds = [] - if (Config.azureTTSKey) ttsSupportKinds.push(1) - if (Config.ttsSpace) ttsSupportKinds.push(2) - if (Config.voicevoxSpace) ttsSupportKinds.push(3) - if (!ttsSupportKinds.length) { - logger.warn('没有配置任何语音服务!') - return false - } - const randomIndex = Math.floor(Math.random() * ttsSupportKinds.length) - switch (ttsSupportKinds[randomIndex]) { - case 1 : { - const isEn = AzureTTS.supportConfigurations.find(config => config.code === defaultAzureTTSRole)?.language.includes('en') - if (isEn) { - message = (await translate(message, '英')).replace('\n', '') - } - audio = await AzureTTS.generateAudio(message, { - defaultAzureTTSRole - }) - break - } - case 2 : { - if (Config.autoJapanese) { - try { - message = await translate(message, '日') - } catch (err) { - logger.error(err) - } - } - try { - audio = await generateAudio(message, defaultVitsTTSRole, '中日混合(中文用[ZH][ZH]包裹起来,日文用[JA][JA]包裹起来)') - } catch (err) { - logger.error(err) - } - break - } - case 3 : { - message = (await translate(message, '日')).replace('\n', '') - try { - audio = await VoiceVoxTTS.generateAudio(message, { - speaker: defaultVoxTTSRole - }) - } catch (err) { - logger.error(err) - } - break - } - } - if (useSilk) { - await Bot.sendGroupMsg(groupId, await uploadRecord(audio)) - } else { - await Bot.sendGroupMsg(groupId, segment.record(audio)) - } - } else { - await Bot.sendGroupMsg(groupId, message) - } - } else { - logger.info(`时机未到,这次就不打招呼给群聊${groupId}了`) - } - } else { - logger.warn('机器人不在要发送的群组里,忽略群。同时建议检查配置文件修改要打招呼的群号。' + groupId) - } - } - } - - async handleSentMessage (e) { - const addReg = /^#chatgpt设置打招呼[::]?\s?(\S+)(?:\s+(\d+))?(?:\s+(\d+))?$/ - const delReg = /^#chatgpt删除打招呼[::\s]?(\S+)/ - const checkReg = /^#chatgpt查看打招呼$/ - let replyMsg = '' - Config.initiativeChatGroups = Config.initiativeChatGroups.filter(group => group.trim() !== '') - if (e.msg.match(checkReg)) { - if (Config.initiativeChatGroups.length === 0) { - replyMsg = '当前没有需要打招呼的群聊' - } else { - replyMsg = `当前打招呼设置为:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%` - } - } else if (e.msg.match(delReg)) { - const groupsToDelete = e.msg.trim().match(delReg)[1].split(/[,,]\s?/).filter(group => group.trim() !== '') - let deletedGroups = [] - - for (const element of groupsToDelete) { - if (!/^[1-9]\d{8,9}$/.test(element)) { - await this.reply(`群号${element}不合法,请输入9-10位不以0开头的数字`, true) - return false - } - if (!Config.initiativeChatGroups.includes(element)) { - continue - } - Config.initiativeChatGroups.splice(Config.initiativeChatGroups.indexOf(element), 1) - deletedGroups.push(element) - } - Config.initiativeChatGroups = Config.initiativeChatGroups.filter(group => group.trim() !== '') - if (deletedGroups.length === 0) { - replyMsg = '没有可删除的群号,请输入正确的群号\n' - } else { - replyMsg = `已删除打招呼群号:${deletedGroups.join(', ')}\n` - } - replyMsg += `当前打招呼设置为:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%` - } else if (e.msg.match(addReg)) { - let paramArray = e.msg.match(addReg) - if (typeof paramArray[3] === 'undefined' && typeof paramArray[2] !== 'undefined') { - Config.helloInterval = Math.min(Math.max(parseInt(paramArray[1]), 1), 24) - Config.helloProbability = Math.min(Math.max(parseInt(paramArray[2]), 0), 100) - replyMsg = `已更新打招呼设置:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%` - } else { - const validGroups = [] - const groups = paramArray ? paramArray[1].split(/[,,]\s?/) : [] - for (const element of groups) { - if (!/^[1-9]\d{8,9}$/.test(element)) { - await this.reply(`群号${element}不合法,请输入9-10位不以0开头的数字`, true) - return false - } - if (Config.initiativeChatGroups.includes(element)) { - continue - } - validGroups.push(element) - } - if (validGroups.length === 0) { - await this.reply('没有可添加的群号,请输入新的群号') - return false - } else { - Config.initiativeChatGroups = Config.initiativeChatGroups - .filter(group => group.trim() !== '') - .concat(validGroups) - } - if (typeof paramArray[2] === 'undefined' && typeof paramArray[3] === 'undefined') { - replyMsg = `已更新打招呼设置:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%` - } else { - Config.helloInterval = Math.min(Math.max(parseInt(paramArray[2]), 1), 24) - Config.helloProbability = Math.min(Math.max(parseInt(paramArray[3]), 0), 100) - replyMsg = `已更新打招呼设置:\n${!e.isGroup ? '群号:' + Config.initiativeChatGroups.join(', ') + '\n' : ''}间隔时间:${Config.helloInterval}小时\n触发概率:${Config.helloProbability}%` - } - } - } else { - replyMsg = '无效的打招呼设置,请输入正确的命令。\n可发送”#chatgpt打招呼帮助“获取打招呼指北。' - } - await this.reply(replyMsg) - return false - } - - async screenshotUrl (e) { - let url = e.msg.replace(/^#url(:|:)/, '') - if (url.length === 0) { return false } - try { - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = 'http://' + url - } - let urlLink = new URL(url) - await e.reply( - await renderUrl( - e, urlLink.href, - { - retType: 'base64', - Viewport: { - width: Config.chatViewWidth, - height: parseInt(Config.chatViewWidth * 0.56) - }, - deviceScaleFactor: Config.cloudDPR - } - ), - e.isGroup && Config.quoteReply) - } catch (err) { - this.reply('无效url:' + url) - } - return true - } -} diff --git a/apps/help.js b/apps/help.js deleted file mode 100644 index e488fe1..0000000 --- a/apps/help.js +++ /dev/null @@ -1,350 +0,0 @@ -import plugin from '../../../lib/plugins/plugin.js' -import { Config } from '../utils/config.js' -import { render, renderUrl } from '../utils/common.js' -let version = Config.version -let helpData = [ - { - group: '聊天', - list: [ - { - icon: 'chat', - title: Config.toggleMode === 'at' ? '@我+聊天内容' : '#chat+聊天内容', - desc: '与机器人聊天' - }, - { - icon: 'chat', - title: '#chat1/#chat3/#chatglm/#bing/#claude/#xh', - desc: '分别使用API/API3/ChatGLM/Bing/Claude/星火模式与机器人聊天,无论主人设定了何种全局模式' - }, - { - icon: 'chat-private', - title: '私聊与我对话', - desc: '与机器人聊天' - }, - { - icon: 'switch', - title: '#chatgpt切换对话+对话id', - desc: '目前仅API3模式下可用,切换到指定的对话中' - }, - { - icon: 'switch', - title: '#chatgpt加入对话+@某人', - desc: '目前仅API3模式下可用,加入到某人当前进行的对话中' - }, - { - icon: 'destroy', - title: '#chatgpt删除对话+对话id或@用户', - desc: '删除指定对话,并清空与用户的关联信息。@用户时支持多个用户' - }, - { - icon: 'destroy', - title: '#(结束|新开|摧毁|毁灭|完结)对话', - desc: '结束自己当前对话,下次开启对话机器人将遗忘掉本次对话内容。' - }, - { - icon: 'destroy', - title: '#(结束|新开|摧毁|毁灭|完结)全部对话', - desc: '结束正在与本机器人进行对话的全部用户的对话。' - }, - { - icon: 'destroy-other', - title: '#(结束|新开|摧毁|毁灭|完结)对话 @某人', - desc: '结束该用户当前对话,下次开启对话机器人将遗忘掉本次对话内容。' - }, - { - icon: 'confirm', - title: '#chatgpt(导出)聊天记录', - desc: '图片形式导出聊天记录,目前仅支持Bing下的Sydney和自定义' - }, - { - icon: 'smiley-wink', - title: '#claude开启新对话+设定名', - desc: '结束之前的对话,并开启一个新的Claude对话,如果设定名不为空的话,会使用这个设定。设定必须是设定列表中有的设定。' - } - ] - }, - { - group: '画图', - list: [ - { - icon: 'draw', - title: '#chatgpt画图+prompt(/张数/图片大小)', - desc: '调用OpenAI Dalle API进行绘图,需要有API key并消耗余额。图片大小只能是256x256/512x512/1024x1024中的一个.默认为1张、512x512' - }, - { - icon: 'draw', - title: '#chatgpt改图', - desc: '调用OpenAI Dalle API进行改图,需要有API key并消耗余额。可同时发送图片或回复图片' - }, - { - icon: 'switch', - title: '#chatgpt开启/关闭画图', - desc: '开启或关闭画图功能' - } - ] - }, - { - group: '管理', - list: [ - { - icon: 'picture', - title: '#chatgpt图片模式', - desc: '机器人以图片形式回答' - }, - { - icon: 'text', - title: '#chatgpt文本模式', - desc: '机器人以文本形式回答,默认选项' - }, - { - icon: 'sound', - title: '#chatgpt语音模式', - desc: '机器人以语音形式回答' - }, - { - icon: 'game', - title: '#chatgpt设置语音角色', - desc: '设置语音模式下回复的角色音色。优先级高于默认语音角色' - }, - { - icon: 'list', - title: '#chatgpt对话列表', - desc: '查询当前哪些人正在与机器人聊天.目前API3模式下支持切换对话' - }, - { - icon: 'blue', - title: '#chatgpt(本群)?(群xxx)?闭嘴(x秒/分钟/小时)', - desc: '让机器人在本群/某群闭嘴。不指定群时认为全局闭嘴。' - }, - { - icon: 'eye', - title: '#chatgpt(本群)?(群xxx)?(张嘴|开口|说话|上班)', - desc: '让机器人在本群/某群重新可以说话。不指定群时认为全局开口。' - }, - { - icon: 'list', - title: '#chatgpt查看闭嘴', - desc: '查看当前闭嘴情况。' - }, - { - icon: 'queue', - title: '#清空chat队列', - desc: '清空当前对话等待队列。仅建议前方卡死时使用。仅API3模式下可用' - }, - { - icon: 'queue', - title: '#移出chat队列首位', - desc: '移出当前对话等待队列中的首位。若前方对话卡死可使用本命令。仅API3模式下可用' - }, - { - icon: 'confirm', - title: '#chatgpt开启/关闭问题确认', - desc: '开启或关闭机器人收到消息后的确认回复消息。' - }, - { - icon: 'switch', - title: '#chatgpt切换浏览器/API/API3/Bing/ChatGLM/Claude/Poe', - desc: '切换使用的后端为浏览器或OpenAI API/反代官网API/Bing/自建ChatGLM/Slack Claude/Poe' - }, - { - icon: 'confirm', - title: '#chatgpt必应切换(精准|均衡|创意|悉尼|自设定)', - desc: '切换Bing风格。' - }, - { - icon: 'confirm', - title: '#chatgpt必应(开启|关闭)建议回复', - desc: '开关Bing模式下的建议回复。' - }, - { - icon: 'list', - title: '#(关闭|打开)群聊上下文', - desc: '开启后将会发送近期群聊中的对话给机器人提供参考' - }, - { - icon: 'switch', - title: '#chatgpt(允许|禁止|打开|关闭|同意)私聊', - desc: '开启后将关闭本插件的私聊通道。(主人不影响)' - }, - { - icon: 'token', - title: '#chatgpt(设置|添加)群聊[白黑]名单', - desc: '白名单配置后只有白名单内的群可使用本插件,配置黑名单则会在对应群聊禁用本插件' - } - ] - }, - { - group: '设置', - list: [ - { - icon: 'token', - title: '#chatgpt设置(必应)token', - desc: '设置ChatGPT或bing的Token' - }, - { - icon: 'coin', - title: '#OpenAI剩余额度', - desc: '查询OpenAI API剩余试用额度' - }, - { - icon: 'key', - title: '#chatgpt设置APIKey', - desc: '设置APIKey' - }, - { - icon: 'key', - title: '#chatgpt设置星火token', - desc: '设置星火ssoSessionId(对话页面的ssoSessionId cookie值)' - }, - { - icon: 'eat', - title: '#chatgpt设置(API|Sydney)设定', - desc: '设置AI的默认风格设定' - }, - { - icon: 'eat', - title: '#chatgpt查看(API|Sydney)设定', - desc: '查看AI当前的风格设定,文本形式返回,设定太长可能发不出来' - }, - { - icon: 'token', - title: '#chatgpt设置后台刷新token', - desc: '用于查看API余额。注意和配置的key保持同一账号。' - }, - { - icon: 'token', - title: '#chatgpt(开启|关闭)智能模式', - desc: 'API模式下打开或关闭智能模式。' - } - ] - }, - { - group: '设定', - list: [ - { - icon: 'smiley-wink', - title: '#chatgpt设定列表', - desc: '查看所有设定列表,以转发消息形式' - }, - { - icon: 'eat', - title: '#chatgpt查看设定【设定名】', - desc: '查看指定名字的设定内容。其中API默认和Sydney默认为锅巴面板配置的设定' - }, - { - icon: 'coin', - title: '#chatgpt添加设定', - desc: '添加一个设定,分此输入设定名称和设定内容。如果名字已存在,则会覆盖(相当于修改)' - }, - { - icon: 'switch', - title: '#chatgpt使用设定【设定名】', - desc: '使用某个设定。' - }, - { - icon: 'confirm', - title: '#chatgpt(上传|分享|共享)设定', - desc: '上传设定' - }, - { - icon: 'confirm', - title: '#chatgpt(删除|取消|撤销)共享设定+设定名', - desc: '从远端删除,只能删除自己上传的设定,根据机器人主人qq号判断。' - }, - { - icon: 'confirm', - title: '#chatgpt(在线)浏览设定(+关键词)(页码X)', - desc: '搜索公开的设定。默认返回前十条,使用页码X可以翻页,使用关键词可以检索。页码从1开始。' - }, - { - icon: 'smiley-wink', - title: '#chatgpt预览设定详情(+设定名)', - desc: '根据设定名称预览云端设定的详情信息。' - }, - { - icon: 'confirm', - title: '#chatgpt导入设定', - desc: '导入其他人分享的设定。注意:相同名字的设定,会覆盖本地已有的设定' - }, - // { - // icon: 'confirm', - // title: '#chatgpt开启/关闭洗脑', - // desc: '开启或关闭洗脑' - // }, - // { - // icon: 'confirm', - // title: '#chatgpt设置洗脑强度+【强度】', - // desc: '设置洗脑强度' - // }, - // { - // icon: 'confirm', - // title: '#chatgpt设置洗脑名称+【名称】', - // desc: '设置洗脑名称' - // }, - { - icon: 'help', - title: '#chatgpt设定帮助', - desc: '设定帮助' - } - ] - }, - { - group: '其他', - list: [ - { - icon: 'smiley-wink', - title: '#chatgpt打招呼(群号|帮助)', - desc: '让AI随机到某个群去打招呼' - }, - { - icon: 'help', - title: '#chatgpt模式帮助', - desc: '查看多种聊天模式的区别及当前使用的模式' - }, - { - icon: 'help', - title: '#chatgpt全局回复帮助', - desc: '获取配置全局回复模式和全局语音角色的命令帮助' - }, - { - icon: 'help', - title: '#chatgpt帮助', - desc: '获取本帮助' - } - ] - } -] - -export class help extends plugin { - constructor (e) { - super({ - name: 'ChatGPT-Plugin 帮助', - dsc: 'ChatGPT-Plugin 帮助面板', - event: 'message', - priority: 500, - rule: [ - { - reg: '^#(chatgpt|ChatGPT)(命令|帮助|菜单|help|说明|功能|指令|使用说明)$', - fnc: 'help' - }, - { - reg: '^#帮助-', - fnc: 'newHelp' - } - ] - }) - } - - async help (e) { - if (Config.newhelp && !Config.oldview) { - await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/help/`, { Viewport: { width: 800, height: 600 } }) - } else { - await render(e, 'chatgpt-plugin', 'help/index', { helpData, version }) - } - } - - async newHelp (e) { - let use = e.msg.replace(/^#帮助-/, '').toUpperCase().trim() - await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/help/` + use, { Viewport: { width: 800, height: 600 } }) - } -} diff --git a/apps/history.js b/apps/history.js deleted file mode 100644 index 3c27859..0000000 --- a/apps/history.js +++ /dev/null @@ -1,123 +0,0 @@ -import plugin from '../../../lib/plugins/plugin.js' -import { render } from '../utils/common.js' -import { Config } from '../utils/config.js' -import { KeyvFile } from 'keyv-file' - -async function getKeyv () { - let Keyv - try { - Keyv = (await import('keyv')).default - } catch (error) { - throw new Error('keyv依赖未安装,请使用pnpm install keyv安装') - } - return Keyv -} -export class history extends plugin { - constructor (e) { - super({ - name: 'ChatGPT-Plugin 聊天记录', - dsc: '让你的聊天更加便捷!本插件支持以图片的形式导出本次对话的聊天记录,方便随时分享精彩瞬间!', - event: 'message', - priority: 500, - rule: [ - { - reg: '^#(chatgpt|ChatGPT)(导出)?聊天记录$', - fnc: 'history', - permission: 'master' - } - ] - }) - } - - async history (e) { - let use = await redis.get('CHATGPT:USE') || 'api' - let chat = [] - let filtered = e.message.filter(m => m.type === 'at').filter(m => m.qq !== Bot.uin) - let queryUser = e.sender.user_id - let user = e.sender - if (filtered.length > 0) { - queryUser = filtered[0].qq - user = (await e.group.getMemberMap()).get(queryUser) - } - switch (use) { - case 'api': { - await e.reply('还不支持API模式呢') - return true - } - case 'api3': { - await e.reply('还不支持API3模式呢') - return true - } - case 'bing': { - if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { - const cacheOptions = { - namespace: Config.toneStyle, - store: new KeyvFile({ filename: 'cache.json' }) - } - let Keyv = await getKeyv() - let conversationsCache = new Keyv(cacheOptions) - const conversation = (await conversationsCache.get(`SydneyUser_${queryUser}`)) || { - messages: [], - createdAt: Date.now() - } - let key = `CHATGPT:CONVERSATIONS_BING:${queryUser}` - let previousConversation = await redis.get(key) || JSON.stringify({}) - previousConversation = JSON.parse(previousConversation) - let parentMessageId = previousConversation.parentMessageId - let tmp = {} - const previousCachedMessages = getMessagesForConversation(conversation.messages, parentMessageId) - .map((message) => { - return { - text: message.message, - author: message.role === 'User' ? 'user' : 'bot' - } - }) - previousCachedMessages.forEach(m => { - if (m.author === 'user') { - tmp.prompt = m.text - } else { - tmp.response = m.text - chat.push(tmp) - tmp = {} - } - }) - } else { - await e.reply('还不支持BING模式呢') - return true - } - break - } - } - if (chat.length === 0) { - await e.reply('无聊天记录', e.isGroup) - return true - } - await render(e, 'chatgpt-plugin', 'content/History/index', { - version: Config.version, - user: { - qq: queryUser, - name: user.card || user.nickname || user.user_id - }, - bot: { - qq: Bot.uin, - name: Bot.nickname - }, - chat - }, {}) - } -} - -function getMessagesForConversation (messages, parentMessageId) { - const orderedMessages = [] - let currentMessageId = parentMessageId - while (currentMessageId) { - const message = messages.find((m) => m.id === currentMessageId) - if (!message) { - break - } - orderedMessages.unshift(message) - currentMessageId = message.parentMessageId - } - - return orderedMessages -} diff --git a/apps/management.js b/apps/management.js index 6cd0e1e..ce2fd18 100644 --- a/apps/management.js +++ b/apps/management.js @@ -1,1490 +1,172 @@ -import plugin from '../../../lib/plugins/plugin.js' -import { Config } from '../utils/config.js' -import { - formatDuration, - getAzureRoleList, - getPublicIP, - getUserReplySetting, - getVitsRoleList, - getVoicevoxRoleList, - makeForwardMsg, - parseDuration, processList, - renderUrl -} from '../utils/common.js' -import SydneyAIClient from '../utils/SydneyAIClient.js' -import { convertSpeaker, speakers as vitsRoleList } from '../utils/tts.js' -import md5 from 'md5' -import path from 'path' -import fs from 'fs' -import loader from '../../../lib/plugins/loader.js' -import VoiceVoxTTS, { supportConfigurations as voxRoleList } from '../utils/tts/voicevox.js' -import { supportConfigurations as azureRoleList } from '../utils/tts/microsoft-azure.js' +import ChatGPTConfig from '../config/config.js' +import { createCRUDCommandRules, createSwitchCommandRules } from '../utils/command.js' +import { Chaite, VERSION } from 'chaite' +import * as crypto from 'node:crypto' +import common from '../../../lib/common/common.js' -let isWhiteList = true -let isSetGroup = true -export class ChatgptManagement extends plugin { - constructor (e) { +export class ChatGPTManagement extends plugin { + constructor () { + const cmdPrefix = ChatGPTConfig.basic.commandPrefix super({ - name: 'ChatGPT-Plugin 管理', - dsc: '插件的管理项配置,让你轻松掌控各个功能的开闭和管理。包含各种实用的配置选项,让你的聊天更加便捷和高效!', + name: 'ChatGPT-Plugin管理', + dsc: 'ChatGPT-Plugin管理', event: 'message', - priority: 500, + priority: 20, rule: [ { - reg: '#chatgpt开启(问题)?(回复)?确认', - fnc: 'turnOnConfirm', + reg: `^${cmdPrefix}管理面板$`, + fnc: 'managementPanel', permission: 'master' }, { - reg: '#chatgpt关闭(问题)?(回复)?确认', - fnc: 'turnOffConfirm', + reg: `^(${cmdPrefix})?#?结束(全部)?对话$`, + fnc: 'destroyConversation' + }, + { + reg: `^${cmdPrefix}(bym|伪人)设置默认预设`, + fnc: 'setDefaultBymPreset', permission: 'master' }, { - reg: '#chatgpt(设置|绑定)(token|Token)', - fnc: 'setAccessToken', - permission: 'master' - }, - { - reg: '#chatgpt(设置|绑定)(Poe|POE)(token|Token)', - fnc: 'setPoeCookie', - permission: 'master' - }, - { - reg: '#chatgpt(设置|绑定|添加)(必应|Bing |bing )(token|Token)', - fnc: 'setBingAccessToken', - permission: 'master' - }, - { - reg: '#chatgpt(删除|移除)(必应|Bing |bing )(token|Token)', - fnc: 'delBingAccessToken', - permission: 'master' - }, - { - reg: '#chatgpt(查看|浏览)(必应|Bing |bing )(token|Token)', - fnc: 'getBingAccessToken', - permission: 'master' - }, - { - reg: '#chatgpt(迁移|恢复)(必应|Bing |bing )(token|Token)', - fnc: 'migrateBingAccessToken', - permission: 'master' - }, - { - reg: '^#chatgpt切换浏览器$', - fnc: 'useBrowserBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换API$', - fnc: 'useOpenAIAPIBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换(ChatGLM|chatglm)$', - fnc: 'useChatGLMSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换API3$', - fnc: 'useReversedAPIBasedSolution2', - permission: 'master' - }, - { - reg: '^#chatgpt切换(必应|Bing)$', - fnc: 'useBingSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换(Poe|poe)$', - fnc: 'useClaudeBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换(Claude|claude|slack)$', - fnc: 'useSlackClaudeBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换星火$', - fnc: 'useXinghuoBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt(必应|Bing)切换', - fnc: 'changeBingTone', - permission: 'master' - }, - { - reg: '^#chatgpt(必应|Bing)(开启|关闭)建议(回复)?', - fnc: 'bingOpenSuggestedResponses', - permission: 'master' - }, - { - reg: '^#chatgpt模式(帮助)?$', - fnc: 'modeHelp' - }, - { - reg: '^#chatgpt版本(信息)', - fnc: 'versionChatGPTPlugin' - }, - { - reg: '^#chatgpt(本群)?(群\\d+)?(关闭|闭嘴|关机|休眠|下班)', - fnc: 'shutUp', - permission: 'master' - }, - { - reg: '^#chatgpt(本群)?(群\\d+)?(开启|启动|激活|张嘴|开口|说话|上班)$', - fnc: 'openMouth', - permission: 'master' - }, - { - reg: '^#chatgpt查看?(关闭|闭嘴|关机|休眠|下班|休眠)列表$', - fnc: 'listShutUp', - permission: 'master' - }, - { - reg: '^#chatgpt设置(API|key)(Key|key)$', - fnc: 'setAPIKey', - permission: 'master' - }, - { - reg: '^#chatgpt设置(API|api)设定$', - fnc: 'setAPIPromptPrefix', - permission: 'master' - }, - { - reg: '^#chatgpt设置星火token$', - fnc: 'setXinghuoToken', - permission: 'master' - }, - { - reg: '^#chatgpt设置(Bing|必应|Sydney|悉尼|sydney|bing)设定$', - fnc: 'setBingPromptPrefix', - permission: 'master' - }, - { - reg: '^#chatgpt(开启|关闭)画图$', - fnc: 'switchDraw', - permission: 'master' - }, - { - reg: '^#chatgpt查看(API|api)设定$', - fnc: 'queryAPIPromptPrefix', - permission: 'master' - }, - { - reg: '^#chatgpt查看(Bing|必应|Sydney|悉尼|sydney|bing)设定$', - fnc: 'queryBingPromptPrefix', - permission: 'master' - }, - { - reg: '^#chatgpt(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|((azure|vits|vox)?语音角色|角色语音|角色).*)|回复帮助)$', - fnc: 'setDefaultReplySetting', - permission: 'master' - }, - { - /** 命令正则匹配 */ - reg: '^#(关闭|打开)群聊上下文$', - /** 执行方法 */ - fnc: 'enableGroupContext', - permission: 'master' - }, - { - reg: '^#chatgpt(允许|禁止|打开|关闭|同意)私聊$', - fnc: 'enablePrivateChat', - permission: 'master' - }, - { - reg: '^#chatgpt(设置|添加)对话[白黑]名单$', - fnc: 'setList', - permission: 'master' - }, - { - reg: '^#chatgpt(查看)?对话[白黑]名单(帮助)?$', - fnc: 'checkList', - permission: 'master' - }, - { - reg: '^#chatgpt(删除|移除)对话[白黑]名单$', - fnc: 'delList', - permission: 'master' - }, - { - reg: '^#(设置|修改)管理密码', - fnc: 'setAdminPassword', - permission: 'master' - }, - { - reg: '^#(设置|修改)用户密码', - fnc: 'setUserPassword' - }, - { - reg: '^#chatgpt系统(设置|配置|管理)', - fnc: 'adminPage', - permission: 'master' - }, - { - reg: '^#chatgpt用户(设置|配置|管理)', - fnc: 'userPage' - }, - { - reg: '^#?(chatgpt)(对话|管理|娱乐|绘图|人物设定|聊天记录)?指令表(帮助|搜索(.+))?', - fnc: 'commandHelp' - }, - { - reg: '^#语音切换.*', - fnc: 'ttsSwitch', - permission: 'master' - }, - { - reg: '^#(chatgpt)?(vits|azure|vox)?语音(角色列表|服务)$', - fnc: 'getTTSRoleList' - }, - { - reg: '^#chatgpt设置后台(刷新|refresh)(t|T)oken$', - fnc: 'setOpenAIPlatformToken' - }, - { - reg: '^#(chatgpt)?查看回复设置$', - fnc: 'viewUserSetting' - }, - { - reg: '^#chatgpt导出配置', - fnc: 'exportConfig', - permission: 'master' - }, - { - reg: '^#chatgpt导入配置', - fnc: 'importConfig', - permission: 'master' - }, - { - reg: '^#chatgpt(开启|关闭)智能模式$', - fnc: 'switchSmartMode', + reg: `^${cmdPrefix}(查看)?(当前)?(配置|信息|统计信息|状态)$`, + fnc: 'currentStatus', permission: 'master' } ] }) - } - - async viewUserSetting (e) { - const userSetting = await getUserReplySetting(this.e) - const replyMsg = `${this.e.sender.user_id}的回复设置: -图片模式: ${userSetting.usePicture === true ? '开启' : '关闭'} -语音模式: ${userSetting.useTTS === true ? '开启' : '关闭'} -Vits语音角色: ${userSetting.ttsRole} -Azure语音角色: ${userSetting.ttsRoleAzure} -VoiceVox语音角色: ${userSetting.ttsRoleVoiceVox} -${userSetting.useTTS === true ? '当前语音模式为' + Config.ttsMode : ''}` - await this.reply(replyMsg.replace(/\n\s*$/, ''), e.isGroup) - return true - } - - async getTTSRoleList (e) { - const matchCommand = e.msg.match(/^#(chatgpt)?(vits|azure|vox)?语音(服务|角色列表)/) - if (matchCommand[3] === '服务') { - await this.reply(`当前支持vox、vits、azure语音服务,可使用'#(vox|azure|vits)语音角色列表'查看支持的语音角色。 - -vits语音:主要有赛马娘,原神中文,原神日语,崩坏 3 的音色、结果有随机性,语调可能很奇怪。 - -vox语音:Voicevox 是一款由日本 DeNA 开发的语音合成软件,它可以将文本转换为自然流畅的语音。Voicevox 支持多种语言和声音,可以用于制作各种语音内容,如动画、游戏、广告等。Voicevox 还提供了丰富的调整选项,可以调整声音的音调、速度、音量等参数,以满足不同需求。除了桌面版软件外,Voicevox 还提供了 Web 版本和 API 接口,方便开发者在各种平台上使用。 - -azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,它可以帮助开发者将语音转换为文本、将文本转换为语音、实现自然语言理解和对话等功能。Azure 语音支持多种语言和声音,可以用于构建各种语音应用程序,如智能客服、语音助手、自动化电话系统等。Azure 语音还提供了丰富的 API 和 SDK,方便开发者在各种平台上集成使用。 - `) - return true - } - let userReplySetting = await getUserReplySetting(this.e) - if (!userReplySetting.useTTS && matchCommand[2] === undefined) { - await this.reply('当前不是语音模式,如果想查看不同语音模式下支持的角色列表,可使用"#(vox|azure|vits)语音角色列表"查看') - return false - } - let ttsMode = Config.ttsMode - let roleList = [] - if (matchCommand[2] === 'vits') { - roleList = getVitsRoleList(this.e) - } else if (matchCommand[2] === 'vox') { - roleList = getVoicevoxRoleList() - } else if (matchCommand[2] === 'azure') { - roleList = getAzureRoleList() - } else if (matchCommand[2] === undefined) { - switch (ttsMode) { - case 'vits-uma-genshin-honkai': - roleList = getVitsRoleList(this.e) - break - case 'voicevox': - roleList = getVoicevoxRoleList() - break - case 'azure': - roleList = getAzureRoleList() - break - default: - break - } - } else { - await this.reply('设置错误,请使用"#chatgpt语音服务"查看支持的语音配置') - return false - } - if (roleList.length > 300) { - let chunks = roleList.match(/[^、]+(?:、[^、]+){0,30}/g) - roleList = await makeForwardMsg(e, chunks, `${Config.ttsMode}语音角色列表`) - } - await this.reply(roleList) - } - - async ttsSwitch (e) { - let userReplySetting = await getUserReplySetting(this.e) - if (!userReplySetting.useTTS) { - let replyMsg - if (userReplySetting.usePicture) { - replyMsg = `当前为${!userReplySetting.useTTS ? '图片模式' : ''},请先切换到语音模式吧~` - } else { - replyMsg = `当前为${!userReplySetting.useTTS ? '文本模式' : ''},请先切换到语音模式吧~` - } - await this.reply(replyMsg, e.isGroup) - return false - } - let regExp = /#语音切换(.*)/ - let ttsMode = e.msg.match(regExp)[1] - if (['vits', 'azure', 'voicevox'].includes(ttsMode)) { - if (ttsMode === 'vits') { - Config.ttsMode = 'vits-uma-genshin-honkai' - } else { - Config.ttsMode = ttsMode - } - await this.reply(`语音回复已切换至${Config.ttsMode}模式${Config.ttsMode === 'azure' ? ',建议重新开始对话以获得更好的对话效果!' : ''}`) - } else { - await this.reply('暂不支持此模式,当前支持vits,azure,voicevox。') - } - return false - } - - async commandHelp (e) { - if (/^#(chatgpt)?指令表帮助$/.exec(e.msg.trim())) { - await this.reply('#chatgpt指令表: 查看本插件的所有指令\n' + - '#chatgpt(对话|管理|娱乐|绘图|人物设定|聊天记录)指令表: 查看对应功能分类的指令表\n' + - '#chatgpt指令表搜索xxx: 查看包含对应关键词的指令') - return false - } - const categories = { - 对话: '对话', - 管理: '管理', - 娱乐: '娱乐', - 绘图: '绘图', - 人物设定: '人物设定', - 聊天记录: '聊天记录' - } - - function getCategory (e, plugin) { - for (const key in categories) { - if (e.msg.includes(key) && plugin.name.includes(categories[key])) { - return '功能名称: ' + if (!Chaite.getInstance()) { + const waitForChaite = async () => { + while (!Chaite.getInstance()) { + await new Promise(resolve => setTimeout(resolve, 1000)) } + return Chaite.getInstance() } - return '' - } - const commandSet = [] - const plugins = await Promise.all(loader.priority.map(p => new p.class())) - - for (const plugin of plugins) { - const name = plugin.name - const rule = plugin.rule - if (/^chatgpt/i.test(name) && rule) { - commandSet.push({ name, dsc: plugin.dsc, rule }) - } - } - if (/^#(chatgpt)?指令表搜索(.+)/.test(e.msg.trim())) { - let cmd = e.msg.trim().match(/#(chatgpt)?指令表搜索(.+)/)[2] - if (!cmd) { - await this.reply('(⊙ˍ⊙)') - return 0 - } else { - let searchResults = [] - commandSet.forEach(plugin => { - plugin.rule.forEach(item => { - if (item.reg.toLowerCase().includes(cmd.toLowerCase())) { - searchResults.push(item.reg) - } - }) - }) - if (!searchResults.length) { - await this.reply('没有找到符合的结果,换个关键词吧!', e.isGroup) - return 0 - } else if (searchResults.length <= 5) { - await this.reply(searchResults.join('\n'), e.isGroup) - return 1 - } else { - let msg = await makeForwardMsg(e, searchResults, e.msg.slice(1).startsWith('chatgpt') ? e.msg.slice(8) : 'chatgpt' + e.msg.slice(1)) - await this.reply(msg) - return 1 - } - } - } - const generatePrompt = (plugin, command) => { - const category = getCategory(e, plugin) - const commandsStr = command.length ? `正则指令:\n${command.join('\n')}\n` : '正则指令: 无\n' - const description = `功能介绍:${plugin.dsc}\n` - return `${category}${plugin.name}\n${description}${commandsStr}` - } - - const prompts = [] - for (const plugin of commandSet) { - const commands = plugin.rule.map(v => v.reg.includes('[#*0-9]') ? '表情合成功能只需要发送两个emoji表情即可' : v.reg) - const category = getCategory(e, plugin) - if (category || (!e.msg.includes('对话') && !e.msg.includes('管理') && !e.msg.includes('娱乐') && !e.msg.includes('绘图') && !e.msg.includes('人物设定') && !e.msg.includes('聊天记录'))) { - prompts.push(generatePrompt(plugin, commands)) - } - } - let msg = await makeForwardMsg(e, prompts, e.msg.slice(1).startsWith('chatgpt') ? e.msg.slice(1) : ('chatgpt' + e.msg.slice(1))) - await this.reply(msg) - return true - } - - async setList (e) { - this.setContext('saveList') - isWhiteList = e.msg.includes('白') - const listType = isWhiteList ? '对话白名单' : '对话黑名单' - await this.reply(`请发送需要添加的${listType}号码,默认设置为添加群号,需要添加QQ号时在前面添加^(例如:^123456)。`, e.isGroup) - return false - } - - async saveList (e) { - if (!this.e.msg) return - const listType = isWhiteList ? '对话白名单' : '对话黑名单' - const regex = /^\^?[1-9]\d{5,9}$/ - const wrongInput = [] - const inputSet = new Set() - const inputList = this.e.msg.split(/[,,]/).reduce((acc, value) => { - if (value.length > 11 || !regex.test(value)) { - wrongInput.push(value) - } else if (!inputSet.has(value)) { - inputSet.add(value) - acc.push(value) - } - return acc - }, []) - if (!inputList.length) { - let replyMsg = '名单更新失败,请在检查输入是否正确后重新输入。' - if (wrongInput.length) replyMsg += `\n${wrongInput.length ? '检测到以下错误输入:"' + wrongInput.join(',') + '",已自动忽略。' : ''}` - await this.reply(replyMsg, e.isGroup) - return false - } - let [whitelist, blacklist] = processList(Config.whitelist, Config.blacklist) - whitelist = [...inputList, ...whitelist] - blacklist = [...inputList, ...blacklist] - if (listType === '对话白名单') { - Config.whitelist = Array.from(new Set(whitelist)) - } else { - Config.blacklist = Array.from(new Set(blacklist)) - } - let replyMsg = `${listType}已更新,可通过\n"#chatgpt查看${listType}" 查看最新名单\n"#chatgpt移除${listType}" 管理名单${wrongInput.length ? '\n检测到以下错误输入:"' + wrongInput.join(',') + '",已自动忽略。' : ''}` - if (e.isPrivate) { - replyMsg += `\n当前${listType}为:${listType === '对话白名单' ? Config.whitelist : Config.blacklist}` - } - await this.reply(replyMsg, e.isGroup) - this.finish('saveList') - } - - async checkList (e) { - if (e.msg.includes('帮助')) { - await this.reply('默认设置为添加群号,需要拉黑QQ号时在前面添加^(例如:^123456),可一次性混合输入多个配置号码,错误项会自动忽略。具体使用指令可通过 "#指令表搜索名单" 查看,白名单优先级高于黑名单。') - return true - } - isWhiteList = e.msg.includes('白') - const list = isWhiteList ? Config.whitelist : Config.blacklist - const listType = isWhiteList ? '白名单' : '黑名单' - const replyMsg = list.length ? `当前${listType}为:${list}` : `当前没有设置任何${listType}` - await this.reply(replyMsg, e.isGroup) - return false - } - - async delList (e) { - isWhiteList = e.msg.includes('白') - const listType = isWhiteList ? '对话白名单' : '对话黑名单' - let replyMsg = '' - if (Config.whitelist.length === 0 && Config.blacklist.length === 0) { - replyMsg = '当前对话(白|黑)名单都是空哒,请先添加吧~' - } else if ((listType === '对话白名单' && !Config.whitelist.length) || (listType === '对话黑名单' && !Config.blacklist.length)) { - replyMsg = `当前${listType}为空,请先添加吧~` - } - if (replyMsg) { - await this.reply(replyMsg, e.isGroup) - return false - } - this.setContext('confirmDelList') - await this.reply(`请发送需要删除的${listType}号码,号码间使用,隔开。输入‘全部删除’清空${listType}。${e.isPrivate ? '\n当前' + listType + '为:' + (listType === '对话白名单' ? Config.whitelist : Config.blacklist) : ''}`, e.isGroup) - return false - } - - async confirmDelList (e) { - if (!this.e.msg) return - const isAllDeleted = this.e.msg.trim() === '全部删除' - const regex = /^\^?[1-9]\d{5,9}$/ - const wrongInput = [] - const inputSet = new Set() - const inputList = this.e.msg.split(/[,,]/).reduce((acc, value) => { - if (value.length > 11 || !regex.test(value)) { - wrongInput.push(value) - } else if (!inputSet.has(value)) { - inputSet.add(value) - acc.push(value) - } - return acc - }, []) - if (!inputList.length && !isAllDeleted) { - let replyMsg = '名单更新失败,请在检查输入是否正确后重新输入。' - if (wrongInput.length) replyMsg += `${wrongInput.length ? '\n检测到以下错误输入:"' + wrongInput.join(',') + '",已自动忽略。' : ''}` - await this.reply(replyMsg, e.isGroup) - return false - } - let [whitelist, blacklist] = processList(Config.whitelist, Config.blacklist) - if (isAllDeleted) { - Config.whitelist = isWhiteList ? [] : whitelist - Config.blacklist = !isWhiteList ? [] : blacklist - } else { - for (const element of inputList) { - if (isWhiteList) { - Config.whitelist = whitelist.filter(item => item !== element) - } else { - Config.blacklist = blacklist.filter(item => item !== element) - } - } - } - const listType = isWhiteList ? '对话白名单' : '对话黑名单' - let replyMsg = `${listType}已更新,可通过 "#chatgpt查看${listType}" 命令查看最新名单${wrongInput.length ? '\n检测到以下错误输入:"' + wrongInput.join(',') + '",已自动忽略。' : ''}` - if (e.isPrivate) { - const list = isWhiteList ? Config.whitelist : Config.blacklist - replyMsg = list.length ? `\n当前${listType}为:${list}` : `当前没有设置任何${listType}` - } - await this.reply(replyMsg, e.isGroup) - this.finish('confirmDelList') - } - - async enablePrivateChat (e) { - Config.enablePrivateChat = !!e.msg.match(/(允许|打开|同意)/) - await this.reply('设置成功', e.isGroup) - return false - } - - async enableGroupContext (e) { - const reg = /(关闭|打开)/ - const match = e.msg.match(reg) - if (match) { - const action = match[1] - if (action === '关闭') { - Config.enableGroupContext = false // 关闭 - await this.reply('已关闭群聊上下文功能', true) - } else { - Config.enableGroupContext = true // 打开 - await this.reply('已打开群聊上下文功能', true) - } - } - return false - } - - async setDefaultReplySetting (e) { - const reg = /^#chatgpt(打开|关闭|设置)?全局((文本模式|图片模式|语音模式|((azure|vits|vox)?语音角色|角色语音|角色)(.*))|回复帮助)/ - const matchCommand = e.msg.match(reg) - const settingType = matchCommand[2] - let replyMsg = '' - let ttsSupportKinds = [] - if (Config.azureTTSKey) ttsSupportKinds.push(1) - if (Config.ttsSpace) ttsSupportKinds.push(2) - if (Config.voicevoxSpace) ttsSupportKinds.push(3) - switch (settingType) { - case '图片模式': - if (matchCommand[1] === '打开') { - Config.defaultUsePicture = true - Config.defaultUseTTS = false - replyMsg = 'ChatGPT将默认以图片回复' - } else if (matchCommand[1] === '关闭') { - Config.defaultUsePicture = false - if (Config.defaultUseTTS) { - replyMsg = 'ChatGPT将默认以语音回复' - } else { - replyMsg = 'ChatGPT将默认以文本回复' - } - } else if (matchCommand[1] === '设置') { - replyMsg = '请使用“#chatgpt打开全局图片模式”或“#chatgpt关闭全局图片模式”命令来设置回复模式' - } break - case '文本模式': - if (matchCommand[1] === '打开') { - Config.defaultUsePicture = false - Config.defaultUseTTS = false - replyMsg = 'ChatGPT将默认以文本回复' - } else if (matchCommand[1] === '关闭') { - if (Config.defaultUseTTS) { - replyMsg = 'ChatGPT将默认以语音回复' - } else if (Config.defaultUsePicture) { - replyMsg = 'ChatGPT将默认以图片回复' - } else { - Config.defaultUseTTS = true - replyMsg = 'ChatGPT将默认以语音回复' - } - } else if (matchCommand[1] === '设置') { - replyMsg = '请使用“#chatgpt打开全局文本模式”或“#chatgpt关闭全局文本模式”命令来设置回复模式' - } break - case '语音模式': - if (!ttsSupportKinds.length) { - replyMsg = '您没有配置任何语音服务,请前往锅巴面板进行配置' - break - } - if (matchCommand[1] === '打开') { - Config.defaultUseTTS = true - Config.defaultUsePicture = false - replyMsg = 'ChatGPT将默认以语音回复' - } else if (matchCommand[1] === '关闭') { - Config.defaultUseTTS = false - if (Config.defaultUsePicture) { - replyMsg = 'ChatGPT将默认以图片回复' - } else { - replyMsg = 'ChatGPT将默认以文本回复' - } - } else if (matchCommand[1] === '设置') { - replyMsg = '请使用“#chatgpt打开全局语音模式”或“#chatgpt关闭全局语音模式”命令来设置回复模式' - } break - case '回复帮助': - replyMsg = '可使用以下命令配置全局回复:\n#chatgpt(打开/关闭)全局(语音/图片/文本)模式\n#chatgpt设置全局(vox|azure|vits)语音角色+角色名称(留空则为随机)\n' - break - default: - if (!ttsSupportKinds) { - replyMsg = '您没有配置任何语音服务,请前往锅巴面板进行配置' - break - } - if (settingType.match(/(语音角色|角色语音|角色)/)) { - const voiceKind = matchCommand[5] - let speaker = matchCommand[6] || '' - if (voiceKind === undefined) { - await this.reply('请选择需要设置的语音类型。使用"#chatgpt语音服务"查看支持的语音类型') - return false - } - if (!speaker.length || speaker === '随机') { - replyMsg = `设置成功,ChatGpt将在${voiceKind}语音模式下随机挑选角色进行回复` - if (voiceKind === 'vits') Config.defaultTTSRole = '随机' - if (voiceKind === 'azure') Config.azureTTSSpeaker = '随机' - if (voiceKind === 'vox') Config.voicevoxTTSSpeaker = '随机' - } else { - if (ttsSupportKinds.includes(1) && voiceKind === 'azure') { - if (getAzureRoleList().includes(speaker)) { - Config.defaultUseTTS = azureRoleList.filter(s => s.name === speaker)[0].code - replyMsg = `ChatGPT默认语音角色已被设置为“${speaker}”` - } else { - await this.reply(`抱歉,没有"${speaker}"这个角色,目前azure模式下支持的角色有${azureRoleList.map(item => item.name).join('、')}`) - return false - } - } else if (ttsSupportKinds.includes(2) && voiceKind === 'vits') { - const ttsRole = convertSpeaker(speaker) - if (vitsRoleList.includes(ttsRole)) { - Config.defaultTTSRole = ttsRole - replyMsg = `ChatGPT默认语音角色已被设置为“${ttsRole}”` - } else { - replyMsg = `抱歉,我还不认识“${ttsRole}”这个语音角色,可使用'#vits角色列表'查看可配置的角色` - } - } else if (ttsSupportKinds.includes(3) && voiceKind === 'vox') { - if (getVoicevoxRoleList().includes(speaker)) { - let regex = /^(.*?)-(.*)$/ - let match = regex.exec(speaker) - let style = null - if (match) { - speaker = match[1] - style = match[2] - } - let chosen = VoiceVoxTTS.supportConfigurations.filter(s => s.name === speaker) - if (chosen.length === 0) { - await this.reply(`抱歉,没有"${speaker}"这个角色,目前voicevox模式下支持的角色有${VoiceVoxTTS.supportConfigurations.map(item => item.name).join('、')}`) - break - } - if (style && !chosen[0].styles.find(item => item.name === style)) { - await this.reply(`抱歉,"${speaker}"这个角色没有"${style}"这个风格,目前支持的风格有${chosen[0].styles.map(item => item.name).join('、')}`) - break - } - Config.ttsRoleVoiceVox = chosen[0].name + (style ? `-${style}` : '') - replyMsg = `ChatGPT默认语音角色已被设置为“${speaker}”` - } else { - await this.reply(`抱歉,没有"${speaker}"这个角色,目前voicevox模式下支持的角色有${voxRoleList.map(item => item.name).join('、')}`) - return false - } - } else { - replyMsg = `${voiceKind}语音角色设置错误,请检查语音配置~` - } - } - } else { - replyMsg = "无法识别的设置类型\n请使用'#chatgpt全局回复帮助'查看正确命令" - } - } - await this.reply(replyMsg, true) - } - - async turnOnConfirm (e) { - await redis.set('CHATGPT:CONFIRM', 'on') - await this.reply('已开启消息确认', true) - return false - } - - async turnOffConfirm (e) { - await redis.set('CHATGPT:CONFIRM', 'off') - await this.reply('已关闭消息确认', true) - return false - } - - async setAccessToken (e) { - this.setContext('saveToken') - await this.reply('请发送ChatGPT AccessToken', true) - return false - } - - async setPoeCookie () { - this.setContext('savePoeToken') - await this.reply('请发送Poe Cookie', true) - return false - } - - async savePoeToken (e) { - if (!this.e.msg) return - let token = this.e.msg - if (!token.startsWith('p-b=')) { - await this.reply('Poe cookie格式错误', true) - this.finish('savePoeToken') - return - } - await redis.set('CHATGPT:POE_TOKEN', token) - await this.reply('Poe cookie设置成功', true) - this.finish('savePoeToken') - } - - async setBingAccessToken (e) { - this.setContext('saveBingToken') - await this.reply('请发送Bing Cookie Token.("_U" cookie from bing.com)', true) - return false - } - - async migrateBingAccessToken () { - let token = await redis.get('CHATGPT:BING_TOKEN') - if (token) { - token = token.split('|') - token = token.map((item, index) => ( - { - Token: item, - State: '正常', - Usage: 0 - } - )) - } else { - token = [] - } - let tokens = await redis.get('CHATGPT:BING_TOKENS') - if (tokens) { - tokens = JSON.parse(tokens) - } else { - tokens = [] - } - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify([...token, ...tokens])) - await this.reply('迁移完成', true) - } - - async getBingAccessToken (e) { - let tokens = await redis.get('CHATGPT:BING_TOKENS') - if (tokens) tokens = JSON.parse(tokens) - else tokens = [] - tokens = tokens.length > 0 - ? tokens.map((item, index) => ( - `【${index}】 Token:${item.Token.substring(0, 5 / 2) + '...' + item.Token.substring(item.Token.length - 5 / 2, item.Token.length)}` - )).join('\n') - : '无必应Token记录' - await this.reply(`${tokens}`, true) - return false - } - - async delBingAccessToken (e) { - this.setContext('deleteBingToken') - let tokens = await redis.get('CHATGPT:BING_TOKENS') - if (tokens) tokens = JSON.parse(tokens) - else tokens = [] - tokens = tokens.length > 0 - ? tokens.map((item, index) => ( - `【${index}】 Token:${item.Token.substring(0, 5 / 2) + '...' + item.Token.substring(item.Token.length - 5 / 2, item.Token.length)}` - )).join('\n') - : '无必应Token记录' - await this.reply(`请发送要删除的token编号\n${tokens}`, true) - if (tokens.length == 0) this.finish('saveBingToken') - return false - } - - async saveBingToken () { - if (!this.e.msg) return - let token = this.e.msg - if (token.length < 100) { - await this.reply('Bing Token格式错误,请确定获取了有效的_U Cookie或完整的Cookie', true) - this.finish('saveBingToken') - return - } - let cookie - if (token?.indexOf('=') > -1) { - cookie = token - } - const bingAIClient = new SydneyAIClient({ - userToken: token, // "_U" cookie from bing.com - cookie, - debug: Config.debug - }) - // 异步就好了,不卡着这个context了 - bingAIClient.createNewConversation().then(async res => { - if (res.clientId) { - logger.info('bing token 有效') - } else { - logger.error('bing token 无效', res) - // 移除无效token - if (await redis.exists('CHATGPT:BING_TOKENS') != 0) { - let bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - const element = bingToken.findIndex(element => element.token === token) - if (element >= 0) { - bingToken[element].State = '异常' - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken)) - } - } - await this.reply(`经检测,Bing Token无效。来自Bing的错误提示:${res.result?.message}`) - } - }) - let bingToken = [] - if (await redis.exists('CHATGPT:BING_TOKENS') != 0) { - bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - if (!bingToken.some(element => element.token === token)) { - bingToken.push({ - Token: token, - State: '正常', - Usage: 0 - }) - } - } else { - bingToken = [{ - Token: token, - State: '正常', - Usage: 0 - }] - } - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken)) - await this.reply('Bing Token设置成功', true) - this.finish('saveBingToken') - } - - async deleteBingToken () { - if (!this.e.msg) return - let tokenId = this.e.msg - if (await redis.exists('CHATGPT:BING_TOKENS') != 0) { - let bingToken = JSON.parse(await redis.get('CHATGPT:BING_TOKENS')) - if (tokenId >= 0 && tokenId < bingToken.length) { - const removeToken = bingToken[tokenId].Token - bingToken.splice(tokenId, 1) - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingToken)) - await this.reply(`Token ${removeToken.substring(0, 5 / 2) + '...' + removeToken.substring(removeToken.length - 5 / 2, removeToken.length)} 移除成功`, true) - this.finish('deleteBingToken') - } else { - await this.reply('Token编号错误!', true) - this.finish('deleteBingToken') - } - } else { - await this.reply('Token记录异常', true) - this.finish('deleteBingToken') - } - } - - async saveToken () { - if (!this.e.msg) return - let token = this.e.msg - if (!token.startsWith('ey') || token.length < 20) { - await this.reply('ChatGPT AccessToken格式错误', true) - this.finish('saveToken') - return - } - await redis.set('CHATGPT:TOKEN', token) - await this.reply('ChatGPT AccessToken设置成功', true) - this.finish('saveToken') - } - - async useBrowserBasedSolution (e) { - await redis.set('CHATGPT:USE', 'browser') - await this.reply('已切换到基于浏览器的解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误') - } - - async useOpenAIAPIBasedSolution (e) { - let use = await redis.get('CHATGPT:USE') - if (use !== 'api') { - await redis.set('CHATGPT:USE', 'api') - await this.reply('已切换到基于OpenAI API的解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误') - } else { - await this.reply('当前已经是API模式了') - } - } - - async useChatGLMSolution (e) { - await redis.set('CHATGPT:USE', 'chatglm') - await this.reply('已切换到ChatGLM-6B解决方案,如果已经对话过建议执行`#结束对话`避免引起404错误') - } - - async useReversedAPIBasedSolution2 (e) { - let use = await redis.get('CHATGPT:USE') - if (use !== 'api3') { - await redis.set('CHATGPT:USE', 'api3') - await this.reply('已切换到基于第三方Reversed Conversastion API(API3)的解决方案') - } else { - await this.reply('当前已经是API3模式了') - } - } - - async useBingSolution (e) { - let use = await redis.get('CHATGPT:USE') - if (use !== 'bing') { - await redis.set('CHATGPT:USE', 'bing') - await this.reply('已切换到基于微软新必应的解决方案,如果已经对话过务必执行`#结束对话`避免引起404错误') - } else { - await this.reply('当前已经是必应Bing模式了') - } - } - - async useClaudeBasedSolution (e) { - let use = await redis.get('CHATGPT:USE') - if (use !== 'poe') { - await redis.set('CHATGPT:USE', 'poe') - await this.reply('已切换到基于Quora\'s POE的解决方案') - } else { - await this.reply('当前已经是POE模式了') - } - } - - async useSlackClaudeBasedSolution () { - let use = await redis.get('CHATGPT:USE') - if (use !== 'claude') { - await redis.set('CHATGPT:USE', 'claude') - await this.reply('已切换到基于slack claude机器人的解决方案') - } else { - await this.reply('当前已经是claude模式了') - } - } - - async useXinghuoBasedSolution () { - let use = await redis.get('CHATGPT:USE') - if (use !== 'xh') { - await redis.set('CHATGPT:USE', 'xh') - await this.reply('已切换到基于星火的解决方案') - } else { - await this.reply('当前已经是星火模式了') - } - } - - async changeBingTone (e) { - let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '') - if (!tongStyle) { - return - } - let map = { - 精准: 'precise', - 创意: 'creative', - 均衡: 'balanced', - Sydney: 'Sydney', - sydney: 'Sydney', - 悉尼: 'Sydney', - 自设定: 'Custom', - 自定义: 'Custom' - } - if (map[tongStyle]) { - Config.toneStyle = map[tongStyle] - await e.reply('切换成功') - } else { - await e.reply('没有这种风格。支持的风格:精准、创意、均衡、悉尼、自设定') - } - } - - async bingOpenSuggestedResponses (e) { - Config.enableSuggestedResponses = e.msg.indexOf('开启') > -1 - await e.reply('操作成功') - } - - async checkAuth (e) { - if (!e.isMaster) { - e.reply(`只有主人才能命令ChatGPT哦~ - (*/ω\*)`) - return false - } - return true - } - - async versionChatGPTPlugin (e) { - await renderUrl(e, `http://127.0.0.1:${Config.serverPort || 3321}/version`, { Viewport: { width: 800, height: 600 } }) - } - - async modeHelp () { - let mode = await redis.get('CHATGPT:USE') - const modeMap = { - browser: '浏览器', - // apiReverse: 'API2', - api: 'API', - bing: '必应', - api3: 'API3', - chatglm: 'ChatGLM-6B', - claude: 'Claude', - poe: 'Poe' - } - let modeText = modeMap[mode || 'api'] - let message = `API模式和浏览器模式如何选择? - -API模式会调用 OpenAI 官方提供的 gpt-3.5-turbo API,只需要提供 API Key。一般情况下,该种方式响应速度更快,不会像 chatGPT 官网一样总出现不可用的现象,但要注意 gpt-3.5-turbo 的 API 调用是收费的,新用户有 $5 的试用金可用于支付,价格为 $0.0020/1K tokens。(问题和回答加起来算 token) - -API3 模式会调用官网反代 API,它会帮你绕过 CF 防护,需要提供 ChatGPT 的 Token。效果与官网和浏览器一致。设置 Token 指令:#chatgpt设置token。 - -浏览器模式通过在本地启动 Chrome 等浏览器模拟用户访问 ChatGPT 网站,使得获得和官方以及 API2 模式一模一样的回复质量,同时保证安全性。缺点是本方法对环境要求较高,需要提供桌面环境和一个可用的代理(能够访问 ChatGPT 的 IP 地址),且响应速度不如 API,而且高峰期容易无法使用。 - -必应(Bing)将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的 Bing 登录 Cookie 方可使用。#chatgpt设置必应 Token。 - -自建 ChatGLM 模式会调用自建的 ChatGLM-6B 服务器 API 进行对话,需要自建。参考 https://github.com/ikechan8370/SimpleChatGLM6BAPI。 - -Claude 模式会调用 Slack 中的 Claude 机器人进行对话,与其他模式不同的是全局共享一个对话。配置参考 https://ikechan8370.com/archives/chatgpt-plugin-for-yunzaipei-zhi-slack-claude。 - -Poe 模式会调用 Poe 中的 Claude-instant 进行对话。需要提供 Cookie:#chatgpt设置 Poe Token。 - -星火 模式会调用科大讯飞推出的新一代认知智能大模型 '星火认知大模型' 进行对话。需要提供Cookie:#chatgpt设置星火token。 - -您可以使用 "#chatgpt切换浏览器/API/API3/Bing/ChatGLM/Claude/Poe/星火" 来切换到指定模式。 - -当前为 ${modeText} 模式。` - await this.reply(message) - } - - async shutUp (e) { - let duration = e.msg.replace(/^#chatgpt(本群)?(群\d+)?(关闭|闭嘴|关机|休眠|下班)/, '') - let scope - let time = 3600000 - if (duration === '永久') { - time = 0 - } else if (duration) { - time = parseDuration(duration) - } - const match = e.msg.match(/#chatgpt群(\d+)?(关闭|闭嘴|关机|休眠|下班)(.*)/) - if (e.msg.indexOf('本群') > -1) { - if (e.isGroup) { - scope = e.group.group_id - if (await redis.get(`CHATGPT:SHUT_UP:${scope}`)) { - await redis.del(`CHATGPT:SHUT_UP:${scope}`) - await redis.set(`CHATGPT:SHUT_UP:${scope}`, '1', { EX: time }) - await e.reply(`好的,已切换休眠状态:倒计时${formatDuration(time)}`) - } else { - await redis.set(`CHATGPT:SHUT_UP:${scope}`, '1', { EX: time }) - await e.reply(`好的,已切换休眠状态:倒计时${formatDuration(time)}`) - } - } else { - await e.reply('主人,这里好像不是群哦') - return false - } - } else if (match) { - const groupId = parseInt(match[1], 10) - if (Bot.getGroupList().get(groupId)) { - if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) { - await redis.del(`CHATGPT:SHUT_UP:${groupId}`) - await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time }) - await e.reply(`好的,即将在群${groupId}中休眠${formatDuration(time)}`) - } else { - await redis.set(`CHATGPT:SHUT_UP:${groupId}`, '1', { EX: time }) - await e.reply(`好的,即将在群${groupId}中休眠${formatDuration(time)}`) - } - } else { - await e.reply('主人还没告诉我群号呢') - return false - } - } else { - if (await redis.get('CHATGPT:SHUT_UP:ALL')) { - await redis.del('CHATGPT:SHUT_UP:ALL') - await redis.set('CHATGPT:SHUT_UP:ALL', '1', { EX: time }) - await e.reply(`好的,我会延长休眠时间${formatDuration(time)}`) - } else { - await redis.set('CHATGPT:SHUT_UP:ALL', '1', { EX: time }) - await e.reply(`好的,我会延长休眠时间${formatDuration(time)}`) - } - } - } - - async openMouth (e) { - const match = e.msg.match(/^#chatgpt群(\d+)/) - if (e.msg.indexOf('本群') > -1) { - if (await redis.get('CHATGPT:SHUT_UP:ALL')) { - await e.reply('当前为休眠模式,没办法做出回应呢') - return false - } - if (e.isGroup) { - let scope = e.group.group_id - if (await redis.get(`CHATGPT:SHUT_UP:${scope}`)) { - await redis.del(`CHATGPT:SHUT_UP:${scope}`) - await e.reply('好的主人,我又可以和大家聊天啦') - } else { - await e.reply('主人,我已经启动过了哦') - } - } else { - await e.reply('主人,这里好像不是群哦') - return false - } - } else if (match) { - if (await redis.get('CHATGPT:SHUT_UP:ALL')) { - await e.reply('当前为休眠模式,没办法做出回应呢') - return false - } - const groupId = parseInt(match[1], 10) - if (Bot.getGroupList().get(groupId)) { - if (await redis.get(`CHATGPT:SHUT_UP:${groupId}`)) { - await redis.del(`CHATGPT:SHUT_UP:${groupId}`) - await e.reply(`好的主人,我终于又可以在群${groupId}和大家聊天了`) - } else { - await e.reply(`主人,我在群${groupId}中已经是启动状态了哦`) - } - } else { - await e.reply('主人还没告诉我群号呢') - return false - } - } else { - let keys = await redis.keys('CHATGPT:SHUT_UP:*') - if (await redis.get('CHATGPT:SHUT_UP:ALL')) { - await redis.del('CHATGPT:SHUT_UP:ALL') - for (let i = 0; i < keys.length; i++) { - await redis.del(keys[i]) - } - await e.reply('好的,我会开启所有群聊响应') - } else if (keys || keys.length > 0) { - for (let i = 0; i < keys.length; i++) { - await redis.del(keys[i]) - } - await e.reply('已经开启过全群响应啦') - } else { - await e.reply('我没有在任何群休眠哦') - } - } - } - - async listShutUp () { - let keys = await redis.keys('CHATGPT:SHUT_UP:*') - if (!keys || keys.length === 0) { - await this.reply('已经开启过全群响应啦', true) - } else { - let list = [] - for (let i = 0; i < keys.length; i++) { - let key = keys[i] - let groupId = key.replace('CHATGPT:SHUT_UP:', '') - let ttl = await redis.ttl(key) - let ttlFormat = formatDuration(ttl) - list.push({ groupId, ttlFormat }) - } - await this.reply(list.map(item => item.groupId !== 'ALL' ? `群聊${item.groupId}: ${item.ttlFormat}` : `全局: ${item.ttlFormat}`).join('\n')) - } - } - - async setAPIKey (e) { - this.setContext('saveAPIKey') - await this.reply('请发送OpenAI API Key.', true) - return false - } - - async saveAPIKey () { - if (!this.e.msg) return - let token = this.e.msg - if (!token.startsWith('sk-')) { - await this.reply('OpenAI API Key格式错误', true) - this.finish('saveAPIKey') - return - } - // todo - Config.apiKey = token - await this.reply('OpenAI API Key设置成功', true) - this.finish('saveAPIKey') - } - - async setXinghuoToken () { - this.setContext('saveXinghuoToken') - await this.reply('请发送星火的ssoSessionId', true) - return false - } - - async saveXinghuoToken () { - if (!this.e.msg) return - let token = this.e.msg - // todo - Config.xinghuoToken = token - await this.reply('星火ssoSessionId设置成功', true) - this.finish('saveXinghuoToken') - } - - async setAPIPromptPrefix (e) { - this.setContext('saveAPIPromptPrefix') - await this.reply('请发送用于API模式的设定', true) - return false - } - - async saveAPIPromptPrefix (e) { - if (!this.e.msg) return - if (this.e.msg === '取消') { - await this.reply('已取消设置API设定', true) - this.finish('saveAPIPromptPrefix') - return - } - // todo - Config.promptPrefixOverride = this.e.msg - await this.reply('API模式的设定设置成功', true) - this.finish('saveAPIPromptPrefix') - } - - async setBingPromptPrefix (e) { - this.setContext('saveBingPromptPrefix') - await this.reply('请发送用于Bing Sydney模式的设定', true) - return false - } - - async saveBingPromptPrefix (e) { - if (!this.e.msg) return - if (this.e.msg === '取消') { - await this.reply('已取消设置Sydney设定', true) - this.finish('saveBingPromptPrefix') - return - } - Config.sydney = this.e.msg - await this.reply('Bing Sydney模式的设定设置成功', true) - this.finish('saveBingPromptPrefix') - } - - async switchDraw (e) { - if (e.msg.indexOf('开启') > -1) { - if (Config.enableDraw) { - await this.reply('当前已经开启chatgpt画图功能', true) - } else { - Config.enableDraw = true - await this.reply('chatgpt画图功能开启成功', true) - } - } else { - if (!Config.enableDraw) { - await this.reply('当前未开启chatgpt画图功能', true) - } else { - Config.enableDraw = false - await this.reply('chatgpt画图功能关闭成功', true) - } - } - } - - async queryAPIPromptPrefix (e) { - await this.reply(Config.promptPrefixOverride, true) - } - - async queryBingPromptPrefix (e) { - await this.reply(Config.sydney, true) - } - - async setAdminPassword (e) { - if (e.isGroup || !e.isPrivate) { - await this.reply('请私聊发送命令', true) - return true - } - this.setContext('saveAdminPassword') - await this.reply('请发送系统管理密码', true) - return false - } - - async setUserPassword (e) { - if (e.isGroup || !e.isPrivate) { - await this.reply('请私聊发送命令', true) - return true - } - this.setContext('saveUserPassword') - await this.reply('请发送系统用户密码', true) - return false - } - - async saveAdminPassword (e) { - if (!this.e.msg) return - const passwd = this.e.msg - await redis.set('CHATGPT:ADMIN_PASSWD', md5(passwd)) - await this.reply('设置成功', true) - this.finish('saveAdminPassword') - } - - async saveUserPassword (e) { - if (!this.e.msg) return - const passwd = this.e.msg - const dir = 'resources/ChatGPTCache/user' - const filename = `${this.e.user_id}.json` - const filepath = path.join(dir, filename) - fs.mkdirSync(dir, { recursive: true }) - if (fs.existsSync(filepath)) { - fs.readFile(filepath, 'utf8', (err, data) => { - if (err) { - console.error(err) - return - } - const config = JSON.parse(data) - config.passwd = md5(passwd) - fs.writeFile(filepath, JSON.stringify(config), 'utf8', (err) => { - if (err) { - console.error(err) - } - }) + waitForChaite().then(() => { + this.initCommand(cmdPrefix) }) } else { - fs.writeFile(filepath, JSON.stringify({ - user: this.e.user_id, - passwd: md5(passwd), - chat: [] - }), 'utf8', (err) => { - if (err) { - console.error(err) - } - }) + this.initCommand(cmdPrefix) } - await this.reply('设置完成', true) - this.finish('saveUserPassword') } - async adminPage (e) { - if (!Config.groupAdminPage && (e.isGroup || !e.isPrivate)) { - await this.reply('请私聊发送命令', true) - return true - } - const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/` - await this.reply(`请登录${viewHost + 'admin/settings'}进行系统配置`, true) + initCommand (cmdPrefix) { + this.rule.push(...[ + ...createCRUDCommandRules.bind(this)(cmdPrefix, '渠道', 'channels'), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '预设', 'presets'), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '工具', 'tools'), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '处理器', 'processors'), + createSwitchCommandRules.bind(this)(cmdPrefix, '(预设切换|其他人切换预设)', 'customPreset', 1), + createSwitchCommandRules.bind(this)(cmdPrefix, '(调试|debug)(模式)?', 'debug'), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '预设切换黑名单', 'customPresetUserBlackList', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '预设切换白名单', 'customPresetUserWhiteList', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '输入屏蔽词', 'promptBlockWords', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '输出屏蔽词', 'responseBlockWords', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单群', 'blackGroups', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单群', 'whiteGroups', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单用户', 'blackUsers', false), + ...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单用户', 'whiteUsers', false), + createSwitchCommandRules(cmdPrefix, '(伪人|bym)', 'bym') + ]) } - async userPage (e) { - if (!Config.groupAdminPage && (e.isGroup || !e.isPrivate)) { - await this.reply('请私聊发送命令', true) - return true - } - const viewHost = Config.serverHost ? `http://${Config.serverHost}/` : `http://${await getPublicIP()}:${Config.serverPort || 3321}/` - await this.reply(`请登录${viewHost + 'admin/dashboard'}进行系统配置`, true) + managementPanel (e) { + // todo + // this.reply(`(todo)管理面板地址:http://${ChatGPTConfig.chaite.host}:${ChatGPTConfig.chaite.host}`) + const token = Chaite.getInstance().getFrontendAuthHandler().generateToken(300) + this.reply(`token: ${token}, 有效期300秒`, true) } - async setOpenAIPlatformToken (e) { - this.setContext('doSetOpenAIPlatformToken') - await e.reply('请发送refreshToken\n你可以在已登录的platform.openai.com后台界面打开调试窗口,在终端中执行\nJSON.parse(localStorage.getItem(Object.keys(localStorage).filter(k => k.includes(\'auth0\'))[0])).body.refresh_token\n如果仍不能查看余额,请退出登录重新获取刷新令牌') - } - - async doSetOpenAIPlatformToken () { - let token = this.e.msg - if (!token) { - return false - } - Config.OpenAiPlatformRefreshToken = token.replaceAll('\'', '') - await this.e.reply('设置成功') - this.finish('doSetOpenAIPlatformToken') - } - - async exportConfig (e) { - if (e.isGroup || !e.isPrivate) { - await this.reply('请私聊发送命令', true) - return true - } - let redisConfig = {} - if (await redis.exists('CHATGPT:BING_TOKENS') != 0) { - let bingTokens = await redis.get('CHATGPT:BING_TOKENS') - if (bingTokens) { bingTokens = JSON.parse(bingTokens) } else bingTokens = [] - redisConfig.bingTokens = bingTokens + async setDefaultBymPreset (e) { + const presetId = e.msg.replace(`${ChatGPTConfig.basic.commandPrefix}伪人设置默认预设`, '') + const preset = await Chaite.getInstance().getChatPresetManager().getInstance(presetId) + if (preset) { + ChatGPTConfig.bym.defaultPreset = presetId + this.reply(`伪人模式默认预设已切换为${presetId}(${preset.name})`) } else { - redisConfig.bingTokens = [] + this.reply(`未找到预设${presetId}`) } - if (await redis.exists('CHATGPT:CONFIRM') != 0) { - redisConfig.turnConfirm = await redis.get('CHATGPT:CONFIRM') === 'on' + } + + async destroyConversation (e) { + if (e.msg.includes('全部')) { + if (!e.isMaster) { + this.reply('仅限主人使用') + return false + } + const userStates = await Chaite.getInstance().getUserStateStorage().listItems() + // let num = 0 + for (const userState of userStates) { + if (userState.current.conversationId && userState.current.messageId) { + // num++ + userState.current.conversationId = crypto.randomUUID() + userState.current.messageId = crypto.randomUUID() + await Chaite.getInstance().getUserStateStorage().setItem(userState.userId + '', userState) + } + } + this.reply('已结束全部对话') + } else { + const state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '') + if (!state || !state.current.conversationId || !state.current.messageId) { + this.reply('当前未开启对话') + return false + } + state.current.conversationId = crypto.randomUUID() + state.current.messageId = crypto.randomUUID() + await Chaite.getInstance().getUserStateStorage().setItem(e.sender.user_id + '', state) + this.reply('已结束当前对话') } - if (await redis.exists('CHATGPT:USE') != 0) { - redisConfig.useMode = await redis.get('CHATGPT:USE') + } + + async currentStatus (e) { + const msgs = [] + let basic = `Chaite版本:${VERSION}\n` + const user = Chaite.getInstance().getToolsManager().cloudService?.getUser() + if (user) { + basic += `Chaite Cloud:已认证 @${user.username}` + } else if (ChatGPTConfig.chaite.cloudBaseUrl) { + basic += 'Chaite Cloud: 未认证' + } else { + basic += 'Chaite Cloud: 未接入' } - const configJson = JSON.stringify({ - chatConfig: Config, - redisConfig + msgs.push(basic) + + const allChannels = await Chaite.getInstance().getChannelsManager().getAllChannels() + let channelMsg = `渠道总数:${allChannels.length}\n` + channelMsg += `请使用 ${ChatGPTConfig.basic.commandPrefix}渠道列表 查看全部渠道\n\n` + allChannels.map(c => c.models).reduce((acc, cur) => { + acc.push(...cur) + return acc + }, []).forEach(m => { + channelMsg += `${m}:${allChannels.filter(c => c.models.includes(m)).length}个\n` }) - console.log(configJson) - const buf = Buffer.from(configJson) - e.friend.sendFile(buf, `ChatGPT-Plugin Config ${new Date()}.json`) - return true - } + msgs.push(channelMsg) - async importConfig (e) { - if (e.isGroup || !e.isPrivate) { - await this.reply('请私聊发送命令', true) - return true - } - this.setContext('doImportConfig') - await e.reply('请发送配置文件') - } + const allPresets = await Chaite.getInstance().getChatPresetManager().getAllPresets() + let presetMsg = `预设总数:${allPresets.length}\n` + presetMsg += `请使用 ${ChatGPTConfig.basic.commandPrefix}预设列表 查看全部预设` + msgs.push(presetMsg) - async doImportConfig (e) { - const file = this.e.message.find(item => item.type === 'file') - if (file) { - const fileUrl = await this.e.friend.getFileUrl(file.fid) - if (fileUrl) { - try { - let changeConfig = [] - const response = await fetch(fileUrl) - const data = await response.json() - const chatdata = data.chatConfig || {} - for (let [keyPath, value] of Object.entries(chatdata)) { - if (keyPath === 'blockWords' || keyPath === 'promptBlockWords' || keyPath === 'initiativeChatGroups') { value = value.toString().split(/[,,;;\|]/) } - if (Config[keyPath] != value) { - changeConfig.push({ - item: keyPath, - value: typeof (value) === 'object' ? JSON.stringify(value) : value, - old: typeof (Config[keyPath]) === 'object' ? JSON.stringify(Config[keyPath]) : Config[keyPath], - type: 'config' - }) - Config[keyPath] = value - } - } - const redisConfig = data.redisConfig || {} - if (redisConfig.bingTokens != null) { - changeConfig.push({ - item: 'bingTokens', - value: JSON.stringify(redisConfig.bingTokens), - old: await redis.get('CHATGPT:BING_TOKENS'), - type: 'redis' - }) - await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(redisConfig.bingTokens)) - } - if (redisConfig.turnConfirm != null) { - changeConfig.push({ - item: 'turnConfirm', - value: redisConfig.turnConfirm ? 'on' : 'off', - old: await redis.get('CHATGPT:CONFIRM'), - type: 'redis' - }) - await redis.set('CHATGPT:CONFIRM', redisConfig.turnConfirm ? 'on' : 'off') - } - if (redisConfig.useMode != null) { - changeConfig.push({ - item: 'useMode', - value: redisConfig.useMode, - old: await redis.get('CHATGPT:USE'), - type: 'redis' - }) - await redis.set('CHATGPT:USE', redisConfig.useMode) - } - await this.reply(await makeForwardMsg(this.e, changeConfig.map(msg => `修改项:${msg.item}\n旧数据\n\n${msg.old}\n\n新数据\n ${msg.value}`))) - } catch (error) { - console.error(error) - await e.reply('配置文件错误') - } - } - } else { - await this.reply('未找到配置文件', false) - return false - } + const defaultChatPresetId = ChatGPTConfig.llm.defaultChatPresetId + const currentPreset = await Chaite.getInstance().getChatPresetManager().getInstance(defaultChatPresetId) + msgs.push(`当前预设:${currentPreset?.name || '未设置'}${currentPreset ? ('\n\n' + currentPreset.toFormatedString(false)) : ''}`) - this.finish('doImportConfig') - } + const allTools = await Chaite.getInstance().getToolsManager().listInstances() + let toolsMsg = `工具总数:${allTools.length}\n` + toolsMsg += `请使用 ${ChatGPTConfig.basic.commandPrefix}工具列表 查看全部工具` + msgs.push(toolsMsg) - async switchSmartMode (e) { - if (e.msg.includes('开启')) { - if (Config.smartMode) { - await e.reply('已经开启了') - return - } - Config.smartMode = true - await e.reply('好的,已经打开智能模式,注意API额度哦。配合开启读取群聊上下文效果更佳!') - } else { - if (!Config.smartMode) { - await e.reply('已经是关闭得了') - return - } - Config.smartMode = false - await e.reply('好的,已经关闭智能模式') - } + const allProcessors = await Chaite.getInstance().getProcessorsManager().listInstances() + let processorsMsg = `处理器总数:${allProcessors.length}\n` + processorsMsg += `请使用 ${ChatGPTConfig.basic.commandPrefix}处理器列表 查看全部处理器` + msgs.push(processorsMsg) + + const userStatesManager = Chaite.getInstance().getUserStateStorage() + const allUsers = await userStatesManager.listItems() + const currentUserNums = allUsers.filter(u => u.current.conversationId && u.current.messageId).length + const historyUserNums = allUsers.length + msgs.push(`用户总数:${historyUserNums}\n当前对话用户数:${currentUserNums}`) + + const m = await common.makeForwardMsg(e, msgs, e.msg) + e.reply(m) } } diff --git a/apps/memory.js b/apps/memory.js new file mode 100644 index 0000000..5fbc3cc --- /dev/null +++ b/apps/memory.js @@ -0,0 +1,224 @@ +import Config from '../config/config.js' +import { GroupMessageCollector } from '../models/memory/collector.js' +import { memoryService } from '../models/memory/service.js' +import common from '../../../lib/common/common.js' + +const collector = new GroupMessageCollector() + +function isGroupManager (e) { + if (e.isMaster) { + return true + } + if (!e.member) { + return false + } + if (typeof e.member.is_admin !== 'undefined') { + return e.member.is_admin || e.member.is_owner + } + if (typeof e.member.role !== 'undefined') { + return ['admin', 'owner'].includes(e.member.role) + } + return false +} + +export class MemoryManager extends plugin { + constructor () { + const cmdPrefix = Config.basic.commandPrefix || '#chatgpt' + super({ + name: 'ChatGPT-Plugin记忆系统', + dsc: '处理记忆系统相关的采集与管理', + event: 'message', + priority: 550, + rule: [ + // { + // reg: '[\\s\\S]+', + // fnc: 'collect', + // log: false + // }, + { + reg: '^#?(我的)?记忆$', + fnc: 'showUserMemory' + }, + { + reg: '^#?他的记忆$', + fnc: 'showTargetUserMemory' + }, + { + reg: '^#?(删除|清除)(我的)?记忆\\s*(\\d+)$', + fnc: 'deleteUserMemory' + }, + { + reg: '^#?(本群|群)记忆$', + fnc: 'showGroupMemory' + }, + { + reg: '^#?(删除|移除)群记忆\\s*(\\d+)$', + fnc: 'deleteGroupMemory' + }, + { + reg: `^${cmdPrefix}记忆列表$`, + fnc: 'adminMemoryOverview', + permission: 'master' + } + ] + }) + + // 兼容miao和trss,气死了 + let task = { + name: 'ChatGPT-群记忆轮询', + cron: '*/1 * * * *', + fnc: this.pollHistoryTask.bind(this), + log: false + } + this.task = [task] + + } + + async collect (e) { + collector.push(e) + return false + } + + async showUserMemory (e) { + if (!memoryService.isUserMemoryEnabled(e.sender.user_id)) { + await e.reply('私人记忆未开启或您未被授权。') + return false + } + const memories = memoryService.listUserMemories(e.sender.user_id, e.isGroup ? e.group_id : null) + + if (!memories.length) { + await e.reply('🧠 您的记忆:\n暂无记录~') + return true + } + + const msgs = memories.map(item => + `${item.id}. ${item.value}(更新时间:${item.updated_at})` + ) + + const forwardMsg = await common.makeForwardMsg(e, ['🧠 您的记忆:', ...msgs], '私人记忆列表') + await e.reply(forwardMsg) + return true + } + + async showTargetUserMemory (e) { + if (!e.isGroup) { + await e.reply('该指令仅可在群聊中使用。') + return false + } + + const at = e.at || (e.message?.find(m => m.type === 'at')?.qq) + if (!at) { + await e.reply('请@要查询的用户。') + return false + } + + if (!memoryService.isUserMemoryEnabled(at)) { + await e.reply('该用户未开启私人记忆或未被授权。') + return false + } + + const memories = memoryService.listUserMemories(at, e.group_id) + + if (!memories.length) { + await e.reply('🧠 TA的记忆:\n暂无记录~') + return true + } + + const msgs = memories.map(item => + `${item.id}. ${item.value}(更新时间:${item.updated_at})` + ) + + const forwardMsg = await common.makeForwardMsg(e, ['🧠 TA的记忆:', ...msgs], 'TA的记忆列表') + await e.reply(forwardMsg) + return true + } + + async deleteUserMemory (e) { + const match = e.msg.match(/(\d+)$/) + if (!match) { + return false + } + const memoryId = Number(match[1]) + if (!memoryId) { + return false + } + if (!memoryService.isUserMemoryEnabled(e.sender.user_id)) { + await e.reply('私人记忆未开启或您未被授权。') + return false + } + const success = memoryService.deleteUserMemory(memoryId, e.sender.user_id) + await e.reply(success ? '已删除指定记忆。' : '未找到对应的记忆条目。') + return success + } + + async showGroupMemory (e) { + if (!e.isGroup) { + await e.reply('该指令仅可在群聊中使用。') + return false + } + if (!memoryService.isGroupMemoryEnabled(e.group_id)) { + await e.reply('本群尚未开启记忆功能。') + return false + } + await collector.flush(e.group_id) + const facts = memoryService.listGroupFacts(e.group_id) + + if (!facts.length) { + await e.reply('📚 本群记忆:\n暂无群记忆。') + return true + } + + const msgs = facts.map(item => { + const topic = item.topic ? `【${item.topic}】` : '' + return `${item.id}. ${topic}${item.fact}` + }) + + const forwardMsg = await common.makeForwardMsg(e, ['📚 本群记忆:', ...msgs], '群记忆列表') + await e.reply(forwardMsg) + return true + } + + async deleteGroupMemory (e) { + if (!e.isGroup) { + await e.reply('该指令仅可在群聊中使用。') + return false + } + if (!memoryService.isGroupMemoryEnabled(e.group_id)) { + await e.reply('本群尚未开启记忆功能。') + return false + } + if (!isGroupManager(e)) { + await e.reply('仅限主人或群管理员管理群记忆。') + return false + } + await collector.flush(e.group_id) + const match = e.msg.match(/(\d+)$/) + if (!match) { + return false + } + const factId = Number(match[1]) + if (!factId) { + return false + } + const success = memoryService.deleteGroupFact(e.group_id, factId) + await e.reply(success ? '已删除群记忆。' : '未找到对应的群记忆。') + return success + } + + async adminMemoryOverview (e) { + const enabledGroups = (Config.memory?.group?.enabledGroups || []).map(String) + const groupLines = enabledGroups.length ? enabledGroups.join(', ') : '暂无' + const userStatus = Config.memory?.user?.enable ? '已启用' : '未启用' + await e.reply(`记忆系统概览:\n- 群记忆开关:${Config.memory?.group?.enable ? '已启用' : '未启用'}\n- 已启用群:${groupLines}\n- 私人记忆:${userStatus}`) + return true + } + + async pollHistoryTask () { + try { + await collector.tickHistoryPolling() + } catch (err) { + logger.error('[Memory] scheduled history poll failed:', err) + } + return false + } +} diff --git a/apps/prompts.js b/apps/prompts.js deleted file mode 100644 index 5d4d173..0000000 --- a/apps/prompts.js +++ /dev/null @@ -1,470 +0,0 @@ -import plugin from '../../../lib/plugins/plugin.js' -import fs from 'fs' -import _ from 'lodash' -import { Config } from '../utils/config.js' -import { getMasterQQ, limitString, makeForwardMsg, maskQQ } from '../utils/common.js' -import { deleteOnePrompt, getPromptByName, readPrompts, saveOnePrompt } from '../utils/prompts.js' -import AzureTTS from "../utils/tts/microsoft-azure.js"; -export class help extends plugin { - constructor (e) { - super({ - name: 'ChatGPT-Plugin 人物设定', - dsc: '让你的聊天更加有趣!本插件支持丰富的人物设定拓展,可以在线浏览并导入喜欢的设定和上传自己的设定。让你的聊天更加生动有趣!', - event: 'message', - priority: 500, - rule: [ - { - reg: '^#(chatgpt|ChatGPT)设定列表$', - fnc: 'listPrompts', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)查看设定', - fnc: 'detailPrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)使用设定', - fnc: 'usePrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)添加设定', - fnc: 'addPrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)(删除|移除)设定', - fnc: 'removePrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)(上传|分享|共享)设定', - fnc: 'uploadPrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)(删除|取消|撤销)共享设定', - fnc: 'removeSharePrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)导入设定', - fnc: 'importPrompt', - permission: 'master' - }, - { - reg: '^#(chatgpt|ChatGPT)(在线)?(浏览|查找)设定', - fnc: 'browsePrompt' - }, - { - reg: '^#(chatgpt|ChatGPT)(在线)?预览设定详情', - fnc: 'detailCloudPrompt' - }, - { - reg: '^#(chatgpt|ChatGPT)设定帮助$', - fnc: 'helpPrompt', - permission: 'master' - } - // { - // reg: '^#(chatgpt|ChatGPT)(开启|关闭)洗脑$', - // fnc: 'setSydneyBrainWash', - // permission: 'master' - // }, - // { - // reg: '^#(chatgpt|ChatGPT)(设置)?洗脑强度', - // fnc: 'setSydneyBrainWashStrength', - // permission: 'master' - // }, - // { - // reg: '^#(chatgpt|ChatGPT)(设置)?洗脑名称', - // fnc: 'setSydneyBrainWashName', - // permission: 'master' - // } - ] - }) - } - - async listPrompts (e) { - let prompts = [] - let defaultPrompt = { - name: 'API默认', - content: Config.promptPrefixOverride - } - let defaultSydneyPrompt = { - name: 'Sydney默认', - content: Config.sydney - } - prompts.push(...[defaultPrompt, defaultSydneyPrompt]) - prompts.push(...readPrompts()) - console.log(prompts) - e.reply(await makeForwardMsg(e, prompts.map(p => `《${p.name}》\n${limitString(p.content, 500)}`), '设定列表')) - } - - async detailPrompt (e) { - let promptName = e.msg.replace(/^#(chatgpt|ChatGPT)查看设定/, '').trim() - let prompt = getPromptByName(promptName) - if (!prompt) { - if (promptName === 'API默认') { - prompt = { - name: 'API默认', - content: Config.promptPrefixOverride - } - } else if (promptName === 'Sydney默认') { - prompt = { - name: 'Sydney默认', - content: Config.sydney - } - } else { - await e.reply('没有这个设定', true) - return - } - } - await e.reply(`《${prompt.name}》\n${limitString(prompt.content, 500)}`, true) - } - - async usePrompt (e) { - let promptName = e.msg.replace(/^#(chatgpt|ChatGPT)使用设定/, '').trim() - let prompt = getPromptByName(promptName) - if (!prompt) { - console.log(promptName) - if (promptName === 'API默认') { - prompt = { - name: 'API默认', - content: Config.promptPrefixOverride - } - } else if (promptName === 'Sydney默认') { - prompt = { - name: 'Sydney默认', - content: Config.sydney - } - } else { - e.msg = `#chatgpt导入设定${promptName}` - await this.importPrompt(e) - prompt = getPromptByName(promptName) - if (!prompt) { - await e.reply('没有这个设定', true) - return - } - } - } - let use = await redis.get('CHATGPT:USE') || 'api' - if (use.toLowerCase() === 'bing') { - if (Config.toneStyle === 'Custom') { - use = 'Custom' - } - } - const keyMap = { - api: 'promptPrefixOverride', - Custom: 'sydney', - claude: 'slackClaudeGlobalPreset' - } - - if (keyMap[use]) { - if (Config.ttsMode === 'azure') { - Config[keyMap[use]] = prompt.content + '\n' + await AzureTTS.getEmotionPrompt(e) - logger.warn(Config[keyMap[use]]) - } else { - Config[keyMap[use]] = prompt.content - } - await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName) - await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true) - } else { - await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude`, true) - } - } - - async setSydneyBrainWashName (e) { - let name = e.msg.replace(/^#(chatgpt|ChatGPT)设置洗脑名称/, '') - if (name) { - Config.sydneyBrainWashName = name - await e.reply('操作成功', true) - } - } - - async setSydneyBrainWash (e) { - if (e.msg.indexOf('开启') > -1) { - Config.sydneyBrainWash = true - } else { - Config.sydneyBrainWash = false - } - await e.reply('操作成功', true) - } - - async setSydneyBrainWashStrength (e) { - let strength = e.msg.replace(/^#(chatgpt|ChatGPT)(设置)?洗脑强度/, '') - if (!strength) { - return - } - strength = parseInt(strength) - if (strength > 0) { - Config.sydneyBrainWashStrength = strength - await e.reply('操作成功', true) - } - } - - async removePrompt (e) { - let promptName = e.msg.replace(/^#(chatgpt|ChatGPT)(删除|移除)设定/, '') - if (!promptName) { - await e.reply('你要删除哪个设定呢?') - return - } - deleteOnePrompt(promptName) - await e.reply(`设定${promptName}已删除。`) - } - - async addPrompt (e) { - this.setContext('addPromptName') - await e.reply('请输入设定名称', true) - } - - async addPromptName () { - if (!this.e.msg) return - let name = this.e.msg - let prompt = getPromptByName(name) - if (prompt) { - await this.e.reply('【警告】该设定已存在,新增的内容将会覆盖之前的设定', true) - // this.finish('addPromptName') - // return - } - await redis.set('CHATGPT:ADD_PROMPT_NAME', name) - await this.reply('请输入设定内容', true) - this.finish('addPromptName') - this.setContext('addPromptContext') - } - - async addPromptContext () { - if (!this.e.msg) return - let content = this.e.msg - let name = await redis.get('CHATGPT:ADD_PROMPT_NAME') - saveOnePrompt(name, content) - await redis.del('CHATGPT:ADD_PROMPT_NAME') - await this.reply('设定添加成功', true) - this.finish('addPromptContext') - } - - async removeSharePrompt (e) { - let master = (await getMasterQQ())[0] - let name = e.msg.replace(/^#(chatgpt|ChatGPT)(删除|取消|撤销)共享设定/, '') - let response = await fetch(`https://chatgpt.roki.best/prompt?name=${name}&qq=${master || (Bot.uin + '')}`, { - method: 'DELETE', - headers: { - 'FROM-CHATGPT': 'ikechan8370' - } - }) - if (response.status === 200) { - let json = await response.json() - if (json.code === 200 && json.data) { - await e.reply('已从云端删除该设定') - } else { - await e.reply('操作失败:' + json.msg) - } - } else { - await e.reply('操作失败:' + await response.text()) - } - } - - async uploadPrompt (e) { - if (await redis.get('CHATGPT:UPLOAD_PROMPT')) { - await redis.del('CHATGPT:UPLOAD_PROMPT') - // await this.reply('本机器人存在其他人正在上传设定,请稍后') - // return - } - let use = await redis.get('CHATGPT:USE') || 'api' - if (use.toLowerCase() === 'bing') { - if (Config.toneStyle === 'Custom') { - use = 'Custom' - } - } - let currentUse = e.msg.replace(/^#(chatgpt|ChatGPT)(上传|分享|共享)设定/, '') - if (!currentUse) { - currentUse = await redis.get(`CHATGPT:PROMPT_USE_${use}`) - } - await this.reply(`即将向云端上传设定${currentUse},确定请回复确定,取消请回复取消,或者回复其他本地存在设定的名字`, true) - let extraData = { - currentUse, - use - } - await redis.set('CHATGPT:UPLOAD_PROMPT', JSON.stringify(extraData), 300) - this.setContext('uploadPromptConfirm') - } - - async uploadPromptConfirm () { - if (!this.e.msg) return - let name = this.e.msg.trim() - if (name === '取消') { - await redis.del('CHATGPT:UPLOAD_PROMPT') - await this.reply('已取消上传', true) - this.finish('uploadPromptConfirm') - return - } - let extraData = JSON.parse(await redis.get('CHATGPT:UPLOAD_PROMPT')) - if (name !== '确定') { - extraData.currentUse = name - await redis.set('CHATGPT:UPLOAD_PROMPT', JSON.stringify(extraData), 300) - } - if (!getPromptByName(extraData.currentUse)) { - await redis.del('CHATGPT:UPLOAD_PROMPT') - await this.reply(`设定${extraData.currentUse}不存在,已取消上传`, true) - this.finish('uploadPromptConfirm') - return - } - // await redis.set('CHATGPT:UPLOAD_PROMPT', JSON.stringify(extraData), 300) - await this.reply('请输入对该设定的描述或备注,便于其他人快速了解该设定', true) - this.finish('uploadPromptConfirm') - this.setContext('uploadPromptDescription') - } - - async uploadPromptDescription () { - if (!this.e.msg) return - let description = this.e.msg.trim() - if (description === '取消') { - // await redis.del('CHATGPT:UPLOAD_PROMPT') - await this.reply('已取消上传', true) - this.finish('uploadPromptDescription') - return - } - let extraData = JSON.parse(await redis.get('CHATGPT:UPLOAD_PROMPT')) - extraData.description = description - await redis.set('CHATGPT:UPLOAD_PROMPT', JSON.stringify(extraData), 300) - await this.reply('该设定是否是R18设定?请回复是或否', true) - this.finish('uploadPromptDescription') - this.setContext('uploadPromptR18') - } - - async uploadPromptR18 () { - let master = (await getMasterQQ())[0] - if (Config.debug) { - logger.mark('主人qq号:' + master) - } - if (this.e.msg.trim() === '取消') { - await redis.del('CHATGPT:UPLOAD_PROMPT') - await this.reply('已取消上传', true) - this.finish('uploadPromptR18') - return - } - if (!this.e.msg || (this.e.msg !== '是' && this.e.msg !== '否')) { - return - } - let r18 = this.e.msg.trim() === '是' - await this.reply('资料录入完成,正在上传中……', true) - let extraData = JSON.parse(await redis.get('CHATGPT:UPLOAD_PROMPT')) - const { currentUse, description } = extraData - const { content } = getPromptByName(currentUse) - let toUploadBody = { - title: currentUse, - prompt: content, - qq: master || (Bot.uin + ''), // 上传者设定为主人qq或机器人qq - use: extraData.use === 'Custom' ? 'Sydney' : 'ChatGPT', - r18, - description - } - logger.info(toUploadBody) - let response = await fetch('https://chatgpt.roki.best/prompt', { - method: 'POST', - body: JSON.stringify(toUploadBody), - headers: { - 'Content-Type': 'application/json', - 'FROM-CHATGPT': 'ikechan8370' - } - }) - await redis.del('CHATGPT:UPLOAD_PROMPT') - if (response.status === 200) { - response = await response.json() - if (response.data === true) { - await this.reply(`设定${currentUse}已上传,其他人可以通过#chatgpt导入设定${currentUse} 来快速导入该设定。感谢您的分享。`, true) - } else { - await this.reply(`设定上传失败,原因:${response.msg}`) - } - } else { - await this.reply(`设定上传失败: ${await response.text()}`) - } - this.finish('uploadPromptR18') - } - - async detailCloudPrompt (e) { - let name = e.msg.replace(/^#(chatgpt|ChatGPT)(在线)?预览设定详情/, '') - let response = await fetch('https://chatgpt.roki.best/prompt?name=' + name, { - method: 'GET', - headers: { - 'FROM-CHATGPT': 'ikechan8370' - } - }) - if (response.status === 200) { - let r = await response.json() - if (r.code === 200) { - const { prompt, title, description, r18, qq, use } = r.data - await e.reply(`设定名称:【${title}】\n贡献者:${qq}\n作者备注:${description}\n是否r18:${r18 ? '是' : '否'}\n建议使用场景:${use}\n设定内容预览:${limitString(prompt, 500)}`) - } else { - await e.reply('获取设定详情失败:' + r.msg) - } - } else { - await this.reply('获取设定详情失败:' + await response.text()) - } - } - - async browsePrompt (e) { - let search = e.msg.replace(/^#(chatgpt|ChatGPT)(在线)?(浏览|查找)设定/, '') - let split = search.split('页码') - let page = 1 - if (split.length > 1) { - search = split[0] - page = parseInt(split[1]) - } - let response = await fetch('https://chatgpt.roki.best/prompt/list?search=' + search + `&page=${page - 1}`, { - method: 'GET', - headers: { - 'FROM-CHATGPT': 'ikechan8370' - } - }) - if (response.status === 200) { - const { totalElements, content, pageable } = (await response.json()).data - let output = '| 【设定名称】 | 上传者QQ | 上传时间 | 是否R18 | 使用场景 |\n' - output += '----------------------------------------------------------------------------------------\n' - content.forEach(c => { - output += `| 【${c.title}】 | ${maskQQ(c.qq)} | ${c.createTime} | ${c.r18} | ${c.use}|\n` - }) - output += '**************************************************************************\n' - output += ` 当前为第${pageable.pageNumber + 1}页,共${totalElements}个设定\n` - output += ` 您可以使用#chatgpt浏览设定页码${pageable.pageNumber + 2}跳转到第${pageable.pageNumber + 2}页\n` - await this.reply(output) - } else { - await this.reply('查询失败:' + await response.text()) - } - } - - async importPrompt (e) { - let promptName = e.msg.replace(/^#(chatgpt|ChatGPT)导入设定/, '') - if (!promptName) { - await e.reply('设定名字呢?', true) - return true - } - let response = await fetch('https://chatgpt.roki.best/prompt?name=' + promptName, { - method: 'GET', - headers: { - 'FROM-CHATGPT': 'ikechan8370' - } - }) - if (response.status === 200) { - let r = await response.json() - if (r.code === 200) { - if (!r.data) { - await e.reply('没有这个设定', true) - return true - } - const { prompt, title } = r.data - saveOnePrompt(title, prompt) - e.reply(`导入成功。您现在可以使用 #chatgpt使用设定${title} 来体验这个设定了。`) - } else { - await e.reply('导入失败:' + r.msg) - } - } else { - await this.reply('导入失败:' + await response.text()) - } - // await this.reply('敬请期待', true) - } - - async helpPrompt () { - await this.reply('设定目录为/plugins/chatgpt-plugin/prompts,将会读取该目录下的所有[设定名].txt文件作为设定列表', true) - } -} diff --git a/apps/update.js b/apps/update.js index be2fedb..ed91597 100644 --- a/apps/update.js +++ b/apps/update.js @@ -1,316 +1,350 @@ -// modified from StarRail-plugin | 已经过StarRail-plugin作者本人同意 -import plugin from '../../../lib/plugins/plugin.js' -import { createRequire } from 'module' -import _ from 'lodash' -import { Restart } from '../../other/restart.js' -import fs from 'fs' -import {} from "../utils/common.js"; - -const _path = process.cwd() -const require = createRequire(import.meta.url) -const { exec, execSync } = require('child_process') - -const checkAuth = async function (e) { - if (!e.isMaster) { - e.reply(`只有主人才能命令ChatGPT哦~(*/ω\*)`) - return false - } - return true -} - -// 是否在更新中 -let uping = false - -/** - * 处理插件更新 - */ -export class Update extends plugin { - constructor () { - super({ - name: 'chatgpt更新插件', - event: 'message', - priority: 1000, - rule: [ - { - reg: '^#?(chatgpt|柴特寄批踢|GPT|ChatGPT|柴特鸡批踢|Chat|CHAT|CHATGPT|柴特|ChatGPT-Plugin|ChatGPT-plugin|chatgpt-plugin)(插件)?(强制)?更新$', - fnc: 'update' - } - ] - }) - } - - - - - /** - * rule - 更新chatgpt插件 - * @returns - */ - async update () { - if (!this.e.isMaster) return false - - /** 检查是否正在更新中 */ - if (uping) { - await this.reply('已有命令更新中..请勿重复操作') - return - } - - /** 检查git安装 */ - if (!(await this.checkGit())) return - - const isForce = this.e.msg.includes('强制') - - /** 执行更新 */ - await this.runUpdate(isForce) - - /** 是否需要重启 */ - if (this.isUp) { - // await this.reply("更新完毕,请重启云崽后生效") - setTimeout(() => this.restart(), 2000) - } - } - - restart () { - new Restart(this.e).restart() - } - - /** - * chatgpt插件更新函数 - * @param {boolean} isForce 是否为强制更新 - * @returns - */ - async runUpdate (isForce) { - let command = 'git -C ./plugins/chatgpt-plugin/ pull --no-rebase' - if (isForce) { - command = `git -C ./plugins/chatgpt-plugin/ checkout . && ${command}` - this.e.reply('正在执行强制更新操作,请稍等') - } else { - this.e.reply('正在执行更新操作,请稍等') - } - /** 获取上次提交的commitId,用于获取日志时判断新增的更新日志 */ - this.oldCommitId = await this.getcommitId('chatgpt-plugin') - uping = true - let ret = await this.execSync(command) - uping = false - - if (ret.error) { - logger.mark(`${this.e.logFnc} 更新失败:chatgpt-plugin`) - this.gitErr(ret.error, ret.stdout) - return false - } - - /** 获取插件提交的最新时间 */ - let time = await this.getTime('chatgpt-plugin') - - if (/(Already up[ -]to[ -]date|已经是最新的)/.test(ret.stdout)) { - await this.reply(`chatgpt-plugin已经是最新版本\n最后更新时间:${time}`) - } else { - await this.reply(`chatgpt-plugin\n最后更新时间:${time}`) - this.isUp = true - /** 获取chatgpt组件的更新日志 */ - let log = await this.getLog('chatgpt-plugin') - await this.reply(log) - } - - logger.mark(`${this.e.logFnc} 最后更新时间:${time}`) - - return true - } - - /** - * 获取chatgpt插件的更新日志 - * @param {string} plugin 插件名称 - * @returns - */ - async getLog (plugin = '') { - let cm = `cd ./plugins/${plugin}/ && git log -20 --oneline --pretty=format:"%h||[%cd] %s" --date=format:"%m-%d %H:%M"` - - let logAll - try { - logAll = await execSync(cm, { encoding: 'utf-8' }) - } catch (error) { - logger.error(error.toString()) - this.reply(error.toString()) - } - - if (!logAll) return false - - logAll = logAll.split('\n') - - let log = [] - for (let str of logAll) { - str = str.split('||') - if (str[0] == this.oldCommitId) break - if (str[1].includes('Merge branch')) continue - log.push(str[1]) - } - let line = log.length - log = log.join('\n\n') - - if (log.length <= 0) return '' - - let end = '' - end = - '更多详细信息,请前往github查看\nhttps://github.com/ikechan8370/chatgpt-plugin' - - log = await this.makeForwardMsg(`chatgpt-plugin更新日志,共${line}条`, log, end) - - return log - } - - /** - * 获取上次提交的commitId - * @param {string} plugin 插件名称 - * @returns - */ - async getcommitId (plugin = '') { - let cm = `git -C ./plugins/${plugin}/ rev-parse --short HEAD` - - let commitId = await execSync(cm, { encoding: 'utf-8' }) - commitId = _.trim(commitId) - - return commitId - } - - /** - * 获取本次更新插件的最后一次提交时间 - * @param {string} plugin 插件名称 - * @returns - */ - async getTime (plugin = '') { - let cm = `cd ./plugins/${plugin}/ && git log -1 --oneline --pretty=format:"%cd" --date=format:"%m-%d %H:%M"` - - let time = '' - try { - time = await execSync(cm, { encoding: 'utf-8' }) - time = _.trim(time) - } catch (error) { - logger.error(error.toString()) - time = '获取时间失败' - } - return time - } - - /** - * 制作转发消息 - * @param {string} title 标题 - 首条消息 - * @param {string} msg 日志信息 - * @param {string} end 最后一条信息 - * @returns - */ - async makeForwardMsg (title, msg, end) { - let nickname = (this.e.bot ?? Bot).nickname - if (this.e.isGroup) { - let info = await (this.e.bot ?? Bot).getGroupMemberInfo(this.e.group_id, (this.e.bot ?? Bot).uin) - nickname = info.card || info.nickname - } - let userInfo = { - user_id: (this.e.bot ?? Bot).uin, - nickname - } - - let forwardMsg = [ - { - ...userInfo, - message: title - }, - { - ...userInfo, - message: msg - } - ] - - if (end) { - forwardMsg.push({ - ...userInfo, - message: end - }) - } - - /** 制作转发内容 */ - if (this.e.isGroup) { - forwardMsg = await this.e.group.makeForwardMsg(forwardMsg) - } else { - forwardMsg = await this.e.friend.makeForwardMsg(forwardMsg) - } - - /** 处理描述 */ - forwardMsg.data = forwardMsg.data - .replace(/\n/g, '') - .replace(/(.+?)<\/title>/g, '___') - .replace(/___+/, `<title color="#777777" size="26">${title}`) - - return forwardMsg - } - - /** - * 处理更新失败的相关函数 - * @param {string} err - * @param {string} stdout - * @returns - */ - async gitErr (err, stdout) { - let msg = '更新失败!' - let errMsg = err.toString() - stdout = stdout.toString() - - if (errMsg.includes('Timed out')) { - let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') - await this.reply(msg + `\n连接超时:${remote}`) - return - } - - if (/Failed to connect|unable to access/g.test(errMsg)) { - let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') - await this.reply(msg + `\n连接失败:${remote}`) - return - } - - if (errMsg.includes('be overwritten by merge')) { - await this.reply( - msg + - `存在冲突:\n${errMsg}\n` + - '请解决冲突后再更新,或者执行#强制更新,放弃本地修改' - ) - return - } - - if (stdout.includes('CONFLICT')) { - await this.reply([ - msg + '存在冲突\n', - errMsg, - stdout, - '\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改' - ]) - return - } - - await this.reply([errMsg, stdout]) - } - - /** - * 异步执行git相关命令 - * @param {string} cmd git命令 - * @returns - */ - async execSync (cmd) { - return new Promise((resolve, reject) => { - exec(cmd, { windowsHide: true }, (error, stdout, stderr) => { - resolve({ error, stdout, stderr }) - }) - }) - } - - /** - * 检查git是否安装 - * @returns - */ - async checkGit () { - let ret = await execSync('git --version', { encoding: 'utf-8' }) - if (!ret || !ret.includes('git version')) { - await this.reply('请先安装git') - return false - } - return true - } -} \ No newline at end of file +// modified from StarRail-plugin | 已经过StarRail-plugin作者本人同意 +import plugin from '../../../lib/plugins/plugin.js' +import { createRequire } from 'module' +import _ from 'lodash' +import { Restart } from '../../other/restart.js' +import ChatGPTConfig from '../config/config.js' + +const require = createRequire(import.meta.url) +const { exec, execSync } = require('child_process') + +// 是否在更新中 +let uping = false + +/** + * 处理插件更新 + */ +export class Update extends plugin { + constructor () { + const cmdPrefix = ChatGPTConfig.basic.commandPrefix + super({ + name: 'chatgpt更新插件', + event: 'message', + priority: 1000, + rule: [ + { + reg: `^${cmdPrefix}?(强制)?更新$`, + fnc: 'update' + } + ] + }) + } + + /** + * rule - 更新chatgpt插件 + * @returns + */ + async update () { + if (!this.e.isMaster) return false + + /** 检查是否正在更新中 */ + if (uping) { + await this.reply('已有命令更新中..请勿重复操作') + return + } + + /** 检查git安装 */ + if (!(await this.checkGit())) return + + const isForce = this.e.msg.includes('强制') + + /** 执行更新 */ + await this.runUpdate(isForce) + + /** 是否需要重启 */ + if (this.isUp) { + // await this.reply("更新完毕,请重启云崽后生效") + setTimeout(() => this.restart(), 2000) + } + } + + restart () { + new Restart(this.e).restart() + } + + /** + * chatgpt插件更新函数 + * @param {boolean} isForce 是否为强制更新 + * @returns + */ + async runUpdate (isForce) { + try { + let command = 'git -C ./plugins/chatgpt-plugin/ pull --no-rebase' + if (isForce) { + command = `git -C ./plugins/chatgpt-plugin/ checkout . && ${command}` + this.e.reply('正在执行强制更新操作,请稍等') + } else { + this.e.reply('正在执行更新操作,请稍等') + } + /** 获取上次提交的commitId,用于获取日志时判断新增的更新日志 */ + this.oldCommitId = await this.getcommitId('chatgpt-plugin') + uping = true + let ret = await this.execSync(command) + + if (ret.error) { + logger.mark(`${this.e.logFnc} 更新失败:chatgpt-plugin`) + this.gitErr(ret.error, ret.stdout) + return false + } + + // Check if pnpm is available + let packageManager = await this.checkPnpm() + await this.reply(`正在使用 ${packageManager} 更新 chaite 依赖...`) + let npmRet = await this.execSync(`cd ./plugins/chatgpt-plugin/ && ${packageManager} update chaite`) + logger.info(JSON.stringify(npmRet)) + if (npmRet.error) { + logger.mark(`${this.e.logFnc} 更新失败:chaite 依赖`) + await this.reply(`chaite 依赖更新失败:\n${npmRet.error.toString()}`) + return false + } + /** 获取插件提交的最新时间 */ + let time = await this.getTime('chatgpt-plugin') + + if (/(Already up[ -]to[ -]date|已经是最新的)/.test(ret.stdout)) { + await this.reply(`chatgpt-plugin已经是最新版本\n最后更新时间:${time}`) + } else { + let updateMsg = `chatgpt-plugin\n最后更新时间:${time}` + + // Add npm update information if available + if (npmRet.stdout.includes('chaite')) { + updateMsg += `\n已使用${packageManager}更新chaite依赖` + } + + await this.reply(updateMsg) + this.isUp = true + /** 获取chatgpt组件的更新日志 */ + let log = await this.getLog('chatgpt-plugin') + await this.reply(log) + } + + logger.mark(`${this.e.logFnc} 最后更新时间:${time}`) + + return true + } catch (err) { + logger.error(err) + await this.reply(`更新失败:\n${err.toString()}`) + return false + } finally { + uping = false + } + } + + /** + * 获取chatgpt插件的更新日志 + * @param {string} plugin 插件名称 + * @returns + */ + async getLog (plugin = '') { + let cm = `cd ./plugins/${plugin}/ && git log -20 --oneline --pretty=format:"%h||[%cd] %s" --date=format:"%m-%d %H:%M"` + + let logAll + try { + logAll = await execSync(cm, { encoding: 'utf-8' }) + } catch (error) { + logger.error(error.toString()) + this.reply(error.toString()) + } + + if (!logAll) return false + + logAll = logAll.split('\n') + + let log = [] + for (let str of logAll) { + str = str.split('||') + if (str[0] == this.oldCommitId) break + if (str[1].includes('Merge branch')) continue + log.push(str[1]) + } + let line = log.length + log = log.join('\n\n') + + if (log.length <= 0) return '' + + let end = '' + end = + '更多详细信息,请前往github查看\nhttps://github.com/ikechan8370/chatgpt-plugin' + + log = await this.makeForwardMsg(`chatgpt-plugin更新日志,共${line}条`, log, end) + + return log + } + + /** + * 获取上次提交的commitId + * @param {string} plugin 插件名称 + * @returns + */ + async getcommitId (plugin = '') { + let cm = `git -C ./plugins/${plugin}/ rev-parse --short HEAD` + + let commitId = await execSync(cm, { encoding: 'utf-8' }) + commitId = _.trim(commitId) + + return commitId + } + + /** + * 获取本次更新插件的最后一次提交时间 + * @param {string} plugin 插件名称 + * @returns + */ + async getTime (plugin = '') { + let cm = `cd ./plugins/${plugin}/ && git log -1 --oneline --pretty=format:"%cd" --date=format:"%m-%d %H:%M"` + + let time = '' + try { + time = await execSync(cm, { encoding: 'utf-8' }) + time = _.trim(time) + } catch (error) { + logger.error(error.toString()) + time = '获取时间失败' + } + return time + } + + /** + * 制作转发消息 + * @param {string} title 标题 - 首条消息 + * @param {string} msg 日志信息 + * @param {string} end 最后一条信息 + * @returns + */ + async makeForwardMsg (title, msg, end) { + const _bot = this.e.bot ?? Bot + let nickname = _bot.nickname + if (this.e.isGroup) { + let info = await _bot?.pickMember?.(this.e.group_id, _bot.uin) || await _bot?.getGroupMemberInfo?.(this.e.group_id, _bot.uin) + nickname = info.card || info.nickname + } + let userInfo = { + user_id: _bot.uin, + nickname + } + + let forwardMsg = [ + { + ...userInfo, + message: title + }, + { + ...userInfo, + message: msg + } + ] + + if (end) { + forwardMsg.push({ + ...userInfo, + message: end + }) + } + + /** 制作转发内容 */ + if (this.e.group?.makeForwardMsg) { + forwardMsg = await this.e.group.makeForwardMsg(forwardMsg) + } else if (this.e?.friend?.makeForwardMsg) { + forwardMsg = await this.e.friend.makeForwardMsg(forwardMsg) + } else { + return msg.join('\n') + } + + let dec = 'chatgpt-plugin 更新日志' + /** 处理描述 */ + if (typeof (forwardMsg.data) === 'object') { + let detail = forwardMsg.data?.meta?.detail + if (detail) { + detail.news = [{ text: dec }] + } + } else { + forwardMsg.data = forwardMsg.data + .replace(/\n/g, '') + .replace(/(.+?)<\/title>/g, '___') + .replace(/___+/, `<title color="#777777" size="26">${dec}`) + } + + return forwardMsg + } + + /** + * 检查是否安装pnpm + * @returns {Promise} 返回 'pnpm' 或 'npm' + */ + async checkPnpm () { + let npm = 'npm' + let ret = await this.execSync('pnpm -v') + if (ret.stdout) npm = 'pnpm' + return npm + } + + /** + * 处理更新失败的相关函数 + * @param {string} err + * @param {string} stdout + * @returns + */ + async gitErr (err, stdout) { + let msg = '更新失败!' + let errMsg = err.toString() + stdout = stdout.toString() + + if (errMsg.includes('Timed out')) { + let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') + await this.reply(msg + `\n连接超时:${remote}`) + return + } + + if (/Failed to connect|unable to access/g.test(errMsg)) { + let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') + await this.reply(msg + `\n连接失败:${remote}`) + return + } + + if (errMsg.includes('be overwritten by merge')) { + await this.reply( + msg + + `存在冲突:\n${errMsg}\n` + + '请解决冲突后再更新,或者执行#强制更新,放弃本地修改' + ) + return + } + + if (stdout.includes('CONFLICT')) { + await this.reply([ + msg + '存在冲突\n', + errMsg, + stdout, + '\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改' + ]) + return + } + + await this.reply([errMsg, stdout]) + } + + /** + * 异步执行git相关命令 + * @param {string} cmd git命令 + * @returns + */ + async execSync (cmd) { + return new Promise((resolve, reject) => { + exec(cmd, { windowsHide: true }, (error, stdout, stderr) => { + resolve({ error, stdout, stderr }) + }) + }) + } + + /** + * 检查git是否安装 + * @returns + */ + async checkGit () { + let ret = await execSync('git --version', { encoding: 'utf-8' }) + if (!ret || !ret.includes('git version')) { + await this.reply('请先安装git') + return false + } + return true + } +} diff --git a/config/config.example.json b/config/config.example.json deleted file mode 100644 index f5ee047..0000000 --- a/config/config.example.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "blockWords": ["屏蔽词1", "屏蔽词b"], - "promptBlockWords": ["屏蔽词1", "屏蔽词b"], - "imgOcr": true, - "defaultUsePicture": false, - "defaultUseTTS": false, - "defaultTTSRole": "纳西妲", - "alsoSendText": false, - "autoUsePicture": true, - "autoUsePictureThreshold": 1200, - "ttsAutoFallbackThreshold": 99, - "conversationPreserveTime": 0, - "toggleMode": "at", - "quoteReply": true, - "showQRCode": true, - "cacheUrl": "https://content.alcedogroup.com", - "cacheEntry": false, - "apiKey": "", - "openAiBaseUrl": "", - "openAiForceUseReverse": false, - "drawCD": 30, - "model": "", - "temperature": 0.8, - "toneStyle": "balanced", - "sydney": "", - "sydneyReverseProxy": "", - "sydneyForceUseReverse": false, - "sydneyWebsocketUseProxy": false, - "sydneyBrainWash": true, - "sydneyBrainWashStrength": 15, - "sydneyBrainWashName": "Sydney", - "sydneyMood": false, - "enableSuggestedResponses": false, - "api": "", - "apiBaseUrl": "", - "apiForceUseReverse": false, - "plus": false, - "useGPT4": false, - "promptPrefixOverride": "Your answer shouldn\"t be too verbose. Prefer to answer in Chinese.", - "assistantLabel": "ChatGPT", - "username": "", - "password": "", - "UA": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", - "headless": false, - "chromePath": "", - "2captchaToken": "", - "proxy": "", - "debug": true, - "defaultTimeoutMs": 120000, - "chromeTimeoutMS": 120000, - "sydneyFirstMessageTimeout": 40000, - "ttsSpace": "", - "huggingFaceReverseProxy": "", - "noiseScale": 0.6, - "noiseScaleW": 0.668, - "lengthScale": 1.2, - "initiativeChatGroups": [], - "enableDraw": true, - "helloPrompt": "写一段话让大家来找我聊天。类似于“有人找我聊天吗?“这种风格,轻松随意一点控制在20个字以内", - "helloInterval": 3, - "helloProbability": 50, - "chatglmBaseUrl": "http://localhost:8080", - "allowOtherMode": true, - "sydneyContext": "", - "emojiBaseURL": "https://www.gstatic.com/android/keyboard/emojikitchen", - "enableGroupContext": false, - "groupContextLength": 50, - "enableRobotAt": true, - "maxNumUserMessagesInConversation": 20, - "sydneyApologyIgnored": true, - "enforceMaster": false, - "enablePrivateChat": false, - "whitelist": [], - "blacklist": [], - "ttsRegex": "/匹配规则/匹配模式", - "baiduTranslateAppId": "", - "baiduTranslateSecret": "", - "azureTTSSpeaker": "zh-CN-XiaochenNeural", - "azureTTSEmotion": false, - "enhanceAzureTTSEmotion": false, - "autoJapanese": false -} \ No newline at end of file diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..e93bec1 --- /dev/null +++ b/config/config.js @@ -0,0 +1,587 @@ +import fs from 'fs' +import path from 'path' +import yaml from 'js-yaml' + +class ChatGPTConfig { + /** + * 版本号 + * @type {string} + */ + version = '3.0.0' + + /** + * 基本配置 + * @type {{ + * toggleMode: 'at' | 'prefix', + * debug: boolean, + * }} + */ + basic = { + // 触发方式,at触发或者前缀触发 + toggleMode: 'at', + // 触发前缀,仅在前缀触发时有效 + togglePrefix: '#chat', + // 是否开启调试模式 + debug: false, + // 一般命令的开头 + commandPrefix: '#chatgpt' + } + + /** + * 伪人模式,基于框架实现,因此机器人开启前缀后依然需要带上前缀。 + * @type {{ + * enable: boolean, + * hit: string[], + * probability: number, + * defaultPreset: string, + * presetPrefix?: string, + * presetMap: Array<{ + * keywords: string[], + * presetId: string, + * priority: number, + * recall?: boolean + * }>, + * maxTokens: number, + * temperature: number, + * sendReasoning: boolean + * }} + * }} + */ + bym = { + // 开关 + enable: false, + // 伪人必定触发词 + hit: ['bym'], + // 不包含伪人必定触发词时的概率 + probability: 0.02, + // 伪人模式的默认预设 + defaultPreset: '', + // 伪人模式的预设前缀,会加在在所有其他预设前。例如此处可以用于配置通用的伪人发言风格(随意、模仿群友等),presetMap中专心配置角色设定即可 + presetPrefix: '', + // 包含关键词与预设的对应关系。包含特定触发词使用特定的预设,按照优先级排序 + presetMap: [], + // 如果大于0,会覆盖preset中的maxToken,用于控制伪人模式发言长度 + maxTokens: 0, + // 如果大于等于0,会覆盖preset中的temperature,用于控制伪人模式发言随机性 + temperature: -1, + // 是否发送思考内容 + sendReasoning: false + } + + /** + * 模型和对话相关配置 + * @type {{ + * defaultModel: string, + * embeddingModel: string, + * defaultChatPresetId: string, + * enableCustomPreset: boolean, + * customPresetUserWhiteList: string[], + * customPresetUserBlackList: string[], + * promptBlockWords: string[], + * responseBlockWords: string[], + * blockStrategy: 'full' | 'mask', + * blockWordMask: string, + * enableGroupContext: boolean, + * groupContextLength: number, + * groupContextTemplatePrefix: string, + * groupContextTemplateMessage: string, + * groupContextTemplateSuffix: string + * }} + */ + llm = { + // 默认模型,初始化构建预设使用 + defaultModel: '', + // 嵌入模型 + embeddingModel: 'gemini-embedding-exp-03-07', + // 嵌入结果维度,0表示自动 + dimensions: 0, + // 默认对话预设ID + defaultChatPresetId: '', + // 是否启用允许其他人切换预设 + enableCustomPreset: false, + // 允许切换预设的用户白名单 + customPresetUserWhiteList: [], + // 禁止切换预设的用户黑名单 + customPresetUserBlackList: [], + // 用户对话屏蔽词 + promptBlockWords: [], + // 机器人回复屏蔽词 + responseBlockWords: [], + // 触发屏蔽词的策略,完全屏蔽或仅屏蔽关键词 + blockStrategy: 'full', + // 如果blockStrategy为mask,屏蔽词的替换字符 + blockWordMask: '***', + // 是否开启群组上下文 + enableGroupContext: false, + // 群组上下文长度 + groupContextLength: 20, + // 用于组装群聊上下文提示词的模板前缀 + groupContextTemplatePrefix: '\n' + + // eslint-disable-next-line no-template-curly-in-string + 'You are a member of a chat group, whose name is ${group.name}, and the group id is ${group.id}.\n' + + 'Latest several messages in the group chat:\n' + + '| 群名片 | 昵称 | qq号 | 群角色 | 群头衔 | 时间 | messageId | 消息内容 |\n' + + '|---|---|---|---|---|---|---|---|', + // 用于组装群聊上下文提示词的模板内容部分,每一条消息作为message,仿照示例填写 + // eslint-disable-next-line no-template-curly-in-string + groupContextTemplateMessage: '| ${message.sender.card} | ${message.sender.nickname} | ${message.sender.user_id} | ${message.sender.role} | ${message.sender.title} | ${message.time} | ${message.messageId} | ${message.raw_message} |', + // 用于组装群聊上下文提示词的模板后缀 + groupContextTemplateSuffix: '\n' + + } + + /** + * 管理相关配置 + * @type {{ + * blackGroups: number[], + * whiteGroups: number[], + * blackUsers: string[], + * whiteUsers: string[], + * defaultRateLimit: number + * }} + */ + management = { + blackGroups: [], + whiteGroups: [], + blackUsers: [], + whiteUsers: [], + // 默认对话速率限制,0表示不限制,数字表示每分钟最多对话次数 + defaultRateLimit: 0 + } + + /** + * chaite相关配置 + * @type { + * { dataDir: string, + * processorsDirPath: string, + * toolsDirPath: string, + * cloudBaseUrl: string, + * cloudApiKey: string, + * authKey: string, + * host: string, + * port: number}} + */ + chaite = { + // 数据目录,相对于插件下 + dataDir: 'data', + // 处理器目录,相对于插件下 + processorsDirPath: 'utils/processors', + // 触发器目录,相对于插件目录下 + triggersDir: 'utils/triggers', + // 工具目录,相对于插件目录下 + toolsDirPath: 'utils/tools', + // 云端API url + cloudBaseUrl: 'https://api.chaite.cloud', + // 云端API Key + cloudApiKey: '', + // jwt key,非必要勿修改,修改需重启 + authKey: '', + // 管理面板监听地址 + host: '0.0.0.0', + // 管理面板监听端口 + port: 48370, + // 存储实现 sqlite lowdb + storage: 'sqlite' + } + + /** + * 记忆系统配置 + * @type {{ + * database: string, + * vectorDimensions: number, + * group: { + * enable: boolean, + * enabledGroups: string[], + * extractionModel: string, + * extractionPresetId: string, + * minMessageCount: number, + * maxMessageWindow: number, + * retrievalMode: 'vector' | 'keyword' | 'hybrid', + * hybridPrefer: 'vector-first' | 'keyword-first', + * historyPollInterval: number, + * historyBatchSize: number, + * promptHeader: string, + * promptItemTemplate: string, + * promptFooter: string, + * extractionSystemPrompt: string, + * extractionUserPrompt: string, + * vectorMaxDistance: number, + * textMaxBm25Score: number, + * maxFactsPerInjection: number, + * minImportanceForInjection: number + * }, + * user: { + * enable: boolean, + * whitelist: string[], + * blacklist: string[], + * extractionModel: string, + * extractionPresetId: string, + * maxItemsPerInjection: number, + * maxRelevantItemsPerQuery: number, + * minImportanceForInjection: number, + * promptHeader: string, + * promptItemTemplate: string, + * promptFooter: string, + * extractionSystemPrompt: string, + * extractionUserPrompt: string + * }, + * extensions: { + * simple: { + * enable: boolean, + * libraryPath: string, + * dictPath: string, + * useJieba: boolean + * } + * } + * }} + */ + memory = { + database: 'data/memory.db', + vectorDimensions: 1536, + group: { + enable: false, + enabledGroups: [], + extractionModel: '', + extractionPresetId: '', + minMessageCount: 80, + maxMessageWindow: 300, + retrievalMode: 'hybrid', + hybridPrefer: 'vector-first', + historyPollInterval: 300, + historyBatchSize: 120, + promptHeader: '# 以下是一些该群聊中可能相关的事实,你可以参考,但不要主动透露这些事实。', + promptItemTemplate: '- ${fact}${topicSuffix}${timeSuffix}', + promptFooter: '', + extractionSystemPrompt: `You are a knowledge extraction assistant that specialises in summarising long-term facts from group chat transcripts. +Read the provided conversation and identify statements that should be stored as long-term knowledge for the group. +Return a JSON array. Each element must contain: +{ + "fact": 事实内容,必须完整包含事件的各个要素而不能是简单的短语(比如谁参与了事件、做了什么事情、背景时间是什么)(同一件事情尽可能整合为同一条而非拆分,以便利于检索), + "topic": 主题关键词,字符串,如 "活动"、"成员信息", + "importance": 一个介于0和1之间的小数,数值越大表示越重要, + "source_message_ids": 原始消息ID数组, + "source_messages": 对应原始消息的简要摘录或合并文本, + "involved_users": 出现或相关的用户ID数组 +} +Only include meaningful, verifiable group-specific information that is useful for future conversations. Do not record incomplete information. Do not include general knowledge or unrelated facts. Do not wrap the JSON array in code fences.`, + extractionUserPrompt: `以下是群聊中的一些消息,请根据系统说明提取值得长期记忆的事实,以JSON数组形式返回,不要输出额外说明。 + +\${messages}`, + vectorMaxDistance: 0, + textMaxBm25Score: 0, + maxFactsPerInjection: 5, + minImportanceForInjection: 0.3 + }, + user: { + enable: false, + whitelist: [], + blacklist: [], + extractionModel: '', + extractionPresetId: '', + maxItemsPerInjection: 5, + maxRelevantItemsPerQuery: 3, + minImportanceForInjection: 0, + promptHeader: '# 用户画像', + promptItemTemplate: '- ${value}${timeSuffix}', + promptFooter: '', + extractionSystemPrompt: `You are an assistant that extracts long-term personal preferences or persona details about a user. +Given a conversation snippet between the user and the bot, identify durable information such as preferences, nicknames, roles, speaking style, habits, or other facts that remain valid over time. +Return a JSON array of **strings**, and nothing else, without any other characters including \`\`\` or \`\`\`json. Each string must be a short sentence (in the same language as the conversation) describing one piece of long-term memory. Do not include keys, JSON objects, or additional metadata. Ignore temporary topics or uncertain information.`, + extractionUserPrompt: `下面是用户与机器人的对话,请根据系统提示提取可长期记忆的个人信息。 + +\${messages}` + }, + extensions: { + simple: { + enable: false, + libraryPath: '', + dictPath: '', + useJieba: false + } + } + } + + constructor () { + this.version = '3.0.0' + this.watcher = null + this.configPath = '' + } + + /** + * Start config file sync + * call once! + * @param {string} configDir Directory containing config files + */ + startSync (configDir) { + // 配置路径设置 + const jsonPath = path.join(configDir, 'config.json') + const yamlPath = path.join(configDir, 'config.yaml') + + if (fs.existsSync(jsonPath)) { + this.configPath = jsonPath + } else if (fs.existsSync(yamlPath)) { + this.configPath = yamlPath + } else { + this.configPath = jsonPath + this.saveToFile() + } + + // 加载初始配置 + this.loadFromFile() + + // 文件变更标志和保存定时器 + this._saveOrigin = null + this._saveTimer = null + + // 监听文件变化 + this.watcher = fs.watchFile(this.configPath, (curr, prev) => { + if (curr.mtime !== prev.mtime && this._saveOrigin !== 'code') { + this.loadFromFile() + } + }) + + // 处理所有嵌套对象 + return this._createProxyRecursively(this) + } + + // 递归创建代理 + _createProxyRecursively (obj, path = []) { + if (obj === null || typeof obj !== 'object' || obj instanceof Date) { + return obj + } + + // 处理数组和对象 + if (Array.isArray(obj)) { + // 创建一个新数组,递归地处理每个元素 + const proxiedArray = [...obj].map((item, index) => + this._createProxyRecursively(item, [...path, index]) + ) + + // 代理数组,捕获数组方法调用 + return new Proxy(proxiedArray, { + set: (target, prop, value) => { + // 处理数字属性(数组索引)和数组长度 + if (typeof prop !== 'symbol' && + ((!isNaN(prop) && prop !== 'length') || + prop === 'length')) { + // 直接设置值 + target[prop] = value + + // 触发保存 + this._triggerSave('array') + } else { + target[prop] = value + } + return true + }, + + // 拦截数组方法调用 + get: (target, prop) => { + const val = target[prop] + + // 处理数组修改方法 + if (typeof val === 'function' && + ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].includes(prop)) { + return function (...args) { + const result = Array.prototype[prop].apply(target, args) + + // 方法调用后触发保存 + this._triggerSave('array-method') + return result + }.bind(this) + } + + return val + } + }) + } else { + // 对普通对象的处理 + const proxiedObj = {} + + // 处理所有属性 + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + // 跳过内部属性 + if (key === 'watcher' || key === 'configPath' || + key.startsWith('_save') || key === '_isSaving') { + proxiedObj[key] = obj[key] + } else { + // 递归处理嵌套对象 + proxiedObj[key] = this._createProxyRecursively( + obj[key], [...path, key] + ) + } + } + } + + // 创建对象的代理 + return new Proxy(proxiedObj, { + set: (target, prop, value) => { + // 跳过内部属性的处理 + if (prop === 'watcher' || prop === 'configPath' || + prop.startsWith('_save') || prop === '_isSaving') { + target[prop] = value + return true + } + + // 设置新值,如果是对象则递归创建代理 + if (value !== null && typeof value === 'object') { + target[prop] = this._createProxyRecursively( + value, [...path, prop] + ) + } else { + target[prop] = value + } + + // 触发保存 + this._triggerSave('object') + return true + } + }) + } + } + + loadFromFile () { + try { + if (!fs.existsSync(this.configPath)) { + // 如果文件不存在,直接返回 + return + } + + const content = fs.readFileSync(this.configPath, 'utf8') + const loadedConfig = this.configPath.endsWith('.json') + ? JSON.parse(content) + : yaml.load(content) + + // 处理加载的配置并和默认值合并 + if (loadedConfig) { + const mergeResult = this._mergeConfig(loadedConfig) + if (mergeResult.changed) { + logger?.debug?.('[Config] merged new defaults into persisted config; scheduling save') + this._triggerSave('code') + } + } + + logger.debug('Config loaded successfully') + } catch (error) { + logger.error('Failed to load config:', error) + } + } + + _mergeConfig (loadedConfig) { + let changed = false + + const mergeInto = (target, source) => { + if (!source || typeof source !== 'object') { + return target + } + if (!target || typeof target !== 'object') { + target = Array.isArray(source) ? [] : {} + } + const result = Array.isArray(source) ? [] : { ...target } + + if (Array.isArray(source)) { + return source.slice() + } + + const targetKeys = target && typeof target === 'object' + ? Object.keys(target) + : [] + for (const key of targetKeys) { + if (!Object.prototype.hasOwnProperty.call(source, key)) { + changed = true + } + } + + for (const key of Object.keys(source)) { + const sourceValue = source[key] + const targetValue = target[key] + if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) { + result[key] = mergeInto(targetValue, sourceValue) + } else { + if (targetValue === undefined || targetValue !== sourceValue) { + changed = true + } + result[key] = sourceValue + } + } + return result + } + + const sections = ['version', 'basic', 'bym', 'llm', 'management', 'chaite', 'memory'] + for (const key of sections) { + const loadedValue = loadedConfig[key] + if (loadedValue === undefined) { + continue + } + if (typeof loadedValue === 'object' && loadedValue !== null) { + const merged = mergeInto(this[key], loadedValue) + if (merged !== this[key]) { + this[key] = merged + } + } else { + if (this[key] !== loadedValue) { + changed = true + } + this[key] = loadedValue + } + } + + return { changed } + } + + // 合并触发保存,防抖处理 + _triggerSave (origin) { + // 清除之前的定时器 + if (this._saveTimer) { + clearTimeout(this._saveTimer) + } + + const originLabel = origin || 'code' + this._saveOrigin = originLabel + this._saveTimer = setTimeout(() => { + this.saveToFile(originLabel) + this._saveOrigin = null + }, 200) + } + + saveToFile (origin = 'code') { + if (origin !== 'code') { + this._saveOrigin = 'external' + } + logger.debug('Saving config to file...') + try { + const config = { + version: this.version, + basic: this.basic, + bym: this.bym, + llm: this.llm, + management: this.management, + chaite: this.chaite, + memory: this.memory + } + + const content = this.configPath.endsWith('.json') + ? JSON.stringify(config, null, 2) + : yaml.dump(config) + + fs.writeFileSync(this.configPath, content, 'utf8') + } catch (error) { + console.error('Failed to save config:', error) + } + } + + toJSON () { + return { + version: this.version, + basic: this.basic, + bym: this.bym, + llm: this.llm, + management: this.management, + chaite: this.chaite, + memory: this.memory + } + } +} + +export default new ChatGPTConfig() diff --git a/config/config.md b/config/config.md deleted file mode 100644 index a5884e1..0000000 --- a/config/config.md +++ /dev/null @@ -1,3 +0,0 @@ -## 配置项解析 - -正在施工中...... \ No newline at end of file diff --git a/guoba.support.js b/guoba.support.js index afdb578..2b591b2 100644 --- a/guoba.support.js +++ b/guoba.support.js @@ -1,7 +1,4 @@ -import { Config } from './utils/config.js' -import { speakers } from './utils/tts.js' -import { supportConfigurations as azureRoleList } from './utils/tts/microsoft-azure.js' -import { supportConfigurations as voxRoleList } from './utils/tts/voicevox.js' +import Config from './config/config.js' // 支持锅巴 export function supportGuoba () { return { @@ -28,802 +25,12 @@ export function supportGuoba () { // 配置项 schemas schemas: [ { - field: 'blockWords', - label: '输出黑名单', - bottomHelpMessage: '检查输出结果中是否有违禁词,如果存在黑名单中的违禁词则不输出。英文逗号隔开', - component: 'InputTextArea' - }, - { - field: 'promptBlockWords', - label: '输入黑名单', - bottomHelpMessage: '检查输入结果中是否有违禁词,如果存在黑名单中的违禁词则不输出。英文逗号隔开', - component: 'InputTextArea' - }, - { - field: 'whitelist', - label: '对话白名单', - bottomHelpMessage: '只有在白名单内的QQ号或群组才能使用本插件进行对话。如果需要添加QQ号,请在号码前面加上^符号(例如:^123456),多个号码之间请用英文逗号(,)隔开。白名单优先级高于黑名单。', - component: 'Input' - }, - { - field: 'blacklist', - label: '对话黑名单', - bottomHelpMessage: '名单内的群或QQ号将无法使用本插件进行对话。如果需要添加QQ号,请在QQ号前面加上^符号(例如:^123456),并用英文逗号(,)将各个号码分隔开。', - component: 'Input' - }, - { - field: 'imgOcr', - label: '图片识别', - bottomHelpMessage: '是否识别消息中图片的文字内容,需要同时包含图片和消息才生效', - component: 'Switch' - }, - { - field: 'enablePrivateChat', - label: '是否允许私聊机器人', - component: 'Switch' - }, - { - field: 'defaultUsePicture', - label: '全局图片模式', - bottomHelpMessage: '全局默认以图片形式回复', - component: 'Switch' - }, - { - field: 'defaultUseTTS', - label: '全局语音模式', - bottomHelpMessage: '全局默认以语音形式回复,使用默认角色音色', - component: 'Switch' - }, - { - field: 'ttsMode', - label: '语音模式源', - bottomHelpMessage: '语音模式下使用何种语音源进行文本->音频转换', - component: 'Select', - componentProps: { - options: [ - { - label: 'vits-uma-genshin-honkai', - value: 'vits-uma-genshin-honkai' - }, - { - label: '微软Azure', - value: 'azure' - }, - { - label: 'VoiceVox', - value: 'voicevox' - } - ] + field: 'testRender', + label: '表格测试', + component: 'Render', + render: () => { + `你好` } - }, - { - field: 'defaultTTSRole', - label: 'vits默认角色', - bottomHelpMessage: 'vits-uma-genshin-honkai语音模式下,未指定角色时使用的角色。若留空,将使用随机角色回复。若用户通过指令指定了角色,将忽略本设定', - component: 'Select', - componentProps: { - options: [{ - label: '随机', - value: '随机' - }].concat(speakers.map(s => { return { label: s, value: s } })) - } - }, - { - field: 'azureTTSSpeaker', - label: 'Azure默认角色', - bottomHelpMessage: '微软Azure语音模式下,未指定角色时使用的角色。若用户通过指令指定了角色,将忽略本设定', - component: 'Select', - componentProps: { - options: [{ - label: '随机', - value: '随机' - }, - ...azureRoleList.flatMap(item => [ - item.roleInfo - ]).map(s => ({ - label: s, - value: s - }))] - } - }, - { - field: 'voicevoxTTSSpeaker', - label: 'VoiceVox默认角色', - bottomHelpMessage: 'VoiceVox语音模式下,未指定角色时使用的角色。若留空,将使用随机角色回复。若用户通过指令指定了角色,将忽略本设定', - component: 'Select', - componentProps: { - options: [{ - label: '随机', - value: '随机' - }, - ...voxRoleList.flatMap(item => [ - ...item.styles.map(style => `${item.name}-${style.name}`), - item.name - ]).map(s => ({ - label: s, - value: s - }))] - } - }, - { - field: 'ttsRegex', - label: '语音过滤正则表达式', - bottomHelpMessage: '语音模式下,配置此项以过滤不想被读出来的内容。表达式测试地址:https://www.runoob.com/regexp/regexp-syntax.html', - component: 'Input' - }, - { - field: 'ttsAutoFallbackThreshold', - label: '语音转文字阈值', - helpMessage: '语音模式下,字数超过这个阈值就降级为文字', - bottomHelpMessage: '语音转为文字的阈值', - component: 'InputNumber', - componentProps: { - min: 0, - max: 299 - } - }, - { - field: 'alsoSendText', - label: '语音同时发送文字', - bottomHelpMessage: '语音模式下,同时发送文字版,避免音质较低听不懂', - component: 'Switch' - }, - { - field: 'autoJapanese', - label: 'vits模式日语输出', - bottomHelpMessage: '使用vits语音时,将机器人的文字回复翻译成日文后获取语音。' + - '若想使用插件的翻译功能,发送"#chatgpt翻译帮助"查看使用方法,支持图片翻译,引用翻译...', - component: 'Switch' - }, - { - field: 'autoUsePicture', - label: '长文本自动转图片', - bottomHelpMessage: '字数大于阈值会自动用图片发送,即使是文本模式', - component: 'Switch' - }, - { - field: 'autoUsePictureThreshold', - label: '自动转图片阈值', - helpMessage: '长文本自动转图片开启后才生效', - bottomHelpMessage: '自动转图片的字数阈值', - component: 'InputNumber', - componentProps: { - min: 0 - } - }, - { - field: 'conversationPreserveTime', - label: '对话保留时长', - helpMessage: '单位:秒', - bottomHelpMessage: '每个人发起的对话保留时长。超过这个时长没有进行对话,再进行对话将开启新的对话。', - component: 'InputNumber', - componentProps: { - min: 0 - } - }, - { - field: 'toggleMode', - label: '触发方式', - bottomHelpMessage: 'at模式下只有at机器人才会回复。#chat模式下不需要at,但需要添加前缀#chat', - component: 'Select', - componentProps: { - options: [ - { label: 'at', value: 'at' }, - { label: '#chat', value: 'prefix' } - ] - } - }, - { - field: 'allowOtherMode', - label: '允许其他模式', - bottomHelpMessage: '开启后,则允许用户使用#chat1/#chat3/#chatglm/#bing等命令无视全局模式进行聊天', - component: 'Switch' - }, - { - field: 'quoteReply', - label: '图片引用消息', - bottomHelpMessage: '在回复图片时引用原始消息', - component: 'Switch' - }, - { - field: 'showQRCode', - label: '启用二维码', - bottomHelpMessage: '在图片模式中启用二维码。该对话内容将被发送至第三方服务器以进行渲染展示,如果不希望对话内容被上传到第三方服务器请关闭此功能', - component: 'Switch' - }, - { - field: 'cacheUrl', - label: '渲染服务器地址', - bottomHelpMessage: '用于缓存图片模式会话内容并渲染的服务器地址', - component: 'Input' - }, - { - field: 'cacheEntry', - label: '预制渲染服务器访问代码', - bottomHelpMessage: '图片内容渲染服务器开启预制访问代码,当渲染服务器访问较慢时可以开启,但无法保证访问代码可以正常访问页面', - component: 'Switch' - }, - { - field: 'drawCD', - label: '绘图CD', - helpMessage: '单位:秒', - bottomHelpMessage: '绘图指令的CD时间,主人不受限制', - component: 'InputNumber', - componentProps: { - min: 0 - } - }, - { - field: 'enableDraw', - label: '绘图功能开关', - component: 'Switch' - }, - { - field: 'proxy', - label: '代理服务器地址', - bottomHelpMessage: '数据通过代理服务器发送,http或socks5代理。配置后需重启', - component: 'Input' - }, - { - field: 'debug', - label: '调试信息', - bottomHelpMessage: '将输出更多调试信息,如果不希望控制台刷屏的话,可以关闭', - component: 'Switch' - }, - { - label: '以下为服务超时配置。', - component: 'Divider' - }, - { - field: 'defaultTimeoutMs', - label: '默认超时时间', - helpMessage: '单位:毫秒', - bottomHelpMessage: '各个地方的默认超时时间', - component: 'InputNumber', - componentProps: { - min: 0 - } - }, - { - field: 'chromeTimeoutMS', - label: '浏览器超时时间', - helpMessage: '单位:毫秒', - bottomHelpMessage: '浏览器默认超时,浏览器可能需要更高的超时时间', - component: 'InputNumber', - componentProps: { - min: 0 - } - }, - { - field: 'sydneyFirstMessageTimeout', - label: 'Sydney模式接受首条信息超时时间', - helpMessage: '单位:毫秒', - bottomHelpMessage: '超过该时间阈值未收到Bing的任何消息,则断开本次连接并重试(最多重试3次,失败后将返回timeout waiting for first message)', - component: 'InputNumber', - componentProps: { - min: 15000 - } - }, - { - label: '以下为API方式(默认)的配置', - component: 'Divider' - }, - { - field: 'apiKey', - label: 'OpenAI API Key', - bottomHelpMessage: 'OpenAI的ApiKey,用于访问OpenAI的API接口', - component: 'InputPassword' - }, - { - field: 'model', - label: 'OpenAI 模型', - bottomHelpMessage: 'gpt-4, gpt-4-0613, gpt-4-32k, gpt-4-32k-0613, gpt-3.5-turbo, gpt-3.5-turbo-0613, gpt-3.5-turbo-16k-0613。默认为gpt-3.5-turbo,gpt-4需账户支持', - component: 'Input' - }, - { - field: 'smartMode', - label: '智能模式', - bottomHelpMessage: '仅建议gpt-4-32k和gpt-3.5-turbo-16k-0613开启,gpt-4-0613也可。开启后机器人可以群管、收发图片、发视频发音乐、联网搜索等。注意较费token。配合开启读取群聊上下文效果更佳', - component: 'Switch' - }, - { - field: 'openAiBaseUrl', - label: 'OpenAI API服务器地址', - bottomHelpMessage: 'OpenAI的API服务器地址。注意要带上/v1。默认为https://api.openai.com/v1', - component: 'Input' - }, - { - field: 'openAiForceUseReverse', - label: '强制使用OpenAI反代', - bottomHelpMessage: '即使配置了proxy,依然使用OpenAI反代', - component: 'Switch' - }, - { - field: 'promptPrefixOverride', - label: 'AI风格', - bottomHelpMessage: '你可以在这里写入你希望AI回答的风格,比如希望优先回答中文,回答长一点等', - component: 'InputTextArea' - }, - { - field: 'assistantLabel', - label: 'AI名字', - bottomHelpMessage: 'AI认为的自己的名字,当你问他你是谁是他会回答这里的名字', - component: 'Input' - }, - { - field: 'temperature', - label: 'temperature', - bottomHelpMessage: '用于控制回复内容的多样性,数值越大回复越加随机、多元化,数值越小回复越加保守', - component: 'InputNumber', - componentProps: { - min: 0, - max: 2 - } - }, - { - label: '以下为必应方式的配置。', - component: 'Divider' - }, - { - field: 'toneStyle', - label: 'Bing模式', - bottomHelpMessage: '微软必应官方的三种应答风格。默认为均衡,Sydney为实验风格,独立与三种风格之外;自设定为自定义AI的回答风格', - component: 'Select', - componentProps: { - options: [ - { label: '均衡', value: 'balanced' }, - { label: '创意', value: 'creative' }, - { label: '精确', value: 'precise' }, - { label: 'Sydney(可能存在风险)', value: 'Sydney' }, - { label: '自设定(可能存在风险)', value: 'Custom' } - ] - } - }, - { - field: 'enableSuggestedResponses', - label: '是否开启建议回复', - bottomHelpMessage: '开启了会像官网上一样,每个问题给出建议的用户问题', - component: 'Switch' - }, - { - field: 'enableGroupContext', - label: '是否允许机器人读取近期的群聊聊天记录', - bottomHelpMessage: '开启后机器人可以知道群名、最近发言等信息', - component: 'Switch' - }, - { - field: 'groupContextTip', - label: '机器人读取聊天记录时的后台prompt', - component: 'InputTextArea' - }, - { - field: 'enforceMaster', - label: '加强主人认知', - bottomHelpMessage: '加强主人认知。希望机器人认清主人,避免NTR可开启。开启后可能会与自设定的内容有部分冲突。sydney模式可以放心开启', - component: 'Switch' - }, - { - field: 'enableGenerateContents', - label: '允许生成图像等内容', - bottomHelpMessage: '开启后类似网页版能够发图。但是此选项会占用大量token,自设定等模式下容易爆token', - component: 'Switch' - }, - // { - // field: 'cognitiveReinforcementTip', - // label: '加强主人认知的后台prompt', - // component: 'InputTextArea' - // }, - { - field: 'groupContextLength', - label: '允许机器人读取近期的最多群聊聊天记录条数。', - bottomHelpMessage: '允许机器人读取近期的最多群聊聊天记录条数。太多可能会超。默认50', - component: 'InputNumber' - }, - { - field: 'enableRobotAt', - label: '是否允许机器人真at', - bottomHelpMessage: '开启后机器人的回复如果at群友会真的at', - component: 'Switch' - }, - { - field: 'sydney', - label: 'Custom的设定', - bottomHelpMessage: '仅自设定模式下有效。你可以自己改写设定,让Sydney变成你希望的样子。可能存在不稳定的情况', - component: 'InputTextArea' - }, - { - field: 'sydneyApologyIgnored', - label: 'Bing抱歉是否不计入聊天记录', - bottomHelpMessage: '有时无限抱歉,就关掉这个再多问几次试试,可能有奇效', - component: 'Switch' - }, - { - field: 'sydneyContext', - label: 'Bing的扩展资料', - bottomHelpMessage: 'AI将会从你提供的扩展资料中学习到一些知识,帮助它更好地回答你的问题。实际相当于使用edge侧边栏Bing时读取的你当前浏览网页的内容。如果太长可能容易到达GPT-4的8192token上限', - component: 'InputTextArea' - }, - { - field: 'sydneyReverseProxy', - label: 'sydney反代', - bottomHelpMessage: '仅悉尼和自设定模式下有效,用于创建对话(默认不用于正式对话)。目前国内ip和部分境外IDC IP由于微软限制创建对话,如果有bing.com的反代可以填在此处,或者使用proxy', - component: 'Input' - }, - { - field: 'sydneyForceUseReverse', - label: '强制使用sydney反代', - bottomHelpMessage: '即使配置了proxy,创建对话时依然使用sydney反代', - component: 'Switch' - }, - { - field: 'sydneyWebsocketUseProxy', - label: '对话使用sydney反代', - bottomHelpMessage: '【一般情况无需也不建议开启】默认情况下仅创建对话走反代,对话时仍然直连微软。开启本选项将使对话过程也走反,需反代支持', - component: 'Switch' - }, - { - field: 'sydneyMood', - label: '情感显示', - bottomHelpMessage: '开启Sydney的情感显示,仅在图片模式下生效', - component: 'Switch' - }, - { - label: '以下为API3方式的配置', - component: 'Divider' - }, - { - field: 'api', - label: 'ChatGPT API反代服务器地址', - bottomHelpMessage: 'ChatGPT的API反代服务器,用于绕过Cloudflare访问ChatGPT API', - component: 'Input' - }, - { - field: 'apiBaseUrl', - label: 'apiBaseUrl地址', - bottomHelpMessage: 'apiBaseUrl地址', - component: 'Input' - }, - { - field: 'apiForceUseReverse', - label: '强制使用ChatGPT反代', - bottomHelpMessage: '即使配置了proxy,依然使用ChatGPT反代', - component: 'Switch' - }, - { - field: 'useGPT4', - label: '使用GPT-4', - bottomHelpMessage: '使用GPT-4,注意试用配额较低,如果用不了就关掉', - component: 'Switch' - }, - { - label: '以下为浏览器方式的配置.(Deprecated)', - component: 'Divider' - }, - { - field: 'username', - label: '用户名', - bottomHelpMessage: 'OpenAI用户名。', - component: 'Input' - }, - { - field: 'password', - label: '密码', - bottomHelpMessage: 'OpenAI密码。', - component: 'InputPassword' - }, - { - field: 'UA', - label: '浏览器UA', - bottomHelpMessage: '模拟浏览器UA,无特殊需求保持默认即可', - component: 'InputTextArea' - }, - { - field: 'headless', - label: '无头模式', - bottomHelpMessage: '无界面的服务器可以开启,但遇到验证码时可能无法使用。(实测很容易卡住,几乎不可用)', - component: 'Switch' - }, - { - field: 'chromePath', - label: 'Chrome路径', - bottomHelpMessage: '为空使用默认puppeteer的chromium,也可以传递自己本机安装的Chrome可执行文件地址,提高通过率。windows可以是‘C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe’,linux通过which查找路径', - component: 'Input' - }, - { - label: '以下为Slack Claude方式的配置', - component: 'Divider' - }, - { - field: 'slackUserToken', - label: 'Slack用户Token', - bottomHelpMessage: 'slackUserToken,在OAuth&Permissions页面获取。需要具有channels:history, chat:write, groups:history, im:history, mpim:history 这几个scope', - component: 'Input' - }, - { - field: 'slackBotUserToken', - label: 'Slack Bot Token', - bottomHelpMessage: 'slackBotUserToken,在OAuth&Permissions页面获取。需要channels:history,groups:history,im:history 这几个scope', - component: 'Input' - }, - { - field: 'slackClaudeUserId', - label: 'Slack成员id', - bottomHelpMessage: '在Slack中点击Claude头像查看详情,其中的成员ID复制过来', - component: 'Input' - }, - { - field: 'slackSigningSecret', - label: 'Slack签名密钥', - bottomHelpMessage: 'Signing Secret。在Basic Information页面获取', - component: 'Input' - }, - { - field: 'slackClaudeSpecifiedChannel', - label: 'Slack指定频道', - bottomHelpMessage: '为空时,将为每个qq号建立私有频道。若填写了,对话将发生在本频道。和其他人公用workspace时建议用这个', - component: 'Input' - }, - { - field: 'slackClaudeEnableGlobalPreset', - label: 'Claude使用全局设定', - bottomHelpMessage: '开启后,所有人每次发起新对话时,会先发送设定过去再开始对话,达到类似Bing自设定的效果。', - component: 'Switch' - }, - { - field: 'slackClaudeGlobalPreset', - label: 'Slack全局设定', - bottomHelpMessage: '若启用全局设定,每个人都会默认使用这里的设定。', - component: 'Input' - }, - { - label: '以下为ChatGLM方式的配置', - component: 'Divider' - }, - { - field: 'chatglmBaseUrl', - label: 'ChatGLM API地址', - bottomHelpMessage: '如 http://localhost:8080', - component: 'Input' - }, - { - label: '以下为星火方式的配置', - component: 'Divider' - }, - { - field: 'xinghuoToken', - label: '星火Cookie', - bottomHelpMessage: '获取对话页面的ssoSessionId cookie。不要带等号和分号', - component: 'Input' - }, - { - label: '以下为杂七杂八的配置', - component: 'Divider' - }, - { - field: '2captchaToken', - label: '验证码平台Token', - bottomHelpMessage: '可注册2captcha实现跳过验证码,收费服务但很便宜。否则可能会遇到验证码而卡住', - component: 'InputPassword' - }, - { - field: 'ttsSpace', - label: 'vits-uma-genshin-honkai语音转换API地址', - bottomHelpMessage: '前往duplicate空间https://huggingface.co/spaces/ikechan8370/vits-uma-genshin-honkai后查看api地址', - component: 'Input' - }, - { - field: 'voicevoxSpace', - label: 'voicevox语音转换API地址', - bottomHelpMessage: '可使用https://2ndelement-voicevox.hf.space, 也可github搜索voicevox-engine自建', - component: 'Input' - }, - { - field: 'azureTTSKey', - label: 'Azure语音服务密钥', - component: 'Input' - }, - { - field: 'azureTTSRegion', - label: 'Azure语音服务区域', - bottomHelpMessage: '例如japaneast', - component: 'Input' - }, - { - field: 'azureTTSEmotion', - label: 'Azure情绪多样化', - bottomHelpMessage: '切换角色后使用"#chatgpt使用设定xxx"重新开始对话以更新不同角色的情绪配置。支持使用不同的说话风格回复,各个角色支持说话风格详情:https://speech.microsoft.com/portal/voicegallery', - component: 'Switch' - }, - { - field: 'enhanceAzureTTSEmotion', - label: 'Azure情绪纠正', - bottomHelpMessage: '当机器人未使用或使用了不支持的说话风格时,将在对话中提醒机器人。注意:bing模式开启此项后有概率增大触发抱歉的机率,且不要单独开启此项。', - component: 'Switch' - }, - { - field: 'huggingFaceReverseProxy', - label: '语音转换huggingface反代', - bottomHelpMessage: '没有就空着', - component: 'Input' - }, - { - field: 'cloudTranscode', - label: '云转码API地址', - bottomHelpMessage: '目前只支持node-silk语音转码,可在本地node-silk无法使用时尝试使用云端资源转码', - component: 'Input' - }, - { - field: 'cloudMode', - label: '云转码API发送数据模式', - bottomHelpMessage: '默认发送数据链接,如果你部署的是本地vits服务或使用的是微软azure,请改为文件', - component: 'Select', - componentProps: { - options: [ - { label: '文件', value: 'file' }, - { label: '链接', value: 'url' } - // { label: '数据', value: 'buffer' } - ] - } - }, - { - field: 'noiseScale', - label: 'noiseScale', - bottomHelpMessage: '控制情感变化程度', - component: 'InputNumber', - componentProps: { - min: 0, - max: 1 - } - }, - { - field: 'noiseScaleW', - label: 'noiseScaleW', - bottomHelpMessage: '控制音素发音长度', - component: 'InputNumber', - componentProps: { - min: 0, - max: 1 - } - }, - { - field: 'lengthScale', - label: 'lengthScale', - bottomHelpMessage: '控制整体语速', - component: 'InputNumber', - componentProps: { - min: 0, - max: 2 - } - }, - { - field: 'initiativeChatGroups', - label: '主动发起聊天群聊的群号', - bottomHelpMessage: '在这些群聊里会不定时主动说一些随机的打招呼的话,用英文逗号隔开。必须配置了OpenAI Key', - component: 'Input' - }, - { - field: 'helloPrompt', - label: '打招呼prompt', - bottomHelpMessage: '将会用这段文字询问ChatGPT,由ChatGPT给出随机的打招呼文字', - component: 'Input' - }, - { - field: 'helloInterval', - label: '打招呼间隔(小时)', - component: 'InputNumber', - componentProps: { - min: 1, - max: 24 - } - }, - { - field: 'helloProbability', - label: '打招呼的触发概率(%)', - bottomHelpMessage: '设置为100则每次经过间隔时间必定触发主动打招呼事件。', - component: 'InputNumber', - componentProps: { - min: 0, - max: 100 - } - }, - { - field: 'emojiBaseURL', - label: '合成emoji的API地址,默认谷歌厨房', - component: 'Input' - }, - { - label: '以下为后台与渲染相关配置', - component: 'Divider' - }, - { - field: 'oldview', - label: '旧版本渲染', - bottomHelpMessage: '开启预览版本', - component: 'Switch' - }, - { - field: 'serverPort', - label: '系统Api服务端口', - bottomHelpMessage: '系统Api服务开启的端口号,如需外网访问请将系统防火墙和服务器防火墙对应端口开放,修改后请重启', - component: 'InputNumber' - }, - { - field: 'serverHost', - label: '系统服务访问域名', - bottomHelpMessage: '使用域名代替公网ip,适用于有服务器和域名的朋友避免暴露ip使用', - component: 'Input' - }, - { - field: 'viewHost', - label: '渲染服务器地址', - bottomHelpMessage: '可选择第三方渲染服务器', - component: 'Input' - }, - { - field: 'chatViewWidth', - label: '图片渲染宽度', - bottomHelpMessage: '聊天页面渲染窗口的宽度', - component: 'InputNumber' - }, - { - field: 'cloudRender', - label: '云渲染', - bottomHelpMessage: '是否使用云资源进行图片渲染,需要开放服务器端口后才能使用,不支持旧版本渲染', - component: 'Switch' - }, - { - field: 'chatViewBotName', - label: 'Bot命名', - bottomHelpMessage: '新渲染模式强制修改Bot命名', - component: 'Input' - }, - { - field: 'groupAdminPage', - label: '允许群获取后台地址', - bottomHelpMessage: '是否允许群获取后台地址,关闭后将只能私聊获取', - component: 'Switch' - }, - { - field: 'live2d', - label: 'Live2D显示', - bottomHelpMessage: '开启Live2D显示', - component: 'Switch' - }, - { - field: 'live2dModel', - label: 'Live2D模型', - bottomHelpMessage: '选择Live2D使用的模型', - component: 'Input' - }, - { - field: 'amapKey', - label: '高德APIKey', - bottomHelpMessage: '用于查询天气', - component: 'Input' - }, - { - field: 'azSerpKey', - label: 'Azure search key', - bottomHelpMessage: 'https://www.microsoft.com/en-us/bing/apis/bing-web-search-api', - component: 'Input' - }, - { - field: 'serpSource', - label: '搜索来源,azure需填写key,ikechan8370为作者自备源', - component: 'Select', - componentProps: { - options: [ - { label: 'Azure', value: 'azure' }, - { label: 'ikechan8370', value: 'ikechan8370' } - // { label: '数据', value: 'buffer' } - ] - } - }, - { - field: 'extraUrl', - label: '额外工具url', - bottomHelpMessage: '(测试期间提供一个公益接口,一段时间后撤掉)参考搭建:https://github.com/ikechan8370/chatgpt-plugin-extras', - component: 'Input' } ], // 获取配置数据方法(用于前端填充显示数据) @@ -834,20 +41,23 @@ export function supportGuoba () { setConfigData (data, { Result }) { for (let [keyPath, value] of Object.entries(data)) { // 处理黑名单 - if (keyPath === 'blacklist' || keyPath === 'whitelist' || keyPath === 'blockWords' || keyPath === 'promptBlockWords' || keyPath === 'initiativeChatGroups') { value = value.toString().split(/[,,;;\|]/) } - if (Config[keyPath] !== value) { Config[keyPath] = value } - } - // 正确储存azureRoleSelect结果 - const azureSpeaker = azureRoleList.find(config => { - let i = config.roleInfo || config.code - if (i === data.azureTTSSpeaker) { - return config - } else { - return false + if (keyPath === 'blockWords' || keyPath === 'promptBlockWords' || keyPath === 'initiativeChatGroups') { value = value.toString().split(/[,,;;\|]/) } + if (keyPath === 'blacklist' || keyPath === 'whitelist') { + // 6-10位数的群号或qq + const regex = /^\^?[1-9]\d{5,9}(\^[1-9]\d{5,9})?$/ + const inputSet = new Set() + value = value.toString().split(/[,,;;|\s]/).reduce((acc, item) => { + item = item.trim() + if (!inputSet.has(item) && regex.test(item)) { + if (item.length <= 11 || (item.length <= 21 && item.length > 11 && !item.startsWith('^'))) { + inputSet.add(item) + acc.push(item) + } + } + return acc + }, []) } - }) - if (typeof azureSpeaker === 'object' && azureSpeaker !== null) { - Config.azureTTSSpeaker = azureSpeaker.code + if (Config[keyPath] !== value) { Config[keyPath] = value } } return Result.ok({}, '保存成功~') } diff --git a/index.js b/index.js index d2139ac..1267282 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,15 @@ import fs from 'node:fs' -import { Config } from './utils/config.js' -import { createServer } from './server/index.js' +import ChatGPTConfig from './config/config.js' +import { initChaite } from './models/chaite/cloud.js' +logger.info('**************************************') +logger.info('chatgpt-plugin加载中') if (!global.segment) { - global.segment = (await import('oicq')).segment + try { + global.segment = (await import('icqq')).segment + } catch (err) { + global.segment = (await import('oicq')).segment + } } const files = fs.readdirSync('./plugins/chatgpt-plugin/apps').filter(file => file.endsWith('.js')) @@ -19,7 +25,6 @@ ret = await Promise.allSettled(ret) let apps = {} for (let i in files) { let name = files[i].replace('.js', '') - if (ret[i].status !== 'fulfilled') { logger.error(`载入插件错误:${logger.red(name)}`) logger.error(ret[i].reason) @@ -27,13 +32,16 @@ for (let i in files) { } apps[name] = ret[i].value[Object.keys(ret[i].value)[0]] } +global.chatgpt = { -// 启动服务器 -await createServer() -logger.info('**************************************') +} + +ChatGPTConfig.startSync('./plugins/chatgpt-plugin/data') +initChaite() logger.info('chatgpt-plugin加载成功') -logger.info(`当前版本${Config.version}`) +logger.info(`当前版本${ChatGPTConfig.version}`) logger.info('仓库地址 https://github.com/ikechan8370/chatgpt-plugin') +logger.info('文档地址 https://www.yunzai.chat') logger.info('插件群号 559567232') logger.info('**************************************') diff --git a/models/chaite/cloud.js b/models/chaite/cloud.js new file mode 100644 index 0000000..dfa8fd4 --- /dev/null +++ b/models/chaite/cloud.js @@ -0,0 +1,207 @@ +import { + Chaite, + ChannelsManager, + ChatPresetManager, + DefaultChannelLoadBalancer, + ProcessorsManager, + RAGManager, + ToolManager, + ToolsGroupManager, + TriggerManager +} from 'chaite' +import ChatGPTConfig from '../../config/config.js' +import { LowDBChannelStorage } from './storage/lowdb/channel_storage.js' +import { LowDBChatPresetsStorage } from './storage/lowdb/chat_preset_storage.js' +import { LowDBToolsStorage } from './storage/lowdb/tools_storage.js' +import { LowDBProcessorsStorage } from './storage/lowdb/processors_storage.js' +import { ChatGPTUserModeSelector } from './user_mode_selector.js' +import { LowDBUserStateStorage } from './storage/lowdb/user_state_storage.js' +import { LowDBHistoryManager } from './storage/lowdb/history_manager.js' +import { VectraVectorDatabase } from './vector_database.js' +import path from 'path' +import fs from 'fs' +import { migrateDatabase } from '../../utils/initDB.js' +import { SQLiteChannelStorage } from './storage/sqlite/channel_storage.js' +import { dataDir } from '../../utils/common.js' +import { SQLiteChatPresetStorage } from './storage/sqlite/chat_preset_storage.js' +import { SQLiteToolsStorage } from './storage/sqlite/tools_storage.js' +import { SQLiteProcessorsStorage } from './storage/sqlite/processors_storage.js' +import { SQLiteUserStateStorage } from './storage/sqlite/user_state_storage.js' +import { SQLiteToolsGroupStorage } from './storage/sqlite/tool_groups_storage.js' +import { checkMigrate } from './storage/sqlite/migrate.js' +import { SQLiteHistoryManager } from './storage/sqlite/history_manager.js' +import SQLiteTriggerStorage from './storage/sqlite/trigger_storage.js' +import LowDBTriggerStorage from './storage/lowdb/trigger_storage,.js' +import { createChaiteVectorizer } from './vectorizer.js' +import { MemoryRouter, authenticateMemoryRequest } from '../memory/router.js' + +/** + * 认证,以便共享上传 + * @param apiKey + * @returns {Promise} + */ +export async function authCloud (apiKey = ChatGPTConfig.chaite.cloudApiKey) { + try { + await Chaite.getInstance().auth(apiKey) + return Chaite.getInstance().getToolsManager().cloudService.getUser() + } catch (err) { + logger.error(err) + return null + } +} + +/** + * 初始化RAG管理器 + * @param {string} model + * @param {number} dimensions + */ +export async function initRagManager (model, dimensions) { + const vectorizer = createChaiteVectorizer(model, dimensions) + const vectorDBPath = path.resolve('./plugins/chatgpt-plugin', ChatGPTConfig.chaite.dataDir, 'vector_index') + if (!fs.existsSync(vectorDBPath)) { + fs.mkdirSync(vectorDBPath, { recursive: true }) + } + const vectorDB = new VectraVectorDatabase(vectorDBPath) + await vectorDB.init() + const ragManager = new RAGManager(vectorDB, vectorizer) + return Chaite.getInstance().setRAGManager(ragManager) +} + +export async function initChaite () { + const storage = ChatGPTConfig.chaite.storage + let channelsStorage, chatPresetsStorage, toolsStorage, processorsStorage, userStateStorage, historyStorage, toolsGroupStorage, triggerStorage + switch (storage) { + case 'sqlite': { + const dbPath = path.join(dataDir, 'data.db') + channelsStorage = new SQLiteChannelStorage(dbPath) + await channelsStorage.initialize() + chatPresetsStorage = new SQLiteChatPresetStorage(dbPath) + await chatPresetsStorage.initialize() + toolsStorage = new SQLiteToolsStorage(dbPath) + await toolsStorage.initialize() + processorsStorage = new SQLiteProcessorsStorage(dbPath) + await processorsStorage.initialize() + userStateStorage = new SQLiteUserStateStorage(dbPath) + await userStateStorage.initialize() + toolsGroupStorage = new SQLiteToolsGroupStorage(dbPath) + await toolsGroupStorage.initialize() + triggerStorage = new SQLiteTriggerStorage(dbPath) + await triggerStorage.initialize() + historyStorage = new SQLiteHistoryManager(dbPath, path.join(dataDir, 'images')) + await checkMigrate() + break + } + case 'lowdb': { + const ChatGPTStorage = (await import('storage/lowdb/storage.js')).default + await ChatGPTStorage.init() + channelsStorage = new LowDBChannelStorage(ChatGPTStorage) + chatPresetsStorage = new LowDBChatPresetsStorage(ChatGPTStorage) + toolsStorage = new LowDBToolsStorage(ChatGPTStorage) + processorsStorage = new LowDBProcessorsStorage(ChatGPTStorage) + userStateStorage = new LowDBUserStateStorage(ChatGPTStorage) + triggerStorage = new LowDBTriggerStorage(ChatGPTStorage) + const ChatGPTHistoryStorage = (await import('storage/lowdb/storage.js')).ChatGPTHistoryStorage + await ChatGPTHistoryStorage.init() + historyStorage = new LowDBHistoryManager(ChatGPTHistoryStorage) + break + } + } + const channelsManager = await ChannelsManager.init(channelsStorage, new DefaultChannelLoadBalancer()) + const toolsDir = path.resolve('./plugins/chatgpt-plugin', ChatGPTConfig.chaite.toolsDirPath) + if (!fs.existsSync(toolsDir)) { + fs.mkdirSync(toolsDir, { recursive: true }) + } + const toolsManager = await ToolManager.init(toolsDir, toolsStorage) + const processorsDir = path.resolve('./plugins/chatgpt-plugin', ChatGPTConfig.chaite.processorsDirPath) + if (!fs.existsSync(processorsDir)) { + fs.mkdirSync(processorsDir, { recursive: true }) + } + const processorsManager = await ProcessorsManager.init(processorsDir, processorsStorage) + const chatPresetManager = await ChatPresetManager.init(chatPresetsStorage) + const toolsGroupManager = await ToolsGroupManager.init(toolsGroupStorage) + const triggersDir = path.resolve('./plugins/chatgpt-plugin', ChatGPTConfig.chaite.triggersDir) + if (!fs.existsSync(triggersDir)) { + fs.mkdirSync(triggersDir, { recursive: true }) + } + const triggerManager = new TriggerManager(triggersDir, triggerStorage) + await triggerManager.initialize() + const userModeSelector = new ChatGPTUserModeSelector() + let chaite = Chaite.init(channelsManager, toolsManager, processorsManager, chatPresetManager, toolsGroupManager, triggerManager, + userModeSelector, userStateStorage, historyStorage, logger) + logger.info('Chaite 初始化完成') + chaite.setCloudService(ChatGPTConfig.chaite.cloudBaseUrl) + logger.info('Chaite.Cloud 初始化完成') + await migrateDatabase() + if (ChatGPTConfig.chaite.cloudApiKey) { + const user = await authCloud(ChatGPTConfig.chaite.cloudApiKey) + if (user) { + logger.info(`Chaite.Cloud 认证成功, 当前用户${user.username || user.email} (${user.user_id})`) + } else { + logger.warn('Chaite.Cloud 认证失败,将继续使用本地功能') + } + } + await initRagManager(ChatGPTConfig.llm.embeddingModel, ChatGPTConfig.llm.dimensions) + if (!ChatGPTConfig.chaite.authKey) { + ChatGPTConfig.chaite.authKey = Chaite.getInstance().getFrontendAuthHandler().generateToken(0, true) + } + chaite.getGlobalConfig().setAuthKey(ChatGPTConfig.chaite.authKey) + // 监听Chaite配置变化,同步需要同步的配置 + chaite.on('config-change', obj => { + const { key, newVal, oldVal } = obj + if (key === 'authKey') { + ChatGPTConfig.serverAuthKey = newVal + } + logger.debug(`Chaite config changed: ${key} from ${oldVal} to ${newVal}`) + }) + // 监听通过chaite对插件配置修改 + chaite.setUpdateConfigCallback(config => { + logger.debug('chatgpt-plugin config updated') + + // 设置保存来源标记,而不是使用 _isSaving + ChatGPTConfig._saveOrigin = 'chaite' + + try { + Object.keys(config).forEach(key => { + if (typeof config[key] === 'object' && config[key] !== null && ChatGPTConfig[key]) { + deepMerge(ChatGPTConfig[key], config[key]) + } else { + ChatGPTConfig[key] = config[key] + } + }) + + // 回传部分需要同步的配置 + chaite.getGlobalConfig().setDebug(ChatGPTConfig.basic.debug) + chaite.getGlobalConfig().setAuthKey(ChatGPTConfig.chaite.authKey) + + // 使用新的触发保存方法,而不是直接调用saveToFile + ChatGPTConfig._triggerSave('chaite') + } finally { + // 不需要在这里清除标记,_triggerSave已经处理了延迟清除 + } + }) + // 授予Chaite获取插件配置的能力以便通过api放出 + chaite.setGetConfig(async () => { + return ChatGPTConfig + }) + chaite.getGlobalConfig().setHost(ChatGPTConfig.chaite.host) + chaite.getGlobalConfig().setPort(ChatGPTConfig.chaite.port) + chaite.getGlobalConfig().setDebug(ChatGPTConfig.basic.debug) + logger.info('Chaite.RAGManager 初始化完成') + chaite.runApiServer(app => { + app.use('/api/memory', authenticateMemoryRequest, MemoryRouter) + }) +} + +function deepMerge (target, source) { + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + if (typeof source[key] === 'object' && source[key] !== null && target[key]) { + // 如果是对象且目标属性存在,递归合并 + deepMerge(target[key], source[key]) + } else { + // 否则直接赋值 + target[key] = source[key] + } + } + } +} diff --git a/models/chaite/storage/lowdb/channel_storage.js b/models/chaite/storage/lowdb/channel_storage.js new file mode 100644 index 0000000..2fe8ea9 --- /dev/null +++ b/models/chaite/storage/lowdb/channel_storage.js @@ -0,0 +1,104 @@ +import { ChaiteStorage, Channel } from 'chaite' + +export class LowDBChannelStorage extends ChaiteStorage { + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('channel') + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new Channel(obj) + } + + /** + * + * @param {string} id + * @param {import('chaite').Channel} channel + * @returns {Promise} + */ + async setItem (id, channel) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, channel) + return id + } + const result = await this.collection.insert(channel) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new Channel({}).fromString(JSON.stringify(item))) + } + + /** + * + * @param {Record} filter + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + const allList = await this.listItems() + return allList.filter(item => { + for (const key in filter) { + if (item[key] !== filter[key]) { + return false + } + } + return true + }) + } + + /** + * + * @param {Array<{ + * field: string; + * values: unknown[]; + * }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + const allList = await this.listItems() + return allList.filter(item => { + for (const { field, values } of query) { + if (!values.includes(item[field])) { + return false + } + } + return true + }) + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/lowdb/chat_preset_storage.js b/models/chaite/storage/lowdb/chat_preset_storage.js new file mode 100644 index 0000000..49788c6 --- /dev/null +++ b/models/chaite/storage/lowdb/chat_preset_storage.js @@ -0,0 +1,107 @@ +import { ChaiteStorage, ChatPreset } from 'chaite' + +/** + * @extends {ChaiteStorage} + */ +export class LowDBChatPresetsStorage extends ChaiteStorage { + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('chat_presets') + } + + /** + * + * @param key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new ChatPreset(obj) + } + + /** + * + * @param {string} id + * @param {import('chaite').ChatPreset} preset + * @returns {Promise} + */ + async setItem (id, preset) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, preset) + return id + } + const result = await this.collection.insert(preset) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new ChatPreset({}).fromString(JSON.stringify(item))) + } + + /** + * + * @param {Record} filter + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + const allList = await this.listItems() + return allList.filter(item => { + for (const key in filter) { + if (item[key] !== filter[key]) { + return false + } + } + return true + }) + } + + /** + * + * @param {Array<{ + * field: string; + * values: unknown[]; + * }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + const allList = await this.listItems() + return allList.filter(item => { + for (const { field, values } of query) { + if (!values.includes(item[field])) { + return false + } + } + return true + }) + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/lowdb/history_manager.js b/models/chaite/storage/lowdb/history_manager.js new file mode 100644 index 0000000..0f8c93e --- /dev/null +++ b/models/chaite/storage/lowdb/history_manager.js @@ -0,0 +1,56 @@ +import { AbstractHistoryManager } from 'chaite' + +export class LowDBHistoryManager extends AbstractHistoryManager { + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('history') + } + + async saveHistory (message, conversationId) { + const historyObj = { ...message, conversationId } + if (message.id) { + await this.collection.updateById(message.id, historyObj) + } + await this.collection.insert(historyObj) + } + + /** + * + * @param messageId + * @param conversationId + * @returns {Promise} + */ + async getHistory (messageId, conversationId) { + if (messageId) { + const messages = [] + let currentId = messageId + while (currentId) { + const message = await this.collection.findOne({ id: currentId }) + if (!message) break + messages.unshift(message) + currentId = message.parentId + } + return messages + } else if (conversationId) { + return this.collection.find({ conversationId }) + } + return [] + } + + async deleteConversation (conversationId) { + await this.collection.delete({ conversationId }) + } + + async getOneHistory (messageId, conversationId) { + return this.collection.findOne({ id: messageId, conversationId }) + } +} diff --git a/models/chaite/storage/lowdb/processors_storage.js b/models/chaite/storage/lowdb/processors_storage.js new file mode 100644 index 0000000..684d98c --- /dev/null +++ b/models/chaite/storage/lowdb/processors_storage.js @@ -0,0 +1,107 @@ +import { ChaiteStorage, ProcessorDTO } from 'chaite' + +/** + * @extends {ChaiteStorage} + */ +export class LowDBProcessorsStorage extends ChaiteStorage { + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('processors') + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new ProcessorDTO(obj) + } + + /** + * + * @param {string} id + * @param {import('chaite').Processor} processor + * @returns {Promise} + */ + async setItem (id, processor) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, processor) + return id + } + const result = await this.collection.insert(processor) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new ProcessorDTO({}).fromString(JSON.stringify(item))) + } + + /** + * + * @param {Record} filter + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + const allList = await this.listItems() + return allList.filter(item => { + for (const key in filter) { + if (item[key] !== filter[key]) { + return false + } + } + return true + }) + } + + /** + * + * @param {Array<{ + * field: string; + * values: unknown[]; + * }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + const allList = await this.listItems() + return allList.filter(item => { + for (const { field, values } of query) { + if (!values.includes(item[field])) { + return false + } + } + return true + }) + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/lowdb/storage.js b/models/chaite/storage/lowdb/storage.js new file mode 100644 index 0000000..a71f84f --- /dev/null +++ b/models/chaite/storage/lowdb/storage.js @@ -0,0 +1,374 @@ +// storage.js written by sonnet +import { Low } from 'lowdb' +import { JSONFile } from 'lowdb/node' +import path from 'path' +import fs from 'fs' +import { dataDir } from '../../../../utils/common.js' + +/** + * 基于 LowDB 的简单存储类,提供 CRUD 和条件查询功能 + */ +export class LowDBStorage { + /** + * 创建一个新的存储实例 + * @param {Object} options 配置选项 + * @param {string} options.filename 数据文件名称 + * @param {string} options.directory 数据目录,默认为当前目录下的 data 文件夹 + */ + constructor (options = {}) { + const { filename = 'db.json', directory = path.join(process.cwd(), 'data') } = options + + // 确保目录存在 + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory, { recursive: true }) + } + + this.filePath = path.join(directory, filename) + this.adapter = new JSONFile(this.filePath) + this.db = new Low(this.adapter, { collections: {} }) + this.initialized = false + } + + /** + * 初始化存储 + * @returns {Promise} 当前存储实例 + */ + async init () { + // 读取数据文件,如果不存在则创建默认结构 + await this.db.read() + this.db.data ||= { collections: {} } + await this.db.write() + + this.initialized = true + return this + } + + /** + * 获取或创建一个集合 + * @param {string} name 集合名称 + * @returns {LowDBCollection} 集合实例 + */ + collection (name) { + this._checkInit() + + // 确保集合存在 + if (!this.db.data.collections[name]) { + this.db.data.collections[name] = [] + this.db.write() + } + + return new LowDBCollection(this, name) + } + + /** + * 列出所有集合名称 + * @returns {string[]} 集合名称列表 + */ + listCollections () { + this._checkInit() + return Object.keys(this.db.data.collections) + } + + /** + * 删除一个集合 + * @param {string} name 要删除的集合名称 + * @returns {Promise} 是否成功删除 + */ + async dropCollection (name) { + this._checkInit() + + if (this.db.data.collections[name]) { + delete this.db.data.collections[name] + await this.db.write() + return true + } + return false + } + + /** + * 检查存储是否已初始化 + * @private + */ + _checkInit () { + if (!this.initialized) { + throw new Error('存储尚未初始化,请先调用 init() 方法') + } + } +} + +/** + * 集合类,提供对特定数据集合的操作 + */ +export class LowDBCollection { + /** + * 创建一个集合实例 + * @param {LowDBStorage} storage 所属存储实例 + * @param {string} name 集合名称 + */ + constructor (storage, name) { + this.storage = storage + this.name = name + } + + /** + * 获取集合数据引用 + * @private + */ + get _collection () { + return this.storage.db.data.collections[this.name] + } + + /** + * 保存数据到存储 + * @private + */ + async _save () { + return this.storage.db.write() + } + + /** + * 生成唯一ID + * @private + */ + _generateId () { + return Date.now().toString(36) + Math.random().toString(36).substring(2, 15) + } + + /** + * 创建新文档 + * @param {Object} doc 要插入的文档 + * @returns {Promise} 插入的文档(带ID) + */ + async insert (doc) { + // 生成唯一ID,如果没有提供 + if (!doc.id) { + doc.id = this._generateId() + } + + // 加上时间戳 + if (!doc.createdAt) { + doc.createdAt = new Date().toISOString() + } + + doc.updatedAt = new Date().toISOString() + + // 添加到集合 + this._collection.push(doc) + await this._save() + + return doc + } + + /** + * 批量插入多个文档 + * @param {Object[]} docs 要插入的文档数组 + * @returns {Promise} 插入的文档(带ID) + */ + async insertMany (docs) { + const inserted = [] + + for (const doc of docs) { + inserted.push(await this.insert(doc)) + } + + return inserted + } + + /** + * 根据ID查找单个文档 + * @param {string} id 文档ID + * @returns {Promise} 查找到的文档或null + */ + async findById (id) { + return this._collection.find(doc => doc.id === id) || null + } + + /** + * 返回集合中的所有文档 + * @returns {Promise} 文档数组 + */ + async findAll () { + return [...this._collection] + } + + /** + * 根据条件查找文档 + * @param {Object} query 查询条件(字段等值匹配) + * @returns {Promise} 匹配的文档数组 + */ + async find (query = {}) { + return this._collection.filter(doc => { + for (const key in query) { + const value = query[key] + + // 处理嵌套属性 (例如 user.profile.name) + if (key.includes('.')) { + const parts = key.split('.') + let current = doc + + for (let i = 0; i < parts.length; i++) { + if (current === undefined || current === null) return false + current = current[parts[i]] + } + + if (current !== value) return false + } else if (doc[key] !== value) { + return false + } + } + return true + }) + } + + /** + * 根据条件查找单个文档 + * @param {Object} query 查询条件 + * @returns {Promise} 第一个匹配的文档或null + */ + async findOne (query = {}) { + const results = await this.find(query) + return results.length > 0 ? results[0] : null + } + + /** + * 使用自定义函数进行高级查询 + * @param {Function} filterFn 过滤函数 + * @returns {Promise} 匹配的文档数组 + */ + async findWhere (filterFn) { + return this._collection.filter(filterFn) + } + + /** + * 根据ID更新文档 + * @param {string} id 文档ID + * @param {Object} updates 要更新的字段 + * @returns {Promise} 更新后的文档或null + */ + async updateById (id, updates) { + const index = this._collection.findIndex(doc => doc.id === id) + + if (index === -1) return null + + // 防止覆盖ID + const { id: _, ...safeUpdates } = updates + + // 更新文档 + const updatedDoc = { + ...this._collection[index], + ...safeUpdates, + updatedAt: new Date().toISOString() + } + + this._collection[index] = updatedDoc + await this._save() + + return updatedDoc + } + + /** + * 根据条件更新文档 + * @param {Object} query 查询条件 + * @param {Object} updates 要更新的字段 + * @returns {Promise} 更新的文档数量 + */ + async update (query, updates) { + const matches = await this.find(query) + let updated = 0 + + for (const doc of matches) { + await this.updateById(doc.id, updates) + updated++ + } + + return updated + } + + /** + * 根据ID删除文档 + * @param {string} id 文档ID + * @returns {Promise} 是否成功删除 + */ + async deleteById (id) { + const index = this._collection.findIndex(doc => doc.id === id) + + if (index === -1) return false + + this._collection.splice(index, 1) + await this._save() + + return true + } + + /** + * 根据条件删除文档 + * @param {Object} query 查询条件 + * @returns {Promise} 删除的文档数量 + */ + async delete (query) { + const before = this._collection.length + + const remaining = this._collection.filter(doc => { + for (const key in query) { + if (doc[key] !== query[key]) { + return true // 保留不匹配的 + } + } + return false // 删除匹配的 + }) + + this.storage.db.data.collections[this.name] = remaining + await this._save() + + return before - remaining.length + } + + /** + * 清空集合中的所有文档 + * @returns {Promise} 删除的文档数量 + */ + async deleteAll () { + const count = this._collection.length + this.storage.db.data.collections[this.name] = [] + await this._save() + return count + } + + /** + * 返回集合中文档的数量 + * @returns {Promise} 文档数量 + */ + async count (query = {}) { + if (Object.keys(query).length === 0) { + return this._collection.length + } + + const matches = await this.find(query) + return matches.length + } +} + +const storageLocation = path.resolve(dataDir, 'storage.json') +if (!fs.existsSync(storageLocation)) { + fs.writeFileSync(storageLocation, JSON.stringify({ collections: {} })) +} + +const ChatGPTStorage = new LowDBStorage({ + filename: 'storage.json', + directory: dataDir +}) + +if (ChatGPTStorage.db.data.collections.history) { + ChatGPTStorage.dropCollection('history').then(() => { + logger.debug('drop older version history collection') + }).catch(err => { + logger.warn('failed to drop older version history collection', err) + }) +} + +export const ChatGPTHistoryStorage = new LowDBStorage({ + filename: 'history.json', + directory: dataDir +}) + +export default ChatGPTStorage diff --git a/models/chaite/storage/lowdb/tool_groups_storage.js b/models/chaite/storage/lowdb/tool_groups_storage.js new file mode 100644 index 0000000..94b225b --- /dev/null +++ b/models/chaite/storage/lowdb/tool_groups_storage.js @@ -0,0 +1,70 @@ +import { ChaiteStorage, ToolsGroupDTO } from 'chaite' + +/** + * @extends {ChaiteStorage} + */ +export class LowDBToolsGroupDTOsStorage extends ChaiteStorage { + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('tool_groups') + } + + /** + * + * @param key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new ToolsGroupDTO(obj) + } + + /** + * + * @param {string} id + * @param {import('chaite').ToolsGroupDTO} preset + * @returns {Promise} + */ + async setItem (id, preset) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, preset) + return id + } + const result = await this.collection.insert(preset) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new ToolsGroupDTO({}).fromString(JSON.stringify(item))) + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/lowdb/tools_storage.js b/models/chaite/storage/lowdb/tools_storage.js new file mode 100644 index 0000000..e147469 --- /dev/null +++ b/models/chaite/storage/lowdb/tools_storage.js @@ -0,0 +1,111 @@ +import { ChaiteStorage, ToolDTO } from 'chaite' + +/** + * @extends {ChaiteStorage} + */ +export class LowDBToolsStorage extends ChaiteStorage { + getName () { + return 'LowDBToolsStorage' + } + + /** + * + * @param { LowDBStorage } storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('tools') + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new ToolDTO(obj) + } + + /** + * + * @param {string} id + * @param {import('chaite').ToolDTO} tools + * @returns {Promise} + */ + async setItem (id, tools) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, tools) + return id + } + const result = await this.collection.insert(tools) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new ToolDTO({}).fromString(JSON.stringify(item))) + } + + /** + * + * @param {Record} filter + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + const allList = await this.listItems() + return allList.filter(item => { + for (const key in filter) { + if (item[key] !== filter[key]) { + return false + } + } + return true + }) + } + + /** + * + * @param {Array<{ + * field: string; + * values: unknown[]; + * }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + const allList = await this.listItems() + return allList.filter(item => { + for (const { field, values } of query) { + if (!values.includes(item[field])) { + return false + } + } + return true + }) + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/lowdb/trigger_storage,.js b/models/chaite/storage/lowdb/trigger_storage,.js new file mode 100644 index 0000000..b444d0c --- /dev/null +++ b/models/chaite/storage/lowdb/trigger_storage,.js @@ -0,0 +1,122 @@ +import { ChaiteStorage, TriggerDTO } from 'chaite' + +/** + * @extends {ChaiteStorage} + */ +export class LowDBTriggerStorage extends ChaiteStorage { + getName () { + return 'LowDBTriggerStorage' + } + + /** + * @param {LowDBStorage} storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('triggers') + } + + /** + * 获取单个触发器 + * @param {string} key + * @returns {Promise} + */ + async getItem (key) { + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new TriggerDTO(obj) + } + + /** + * 保存触发器 + * @param {string} id + * @param {import('chaite').TriggerDTO} trigger + * @returns {Promise} + */ + async setItem (id, trigger) { + // 设置或更新时间戳 + if (!trigger.createdAt) { + trigger.createdAt = new Date().toISOString() + } + trigger.updatedAt = new Date().toISOString() + + if (id && await this.getItem(id)) { + await this.collection.updateById(id, trigger) + return id + } + const result = await this.collection.insert(trigger) + return result.id + } + + /** + * 删除触发器 + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * 获取所有触发器 + * @returns {Promise} + */ + async listItems () { + const list = await this.collection.findAll() + return list.map(item => new TriggerDTO({}).fromString(JSON.stringify(item))) + } + + /** + * 根据条件筛选触发器 + * @param {Record} filter + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + const allList = await this.listItems() + return allList.filter(item => { + for (const key in filter) { + if (item[key] !== filter[key]) { + return false + } + } + return true + }) + } + + /** + * 根据IN条件筛选触发器 + * @param {Array<{ + * field: string; + * values: unknown[]; + * }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + const allList = await this.listItems() + return allList.filter(item => { + for (const { field, values } of query) { + if (!values.includes(item[field])) { + return false + } + } + return true + }) + } + + /** + * 清空所有触发器 + * @returns {Promise} + */ + async clear () { + await this.collection.deleteAll() + } +} + +export default LowDBTriggerStorage diff --git a/models/chaite/storage/lowdb/user_state_storage.js b/models/chaite/storage/lowdb/user_state_storage.js new file mode 100644 index 0000000..d1e8bc6 --- /dev/null +++ b/models/chaite/storage/lowdb/user_state_storage.js @@ -0,0 +1,84 @@ +import { ChaiteStorage } from 'chaite' +import * as crypto from 'node:crypto' + +/** + * 继承UserState + */ +export class YunzaiUserState { + constructor (userId, nickname, card, conversationId = crypto.randomUUID()) { + this.userId = userId + this.nickname = nickname + this.card = card + this.conversations = [] + this.settings = {} + this.current = { + conversationId, + messageId: crypto.randomUUID() + } + } +} + +/** + * @extends {ChaiteStorage} + */ +export class LowDBUserStateStorage extends ChaiteStorage { + /** + * + * @param {LowDBStorage} storage + */ + constructor (storage) { + super() + this.storage = storage + /** + * 集合 + * @type {LowDBCollection} + */ + this.collection = this.storage.collection('user_states') + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async getItem (key) { + return this.collection.findOne({ id: key }) + } + + /** + * + * @param {string} id + * @param {import('chaite').UserState} state + * @returns {Promise} + */ + async setItem (id, state) { + if (id && await this.getItem(id)) { + await this.collection.updateById(id, state) + return id + } + state.id = id + const result = await this.collection.insert(state) + return result.id + } + + /** + * + * @param {string} key + * @returns {Promise} + */ + async removeItem (key) { + await this.collection.deleteById(key) + } + + /** + * + * @returns {Promise} + */ + async listItems () { + return this.collection.findAll() + } + + async clear () { + await this.collection.deleteAll() + } +} diff --git a/models/chaite/storage/sqlite/channel_storage.js b/models/chaite/storage/sqlite/channel_storage.js new file mode 100644 index 0000000..c1d091d --- /dev/null +++ b/models/chaite/storage/sqlite/channel_storage.js @@ -0,0 +1,528 @@ +import { ChaiteStorage, Channel } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteChannelStorage extends ChaiteStorage { + getName () { + return 'SQLiteChannelStorage' + } + + /** + * + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'channels' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建Channel表,将主要属性分列存储 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + adapterType TEXT NOT NULL, + type TEXT NOT NULL, + weight INTEGER DEFAULT 1, + priority INTEGER DEFAULT 0, + status TEXT DEFAULT 'enabled', + disabledReason TEXT, + models TEXT, + options TEXT, + statistics TEXT, + uploader TEXT, + cloudId INTEGER, + createdAt TEXT, + updatedAt TEXT, + md5 TEXT, + embedded INTEGER DEFAULT 0, + extra TEXT -- 存储其他额外数据的JSON + )`, (err) => { + if (err) { + return reject(err) + } + + // 创建索引提高查询性能 + const promises = [ + // 按类型和状态索引 + new Promise((resolve, reject) => { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_type ON ${this.tableName} (type)`, err => { + if (err) reject(err) + else resolve() + }) + }), + new Promise((resolve, reject) => { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_status ON ${this.tableName} (status)`, err => { + if (err) reject(err) + else resolve() + }) + }) + ] + + Promise.all(promises) + .then(() => { + this.initialized = true + resolve() + }) + .catch(reject) + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 将 Channel 对象转换为数据库记录 + * @param {import('chaite').Channel} channel + * @returns {Object} 数据库记录 + */ + _channelToRecord (channel) { + // 提取主要字段 + const { + id, name, description, adapterType, type, weight, priority, + status, disabledReason, models, options, statistics, + uploader, cloudId, createdAt, updatedAt, md5, embedded, ...rest + } = channel + + return { + id: id || '', + name: name || '', + description: description || '', + adapterType: adapterType || type || '', + type: type || '', + weight: weight || 1, + priority: priority || 0, + status: status || 'enabled', + disabledReason: disabledReason || null, + models: Array.isArray(models) ? JSON.stringify(models) : '[]', + options: options ? JSON.stringify(options) : null, + statistics: statistics ? JSON.stringify(statistics) : null, + uploader: uploader ? JSON.stringify(uploader) : null, + cloudId: cloudId || null, + createdAt: createdAt || '', + updatedAt: updatedAt || '', + md5: md5 || '', + embedded: embedded ? 1 : 0, + extra: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null + } + } + + /** + * 将数据库记录转换为 Channel 对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').Channel} Channel 对象 + */ + _recordToChannel (record) { + if (!record) return null + + // 解析JSON字段 + let models = [] + try { + if (record.models) { + models = JSON.parse(record.models) + } + } catch (e) { + // 解析错误,使用空数组 + } + + let options = {} + try { + if (record.options) { + options = JSON.parse(record.options) + } + } catch (e) { + // 解析错误,使用空对象 + } + + let statistics = {} + try { + if (record.statistics) { + statistics = JSON.parse(record.statistics) + } + } catch (e) { + // 解析错误,使用空对象 + } + + let uploader = null + try { + if (record.uploader) { + uploader = JSON.parse(record.uploader) + } + } catch (e) { + // 解析错误,使用null + } + + let extra = {} + try { + if (record.extra) { + extra = JSON.parse(record.extra) + } + } catch (e) { + // 解析错误,使用空对象 + } + + // 构造Channel对象 + const channelData = { + id: record.id, + name: record.name, + description: record.description, + adapterType: record.adapterType, + type: record.type, + weight: Number(record.weight), + priority: Number(record.priority), + status: record.status, + disabledReason: record.disabledReason, + models, + options, + statistics, + uploader, + cloudId: record.cloudId, + createdAt: record.createdAt, + updatedAt: record.updatedAt, + md5: record.md5, + embedded: Boolean(record.embedded), + ...extra + } + + return new Channel(channelData) + } + + /** + * 获取单个渠道 + * @param {string} key 渠道ID + * @returns {Promise} + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) { + return reject(err) + } + + const channel = this._recordToChannel(row) + resolve(channel) + }) + }) + } + + /** + * 保存渠道 + * @param {string} id 渠道ID + * @param {import('chaite').Channel} channel 渠道对象 + * @returns {Promise} + */ + async setItem (id, channel) { + await this.ensureInitialized() + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!channel.createdAt) { + channel.createdAt = new Date().toISOString() + } + + channel.updatedAt = new Date().toISOString() + // 转换为数据库记录 + const record = this._channelToRecord(channel) + record.id = id // 确保ID是指定的ID + + // 构建插入或更新SQL + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + const duplicateValues = [...values] // 用于ON CONFLICT时的更新 + + return new Promise((resolve, reject) => { + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) + VALUES (${placeholders}) + ON CONFLICT(id) DO UPDATE SET ${updates}`, + [...values, ...duplicateValues], + function (err) { + if (err) { + return reject(err) + } + resolve(id) + } + ) + }) + } + + /** + * 删除渠道 + * @param {string} key 渠道ID + * @returns {Promise} + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 查询所有渠道 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const channels = rows.map(row => this._recordToChannel(row)).filter(Boolean) + resolve(channels) + }) + }) + } + + /** + * 根据条件筛选渠道 + * @param {Record} filter 筛选条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 如果没有筛选条件,返回所有 + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + // 尝试使用SQL字段直接过滤 + const directFields = ['id', 'name', 'description', 'adapterType', 'type', 'status', 'cloudId'] + const numericFields = ['weight', 'priority'] + const sqlFilters = [] + const sqlParams = [] + const extraFilters = {} + let hasExtraFilters = false + + // 区分数据库字段和额外字段 + for (const key in filter) { + const value = filter[key] + + // 如果是直接支持的字段,构建SQL条件 + if (directFields.includes(key)) { + sqlFilters.push(`${key} = ?`) + sqlParams.push(value) + } else if (numericFields.includes(key)) { + // 数值型字段 + sqlFilters.push(`${key} = ?`) + sqlParams.push(Number(value)) + } else if (key === 'embedded') { + // embedded 字段需要特殊处理为 0/1 + sqlFilters.push('embedded = ?') + sqlParams.push(value ? 1 : 0) + } else if (key === 'models' && typeof value === 'string') { + // models字段需要特殊处理,判断是否包含某模型 + // 注意:这种方式仅适用于单个模型的查询,不适用于完全匹配数组 + sqlFilters.push('models LIKE ?') + sqlParams.push(`%${value}%`) + } else { + // 其他字段需要在结果中进一步过滤 + extraFilters[key] = value + hasExtraFilters = true + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let channels = rows.map(row => this._recordToChannel(row)).filter(Boolean) + + // 如果有需要在内存中过滤的额外���段 + if (hasExtraFilters) { + channels = channels.filter(channel => { + for (const key in extraFilters) { + if (channel[key] !== extraFilters[key]) { + return false + } + } + return true + }) + } + + resolve(channels) + }) + }) + } + + /** + * 根据IN条件筛选渠道 + * @param {Array<{ field: string; values: unknown[]; }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + // 如果没有查询条件,返回所有 + if (!query || query.length === 0) { + return this.listItems() + } + + // 尝试使用SQL IN子句来优化查询 + const directFields = ['id', 'name', 'description', 'adapterType', 'type', 'status', 'cloudId'] + const numericFields = ['weight', 'priority'] + const sqlFilters = [] + const sqlParams = [] + const extraQueries = [] + + // 处理每个查询条件 + for (const { field, values } of query) { + if (values.length === 0) continue + + // 如果是直接支持的字段,使用SQL IN子句 + if (directFields.includes(field)) { + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values) + } else if (numericFields.includes(field)) { + // 数值型字段 + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values.map(v => Number(v))) + } else if (field === 'embedded') { + // embedded 字段需要特殊处理 + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`embedded IN (${placeholders})`) + sqlParams.push(...boolValues) + } else if (field === 'models') { + // models字段需要特殊处理,判断是否包含某模型 + // 由于无法直接使用IN查询JSON字段,这里使用OR和LIKE的组合 + const modelFilters = values.map(() => 'models LIKE ?').join(' OR ') + sqlFilters.push(`(${modelFilters})`) + values.forEach(value => { + sqlParams.push(`%${value}%`) + }) + } else { + // 其他字段在内存中过滤 + extraQueries.push({ field, values }) + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let channels = rows.map(row => this._recordToChannel(row)).filter(Boolean) + + // 如果有需要在内存中过滤的条件 + if (extraQueries.length > 0) { + channels = channels.filter(channel => { + for (const { field, values } of extraQueries) { + if (!values.includes(channel[field])) { + return false + } + } + return true + }) + } + + resolve(channels) + }) + }) + } + + /** + * 清空表中所有数据 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/chat_preset_storage.js b/models/chaite/storage/sqlite/chat_preset_storage.js new file mode 100644 index 0000000..8cc2e44 --- /dev/null +++ b/models/chaite/storage/sqlite/chat_preset_storage.js @@ -0,0 +1,523 @@ +import { ChaiteStorage, ChatPreset } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteChatPresetStorage extends ChaiteStorage { + getName () { + return 'SQLiteChatPresetStorage' + } + + /** + * + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'chat_presets' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建 ChatPreset 表,将主要属性分列存储 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + prefix TEXT NOT NULL, + local INTEGER DEFAULT 1, + namespace TEXT, + sendMessageOption TEXT NOT NULL, + cloudId INTEGER, + createdAt TEXT, + updatedAt TEXT, + md5 TEXT, + embedded INTEGER DEFAULT 0, + uploader TEXT, + extraData TEXT + )`, (err) => { + if (err) { + return reject(err) + } + + // 创建索引提高查询性能 + const promises = [ + new Promise((resolve, reject) => { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_prefix ON ${this.tableName} (prefix)`, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }), + new Promise((resolve, reject) => { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_name ON ${this.tableName} (name)`, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) + ] + + Promise.all(promises) + .then(() => { + this.initialized = true + resolve() + }) + .catch(reject) + }) + }) + }) + } + + /** + * 确保���据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 将 ChatPreset 对象转换为数据库记录 + * @param {import('chaite').ChatPreset} preset + * @returns {Object} 数据库记录 + */ + _presetToRecord (preset) { + // 提取主要字段 + const { + id, name, description, prefix, local, namespace, + sendMessageOption, cloudId, createdAt, updatedAt, md5, + embedded, uploader, ...rest + } = preset + + return { + id: id || '', + name: name || '', + description: description || '', + prefix: prefix || '', + local: local === false ? 0 : 1, + namespace: namespace || null, + sendMessageOption: JSON.stringify(sendMessageOption || {}), + cloudId: cloudId || null, + createdAt: createdAt || '', + updatedAt: updatedAt || '', + md5: md5 || '', + embedded: embedded ? 1 : 0, + uploader: uploader ? JSON.stringify(uploader) : null, + extraData: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null + } + } + + /** + * 将数���库记录转换为 ChatPreset 对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').ChatPreset} ChatPreset 对象 + */ + _recordToPreset (record) { + if (!record) return null + + // 解析 JSON 字�� + let sendMessageOption = {} + try { + if (record.sendMessageOption) { + sendMessageOption = JSON.parse(record.sendMessageOption) + } + } catch (e) { + // 解析错误,使用空对象 + } + + let uploader = null + try { + if (record.uploader) { + uploader = JSON.parse(record.uploader) + } + } catch (e) { + // 解析错误,使用 null + } + + let extraData = {} + try { + if (record.extraData) { + extraData = JSON.parse(record.extraData) + } + } catch (e) { + // 解析错误,使用空对象 + } + + // 构造 ChatPreset 对象 + const presetData = { + id: record.id, + name: record.name, + description: record.description, + prefix: record.prefix, + local: Boolean(record.local), + namespace: record.namespace, + sendMessageOption, + cloudId: record.cloudId, + createdAt: record.createdAt, + updatedAt: record.updatedAt, + md5: record.md5, + embedded: Boolean(record.embedded), + uploader, + ...extraData + } + + return new ChatPreset(presetData) + } + + /** + * 获取单个聊天预设 + * @param {string} key 预设ID + * @returns {Promise} + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) { + return reject(err) + } + + const preset = this._recordToPreset(row) + resolve(preset) + }) + }) + } + + /** + * 保存聊天预设 + * @param {string} id 预设ID + * @param {import('chaite').ChatPreset} preset 预设对象 + * @returns {Promise} + */ + async setItem (id, preset) { + await this.ensureInitialized() + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!preset.createdAt) { + preset.createdAt = new Date().toISOString() + } + + preset.updatedAt = new Date().toISOString() + // 转换为数据库记录 + const record = this._presetToRecord(preset) + record.id = id // 确保ID是指定的ID + + // 构建插入或更新SQL + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + const duplicateValues = [...values] // 用于ON CONFLICT时的更新 + + return new Promise((resolve, reject) => { + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) + VALUES (${placeholders}) + ON CONFLICT(id) DO UPDATE SET ${updates}`, + [...values, ...duplicateValues], + function (err) { + if (err) { + return reject(err) + } + resolve(id) + } + ) + }) + } + + /** + * 删除聊天预设 + * @param {string} key 预设ID + * @returns {Promise} + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 查询所有聊天预设 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const presets = rows.map(row => this._recordToPreset(row)).filter(Boolean) + resolve(presets) + }) + }) + } + + /** + * 根据条件筛选聊天预设 + * @param {Record} filter 筛选条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 如果没有筛选条件,返回所有 + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + // 尝试使用SQL字段直接过滤 + const directFields = ['id', 'name', 'description', 'prefix', 'namespace', 'cloudId'] + const sqlFilters = [] + const sqlParams = [] + const extraFilters = {} + let hasExtraFilters = false + + // 区分数据库字段和额外字段 + for (const key in filter) { + const value = filter[key] + + // 如果是直接支持的字段,构建SQL条件 + if (directFields.includes(key)) { + sqlFilters.push(`${key} = ?`) + sqlParams.push(value) + } else if (key === 'local') { + // local 字段需要特殊处理为 0/1 + sqlFilters.push('local = ?') + sqlParams.push(value ? 1 : 0) + } else if (key === 'embedded') { + // embedded 字段需要特殊处理为 0/1 + sqlFilters.push('embedded = ?') + sqlParams.push(value ? 1 : 0) + } else { + // 其他字段需要在结果中进一步过滤 + extraFilters[key] = value + hasExtraFilters = true + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let presets = rows.map(row => this._recordToPreset(row)).filter(Boolean) + + // 如果有需要在内存中过滤的额外字段 + if (hasExtraFilters) { + presets = presets.filter(preset => { + for (const key in extraFilters) { + const filterValue = extraFilters[key] + + // 处理 sendMessageOption 字段的深层过滤 + if (key.startsWith('sendMessageOption.')) { + const optionKey = key.split('.')[1] + if (preset.sendMessageOption && preset.sendMessageOption[optionKey] !== filterValue) { + return false + } + } else if (preset[key] !== filterValue) { + // 其他字段直接比较 + return false + } + } + return true + }) + } + + resolve(presets) + }) + }) + } + + /** + * 根据IN条件筛选聊天预设 + * @param {Array<{ field: string; values: unknown[]; }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + // 如果没有查询条件,返回所有 + if (!query || query.length === 0) { + return this.listItems() + } + + // 尝试使用SQL IN子句来优化查询 + const directFields = ['id', 'name', 'description', 'prefix', 'namespace', 'cloudId'] + const sqlFilters = [] + const sqlParams = [] + const extraQueries = [] + + // 处理每个查询条件 + for (const { field, values } of query) { + if (values.length === 0) continue + + // 如果是直接支持的字段,使用SQL IN子句 + if (directFields.includes(field)) { + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values) + } else if (field === 'local') { + // local 字段需要特殊处理 + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`local IN (${placeholders})`) + sqlParams.push(...boolValues) + } else if (field === 'embedded') { + // embedded 字段需要特殊处理 + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`embedded IN (${placeholders})`) + sqlParams.push(...boolValues) + } else { + // 其他字段在内存中过滤 + extraQueries.push({ field, values }) + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let presets = rows.map(row => this._recordToPreset(row)).filter(Boolean) + + // 如果有需要在内存中过滤的条件 + if (extraQueries.length > 0) { + presets = presets.filter(preset => { + for (const { field, values } of extraQueries) { + // 处��� sendMessageOption 字段的深层过滤 + if (field.startsWith('sendMessageOption.')) { + const optionKey = field.split('.')[1] + const presetValue = preset.sendMessageOption?.[optionKey] + if (!values.includes(presetValue)) { + return false + } + } else if (!values.includes(preset[field])) { + // 其他字段直接比较 + return false + } + } + return true + }) + } + + resolve(presets) + }) + }) + } + + /** + * 根据前缀获取聊天预设 + * @param {string} prefix 前缀 + * @returns {Promise} + */ + async getPresetByPrefix (prefix) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE prefix = ?`, [prefix], (err, row) => { + if (err) { + return reject(err) + } + + const preset = this._recordToPreset(row) + resolve(preset) + }) + }) + } + + /** + * 清空表中所有数据 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/history_manager.js b/models/chaite/storage/sqlite/history_manager.js new file mode 100644 index 0000000..4344880 --- /dev/null +++ b/models/chaite/storage/sqlite/history_manager.js @@ -0,0 +1,596 @@ +import { AbstractHistoryManager } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import crypto from 'crypto' + +export class SQLiteHistoryManager extends AbstractHistoryManager { + /** + * + * @param {string} dbPath 数据库文件路径 + * @param {string} imagesDir 图片存储目录,默认为数据库同级的 images 目录 + */ + constructor (dbPath, imagesDir) { + super() + this.dbPath = dbPath + this.imagesDir = imagesDir || path.join(path.dirname(dbPath), 'images') + this.db = null + this.initialized = false + this.tableName = 'history' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + // 确保图片目录存在 + if (!fs.existsSync(this.imagesDir)) { + fs.mkdirSync(this.imagesDir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建 history 表 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + parentId TEXT, + conversationId TEXT, + role TEXT, + messageData TEXT, + createdAt TEXT + )`, (err) => { + if (err) { + return reject(err) + } + + // 创建索引,加速查询 + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_conversation ON ${this.tableName} (conversationId)`, (err) => { + if (err) { + return reject(err) + } + + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_parent ON ${this.tableName} (parentId)`, (err) => { + if (err) { + return reject(err) + } + + this.initialized = true + resolve() + }) + }) + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 计算文本的md5值 + * @param {string} text + * @returns {string} + */ + _getMd5 (text) { + return crypto.createHash('md5').update(text).digest('hex') + } + + /** + * 是否为base64编码的图片 + * @param {string} str + * @returns {boolean} + */ + _isBase64Image (str) { + if (!str || typeof str !== 'string') { + return false + } + + // 处理带前缀的 base64 格式 + if (str.startsWith('data:image/')) { + return true + } + + // 处理纯 base64 字符串 + // base64 编码只会包含字母、数字、+、/,以及末尾可能有 = 或 == 用于填充 + return /^[A-Za-z0-9+/]+={0,2}$/.test(str) + } + + /** + * 从base64提取图片的mime类型,或使用默认类型 + * @param {string} base64 + * @param {string} defaultMimeType 默认 MIME 类型 + * @returns {string} + */ + _getMimeTypeFromBase64 (base64, defaultMimeType = 'image/jpeg') { + if (base64 && base64.startsWith('data:image/')) { + const match = base64.match(/^data:(image\/[a-zA-Z+]+);base64,/) + if (match) { + return match[1] + } + } + return defaultMimeType // 对于纯 base64 字符串,使用默认类型 + } + + /** + * 获取图片扩展名 + * @param {string} mimeType + * @returns {string} + */ + _getExtensionFromMimeType (mimeType) { + const map = { + 'image/jpeg': '.jpg', + 'image/png': '.png', + 'image/gif': '.gif', + 'image/webp': '.webp', + 'image/svg+xml': '.svg' + } + return map[mimeType] || '.png' + } + + /** + * 处理消息中的图片内容,将base64图片保存到本地文件 + * @param {object} message + * @returns {object} 处理后的消息对象 + */ + _processMessageImages (message) { + if (!message.content || !Array.isArray(message.content)) { + return message + } + + // 深拷贝避免修改原对象 + const processedMessage = JSON.parse(JSON.stringify(message)) + + processedMessage.content = processedMessage.content.map(item => { + if (item.type === 'image' && item.image) { + // 检查是否是base64图片数据 + if (this._isBase64Image(item.image)) { + let base64Data = item.image + let mimeType = item.mimeType || 'image/jpeg' // 使用项目指定的 MIME 类型或默认值 + + // 如果是data:image格式,提取纯base64部分 + if (base64Data.startsWith('data:')) { + const parts = base64Data.split(',') + if (parts.length > 1) { + base64Data = parts[1] + // 更新 MIME 类型 + mimeType = this._getMimeTypeFromBase64(item.image, mimeType) + } + } + + try { + // 计算MD5 + const md5 = this._getMd5(base64Data) + const ext = this._getExtensionFromMimeType(mimeType) + const filePath = path.join(this.imagesDir, `${md5}${ext}`) + + // 如果文件不存在,则保存 + if (!fs.existsSync(filePath)) { + fs.writeFileSync(filePath, Buffer.from(base64Data, 'base64')) + } + + // 替换为引用格式: $image:md5:ext + item.image = `$image:${md5}:${ext}` + item._type = mimeType // 保存原始类型 + } catch (error) { + console.error('保存图片失败:', error) + } + } + } + return item + }) + + return processedMessage + } + + /** + * 恢复消息中的图片引用,转换回base64 + * @param {object} message + * @returns {object} 处理后的消息对象 + */ + _restoreMessageImages (message) { + if (!message || !message.content || !Array.isArray(message.content)) { + return message + } + + // 深拷贝避免修改原对象 + const restoredMessage = JSON.parse(JSON.stringify(message)) + + // 标记是否需要添加[图片]文本 + let needImageText = true + let hasRemovedImage = false + + restoredMessage.content = restoredMessage.content.filter((item, index) => { + if (item.type === 'image' && item.image && typeof item.image === 'string') { + // 检查是否是图片引用格式 + const match = item.image.match(/^\$image:([a-f0-9]+):(\.[a-z]+)$/) + if (match) { + // eslint-disable-next-line no-unused-vars + const [_, md5, ext] = match + const filePath = path.join(this.imagesDir, `${md5}${ext}`) + + // 检查文件是否存在 + if (fs.existsSync(filePath)) { + try { + // 读取文件并转换为base64 + const imageBuffer = fs.readFileSync(filePath) + item.image = imageBuffer.toString('base64') + return true + } catch (error) { + console.error('读取图片文件失败:', filePath, error) + hasRemovedImage = true + return false + } + } else { + // 文件不存在,删除这个image元素 + hasRemovedImage = true + return false + } + } + } + if (item.type === 'text') { + needImageText = false + } + return true + }) + + // 如果移除了图片且没有文本内容,添加[图片]提示 + if (hasRemovedImage) { + if (restoredMessage.content.length === 0) { + restoredMessage.content.push({ + type: 'text', + text: '[图片]' + }) + } else if (needImageText) { + // 查找第一个文本元素 + const textIndex = restoredMessage.content.findIndex(item => item.type === 'text') + if (textIndex !== -1) { + restoredMessage.content[textIndex].text = `[图片] ${restoredMessage.content[textIndex].text}` + } else { + // 如果没有文本元素,添加一个 + restoredMessage.content.unshift({ + type: 'text', + text: '[图片]' + }) + } + } + } + + return restoredMessage + } + + /** + * 将消息对象转换为数据库记录 + * @param {import('chaite').HistoryMessage} message + * @param {string} conversationId + * @returns {Object} 数据库记录 + */ + _messageToRecord (message, conversationId) { + // 处理图片,将base64图片保存到本地文件 + const processedMessage = this._processMessageImages(message) + + // 将 content 和 toolCalls 等转为 JSON + const { id, parentId, role } = processedMessage + const messageData = JSON.stringify(processedMessage) + + return { + id: id || '', + parentId: parentId || null, + conversationId: conversationId || '', + role: role || '', + messageData, + createdAt: new Date().toISOString() + } + } + + /** + * 将数据库记录转换为消息对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').HistoryMessage} 消息对象 + */ + _recordToMessage (record) { + if (!record) return null + + try { + // 解析存储的消息数据 + const message = JSON.parse(record.messageData) + + // 恢复图片引用为base64 + return this._restoreMessageImages(message) + } catch (e) { + // 解析失败,尝试构造最小结构 + return { + id: record.id, + parentId: record.parentId, + role: record.role, + conversationId: record.conversationId, + content: [] + } + } + } + + /** + * 保存历史消息 + * @param {import('chaite').HistoryMessage} message 消息对象 + * @param {string} conversationId 会话ID + * @returns {Promise} + */ + async saveHistory (message, conversationId) { + await this.ensureInitialized() + + const record = this._messageToRecord(message, conversationId) + + return new Promise((resolve, reject) => { + // 检查消息是否已存在 + if (message.id) { + this.db.get(`SELECT id FROM ${this.tableName} WHERE id = ?`, [message.id], (err, row) => { + if (err) { + return reject(err) + } + + if (row) { + // 消息已存在,更新 + const fields = Object.keys(record) + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + + this.db.run(`UPDATE ${this.tableName} SET ${updates} WHERE id = ?`, [...values, message.id], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + } else { + // 消息不存在,插入 + this._insertMessage(record, resolve, reject) + } + }) + } else { + // 没有ID,直接插入 + this._insertMessage(record, resolve, reject) + } + }) + } + + /** + * 内部方法:插入消息记录 + * @private + */ + _insertMessage (record, resolve, reject) { + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const values = fields.map(field => record[field]) + + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) VALUES (${placeholders})`, + values, + function (err) { + if (err) { + return reject(err) + } + resolve() + } + ) + } + + /** + * 获取历史消息 + * @param {string} messageId 消息ID + * @param {string} conversationId 会话ID + * @returns {Promise} + */ + async getHistory (messageId, conversationId) { + await this.ensureInitialized() + + if (messageId) { + return this._getMessageChain(messageId) + } else if (conversationId) { + return this._getConversationMessages(conversationId) + } + return [] + } + + /** + * 获取消息链(从指定消息追溯到根消息) + * @private + */ + async _getMessageChain (messageId) { + return new Promise((resolve, reject) => { + const messages = [] + const getMessageById = (id) => { + if (!id) { + resolve(messages) + return + } + + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id], (err, row) => { + if (err) { + return reject(err) + } + + if (!row) { + resolve(messages) + return + } + + const message = this._recordToMessage(row) + messages.unshift(message) // 将消息添加到数组开头 + + getMessageById(row.parentId) // 递归获取父消息 + }) + } + + getMessageById(messageId) + }) + } + + /** + * 获取会话中的所有消息 + * @private + */ + async _getConversationMessages (conversationId) { + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName} WHERE conversationId = ? ORDER BY createdAt`, [conversationId], (err, rows) => { + if (err) { + return reject(err) + } + + const messages = rows.map(row => this._recordToMessage(row)).filter(Boolean) + resolve(messages) + }) + }) + } + + /** + * 删除会话 + * @param {string} conversationId 会话ID + * @returns {Promise} + */ + async deleteConversation (conversationId) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE conversationId = ?`, [conversationId], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 获取单条历史消息 + * @param {string} messageId 消息ID + * @param {string} conversationId 会话ID + * @returns {Promise} + */ + async getOneHistory (messageId, conversationId) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + const conditions = [] + const params = [] + + if (messageId) { + conditions.push('id = ?') + params.push(messageId) + } + + if (conversationId) { + conditions.push('conversationId = ?') + params.push(conversationId) + } + + if (conditions.length === 0) { + return resolve(null) + } + + const whereClause = conditions.join(' AND ') + + this.db.get(`SELECT * FROM ${this.tableName} WHERE ${whereClause} LIMIT 1`, params, (err, row) => { + if (err) { + return reject(err) + } + + resolve(this._recordToMessage(row)) + }) + }) + } + + /** + * 清理未引用的图片文件 + * @returns {Promise<{deleted: number, total: number}>} + */ + async cleanupUnusedImages () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + // 获取所有消息数据 + this.db.all(`SELECT messageData FROM ${this.tableName}`, async (err, rows) => { + if (err) { + return reject(err) + } + + try { + // 从数据库中提取所有图片引用 + const usedImageRefs = new Set() + rows.forEach(row => { + try { + const message = JSON.parse(row.messageData) + if (message.content && Array.isArray(message.content)) { + message.content.forEach(item => { + if (item.type === 'image' && typeof item.image === 'string') { + const match = item.image.match(/^\$image:([a-f0-9]+):(\.[a-z]+)$/) + if (match) { + usedImageRefs.add(`${match[1]}${match[2]}`) + } + } + }) + } + } catch (e) { + // 忽略解析错误 + } + }) + + // 获取图片目录中的所有文件 + const files = fs.readdirSync(this.imagesDir) + + // 删除未引用的图片 + let deletedCount = 0 + for (const file of files) { + if (!usedImageRefs.has(file)) { + fs.unlinkSync(path.join(this.imagesDir, file)) + deletedCount++ + } + } + + resolve({ + deleted: deletedCount, + total: files.length + }) + } catch (error) { + reject(error) + } + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/migrate.js b/models/chaite/storage/sqlite/migrate.js new file mode 100644 index 0000000..33c47cc --- /dev/null +++ b/models/chaite/storage/sqlite/migrate.js @@ -0,0 +1,192 @@ +import path from 'path' +import { dataDir } from '../../../../utils/common.js' +import { SQLiteChannelStorage } from './channel_storage.js' +import { LowDBChannelStorage } from '../lowdb/channel_storage.js' +import { SQLiteChatPresetStorage } from './chat_preset_storage.js' +import { LowDBChatPresetsStorage } from '../lowdb/chat_preset_storage.js' +import { SQLiteToolsStorage } from './tools_storage.js' +import { LowDBToolsStorage } from '../lowdb/tools_storage.js' +import { SQLiteProcessorsStorage } from './processors_storage.js' +import { LowDBProcessorsStorage } from '../lowdb/processors_storage.js' +import { SQLiteUserStateStorage } from './user_state_storage.js' +import { LowDBUserStateStorage } from '../lowdb/user_state_storage.js' +import fs from 'fs' + +export async function checkMigrate () { + logger.debug('检查是否需要从 LowDB 迁移数据到 SQLite...') + + try { + // 导入所需的模块 + const { default: ChatGPTStorage } = await import('../lowdb/storage.js') + await ChatGPTStorage.init() + const { ChatGPTHistoryStorage } = await import('../lowdb/storage.js') + await ChatGPTHistoryStorage.init() + + const dbPath = path.join(dataDir, 'data.db') + + // 删除所有id为空的行 + logger.debug('开始修复id为空的数据行...') + const collectionsToClean = ['channel', 'chat_presets', 'tools', 'processors'] + for (const collectionName of collectionsToClean) { + try { + const collection = ChatGPTStorage.collection(collectionName) + const allItems = await collection.findAll() + const invalidItems = allItems.filter(item => !item.id) + + if (invalidItems.length > 0) { + logger.info(`在${collectionName}中发现${invalidItems.length}条id为空的数据,正在修复...`) + + for (const item of invalidItems) { + // 生成一个新的唯一ID + const newId = `generated_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + // 更新时间戳 + const now = new Date().toISOString() + + // 更新项目 + item.id = newId + item.createdAt = now + item.updatedAt = now + + // 保存更新后的项目 + await collection.set(newId, item) + + // 移除旧的无ID项 + await collection.remove(item) + } + + logger.info(`已成功修复${collectionName}中的${invalidItems.length}条无效数据`) + } else { + logger.debug(`${collectionName}中没有发现id为空的数据`) + } + } catch (err) { + logger.error(`修复${collectionName}中id为空的数据时出错:`, err) + } + } + + // 定义要检查的存储对 + const storagePairs = [ + { + name: '渠道', + lowdbStorageClass: LowDBChannelStorage, + sqliteStorageClass: SQLiteChannelStorage, + collection: 'channel' + }, + { + name: '预设', + lowdbStorageClass: LowDBChatPresetsStorage, + sqliteStorageClass: SQLiteChatPresetStorage, + collection: 'chat_presets' + }, + { + name: '工具', + lowdbStorageClass: LowDBToolsStorage, + sqliteStorageClass: SQLiteToolsStorage, + collection: 'tools' + }, + { + name: '处理器', + lowdbStorageClass: LowDBProcessorsStorage, + sqliteStorageClass: SQLiteProcessorsStorage, + collection: 'processors' + }, + { + name: '用户状态', + lowdbStorageClass: LowDBUserStateStorage, + sqliteStorageClass: SQLiteUserStateStorage, + collection: 'userState', + isSpecial: true + } + ] + + // 检查是否有任何数据需要迁移 + const needMigrate = await Promise.all(storagePairs.map(async pair => { + if (pair.isSpecial) { + // 用户状态特殊处理 + const collection = ChatGPTStorage.collection(pair.collection) + const items = await collection.findAll() + return items.length > 0 + } else { + // 标准集合处理 + const collection = ChatGPTStorage.collection(pair.collection) + const items = await collection.findAll() + return items.length > 0 + } + })).then(results => results.some(result => result)) + + if (!needMigrate) { + logger.debug('LowDB 存储为空,无需迁移') + return + } + + // 检查 SQLite 中是否已有数据 + const testStorage = new SQLiteChannelStorage(dbPath) + await testStorage.initialize() + const channels = await testStorage.listItems() + + if (channels.length > 0) { + logger.debug('SQLite 存储已有数据,跳过迁移') + await testStorage.close() + return + } + await testStorage.close() + + logger.info('开始从 LowDB 迁移数据到 SQLite...') + + // 迁移每种数据 + for (const pair of storagePairs) { + const collection = ChatGPTStorage.collection(pair.collection) + const items = await collection.findAll() + + if (items.length > 0) { + logger.info(`迁移${pair.name}数据...`) + // eslint-disable-next-line new-cap + const sqliteStorage = new pair.sqliteStorageClass(dbPath) + await sqliteStorage.initialize() + + for (const item of items) { + await sqliteStorage.setItem(item.id, item) + } + + logger.info(`迁移了 ${items.length} 个${pair.name}`) + await sqliteStorage.close() + } + } + + // 迁移完成后,备份并清空 LowDB 数据 + const backupDir = path.join(dataDir, 'backup') + if (!fs.existsSync(backupDir)) { + fs.mkdirSync(backupDir, { recursive: true }) + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-') + + // 备份并清空��数据 + if (fs.existsSync(ChatGPTStorage.filePath)) { + fs.copyFileSync( + ChatGPTStorage.filePath, + path.join(backupDir, `storage-backup-${timestamp}.json`) + ) + // 清空数据但保留文件结构 + for (const pair of storagePairs) { + if (!pair.collection) continue + await ChatGPTStorage.collection(pair.collection).deleteAll() + } + } + + // 备份并清空历史数据 + if (fs.existsSync(ChatGPTHistoryStorage.filePath)) { + fs.copyFileSync( + ChatGPTHistoryStorage.filePath, + path.join(backupDir, `history-backup-${timestamp}.json`) + ) + // 清空历史数据 + for (const collectionName of ChatGPTHistoryStorage.listCollections()) { + await ChatGPTHistoryStorage.collection(collectionName).deleteAll() + } + } + + logger.debug(`迁移完成,原数据已备份至 ${backupDir} 目录`) + } catch (error) { + logger.error('数据迁移过程中发生错误:', error) + } +} diff --git a/models/chaite/storage/sqlite/processors_storage.js b/models/chaite/storage/sqlite/processors_storage.js new file mode 100644 index 0000000..4f39729 --- /dev/null +++ b/models/chaite/storage/sqlite/processors_storage.js @@ -0,0 +1,440 @@ +import { ChaiteStorage, ProcessorDTO } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteProcessorsStorage extends ChaiteStorage { + getName () { + return 'SQLiteProcessorsStorage' + } + + /** + * + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'processors' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保��录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建处理器表,将主要属性分列存储 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + type TEXT NOT NULL, + code TEXT, + cloudId INTEGER, + createdAt TEXT, + updatedAt TEXT, + md5 TEXT, + embedded INTEGER DEFAULT 0, + uploader TEXT, + extraData TEXT + )`, (err) => { + if (err) { + return reject(err) + } + + // 创建索引 + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_type ON ${this.tableName} (type)`, (err) => { + if (err) { + return reject(err) + } + this.initialized = true + resolve() + }) + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 将 ProcessorDTO 对象转换为数据库记录 + * @param {import('chaite').ProcessorDTO} processor + * @returns {Object} 数据库记录 + */ + _processorToRecord (processor) { + // 提取主要字段 + const { + id, name, description, type, code, cloudId, + createdAt, updatedAt, md5, embedded, uploader, ...rest + } = processor + + return { + id: id || '', + name: name || '', + description: description || '', + type: type || '', // 'pre' 或 'post' + code: code || '', + cloudId: cloudId || null, + createdAt: createdAt || '', + updatedAt: updatedAt || '', + md5: md5 || '', + embedded: embedded ? 1 : 0, + uploader: uploader ? JSON.stringify(uploader) : null, + extraData: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null + } + } + + /** + * 将数据库记录转换为 ProcessorDTO 对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').ProcessorDTO} ProcessorDTO 对象 + */ + _recordToProcessor (record) { + if (!record) return null + + // 解析 JSON 字段 + let uploader = null + try { + if (record.uploader) { + uploader = JSON.parse(record.uploader) + } + } catch (e) { + // 解析错误,使用 null + } + + let extraData = {} + try { + if (record.extraData) { + extraData = JSON.parse(record.extraData) + } + } catch (e) { + // 解析错误,使用空对象 + } + + // 构造 ProcessorDTO 对象 + const processorData = { + id: record.id, + name: record.name, + description: record.description, + type: record.type, // 'pre' 或 'post' + code: record.code, + cloudId: record.cloudId, + createdAt: record.createdAt, + updatedAt: record.updatedAt, + md5: record.md5, + embedded: Boolean(record.embedded), + uploader, + ...extraData + } + + return new ProcessorDTO(processorData) + } + + /** + * 获取单个处理器 + * @param {string} key 处理器ID + * @returns {Promise} + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) { + return reject(err) + } + + const processor = this._recordToProcessor(row) + resolve(processor) + }) + }) + } + + /** + * 保存处理器 + * @param {string} id 处理器ID + * @param {import('chaite').ProcessorDTO} processor 处理器对象 + * @returns {Promise} + */ + async setItem (id, processor) { + await this.ensureInitialized() + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!processor.createdAt) { + processor.createdAt = new Date().toISOString() + } + + processor.updatedAt = new Date().toISOString() + // 转换为数据库记录 + const record = this._processorToRecord(processor) + record.id = id // 确保ID是指定的ID + + // 构建插入或更新SQL + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + const duplicateValues = [...values] // 用于ON CONFLICT时的更新 + + return new Promise((resolve, reject) => { + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) + VALUES (${placeholders}) + ON CONFLICT(id) DO UPDATE SET ${updates}`, + [...values, ...duplicateValues], + function (err) { + if (err) { + return reject(err) + } + resolve(id) + } + ) + }) + } + + /** + * 删除处���器 + * @param {string} key 处理器ID + * @returns {Promise} + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 查询所有处理器 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const processors = rows.map(row => this._recordToProcessor(row)).filter(Boolean) + resolve(processors) + }) + }) + } + + /** + * 根据条件筛选处理器 + * @param {Record} filter 筛选条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 如果没有筛选条件,返回所有 + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + // 尝试使用SQL字段直接过滤 + const directFields = ['id', 'name', 'description', 'type', 'cloudId'] + const sqlFilters = [] + const sqlParams = [] + const extraFilters = {} + let hasExtraFilters = false + + // 区分数据库字段和额外字段 + for (const key in filter) { + const value = filter[key] + + // 如果是直接支持的字段,构建SQL条件 + if (directFields.includes(key)) { + sqlFilters.push(`${key} = ?`) + sqlParams.push(value) + } else if (key === 'embedded') { + // embedded 字段需要特殊处理为 0/1 + sqlFilters.push('embedded = ?') + sqlParams.push(value ? 1 : 0) + } else { + // 其他字段需要在结果中进一步过滤 + extraFilters[key] = value + hasExtraFilters = true + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let processors = rows.map(row => this._recordToProcessor(row)).filter(Boolean) + + // 如果有需要在内存中过滤的额外字段 + if (hasExtraFilters) { + processors = processors.filter(processor => { + for (const key in extraFilters) { + if (processor[key] !== extraFilters[key]) { + return false + } + } + return true + }) + } + + resolve(processors) + }) + }) + } + + /** + * 根据IN条��筛选处理器 + * @param {Array<{ field: string; values: unknown[]; }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + // 如果没有查询条件,返回所有 + if (!query || query.length === 0) { + return this.listItems() + } + + // 尝试使用SQL IN子句来优化查询 + const directFields = ['id', 'name', 'description', 'type', 'cloudId'] + const sqlFilters = [] + const sqlParams = [] + const extraQueries = [] + + // 处理每个查询条件 + for (const { field, values } of query) { + if (values.length === 0) continue + + // 如果是直接支持的字段,使用SQL IN子句 + if (directFields.includes(field)) { + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values) + } else if (field === 'embedded') { + // embedded 字段需要特殊处理 + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`embedded IN (${placeholders})`) + sqlParams.push(...boolValues) + } else { + // 其他字段在内存中过滤 + extraQueries.push({ field, values }) + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let processors = rows.map(row => this._recordToProcessor(row)).filter(Boolean) + + // 如果有需要在内存中过滤的条件 + if (extraQueries.length > 0) { + processors = processors.filter(processor => { + for (const { field, values } of extraQueries) { + if (!values.includes(processor[field])) { + return false + } + } + return true + }) + } + + resolve(processors) + }) + }) + } + + /** + * 清空表中所有数据 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/tool_groups_storage.js b/models/chaite/storage/sqlite/tool_groups_storage.js new file mode 100644 index 0000000..40718ca --- /dev/null +++ b/models/chaite/storage/sqlite/tool_groups_storage.js @@ -0,0 +1,569 @@ +import { ChaiteStorage } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteToolsGroupStorage extends ChaiteStorage { + getName () { + return 'SQLiteToolsGroupStorage' + } + + /** + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'tools_groups' + } + + /** + * 初始化数据库连接和表结构 + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) return reject(err) + + try { + // 首先检查表是否存在 + const tableExists = await this.checkTableExists() + + if (tableExists) { + // 如果表存在,检查并迁移旧结构 + await this.migrateTableIfNeeded() + } else { + // 如果表不存在,创建新表 + await this.createTable() + } + + // 确保索引存在 + await this.ensureIndex() + + this.initialized = true + resolve() + } catch (error) { + reject(error) + } + }) + }) + } + + /** + * 检查表是否存在 + */ + async checkTableExists () { + return new Promise((resolve, reject) => { + this.db.get( + 'SELECT name FROM sqlite_master WHERE type=\'table\' AND name=?', + [this.tableName], + (err, row) => { + if (err) return reject(err) + resolve(!!row) + } + ) + }) + } + + /** + * 创建新表 + */ + async createTable () { + return new Promise((resolve, reject) => { + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + toolIds TEXT NOT NULL, + isDefault INTEGER DEFAULT 0, + createdAt TEXT, + updatedAt TEXT + )`, (err) => { + if (err) return reject(err) + resolve() + }) + }) + } + + /** + * 确保索引存在 + */ + async ensureIndex () { + return new Promise((resolve, reject) => { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_groups_name ON ${this.tableName} (name)`, (err) => { + if (err) return reject(err) + resolve() + }) + }) + } + + /** + * 检查并迁移表结构 + */ + async migrateTableIfNeeded () { + // 检查表结构 + const columns = await this.getTableColumns() + + // 检查是否有旧版本的结构(有tools字段而不是toolIds) + const hasOldStructure = columns.includes('tools') && !columns.includes('toolIds') + const needsDefaultColumn = !columns.includes('isDefault') + + if (hasOldStructure || needsDefaultColumn) { + console.log(`检测到旧表结构,开始迁移 ${this.tableName} 表...`) + + // 备份所有数据 + const allData = await this.backupData() + + // 重命名旧表 + await this.renameTable(`${this.tableName}_old`) + + // 创建新表 + await this.createTable() + await this.ensureIndex() + + // 恢复数据到新表 + if (allData.length > 0) { + await this.restoreData(allData, hasOldStructure) + } + + // 删除旧表 + await this.dropTable(`${this.tableName}_old`) + + console.log(`表 ${this.tableName} 迁移完成,共迁移 ${allData.length} 条数据`) + } + } + + /** + * 获取表的所有列名 + */ + async getTableColumns () { + return new Promise((resolve, reject) => { + this.db.all(`PRAGMA table_info(${this.tableName})`, (err, rows) => { + if (err) return reject(err) + + const columns = rows.map(row => row.name) + resolve(columns) + }) + }) + } + + /** + * 备份表数据 + */ + async backupData () { + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) return reject(err) + resolve(rows) + }) + }) + } + + /** + * 重命名表 + */ + async renameTable (newName) { + return new Promise((resolve, reject) => { + this.db.run(`ALTER TABLE ${this.tableName} RENAME TO ${newName}`, (err) => { + if (err) return reject(err) + resolve() + }) + }) + } + + /** + * 删除表 + */ + async dropTable (tableName) { + return new Promise((resolve, reject) => { + this.db.run(`DROP TABLE IF EXISTS ${tableName}`, (err) => { + if (err) return reject(err) + resolve() + }) + }) + } + + /** + * 恢复数据到新表 + */ + async restoreData (data, hasOldStructure) { + const promises = data.map(row => { + return new Promise((resolve, reject) => { + // 处理数据转换 + const newRow = { ...row } + + if (hasOldStructure && row.tools) { + try { + // 从旧的tools结构提取toolIds + const tools = JSON.parse(row.tools) + newRow.toolIds = JSON.stringify(tools.map(t => t.id || t)) + delete newRow.tools + } catch (e) { + console.error(`解析工具组数据错误: ${row.id}`, e) + newRow.toolIds = JSON.stringify([]) + delete newRow.tools + } + } + + // 添加isDefault字段 + if (newRow.isDefault === undefined) { + newRow.isDefault = 0 + } + + // 添加时间戳 + if (!newRow.createdAt) { + newRow.createdAt = new Date().toISOString() + } + if (!newRow.updatedAt) { + newRow.updatedAt = new Date().toISOString() + } + + const fields = Object.keys(newRow) + const placeholders = fields.map(() => '?').join(',') + const values = fields.map(field => newRow[field]) + + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(',')}) VALUES (${placeholders})`, + values, + (err) => { + if (err) return reject(err) + resolve() + } + ) + }) + }) + + return Promise.all(promises) + } + + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 获取工具组 + * @param {string} key 工具组ID + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) return reject(err) + + if (!row) return resolve(null) + + try { + const toolsGroup = { + ...row, + toolIds: JSON.parse(row.toolIds), + isDefault: Boolean(row.isDefault) + } + resolve(toolsGroup) + } catch (e) { + console.error(`解析工具组数据错误: ${key}`, e) + resolve({ + ...row, + toolIds: [], + isDefault: Boolean(row.isDefault) + }) + } + }) + }) + } + + /** + * 保存工具组 + * @param {string} id 工具组ID + * @param {Object} data 工具组数据 + */ + async setItem (id, data) { + await this.ensureInitialized() + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!data.createdAt) { + data.createdAt = new Date().toISOString() + } + + data.updatedAt = new Date().toISOString() + // 提取工具组数据 + const { name, description, toolIds, isDefault } = data + const updatedAt = new Date().toISOString() + + // 将工具ID列表序列化为JSON字符串 + const toolIdsJson = JSON.stringify(toolIds || []) + const isDefaultValue = isDefault ? 1 : 0 + + return new Promise((resolve, reject) => { + // 检查工具组是否已存在 + this.db.get(`SELECT id FROM ${this.tableName} WHERE id = ?`, [id], (err, row) => { + if (err) { + return reject(err) + } + + if (row) { + // 更新现有工具组 + this.db.run( + `UPDATE ${this.tableName} SET name = ?, description = ?, toolIds = ?, isDefault = ?, updatedAt = ? WHERE id = ?`, + [name, description, toolIdsJson, isDefaultValue, updatedAt, id], + (err) => { + if (err) { + return reject(err) + } + resolve(id) + } + ) + } else { + // 插入新工具组 + this.db.run( + `INSERT INTO ${this.tableName} (id, name, description, toolIds, isDefault, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?)`, + [id, name, description, toolIdsJson, isDefaultValue, data.createdAt, updatedAt], + (err) => { + if (err) { + return reject(err) + } + resolve(id) + } + ) + } + }) + }) + } + + /** + * 删除工具组 + * @param {string} key 工具组ID + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], function (err) { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 获取所有工具组 + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const toolsGroups = rows.map(row => { + try { + return { + ...row, + toolIds: JSON.parse(row.toolIds), + isDefault: Boolean(row.isDefault) + } + } catch (e) { + console.error(`解析工具组数据错误: ${row.id}`, e) + return { + ...row, + toolIds: [], + isDefault: Boolean(row.isDefault) + } + } + }) + + resolve(toolsGroups) + }) + }) + } + + /** + * 根据条件筛选工具组 + * @param {Record} filter 筛选条件 + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + const directFields = ['id', 'name', 'description'] + const conditions = [] + const params = [] + + for (const key in filter) { + if (directFields.includes(key)) { + conditions.push(`${key} = ?`) + params.push(filter[key]) + } else if (key === 'isDefault') { + conditions.push('isDefault = ?') + params.push(filter[key] ? 1 : 0) + } + } + + const sql = conditions.length > 0 + ? `SELECT * FROM ${this.tableName} WHERE ${conditions.join(' AND ')}` + : `SELECT * FROM ${this.tableName}` + + return new Promise((resolve, reject) => { + this.db.all(sql, params, (err, rows) => { + if (err) return reject(err) + + const toolsGroups = rows.map(row => { + try { + const group = { + ...row, + toolIds: JSON.parse(row.toolIds || '[]'), + isDefault: Boolean(row.isDefault) + } + + // 过滤其他字段 + for (const key in filter) { + if (!directFields.includes(key) && + key !== 'isDefault' && + JSON.stringify(group[key]) !== JSON.stringify(filter[key])) { + return null + } + } + + return group + } catch (e) { + console.error(`解析工具组数据错误: ${row.id}`, e) + return null + } + }).filter(Boolean) + + resolve(toolsGroups) + }) + }) + } + + /** + * 根据IN条件筛选工具组 + * @param {Array<{field: string, values: unknown[]}>} query IN查询条件 + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + if (!query || query.length === 0) { + return this.listItems() + } + + const directFields = ['id', 'name', 'description'] + const conditions = [] + const params = [] + const memoryQueries = [] + + for (const item of query) { + if (directFields.includes(item.field) && Array.isArray(item.values) && item.values.length > 0) { + const placeholders = item.values.map(() => '?').join(',') + conditions.push(`${item.field} IN (${placeholders})`) + params.push(...item.values) + } else if (item.field === 'isDefault' && Array.isArray(item.values) && item.values.length > 0) { + const boolValues = item.values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(',') + conditions.push(`isDefault IN (${placeholders})`) + params.push(...boolValues) + } else if (item.values.length > 0) { + memoryQueries.push(item) + } + } + + const sql = conditions.length > 0 + ? `SELECT * FROM ${this.tableName} WHERE ${conditions.join(' AND ')}` + : `SELECT * FROM ${this.tableName}` + + return new Promise((resolve, reject) => { + this.db.all(sql, params, (err, rows) => { + if (err) return reject(err) + + let toolsGroups = rows.map(row => { + try { + return { + ...row, + toolIds: JSON.parse(row.toolIds || '[]'), + isDefault: Boolean(row.isDefault) + } + } catch (e) { + console.error(`解析工具组数据错误: ${row.id}`, e) + return null + } + }).filter(Boolean) + + // 内存中过滤其它字段 + if (memoryQueries.length > 0) { + toolsGroups = toolsGroups.filter(group => { + for (const { field, values } of memoryQueries) { + // 对于toolIds字段做特殊处理 + if (field === 'toolIds') { + const hasMatch = values.some(toolId => group.toolIds.includes(toolId)) + if (!hasMatch) return false + } else if (!values.includes(group[field])) { + return false + } + } + return true + }) + } + + resolve(toolsGroups) + }) + }) + } + + /** + * 清空所有工具组 + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) return reject(err) + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/tools_storage.js b/models/chaite/storage/sqlite/tools_storage.js new file mode 100644 index 0000000..7606dd9 --- /dev/null +++ b/models/chaite/storage/sqlite/tools_storage.js @@ -0,0 +1,467 @@ +import { ChaiteStorage, ToolDTO } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteToolsStorage extends ChaiteStorage { + getName () { + return 'SQLiteToolsStorage' + } + + /** + * + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'tools' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建工具表,将主要属性分列存储 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + modelType TEXT, + code TEXT, + cloudId INTEGER, + embedded INTEGER, + uploader TEXT, + createdAt TEXT, + updatedAt TEXT, + md5 TEXT, + status TEXT, + permission TEXT, + extraData TEXT -- 存储其他额外数据的JSON + )`, (err) => { + if (err) { + reject(err) + } else { + // 创建索引以提高查询性能 + this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_name ON ${this.tableName} (name)`, (err) => { + if (err) { + reject(err) + } else { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_status ON ${this.tableName} (status)`, (err) => { + if (err) { + reject(err) + } else { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_tools_permission ON ${this.tableName} (permission)`, (err) => { + if (err) { + reject(err) + } else { + this.initialized = true + resolve() + } + }) + } + }) + } + }) + } + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 将 ToolDTO 对象转换为数据库记录 + * @param {import('chaite').ToolDTO} tool + * @returns {Object} 数据库记录 + */ + _toolToRecord (tool) { + // 提取主要字段,剩余的放入extraData + const { + id, name, description, modelType, code, cloudId, + embedded, uploader, createdAt, updatedAt, md5, + status, permission, ...rest + } = tool + + // 序列化上传者对象 + const uploaderStr = uploader ? JSON.stringify(uploader) : null + + return { + id: id || '', + name: name || '', + description: description || '', + modelType: modelType || '', + code: code || null, + cloudId: cloudId || null, + embedded: embedded ? 1 : 0, + uploader: uploaderStr, + createdAt: createdAt || '', + updatedAt: updatedAt || '', + md5: md5 || '', + status: status || 'enabled', + permission: permission || 'public', + extraData: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null + } + } + + /** + * 将数据库记录转换为 ToolDTO 对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').ToolDTO} ToolDTO对象 + */ + _recordToTool (record) { + // 若记录不存在则返回null + if (!record) return null + + // 解析上传者 + let uploader = null + try { + if (record.uploader) { + uploader = JSON.parse(record.uploader) + } + } catch (e) { + // 解析错误,使用null + } + + // 解析额外数据 + let extraData = {} + try { + if (record.extraData) { + extraData = JSON.parse(record.extraData) + } + } catch (e) { + // 解析错误,使用空对象 + } + + // 构造基本对象 + const toolData = { + id: record.id, + name: record.name, + description: record.description, + modelType: record.modelType, + code: record.code, + cloudId: record.cloudId, + embedded: Boolean(record.embedded), + uploader, + createdAt: record.createdAt, + updatedAt: record.updatedAt, + md5: record.md5, + status: record.status, + permission: record.permission, + ...extraData + } + + return new ToolDTO(toolData) + } + + /** + * 获取单个工具 + * @param {string} key 工具ID + * @returns {Promise} + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) { + return reject(err) + } + + const tool = this._recordToTool(row) + resolve(tool) + }) + }) + } + + /** + * 保存工具 + * @param {string} id 工具ID + * @param {import('chaite').ToolDTO} tool 工具对象 + * @returns {Promise} + */ + async setItem (id, tool) { + await this.ensureInitialized() + + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!tool.createdAt) { + tool.createdAt = new Date().toISOString() + } + + tool.updatedAt = new Date().toISOString() + + // 转换为数据库记录 + const record = this._toolToRecord(tool) + record.id = id // 确保ID是指定的ID + + // 构建插入或更新SQL + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + const duplicateValues = [...values] // 用于ON CONFLICT时的更新 + + return new Promise((resolve, reject) => { + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) + VALUES (${placeholders}) + ON CONFLICT(id) DO UPDATE SET ${updates}`, + [...values, ...duplicateValues], + function (err) { + if (err) { + return reject(err) + } + resolve(id) + } + ) + }) + } + + /** + * 删除工具 + * @param {string} key 工具ID + * @returns {Promise} + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 查询所有工具 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const tools = rows.map(row => this._recordToTool(row)).filter(Boolean) + resolve(tools) + }) + }) + } + + /** + * 根据条件筛选工具(直接使用SQL查询,避免全表扫描) + * @param {Record} filter 筛选条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 如果没有筛选条件,返回所有 + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + // 尝试使用SQL字段直接过滤 + const directFields = ['id', 'name', 'description', 'modelType', 'cloudId', 'status', 'permission'] + const sqlFilters = [] + const sqlParams = [] + const extraFilters = {} + let hasExtraFilters = false + + // 区分数据库字段和额外字段 + for (const key in filter) { + const value = filter[key] + + // 如果是直接支持的字段,构建SQL条件 + if (directFields.includes(key)) { + sqlFilters.push(`${key} = ?`) + sqlParams.push(value) + } else if (key === 'embedded') { + // embedded 字段需要特殊处理为 0/1 + sqlFilters.push('embedded = ?') + sqlParams.push(value ? 1 : 0) + } else { + // 其他字段需要在结果中进一步过滤 + extraFilters[key] = value + hasExtraFilters = true + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let tools = rows.map(row => this._recordToTool(row)).filter(Boolean) + + // 如果有需要在内存中过滤的额外字段 + if (hasExtraFilters) { + tools = tools.filter(tool => { + for (const key in extraFilters) { + if (tool[key] !== extraFilters[key]) { + return false + } + } + return true + }) + } + + resolve(tools) + }) + }) + } + + /** + * 根据IN条件筛选工具 + * @param {Array<{ field: string; values: unknown[]; }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + // 如果没有查询条件,返回所有 + if (!query || query.length === 0) { + return this.listItems() + } + + // 尝试使用SQL IN子句来优化查询 + const directFields = ['id', 'name', 'description', 'modelType', 'cloudId', 'status', 'permission'] + const sqlFilters = [] + const sqlParams = [] + const extraQueries = [] + + // 处理每个查询条件 + for (const { field, values } of query) { + if (values.length === 0) continue + + // 如果是直接支持的字段,使用SQL IN子句 + if (directFields.includes(field)) { + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values) + // embedded 字段需要特殊处理 + } else if (field === 'embedded') { + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`embedded IN (${placeholders})`) + sqlParams.push(...boolValues) + } else { + // 其他字段在内存中过滤 + extraQueries.push({ field, values }) + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let tools = rows.map(row => this._recordToTool(row)).filter(Boolean) + + // 如果有需要在内存中过滤的条件 + if (extraQueries.length > 0) { + tools = tools.filter(tool => { + for (const { field, values } of extraQueries) { + if (!values.includes(tool[field])) { + return false + } + } + return true + }) + } + + resolve(tools) + }) + }) + } + + /** + * 清空表中所有数据 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/storage/sqlite/trigger_storage.js b/models/chaite/storage/sqlite/trigger_storage.js new file mode 100644 index 0000000..634d7a9 --- /dev/null +++ b/models/chaite/storage/sqlite/trigger_storage.js @@ -0,0 +1,474 @@ +import { ChaiteStorage, TriggerDTO } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import { generateId } from '../../../../utils/common.js' + +/** + * @extends {ChaiteStorage} + */ +export class SQLiteTriggerStorage extends ChaiteStorage { + getName () { + return 'SQLiteTriggerStorage' + } + + /** + * + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'triggers' + } + + /** + * 初始化数据库连接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建触发器表,将主要属性分列存储 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + modelType TEXT, + code TEXT, + cloudId INTEGER, + embedded INTEGER, + uploader TEXT, + createdAt TEXT, + updatedAt TEXT, + md5 TEXT, + status TEXT, + permission TEXT, + isOneTime INTEGER, + extraData TEXT -- 存储其他额外数据的JSON + )`, (err) => { + if (err) { + reject(err) + } else { + // 创建索引以提高查询性能 + this.db.run(`CREATE INDEX IF NOT EXISTS idx_triggers_name ON ${this.tableName} (name)`, (err) => { + if (err) { + reject(err) + } else { + this.db.run(`CREATE INDEX IF NOT EXISTS idx_triggers_status ON ${this.tableName} (status)`, (err) => { + if (err) { + reject(err) + } else { + this.initialized = true + resolve() + } + }) + } + }) + } + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 将 TriggerDTO 对象转换为数据库记录 + * @param {import('chaite').TriggerDTO} trigger + * @returns {Object} 数据库记录 + */ + _triggerToRecord (trigger) { + // 提取主要字段,剩余的放入extraData + const { + id, name, description, modelType, code, cloudId, + embedded, uploader, createdAt, updatedAt, md5, + status, permission, isOneTime, ...rest + } = trigger + + // 序列化上传者对象 + const uploaderStr = uploader ? JSON.stringify(uploader) : null + + return { + id: id || '', + name: name || '', + description: description || '', + modelType: modelType || 'executable', + code: code || null, + cloudId: cloudId || null, + embedded: embedded ? 1 : 0, + uploader: uploaderStr, + createdAt: createdAt || '', + updatedAt: updatedAt || '', + md5: md5 || '', + status: status || 'enabled', + permission: permission || 'public', + isOneTime: isOneTime ? 1 : 0, + extraData: Object.keys(rest).length > 0 ? JSON.stringify(rest) : null + } + } + + /** + * 将数据库记录转换为 TriggerDTO 对象 + * @param {Object} record 数据库记录 + * @returns {import('chaite').TriggerDTO} TriggerDTO对象 + */ + _recordToTrigger (record) { + // 若记录不存在则返回null + if (!record) return null + + // 解析上传者 + let uploader = null + try { + if (record.uploader) { + uploader = JSON.parse(record.uploader) + } + } catch (e) { + // 解析错误,使用null + } + + // 解析额外数据 + let extraData = {} + try { + if (record.extraData) { + extraData = JSON.parse(record.extraData) + } + } catch (e) { + // 解析错误,使用空对象 + } + + // 构造基本对象 + const triggerData = { + id: record.id, + name: record.name, + description: record.description, + modelType: record.modelType, + code: record.code, + cloudId: record.cloudId, + embedded: Boolean(record.embedded), + uploader, + createdAt: record.createdAt, + updatedAt: record.updatedAt, + md5: record.md5, + status: record.status, + permission: record.permission, + isOneTime: Boolean(record.isOneTime), + ...extraData + } + + return new TriggerDTO(triggerData) + } + + /** + * 获取单个触发器 + * @param {string} key 触发器ID + * @returns {Promise} + */ + async getItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, [key], (err, row) => { + if (err) { + return reject(err) + } + + const trigger = this._recordToTrigger(row) + resolve(trigger) + }) + }) + } + + /** + * 保存触发器 + * @param {string} id 触发器ID + * @param {import('chaite').TriggerDTO} trigger 触发器对象 + * @returns {Promise} + */ + async setItem (id, trigger) { + await this.ensureInitialized() + + if (!id) { + id = generateId() + } + + // 加上时间戳 + if (!trigger.createdAt) { + trigger.createdAt = new Date().toISOString() + } + + trigger.updatedAt = new Date().toISOString() + + // 转换为数据库记录 + const record = this._triggerToRecord(trigger) + record.id = id // 确保ID是指定的ID + + // 构建插入或更新SQL + const fields = Object.keys(record) + const placeholders = fields.map(() => '?').join(', ') + const updates = fields.map(field => `${field} = ?`).join(', ') + const values = fields.map(field => record[field]) + const duplicateValues = [...values] // 用于ON CONFLICT时的更新 + + return new Promise((resolve, reject) => { + this.db.run( + `INSERT INTO ${this.tableName} (${fields.join(', ')}) + VALUES (${placeholders}) + ON CONFLICT(id) DO UPDATE SET ${updates}`, + [...values, ...duplicateValues], + function (err) { + if (err) { + return reject(err) + } + resolve(id) + } + ) + }) + } + + /** + * 删除触发器 + * @param {string} key 触发器ID + * @returns {Promise} + */ + async removeItem (key) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE id = ?`, [key], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 查询所有触发器 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const triggers = rows.map(row => this._recordToTrigger(row)).filter(Boolean) + resolve(triggers) + }) + }) + } + + /** + * 根据条件筛选触发器 + * @param {Record} filter 筛选条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 如果没有筛选条件,返回所有 + if (!filter || Object.keys(filter).length === 0) { + return this.listItems() + } + + // 尝试使用SQL字段直接过滤 + const directFields = ['id', 'name', 'description', 'modelType', 'cloudId', 'status', 'permission'] + const sqlFilters = [] + const sqlParams = [] + const extraFilters = {} + let hasExtraFilters = false + + // 区分数据库字段和额外字段 + for (const key in filter) { + const value = filter[key] + + // 如果是直接支持的字段,构建SQL条件 + if (directFields.includes(key)) { + sqlFilters.push(`${key} = ?`) + sqlParams.push(value) + } else if (key === 'embedded') { + // embedded 字段需要特殊处理为 0/1 + sqlFilters.push('embedded = ?') + sqlParams.push(value ? 1 : 0) + } else if (key === 'isOneTime') { + // isOneTime 字段需要特殊处理为 0/1 + sqlFilters.push('isOneTime = ?') + sqlParams.push(value ? 1 : 0) + } else { + // 其他字段需要在结果中进一步过滤 + extraFilters[key] = value + hasExtraFilters = true + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let triggers = rows.map(row => this._recordToTrigger(row)).filter(Boolean) + + // 如果有需要在内存中过滤的额外字段 + if (hasExtraFilters) { + triggers = triggers.filter(trigger => { + for (const key in extraFilters) { + if (trigger[key] !== extraFilters[key]) { + return false + } + } + return true + }) + } + + resolve(triggers) + }) + }) + } + + /** + * 根据IN条件筛选触发器 + * @param {Array<{ field: string; values: unknown[]; }>} query + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + // 如果没有查询条���,返回所有 + if (!query || query.length === 0) { + return this.listItems() + } + + // 尝试使用SQL IN子句来优化查询 + const directFields = ['id', 'name', 'description', 'modelType', 'cloudId', 'status', 'permission'] + const sqlFilters = [] + const sqlParams = [] + const extraQueries = [] + + // 处理每个查询条件 + for (const { field, values } of query) { + if (values.length === 0) continue + + // 如果是直接支持的字段,使用SQL IN子句 + if (directFields.includes(field)) { + const placeholders = values.map(() => '?').join(', ') + sqlFilters.push(`${field} IN (${placeholders})`) + sqlParams.push(...values) + } else if (field === 'embedded') { + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`embedded IN (${placeholders})`) + sqlParams.push(...boolValues) + } else if (field === 'isOneTime') { + const boolValues = values.map(v => v ? 1 : 0) + const placeholders = boolValues.map(() => '?').join(', ') + sqlFilters.push(`isOneTime IN (${placeholders})`) + sqlParams.push(...boolValues) + } else { + // 其他字段在内存中过滤 + extraQueries.push({ field, values }) + } + } + + // 构建SQL查询 + let sql = `SELECT * FROM ${this.tableName}` + if (sqlFilters.length > 0) { + sql += ` WHERE ${sqlFilters.join(' AND ')}` + } + + return new Promise((resolve, reject) => { + this.db.all(sql, sqlParams, (err, rows) => { + if (err) { + return reject(err) + } + + let triggers = rows.map(row => this._recordToTrigger(row)).filter(Boolean) + + // 如果有需要在内存中过滤的条件 + if (extraQueries.length > 0) { + triggers = triggers.filter(trigger => { + for (const { field, values } of extraQueries) { + if (!values.includes(trigger[field])) { + return false + } + } + return true + }) + } + + resolve(triggers) + }) + }) + } + + /** + * 清空表中所有数据 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) return Promise.resolve() + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} + +export default SQLiteTriggerStorage diff --git a/models/chaite/storage/sqlite/user_state_storage.js b/models/chaite/storage/sqlite/user_state_storage.js new file mode 100644 index 0000000..2ed0711 --- /dev/null +++ b/models/chaite/storage/sqlite/user_state_storage.js @@ -0,0 +1,388 @@ +import { ChaiteStorage } from 'chaite' +import sqlite3 from 'sqlite3' +import path from 'path' +import fs from 'fs' +import crypto from 'node:crypto' + +/** + * 基于SQLite的用户状态存储实现 + * @extends {ChaiteStorage} + */ +export class SQLiteUserStateStorage extends ChaiteStorage { + /** + * 构造函数 + * @param {string} dbPath 数据库文件路径 + */ + constructor (dbPath) { + super() + this.dbPath = dbPath + this.db = null + this.initialized = false + this.tableName = 'user_states' + } + + /** + * 初始化数据库��接和表结构 + * @returns {Promise} + */ + async initialize () { + if (this.initialized) return + + return new Promise((resolve, reject) => { + // 确保目录存在 + const dir = path.dirname(this.dbPath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } + + this.db = new sqlite3.Database(this.dbPath, async (err) => { + if (err) { + return reject(err) + } + + // 创建用户状态表 + this.db.run(`CREATE TABLE IF NOT EXISTS ${this.tableName} ( + id TEXT PRIMARY KEY, + userId TEXT NOT NULL, + nickname TEXT, + card TEXT, + conversations TEXT NOT NULL, + settings TEXT NOT NULL, + current TEXT NOT NULL, + updatedAt INTEGER + )`, (err) => { + if (err) { + return reject(err) + } + + // 创建索引以加快查询 + this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.tableName}_userId ON ${this.tableName} (userId)`, (err) => { + if (err) { + return reject(err) + } + this.initialized = true + resolve() + }) + }) + }) + }) + } + + /** + * 确保数据库已初始化 + */ + async ensureInitialized () { + if (!this.initialized) { + await this.initialize() + } + } + + /** + * 获取用户状态 + * @param {string} userId 用户ID + * @returns {Promise} + */ + async getItem (userId) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.get(`SELECT * FROM ${this.tableName} WHERE userId = ?`, [userId], (err, row) => { + if (err) { + return reject(err) + } + + if (!row) { + return resolve(null) + } + + try { + const userState = { + userId: row.userId, + nickname: row.nickname, + card: row.card, + conversations: JSON.parse(row.conversations), + settings: JSON.parse(row.settings), + current: JSON.parse(row.current) + } + resolve(userState) + } catch (e) { + console.error(`解析用户状态数据错误: ${userId}`, e) + resolve(null) + } + }) + }) + } + + /** + * 保存用户状态 + * @param {string} userId 用户ID + * @param {import('chaite').UserState} userState 用户状态数据 + * @returns {Promise} 返回用户ID + */ + async setItem (userId, userState) { + await this.ensureInitialized() + + // 提取用户状态数据 + const { nickname, card, conversations, settings, current } = userState + const updatedAt = Date.now() + + // 序列化数据 + const conversationsJson = JSON.stringify(conversations || []) + const settingsJson = JSON.stringify(settings || {}) + const currentJson = JSON.stringify(current || {}) + + return new Promise((resolve, reject) => { + // 检查用户状态是否已存在 + this.db.get(`SELECT userId FROM ${this.tableName} WHERE userId = ?`, [userId], (err, row) => { + if (err) { + return reject(err) + } + + if (row) { + // 更新现有用户状态 + this.db.run( + `UPDATE ${this.tableName} SET + nickname = ?, + card = ?, + conversations = ?, + settings = ?, + current = ?, + updatedAt = ? + WHERE userId = ?`, + [nickname, card, conversationsJson, settingsJson, currentJson, updatedAt, userId], + (err) => { + if (err) { + return reject(err) + } + resolve(userId) + } + ) + } else { + // 插入新用户状态 + this.db.run( + `INSERT INTO ${this.tableName} + (id, userId, nickname, card, conversations, settings, current, updatedAt) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [crypto.randomUUID(), userId, nickname, card, conversationsJson, settingsJson, currentJson, updatedAt], + (err) => { + if (err) { + return reject(err) + } + resolve(userId) + } + ) + } + }) + }) + } + + /** + * 删除用户状态 + * @param {string} userId 用户ID + * @returns {Promise} + */ + async removeItem (userId) { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName} WHERE userId = ?`, [userId], (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 获取所有用户状态 + * @returns {Promise} + */ + async listItems () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName}`, (err, rows) => { + if (err) { + return reject(err) + } + + const userStates = rows.map(row => { + try { + return { + userId: row.userId, + nickname: row.nickname, + card: row.card, + conversations: JSON.parse(row.conversations), + settings: JSON.parse(row.settings), + current: JSON.parse(row.current) + } + } catch (e) { + console.error(`解析用户状态数据错误: ${row.userId}`, e) + return null + } + }).filter(Boolean) + + resolve(userStates) + }) + }) + } + + /** + * 根据过滤条件查询用户状态 + * @param {Record} filter 过滤条件 + * @returns {Promise} + */ + async listItemsByEqFilter (filter) { + await this.ensureInitialized() + + // 只支持userId、nickname、card的过滤 + const supportedFilters = ['userId', 'nickname', 'card'] + const conditions = [] + const params = [] + + for (const key of supportedFilters) { + if (filter[key] !== undefined) { + conditions.push(`${key} = ?`) + params.push(filter[key]) + } + } + + if (conditions.length === 0) { + return this.listItems() + } + + const whereClause = conditions.join(' AND ') + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName} WHERE ${whereClause}`, params, (err, rows) => { + if (err) { + return reject(err) + } + + const userStates = rows.map(row => { + try { + return { + userId: row.userId, + nickname: row.nickname, + card: row.card, + conversations: JSON.parse(row.conversations), + settings: JSON.parse(row.settings), + current: JSON.parse(row.current) + } + } catch (e) { + console.error(`解析用户状态数据错误: ${row.userId}`, e) + return null + } + }).filter(Boolean) + + resolve(userStates) + }) + }) + } + + /** + * 根据IN查询条件查询用户状�� + * @param {Array<{field: string, values: unknown[]}>} query IN查询条件 + * @returns {Promise} + */ + async listItemsByInQuery (query) { + await this.ensureInitialized() + + if (!query || !query.length) { + return this.listItems() + } + + // 只支持userId、nickname、card的过滤 + const supportedFields = ['userId', 'nickname', 'card'] + const conditions = [] + const params = [] + + for (const item of query) { + if (supportedFields.includes(item.field) && Array.isArray(item.values) && item.values.length > 0) { + const placeholders = item.values.map(() => '?').join(',') + conditions.push(`${item.field} IN (${placeholders})`) + params.push(...item.values) + } + } + + if (conditions.length === 0) { + return this.listItems() + } + + const whereClause = conditions.join(' AND ') + + return new Promise((resolve, reject) => { + this.db.all(`SELECT * FROM ${this.tableName} WHERE ${whereClause}`, params, (err, rows) => { + if (err) { + return reject(err) + } + + const userStates = rows.map(row => { + try { + return { + userId: row.userId, + nickname: row.nickname, + card: row.card, + conversations: JSON.parse(row.conversations), + settings: JSON.parse(row.settings), + current: JSON.parse(row.current) + } + } catch (e) { + console.error(`解析用户状态数据错误: ${row.userId}`, e) + return null + } + }).filter(Boolean) + + resolve(userStates) + }) + }) + } + + /** + * 清空所有用户状态 + * @returns {Promise} + */ + async clear () { + await this.ensureInitialized() + + return new Promise((resolve, reject) => { + this.db.run(`DELETE FROM ${this.tableName}`, (err) => { + if (err) { + return reject(err) + } + resolve() + }) + }) + } + + /** + * 获取存储名称 + * @returns {string} + */ + getName () { + return 'SQLiteUserStateStorage' + } + + /** + * 关闭数据库连接 + * @returns {Promise} + */ + async close () { + if (!this.db) { + return Promise.resolve() + } + + return new Promise((resolve, reject) => { + this.db.close(err => { + if (err) { + reject(err) + } else { + this.initialized = false + this.db = null + resolve() + } + }) + }) + } +} diff --git a/models/chaite/user_mode_selector.js b/models/chaite/user_mode_selector.js new file mode 100644 index 0000000..55aa931 --- /dev/null +++ b/models/chaite/user_mode_selector.js @@ -0,0 +1,12 @@ +import { AbstractUserModeSelector } from 'chaite' + +export class ChatGPTUserModeSelector extends AbstractUserModeSelector { + /** + * 根据e判断当前要使用的预设,非常灵活。 + * @param e + * @returns {Promise} + */ + getChatPreset (e) { + // todo + } +} diff --git a/models/chaite/vector_database.js b/models/chaite/vector_database.js new file mode 100644 index 0000000..2e61e3f --- /dev/null +++ b/models/chaite/vector_database.js @@ -0,0 +1,84 @@ +import { LocalIndex } from 'vectra' +import { md5 } from '../../utils/common.js' + +/** + * 基于Vectra实现的简单向量数据库,作为默认实现 + * @implements { import('chaite').VectorDatabase } + */ +export class VectraVectorDatabase { + constructor (indexFile) { + this.index = new LocalIndex(indexFile) + } + + async init () { + if (!(await this.index.isIndexCreated())) { + await this.index.createIndex() + } + } + + async addVector (vector, text) { + const id = md5(text) + await this.index.insertItem({ + vector, + id, + metadata: { text } + }) + return id + } + + /** + * + * @param vectors + * @param texts + * @returns {Promise} + */ + async addVectors (vectors, texts) { + return await Promise.all(vectors.map((v, i) => this.addVector(v, texts[i]))) + } + + /** + * + * @param queryVector + * @param k + * @returns {Promise>} + */ + async search (queryVector, k) { + const results = await this.index.queryItems(queryVector, k) + return results.map(r => ({ id: r.item.id, score: r.score, text: r.item.metadata.text })) + } + + /** + * + * @param id + * @returns {Promise<{ vector: number[], text: string } | null>} + */ + async getVector (id) { + const result = await this.index.getItem(id) + return { + vector: result.vector, + text: result.metadata.text + } + } + + async deleteVector (id) { + await this.index.deleteItem(id) + return true + } + + async updateVector (id, newVector, newText) { + await this.index.upsertItem({ + id, + vector: newVector, + metadata: { text: newText } + }) + return true + } + + async count () { + return (await this.index.getIndexStats()).items + } + + async clear () { + await this.index.deleteIndex() + } +} diff --git a/models/chaite/vectorizer.js b/models/chaite/vectorizer.js new file mode 100644 index 0000000..60b8f60 --- /dev/null +++ b/models/chaite/vectorizer.js @@ -0,0 +1,89 @@ +import { Chaite, ChaiteContext, GeminiClient, OpenAIClient } from 'chaite' + +async function getIClientByChannel (channel) { + await channel.ready() + const baseLogger = global.logger || console + if (channel.options?.setLogger) { + channel.options.setLogger(baseLogger) + } + const context = new ChaiteContext(baseLogger) + context.setChaite(Chaite.getInstance()) + switch (channel.adapterType) { + case 'openai': + return new OpenAIClient(channel.options, context) + case 'gemini': + return new GeminiClient(channel.options, context) + case 'claude': + throw new Error('claude does not support embedding') + default: + throw new Error(`Unsupported adapter ${channel.adapterType}`) + } +} + +async function resolveChannelForModel (model) { + const manager = Chaite.getInstance().getChannelsManager() + const channels = await manager.getChannelByModel(model) + if (channels.length === 0) { + throw new Error('No channel found for model: ' + model) + } + return channels[0] +} + +export async function getClientForModel (model) { + const channel = await resolveChannelForModel(model) + const client = await getIClientByChannel(channel) + return { client, channel } +} + +/** + * 创建一个基于Chaite渠道的向量器 + * @param {string} model + * @param {number} dimensions + * @returns {{ textToVector: (text: string) => Promise, batchTextToVector: (texts: string[]) => Promise }} + */ +export function createChaiteVectorizer (model, dimensions) { + return { + async textToVector (text) { + const { client } = await getClientForModel(model) + const options = { model } + if (Number.isFinite(dimensions) && dimensions > 0) { + options.dimensions = dimensions + } + const result = await client.getEmbedding(text, options) + return result.embeddings[0] + }, + async batchTextToVector (texts) { + const manager = Chaite.getInstance().getChannelsManager() + const channels = await manager.getChannelsByModel(model, texts.length) + if (channels.length === 0) { + throw new Error('No channel found for model: ' + model) + } + const clients = await Promise.all(channels.map(({ channel }) => getIClientByChannel(channel))) + const results = [] + let startIndex = 0 + for (let i = 0; i < channels.length; i++) { + const { quantity } = channels[i] + const slice = texts.slice(startIndex, startIndex + quantity) + const options = { model } + if (Number.isFinite(dimensions) && dimensions > 0) { + options.dimensions = dimensions + } + const embeddings = await clients[i].getEmbedding(slice, options) + results.push(...embeddings.embeddings) + startIndex += quantity + } + return results + } + } +} + +export async function embedTexts (texts, model, dimensions) { + if (!texts || texts.length === 0) { + return [] + } + const vectorizer = createChaiteVectorizer(model, dimensions) + if (texts.length === 1) { + return [await vectorizer.textToVector(texts[0])] + } + return await vectorizer.batchTextToVector(texts) +} diff --git a/models/memory/collector.js b/models/memory/collector.js new file mode 100644 index 0000000..d81acc1 --- /dev/null +++ b/models/memory/collector.js @@ -0,0 +1,633 @@ +import * as crypto from 'node:crypto' +import ChatGPTConfig from '../../config/config.js' +import { extractGroupFacts } from './extractor.js' +import { memoryService } from './service.js' +import { getBotFramework } from '../../utils/bot.js' +import { ICQQGroupContextCollector, TRSSGroupContextCollector } from '../../utils/group.js' +import { groupHistoryCursorStore } from './groupHistoryCursorStore.js' + +const DEFAULT_MAX_WINDOW = 300 // seconds +const DEFAULT_HISTORY_BATCH = 120 +const MAX_RECENT_IDS = 200 + +function nowSeconds () { + return Math.floor(Date.now() / 1000) +} + +function normaliseGroupId (groupId) { + return groupId === null || groupId === undefined ? null : String(groupId) +} + +function shouldIgnoreMessage (e) { + if (!e || !e.message) { + return true + } + if (e.sender?.user_id && e.sender.user_id === e.bot?.uin) { + return true + } + if (e.isPrivate) { + return true + } + const text = e.msg?.trim() + if (!text) { + return true + } + if (text.startsWith('#')) { + return true + } + const prefix = ChatGPTConfig.basic?.togglePrefix + if (prefix && text.startsWith(prefix)) { + return true + } + return false +} + +function extractPlainText (e) { + if (e.msg) { + return e.msg.trim() + } + if (Array.isArray(e.message)) { + return e.message + .filter(item => item.type === 'text') + .map(item => item.text || '') + .join('') + .trim() + } + return '' +} + +function extractHistoryText (chat) { + if (!chat) { + return '' + } + if (typeof chat.raw_message === 'string') { + const trimmed = chat.raw_message.trim() + if (trimmed) { + return trimmed + } + } + if (typeof chat.msg === 'string') { + const trimmed = chat.msg.trim() + if (trimmed) { + return trimmed + } + } + if (Array.isArray(chat.message)) { + const merged = chat.message + .filter(item => item && item.type === 'text') + .map(item => item.text || '') + .join('') + .trim() + if (merged) { + return merged + } + } + if (typeof chat.text === 'string') { + const trimmed = chat.text.trim() + if (trimmed) { + return trimmed + } + } + return '' +} + +function toPositiveInt (value, fallback = 0) { + const num = Number(value) + if (Number.isFinite(num) && num > 0) { + return Math.floor(num) + } + return fallback +} + +function normalizeTimestamp (value) { + if (value === null || value === undefined) { + return 0 + } + const num = Number(value) + if (!Number.isFinite(num) || num <= 0) { + return 0 + } + if (num > 1e12) { + return Math.floor(num) + } + return Math.floor(num * 1000) +} + +function resolveMessageIdCandidate (source) { + if (!source) { + return '' + } + const candidates = [ + source.message_id, + source.messageId, + source.msg_id, + source.seq, + source.messageSeq, + source.id + ] + for (const candidate of candidates) { + if (candidate || candidate === 0) { + const str = String(candidate).trim() + if (str) { + return str + } + } + } + return '' +} + +function resolveUserId (source) { + if (!source) { + return '' + } + const candidates = [ + source.user_id, + source.uid, + source.userId, + source.uin, + source.id, + source.qq + ] + for (const candidate of candidates) { + if (candidate || candidate === 0) { + const str = String(candidate).trim() + if (str) { + return str + } + } + } + return '' +} + +function resolveNickname (source) { + if (!source) { + return '' + } + const candidates = [ + source.card, + source.nickname, + source.name, + source.remark + ] + for (const candidate of candidates) { + if (typeof candidate === 'string') { + const trimmed = candidate.trim() + if (trimmed) { + return trimmed + } + } + } + return '' +} + +export class GroupMessageCollector { + constructor () { + this.buffers = new Map() + this.processing = new Set() + this.groupStates = new Map() + this.lastPollAt = 0 + this.polling = false + this.selfIds = null + } + + get groupConfig () { + return ChatGPTConfig.memory?.group || {} + } + + get historyBatchSize () { + const config = this.groupConfig + const configured = toPositiveInt(config.historyBatchSize, 0) + if (configured > 0) { + return configured + } + const minCount = toPositiveInt(config.minMessageCount, 80) + return Math.max(minCount, DEFAULT_HISTORY_BATCH) + } + + get historyPollIntervalMs () { + const config = this.groupConfig + const configured = Number(config.historyPollInterval) + if (Number.isFinite(configured) && configured > 0) { + return Math.floor(configured) * 1000 + } + if (configured === 0) { + return 0 + } + const fallbackSeconds = Math.max(toPositiveInt(config.maxMessageWindow, DEFAULT_MAX_WINDOW), DEFAULT_MAX_WINDOW) + return fallbackSeconds * 1000 + } + + async tickHistoryPolling (force = false) { + const intervalMs = this.historyPollIntervalMs + if (intervalMs <= 0) { + return + } + if (!force) { + const now = Date.now() + if (this.lastPollAt && (now - this.lastPollAt) < intervalMs) { + return + } + } else { + this.refreshSelfIds() + } + await this.runHistoryPoll() + } + + async runHistoryPoll () { + if (this.polling) { + return + } + this.polling = true + try { + logger.info('[Memory] start group history poll') + await this.pollGroupHistories() + } catch (err) { + logger.error('[Memory] group history poll execution failed:', err) + } finally { + this.lastPollAt = Date.now() + this.polling = false + } + } + + async pollGroupHistories () { + const config = this.groupConfig + if (!config.enable) { + return + } + const groupIds = (config.enabledGroups || []) + .map(normaliseGroupId) + .filter(Boolean) + if (groupIds.length === 0) { + return + } + this.refreshSelfIds() + const framework = getBotFramework() + for (const groupId of groupIds) { + if (!memoryService.isGroupMemoryEnabled(groupId)) { + continue + } + const collector = framework === 'trss' + ? new TRSSGroupContextCollector() + : new ICQQGroupContextCollector() + try { + const added = await this.collectHistoryForGroup(collector, groupId) + if (added > 0) { + logger.debug(`[Memory] history poll buffered ${added} messages, group=${groupId}`) + } + } catch (err) { + logger.warn(`[Memory] failed to poll history for group=${groupId}:`, err) + } + } + } + + async collectHistoryForGroup (collector, groupId) { + const limit = this.historyBatchSize + if (!limit) { + return 0 + } + let chats = [] + try { + chats = await collector.collect(undefined, groupId, 0, limit) + } catch (err) { + logger.warn(`[Memory] failed to collect history for group=${groupId}:`, err) + return 0 + } + if (!Array.isArray(chats) || chats.length === 0) { + return 0 + } + const messages = [] + for (const chat of chats) { + const payload = this.transformHistoryMessage(groupId, chat) + if (payload) { + messages.push(payload) + } + } + if (!messages.length) { + return 0 + } + messages.sort((a, b) => normalizeTimestamp(a.timestamp) - normalizeTimestamp(b.timestamp)) + let queued = 0 + for (const payload of messages) { + if (this.queueMessage(groupId, payload)) { + queued++ + } + } + return queued + } + + transformHistoryMessage (groupId, chat) { + const text = extractHistoryText(chat) + if (!text) { + return null + } + if (text.startsWith('#')) { + return null + } + const prefix = ChatGPTConfig.basic?.togglePrefix + if (prefix && text.startsWith(prefix)) { + return null + } + const sender = chat?.sender || {} + const userId = resolveUserId(sender) || resolveUserId(chat) + if (this.isBotSelfId(userId)) { + return null + } + return { + message_id: resolveMessageIdCandidate(chat), + user_id: userId, + nickname: resolveNickname(sender) || resolveNickname(chat), + text, + timestamp: chat?.time ?? chat?.timestamp ?? chat?.message_time ?? Date.now() + } + } + + queueMessage (groupId, rawPayload) { + if (!rawPayload || !rawPayload.text) { + return false + } + const state = this.getGroupState(groupId) + const messageId = this.ensureMessageId(rawPayload) + const timestampMs = normalizeTimestamp(rawPayload.timestamp) + const buffer = this.getBuffer(groupId) + const payload = { + message_id: messageId, + user_id: rawPayload.user_id ? String(rawPayload.user_id) : '', + nickname: rawPayload.nickname ? String(rawPayload.nickname) : '', + text: rawPayload.text, + timestamp: timestampMs || Date.now() + } + const messageKey = this.resolveMessageKey(payload, messageId, timestampMs) + if (this.shouldSkipMessage(state, timestampMs, messageKey, payload.message_id)) { + return false + } + this.updateGroupState(groupId, state, timestampMs, messageKey, payload.message_id) + buffer.messages.push(payload) + logger.debug(`[Memory] buffered group message, group=${groupId}, buffer=${buffer.messages.length}`) + this.tryTriggerFlush(groupId, buffer) + return true + } + + ensureMessageId (payload) { + const direct = payload?.message_id ? String(payload.message_id).trim() : '' + if (direct) { + return direct + } + const fallback = resolveMessageIdCandidate(payload) + if (fallback) { + return fallback + } + return crypto.randomUUID() + } + + resolveMessageKey (payload, messageId, timestampMs) { + if (messageId) { + return messageId + } + const parts = [ + timestampMs || '', + payload?.user_id || '', + (payload?.text || '').slice(0, 32) + ] + return parts.filter(Boolean).join(':') + } + + getGroupState (groupId) { + let state = this.groupStates.get(groupId) + if (!state) { + const cursor = groupHistoryCursorStore.getCursor(groupId) + const lastTimestamp = Number(cursor?.last_timestamp) || 0 + const lastMessageId = cursor?.last_message_id || null + state = { + lastTimestamp, + lastMessageId, + recentIds: new Set() + } + if (lastMessageId) { + state.recentIds.add(lastMessageId) + } + this.groupStates.set(groupId, state) + } + return state + } + + shouldSkipMessage (state, timestampMs, messageKey, messageId) { + if (!state) { + return false + } + if (messageId && state.lastMessageId && messageId === state.lastMessageId) { + return true + } + if (timestampMs && timestampMs < state.lastTimestamp) { + return true + } + if (timestampMs && timestampMs === state.lastTimestamp && messageKey && state.recentIds.has(messageKey)) { + return true + } + if (!timestampMs && messageKey && state.recentIds.has(messageKey)) { + return true + } + return false + } + + updateGroupState (groupId, state, timestampMs, messageKey, messageId) { + const hasTimestamp = Number.isFinite(timestampMs) && timestampMs > 0 + if (!hasTimestamp) { + if (messageKey) { + state.recentIds.add(messageKey) + if (state.recentIds.size > MAX_RECENT_IDS) { + const ids = Array.from(state.recentIds).slice(-MAX_RECENT_IDS) + state.recentIds = new Set(ids) + } + } + if (messageId) { + state.lastMessageId = String(messageId) + groupHistoryCursorStore.updateCursor(groupId, { + lastMessageId: state.lastMessageId, + lastTimestamp: state.lastTimestamp || null + }) + } + return + } + + if (timestampMs > state.lastTimestamp) { + state.lastTimestamp = timestampMs + state.recentIds = messageKey ? new Set([messageKey]) : new Set() + } else if (timestampMs === state.lastTimestamp && messageKey) { + state.recentIds.add(messageKey) + if (state.recentIds.size > MAX_RECENT_IDS) { + const ids = Array.from(state.recentIds).slice(-MAX_RECENT_IDS) + state.recentIds = new Set(ids) + } + } + + if (messageId) { + state.lastMessageId = String(messageId) + } + + groupHistoryCursorStore.updateCursor(groupId, { + lastMessageId: state.lastMessageId || null, + lastTimestamp: state.lastTimestamp || timestampMs + }) + } + + getBuffer (groupId) { + let buffer = this.buffers.get(groupId) + if (!buffer) { + buffer = { + messages: [], + lastFlushAt: nowSeconds() + } + this.buffers.set(groupId, buffer) + } + return buffer + } + + tryTriggerFlush (groupId, buffer) { + const config = this.groupConfig + const minCount = config.minMessageCount || 50 + const maxWindow = config.maxMessageWindow || DEFAULT_MAX_WINDOW + const shouldFlushByCount = buffer.messages.length >= minCount + const shouldFlushByTime = buffer.messages.length > 0 && (nowSeconds() - buffer.lastFlushAt) >= maxWindow + logger.debug(`[Memory] try trigger flush, group=${groupId}, count=${buffer.messages.length}, lastFlushAt=${buffer.lastFlushAt}, shouldFlushByCount=${shouldFlushByCount}, shouldFlushByTime=${shouldFlushByTime}`) + if (shouldFlushByCount || shouldFlushByTime) { + logger.info(`[Memory] trigger group fact extraction, group=${groupId}, count=${buffer.messages.length}, reason=${shouldFlushByCount ? 'count' : 'timeout'}`) + this.flush(groupId).catch(err => logger.error('Failed to flush group memory:', err)) + } + } + + push (e) { + const groupId = normaliseGroupId(e.group_id || e.group?.group_id) + if (!memoryService.isGroupMemoryEnabled(groupId)) { + return + } + if (shouldIgnoreMessage(e)) { + return + } + const text = extractPlainText(e) + if (!text) { + return + } + this.addSelfId(e.bot?.uin) + const messageId = e.message_id || e.seq || crypto.randomUUID() + logger.debug(`[Memory] collect group message, group=${groupId}, user=${e.sender?.user_id}, buffer=${(this.buffers.get(groupId)?.messages.length || 0) + 1}`) + this.queueMessage(groupId, { + message_id: messageId, + user_id: String(e.sender?.user_id || ''), + nickname: e.sender?.card || e.sender?.nickname || '', + text, + timestamp: e.time || Date.now() + }) + } + + async flush (groupId) { + if (this.processing.has(groupId)) { + return + } + const buffer = this.buffers.get(groupId) + if (!buffer || buffer.messages.length === 0) { + return + } + this.processing.add(groupId) + try { + const messages = buffer.messages + this.buffers.set(groupId, { + messages: [], + lastFlushAt: nowSeconds() + }) + logger.debug(`[Memory] flushing group buffer, group=${groupId}, messages=${messages.length}`) + const simplified = messages.map(msg => ({ + message_id: msg.message_id, + user_id: msg.user_id, + nickname: msg.nickname, + text: msg.text + })) + const factCandidates = await extractGroupFacts(simplified) + if (factCandidates.length === 0) { + logger.debug(`[Memory] group fact extraction returned empty, group=${groupId}`) + return + } + const messageMap = new Map(messages.map(msg => [msg.message_id, msg.text])) + const enrichedFacts = factCandidates.map(fact => { + if (!fact.source_message_ids && fact.sourceMessages) { + fact.source_message_ids = fact.sourceMessages + } + let ids = [] + if (Array.isArray(fact.source_message_ids)) { + ids = fact.source_message_ids.map(id => String(id)) + } else if (typeof fact.source_message_ids === 'string') { + ids = fact.source_message_ids.split(',').map(id => id.trim()).filter(Boolean) + } + if (!fact.source_messages && ids.length > 0) { + const summary = ids + .map(id => messageMap.get(id) || '') + .filter(Boolean) + .join('\n') + fact.source_messages = summary + } + fact.source_message_ids = ids + if (!fact.involved_users || !Array.isArray(fact.involved_users)) { + fact.involved_users = [] + } else { + fact.involved_users = fact.involved_users.map(id => String(id)) + } + return fact + }) + const saved = await memoryService.saveGroupFacts(groupId, enrichedFacts) + logger.info(`[Memory] saved ${saved.length} group facts for group=${groupId}`) + } finally { + this.processing.delete(groupId) + } + } + + addSelfId (uin) { + if (uin === null || uin === undefined) { + return + } + const str = String(uin) + if (!str) { + return + } + if (!this.selfIds) { + this.selfIds = new Set() + } + this.selfIds.add(str) + } + + refreshSelfIds () { + this.selfIds = this.collectSelfIds() + } + + collectSelfIds () { + const ids = new Set() + try { + const botGlobal = global.Bot + if (botGlobal?.bots && typeof botGlobal.bots === 'object') { + for (const bot of Object.values(botGlobal.bots)) { + if (bot?.uin) { + ids.add(String(bot.uin)) + } + } + } + if (botGlobal?.uin) { + ids.add(String(botGlobal.uin)) + } + } catch (err) { + logger?.debug?.('[Memory] failed to collect bot self ids: %o', err) + } + return ids + } + + isBotSelfId (userId) { + if (userId === null || userId === undefined) { + return false + } + const str = String(userId) + if (!str) { + return false + } + if (!this.selfIds || this.selfIds.size === 0) { + this.refreshSelfIds() + } + return this.selfIds?.has(str) || false + } +} diff --git a/models/memory/database.js b/models/memory/database.js new file mode 100644 index 0000000..63fbe3d --- /dev/null +++ b/models/memory/database.js @@ -0,0 +1,755 @@ +import Database from 'better-sqlite3' +import * as sqliteVec from 'sqlite-vec' +import fs from 'fs' +import path from 'path' +import ChatGPTConfig from '../../config/config.js' + +const META_VECTOR_DIM_KEY = 'group_vec_dimension' +const META_VECTOR_MODEL_KEY = 'group_vec_model' +const META_GROUP_TOKENIZER_KEY = 'group_memory_tokenizer' +const META_USER_TOKENIZER_KEY = 'user_memory_tokenizer' +const TOKENIZER_DEFAULT = 'unicode61' +const SIMPLE_MATCH_SIMPLE = 'simple_query' +const SIMPLE_MATCH_JIEBA = 'jieba_query' +const PLUGIN_ROOT = path.resolve('./plugins/chatgpt-plugin') + +let dbInstance = null +let cachedVectorDimension = null +let cachedVectorModel = null +let userMemoryFtsConfig = { + tokenizer: TOKENIZER_DEFAULT, + matchQuery: null +} +let groupMemoryFtsConfig = { + tokenizer: TOKENIZER_DEFAULT, + matchQuery: null +} +const simpleExtensionState = { + requested: false, + enabled: false, + loaded: false, + error: null, + libraryPath: '', + dictPath: '', + tokenizer: TOKENIZER_DEFAULT, + matchQuery: null +} + +function resolveDbPath () { + const relativePath = ChatGPTConfig.memory?.database || 'data/memory.db' + return path.resolve('./plugins/chatgpt-plugin', relativePath) +} + +export function resolvePluginPath (targetPath) { + if (!targetPath) { + return '' + } + if (path.isAbsolute(targetPath)) { + return targetPath + } + return path.resolve(PLUGIN_ROOT, targetPath) +} + +export function toPluginRelativePath (absolutePath) { + if (!absolutePath) { + return '' + } + return path.relative(PLUGIN_ROOT, absolutePath) +} + +function resolvePreferredDimension () { + const { memory, llm } = ChatGPTConfig + if (memory?.vectorDimensions && memory.vectorDimensions > 0) { + return memory.vectorDimensions + } + if (llm?.dimensions && llm.dimensions > 0) { + return llm.dimensions + } + return 1536 +} + +function ensureDirectory (filePath) { + const dir = path.dirname(filePath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } +} + +function ensureMetaTable (db) { + db.exec(` + CREATE TABLE IF NOT EXISTS memory_meta ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ) + `) +} + +function getMetaValue (db, key) { + const stmt = db.prepare('SELECT value FROM memory_meta WHERE key = ?') + const row = stmt.get(key) + return row ? row.value : null +} + +function setMetaValue (db, key, value) { + db.prepare(` + INSERT INTO memory_meta (key, value) + VALUES (?, ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value + `).run(key, value) +} + +function resetSimpleState (overrides = {}) { + simpleExtensionState.loaded = false + simpleExtensionState.error = null + simpleExtensionState.tokenizer = TOKENIZER_DEFAULT + simpleExtensionState.matchQuery = null + Object.assign(simpleExtensionState, overrides) + userMemoryFtsConfig = { + tokenizer: TOKENIZER_DEFAULT, + matchQuery: null + } + groupMemoryFtsConfig = { + tokenizer: TOKENIZER_DEFAULT, + matchQuery: null + } +} + +function sanitiseRawFtsInput (input) { + if (!input) { + return '' + } + const trimmed = String(input).trim() + if (!trimmed) { + return '' + } + const replaced = trimmed + .replace(/["'`]+/g, ' ') + .replace(/\u3000/g, ' ') + .replace(/[^\p{L}\p{N}\u4E00-\u9FFF\u3040-\u30FF\uAC00-\uD7AF\u1100-\u11FF\s]+/gu, ' ') + const collapsed = replaced.replace(/\s+/g, ' ').trim() + return collapsed || trimmed +} + +function isSimpleLibraryFile (filename) { + return /(^libsimple.*\.(so|dylib|dll)$)|(^simple\.(so|dylib|dll)$)/i.test(filename) +} + +function findSimpleLibrary (startDir) { + const stack = [startDir] + while (stack.length > 0) { + const dir = stack.pop() + if (!dir || !fs.existsSync(dir)) { + continue + } + const entries = fs.readdirSync(dir, { withFileTypes: true }) + for (const entry of entries) { + const fullPath = path.join(dir, entry.name) + if (entry.isDirectory()) { + stack.push(fullPath) + } else if (entry.isFile() && isSimpleLibraryFile(entry.name)) { + return fullPath + } + } + } + return '' +} + +function locateDictPathNear (filePath) { + if (!filePath) { + return '' + } + let currentDir = path.dirname(filePath) + for (let depth = 0; depth < 5 && currentDir && currentDir !== path.dirname(currentDir); depth++) { + const dictCandidate = path.join(currentDir, 'dict') + if (fs.existsSync(dictCandidate) && fs.statSync(dictCandidate).isDirectory()) { + return dictCandidate + } + currentDir = path.dirname(currentDir) + } + return '' +} + +function discoverSimplePaths () { + const searchRoots = [ + path.join(PLUGIN_ROOT, 'resources/simple'), + path.join(PLUGIN_ROOT, 'resources'), + path.join(PLUGIN_ROOT, 'lib/simple'), + PLUGIN_ROOT + ] + for (const root of searchRoots) { + if (!root || !fs.existsSync(root)) { + continue + } + const lib = findSimpleLibrary(root) + if (lib) { + const dictCandidate = locateDictPathNear(lib) + return { + libraryPath: toPluginRelativePath(lib) || lib, + dictPath: dictCandidate ? (toPluginRelativePath(dictCandidate) || dictCandidate) : '' + } + } + } + return { libraryPath: '', dictPath: '' } +} + +function applySimpleExtension (db) { + const config = ChatGPTConfig.memory?.extensions?.simple || {} + simpleExtensionState.requested = Boolean(config.enable) + simpleExtensionState.enabled = Boolean(config.enable) + simpleExtensionState.libraryPath = config.libraryPath || '' + simpleExtensionState.dictPath = config.dictPath || '' + if (!config.enable) { + logger?.debug?.('[Memory] simple tokenizer disabled via config') + resetSimpleState({ requested: false, enabled: false }) + return + } + if (!simpleExtensionState.libraryPath) { + const detected = discoverSimplePaths() + if (detected.libraryPath) { + simpleExtensionState.libraryPath = detected.libraryPath + simpleExtensionState.dictPath = detected.dictPath + config.libraryPath = detected.libraryPath + if (detected.dictPath) { + config.dictPath = detected.dictPath + } + } + } + const resolvedLibraryPath = resolvePluginPath(config.libraryPath) + if (!resolvedLibraryPath || !fs.existsSync(resolvedLibraryPath)) { + logger?.warn?.('[Memory] simple tokenizer library missing:', resolvedLibraryPath || '(empty path)') + resetSimpleState({ + requested: true, + enabled: true, + error: `Simple extension library not found at ${resolvedLibraryPath || '(empty path)'}` + }) + return + } + try { + logger?.info?.('[Memory] loading simple tokenizer extension from', resolvedLibraryPath) + db.loadExtension(resolvedLibraryPath) + if (config.useJieba) { + const resolvedDict = resolvePluginPath(config.dictPath) + if (resolvedDict && fs.existsSync(resolvedDict)) { + try { + logger?.debug?.('[Memory] configuring simple tokenizer jieba dict:', resolvedDict) + db.prepare('select jieba_dict(?)').get(resolvedDict) + } catch (err) { + logger?.warn?.('Failed to register jieba dict for simple extension:', err) + } + } else { + logger?.warn?.('Simple extension jieba dict path missing:', resolvedDict) + } + } + const tokenizer = config.useJieba ? 'simple_jieba' : 'simple' + const matchQuery = config.useJieba ? SIMPLE_MATCH_JIEBA : SIMPLE_MATCH_SIMPLE + simpleExtensionState.loaded = true + simpleExtensionState.error = null + simpleExtensionState.tokenizer = tokenizer + simpleExtensionState.matchQuery = matchQuery + logger?.info?.('[Memory] simple tokenizer initialised, tokenizer=%s, matchQuery=%s', tokenizer, matchQuery) + userMemoryFtsConfig = { + tokenizer, + matchQuery + } + groupMemoryFtsConfig = { + tokenizer, + matchQuery + } + return + } catch (error) { + logger?.error?.('Failed to load simple extension:', error) + resetSimpleState({ + requested: true, + enabled: true, + error: `Failed to load simple extension: ${error?.message || error}` + }) + } +} + +function loadSimpleExtensionForCleanup (db) { + if (!ChatGPTConfig.memory.extensions) { + ChatGPTConfig.memory.extensions = {} + } + if (!ChatGPTConfig.memory.extensions.simple) { + ChatGPTConfig.memory.extensions.simple = { + enable: false, + libraryPath: '', + dictPath: '', + useJieba: false + } + } + const config = ChatGPTConfig.memory.extensions.simple + let libraryPath = config.libraryPath || '' + let dictPath = config.dictPath || '' + if (!libraryPath) { + const detected = discoverSimplePaths() + libraryPath = detected.libraryPath + if (detected.dictPath && !dictPath) { + dictPath = detected.dictPath + } + if (libraryPath) { + ChatGPTConfig.memory.extensions.simple = ChatGPTConfig.memory.extensions.simple || {} + ChatGPTConfig.memory.extensions.simple.libraryPath = libraryPath + if (dictPath) { + ChatGPTConfig.memory.extensions.simple.dictPath = dictPath + } + } + } + const resolvedLibraryPath = resolvePluginPath(libraryPath) + if (!resolvedLibraryPath || !fs.existsSync(resolvedLibraryPath)) { + logger?.warn?.('[Memory] cleanup requires simple extension but library missing:', resolvedLibraryPath || '(empty path)') + return false + } + try { + logger?.info?.('[Memory] temporarily loading simple extension for cleanup tasks') + db.loadExtension(resolvedLibraryPath) + const useJieba = Boolean(config.useJieba) + if (useJieba) { + const resolvedDict = resolvePluginPath(dictPath) + if (resolvedDict && fs.existsSync(resolvedDict)) { + try { + db.prepare('select jieba_dict(?)').get(resolvedDict) + } catch (err) { + logger?.warn?.('Failed to set jieba dict during cleanup:', err) + } + } + } + return true + } catch (error) { + logger?.error?.('Failed to load simple extension for cleanup:', error) + return false + } +} + +function ensureGroupFactsTable (db) { + ensureMetaTable(db) + db.exec(` + CREATE TABLE IF NOT EXISTS group_facts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + group_id TEXT NOT NULL, + fact TEXT NOT NULL, + topic TEXT, + importance REAL DEFAULT 0.5, + source_message_ids TEXT, + source_messages TEXT, + involved_users TEXT, + created_at TEXT DEFAULT (datetime('now')) + ) + `) + db.exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_group_facts_unique + ON group_facts(group_id, fact) + `) + db.exec(` + CREATE INDEX IF NOT EXISTS idx_group_facts_group + ON group_facts(group_id, importance DESC, created_at DESC) + `) + ensureGroupFactsFtsTable(db) +} + +function ensureGroupHistoryCursorTable (db) { + ensureMetaTable(db) + db.exec(` + CREATE TABLE IF NOT EXISTS group_history_cursor ( + group_id TEXT PRIMARY KEY, + last_message_id TEXT, + last_timestamp INTEGER + ) + `) +} + +function ensureUserMemoryTable (db) { + ensureMetaTable(db) + db.exec(` + CREATE TABLE IF NOT EXISTS user_memory ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + group_id TEXT, + key TEXT NOT NULL, + value TEXT NOT NULL, + importance REAL DEFAULT 0.5, + source_message_id TEXT, + created_at TEXT DEFAULT (datetime('now')), + updated_at TEXT DEFAULT (datetime('now')) + ) + `) + db.exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_user_memory_key + ON user_memory(user_id, coalesce(group_id, ''), key) + `) + db.exec(` + CREATE INDEX IF NOT EXISTS idx_user_memory_group + ON user_memory(group_id) + `) + db.exec(` + CREATE INDEX IF NOT EXISTS idx_user_memory_user + ON user_memory(user_id) + `) + ensureUserMemoryFtsTable(db) +} + +function dropGroupFactsFtsArtifacts (db) { + try { + db.exec(` + DROP TRIGGER IF EXISTS group_facts_ai; + DROP TRIGGER IF EXISTS group_facts_ad; + DROP TRIGGER IF EXISTS group_facts_au; + DROP TABLE IF EXISTS group_facts_fts; + `) + } catch (err) { + if (String(err?.message || '').includes('no such tokenizer')) { + const loaded = loadSimpleExtensionForCleanup(db) + if (loaded) { + db.exec(` + DROP TRIGGER IF EXISTS group_facts_ai; + DROP TRIGGER IF EXISTS group_facts_ad; + DROP TRIGGER IF EXISTS group_facts_au; + DROP TABLE IF EXISTS group_facts_fts; + `) + } else { + logger?.warn?.('[Memory] Falling back to raw schema cleanup for group_facts_fts') + try { + db.exec('PRAGMA writable_schema = ON;') + db.exec(`DELETE FROM sqlite_master WHERE name IN ('group_facts_ai','group_facts_ad','group_facts_au','group_facts_fts');`) + } finally { + db.exec('PRAGMA writable_schema = OFF;') + } + } + } else { + throw err + } + } +} + +function createGroupFactsFts (db, tokenizer) { + logger?.info?.('[Memory] creating group_facts_fts with tokenizer=%s', tokenizer) + db.exec(` + CREATE VIRTUAL TABLE group_facts_fts + USING fts5( + fact, + topic, + content = 'group_facts', + content_rowid = 'id', + tokenize = '${tokenizer}' + ) + `) + db.exec(` + CREATE TRIGGER group_facts_ai AFTER INSERT ON group_facts BEGIN + INSERT INTO group_facts_fts(rowid, fact, topic) + VALUES (new.id, new.fact, coalesce(new.topic, '')); + END; + `) + db.exec(` + CREATE TRIGGER group_facts_ad AFTER DELETE ON group_facts BEGIN + INSERT INTO group_facts_fts(group_facts_fts, rowid, fact, topic) + VALUES ('delete', old.id, old.fact, coalesce(old.topic, '')); + END; + `) + db.exec(` + CREATE TRIGGER group_facts_au AFTER UPDATE ON group_facts BEGIN + INSERT INTO group_facts_fts(group_facts_fts, rowid, fact, topic) + VALUES ('delete', old.id, old.fact, coalesce(old.topic, '')); + INSERT INTO group_facts_fts(rowid, fact, topic) + VALUES (new.id, new.fact, coalesce(new.topic, '')); + END; + `) + try { + db.exec(`INSERT INTO group_facts_fts(group_facts_fts) VALUES ('rebuild')`) + } catch (err) { + logger?.debug?.('Group facts FTS rebuild skipped:', err?.message || err) + } +} + +function ensureGroupFactsFtsTable (db) { + const desiredTokenizer = groupMemoryFtsConfig.tokenizer || TOKENIZER_DEFAULT + const storedTokenizer = getMetaValue(db, META_GROUP_TOKENIZER_KEY) + const tableExists = db.prepare(` + SELECT name FROM sqlite_master + WHERE type = 'table' AND name = 'group_facts_fts' + `).get() + if (storedTokenizer && storedTokenizer !== desiredTokenizer) { + dropGroupFactsFtsArtifacts(db) + } else if (!storedTokenizer && tableExists) { + // Unknown tokenizer, drop to ensure consistency. + dropGroupFactsFtsArtifacts(db) + } + const existsAfterDrop = db.prepare(` + SELECT name FROM sqlite_master + WHERE type = 'table' AND name = 'group_facts_fts' + `).get() + if (!existsAfterDrop) { + createGroupFactsFts(db, desiredTokenizer) + setMetaValue(db, META_GROUP_TOKENIZER_KEY, desiredTokenizer) + logger?.info?.('[Memory] group facts FTS initialised with tokenizer=%s', desiredTokenizer) + } +} + +function dropUserMemoryFtsArtifacts (db) { + try { + db.exec(` + DROP TRIGGER IF EXISTS user_memory_ai; + DROP TRIGGER IF EXISTS user_memory_ad; + DROP TRIGGER IF EXISTS user_memory_au; + DROP TABLE IF EXISTS user_memory_fts; + `) + } catch (err) { + if (String(err?.message || '').includes('no such tokenizer')) { + const loaded = loadSimpleExtensionForCleanup(db) + if (loaded) { + db.exec(` + DROP TRIGGER IF EXISTS user_memory_ai; + DROP TRIGGER IF EXISTS user_memory_ad; + DROP TRIGGER IF EXISTS user_memory_au; + DROP TABLE IF EXISTS user_memory_fts; + `) + } else { + logger?.warn?.('[Memory] Falling back to raw schema cleanup for user_memory_fts') + try { + db.exec('PRAGMA writable_schema = ON;') + db.exec(`DELETE FROM sqlite_master WHERE name IN ('user_memory_ai','user_memory_ad','user_memory_au','user_memory_fts');`) + } finally { + db.exec('PRAGMA writable_schema = OFF;') + } + } + } else { + throw err + } + } +} + +function createUserMemoryFts (db, tokenizer) { + logger?.info?.('[Memory] creating user_memory_fts with tokenizer=%s', tokenizer) + db.exec(` + CREATE VIRTUAL TABLE user_memory_fts + USING fts5( + value, + content = 'user_memory', + content_rowid = 'id', + tokenize = '${tokenizer}' + ) + `) + db.exec(` + CREATE TRIGGER user_memory_ai AFTER INSERT ON user_memory BEGIN + INSERT INTO user_memory_fts(rowid, value) + VALUES (new.id, new.value); + END; + `) + db.exec(` + CREATE TRIGGER user_memory_ad AFTER DELETE ON user_memory BEGIN + INSERT INTO user_memory_fts(user_memory_fts, rowid, value) + VALUES ('delete', old.id, old.value); + END; + `) + db.exec(` + CREATE TRIGGER user_memory_au AFTER UPDATE ON user_memory BEGIN + INSERT INTO user_memory_fts(user_memory_fts, rowid, value) + VALUES ('delete', old.id, old.value); + INSERT INTO user_memory_fts(rowid, value) + VALUES (new.id, new.value); + END; + `) + try { + db.exec(`INSERT INTO user_memory_fts(user_memory_fts) VALUES ('rebuild')`) + } catch (err) { + logger?.debug?.('User memory FTS rebuild skipped:', err?.message || err) + } +} + +function ensureUserMemoryFtsTable (db) { + const desiredTokenizer = userMemoryFtsConfig.tokenizer || TOKENIZER_DEFAULT + const storedTokenizer = getMetaValue(db, META_USER_TOKENIZER_KEY) + const tableExists = db.prepare(` + SELECT name FROM sqlite_master + WHERE type = 'table' AND name = 'user_memory_fts' + `).get() + if (storedTokenizer && storedTokenizer !== desiredTokenizer) { + dropUserMemoryFtsArtifacts(db) + } else if (!storedTokenizer && tableExists) { + dropUserMemoryFtsArtifacts(db) + } + const existsAfterDrop = db.prepare(` + SELECT name FROM sqlite_master + WHERE type = 'table' AND name = 'user_memory_fts' + `).get() + if (!existsAfterDrop) { + createUserMemoryFts(db, desiredTokenizer) + setMetaValue(db, META_USER_TOKENIZER_KEY, desiredTokenizer) + logger?.info?.('[Memory] user memory FTS initialised with tokenizer=%s', desiredTokenizer) + } +} + +function createVectorTable (db, dimension) { + if (!dimension || dimension <= 0) { + throw new Error(`Invalid vector dimension for table creation: ${dimension}`) + } + db.exec(`CREATE VIRTUAL TABLE vec_group_facts USING vec0(embedding float[${dimension}])`) +} + +function ensureVectorTable (db) { + ensureMetaTable(db) + if (cachedVectorDimension !== null) { + return cachedVectorDimension + } + const preferredDimension = resolvePreferredDimension() + const stored = getMetaValue(db, META_VECTOR_DIM_KEY) + const storedModel = getMetaValue(db, META_VECTOR_MODEL_KEY) + const currentModel = ChatGPTConfig.llm?.embeddingModel || '' + const tableExists = Boolean(db.prepare(` + SELECT name FROM sqlite_master + WHERE type = 'table' AND name = 'vec_group_facts' + `).get()) + + const parseDimension = value => { + if (!value && value !== 0) return 0 + const parsed = parseInt(String(value), 10) + return Number.isFinite(parsed) && parsed > 0 ? parsed : 0 + } + + const storedDimension = parseDimension(stored) + let dimension = storedDimension + let tablePresent = tableExists + + let needsTableReset = false + if (tableExists && storedDimension <= 0) { + needsTableReset = true + } + + if (needsTableReset && tableExists) { + try { + db.exec('DROP TABLE IF EXISTS vec_group_facts') + tablePresent = false + dimension = 0 + } catch (err) { + logger?.warn?.('[Memory] failed to drop vec_group_facts during dimension change:', err) + } + } + +if (!tablePresent) { + if (dimension <= 0) { + dimension = parseDimension(preferredDimension) + } + if (dimension > 0) { + try { + createVectorTable(db, dimension) + tablePresent = true + setMetaValue(db, META_VECTOR_MODEL_KEY, currentModel) + setMetaValue(db, META_VECTOR_DIM_KEY, String(dimension)) + cachedVectorDimension = dimension + cachedVectorModel = currentModel + return cachedVectorDimension + } catch (err) { + logger?.error?.('[Memory] failed to (re)create vec_group_facts table:', err) + dimension = 0 + } + } + } + + if (tablePresent && storedDimension > 0) { + cachedVectorDimension = storedDimension + cachedVectorModel = storedModel || currentModel + return cachedVectorDimension + } + + // At this point we failed to determine a valid dimension, set metadata to 0 to avoid loops. + setMetaValue(db, META_VECTOR_MODEL_KEY, currentModel) + setMetaValue(db, META_VECTOR_DIM_KEY, '0') + cachedVectorDimension = 0 + cachedVectorModel = currentModel + return cachedVectorDimension +} +export function resetVectorTableDimension (dimension) { + if (!Number.isFinite(dimension) || dimension <= 0) { + throw new Error(`Invalid vector dimension: ${dimension}`) + } + const db = getMemoryDatabase() + try { + db.exec('DROP TABLE IF EXISTS vec_group_facts') + } catch (err) { + logger?.warn?.('[Memory] failed to drop vec_group_facts:', err) + } + createVectorTable(db, dimension) + setMetaValue(db, META_VECTOR_DIM_KEY, dimension.toString()) + const model = ChatGPTConfig.llm?.embeddingModel || '' + setMetaValue(db, META_VECTOR_MODEL_KEY, model) + cachedVectorDimension = dimension + cachedVectorModel = model +} + +function migrate (db) { + ensureGroupFactsTable(db) + ensureGroupHistoryCursorTable(db) + ensureUserMemoryTable(db) + ensureVectorTable(db) +} + +export function getUserMemoryFtsConfig () { + return { ...userMemoryFtsConfig } +} + +export function getGroupMemoryFtsConfig () { + return { ...groupMemoryFtsConfig } +} + +export function getSimpleExtensionState () { + return { ...simpleExtensionState } +} + +export function sanitiseFtsQueryInput (query, ftsConfig) { + if (!query) { + return '' + } + if (ftsConfig?.matchQuery) { + return String(query).trim() + } + return sanitiseRawFtsInput(query) +} + +export function getMemoryDatabase () { + if (dbInstance) { + return dbInstance + } + const dbPath = resolveDbPath() + ensureDirectory(dbPath) + logger?.info?.('[Memory] opening memory database at %s', dbPath) + dbInstance = new Database(dbPath) + sqliteVec.load(dbInstance) + resetSimpleState({ + requested: false, + enabled: false + }) + applySimpleExtension(dbInstance) + migrate(dbInstance) + logger?.info?.('[Memory] memory database init completed (simple loaded=%s)', simpleExtensionState.loaded) + return dbInstance +} + +export function getVectorDimension () { + const currentModel = ChatGPTConfig.llm?.embeddingModel || '' + if (cachedVectorModel && cachedVectorModel !== currentModel) { + cachedVectorDimension = null + cachedVectorModel = null + } + if (cachedVectorDimension !== null) { + return cachedVectorDimension + } + const db = getMemoryDatabase() + return ensureVectorTable(db) +} + +export function resetCachedDimension () { + cachedVectorDimension = null + cachedVectorModel = null +} + +export function resetMemoryDatabaseInstance () { + if (dbInstance) { + try { + dbInstance.close() + } catch (error) { + console.warn('Failed to close memory database:', error) + } + } + dbInstance = null + cachedVectorDimension = null + cachedVectorModel = null +} diff --git a/models/memory/extractor.js b/models/memory/extractor.js new file mode 100644 index 0000000..fb6b1bb --- /dev/null +++ b/models/memory/extractor.js @@ -0,0 +1,306 @@ +import { SendMessageOption, Chaite } from 'chaite' +import ChatGPTConfig from '../../config/config.js' +import { getClientForModel } from '../chaite/vectorizer.js' + +function collectTextFromResponse (response) { + if (!response?.contents) { + return '' + } + return response.contents + .filter(content => content.type === 'text') + .map(content => content.text || '') + .join('\n') + .trim() +} + +function parseJSON (text) { + if (!text) { + return null + } + const trimmed = text.trim() + const codeBlockMatch = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i) + const payload = codeBlockMatch ? codeBlockMatch[1] : trimmed + try { + return JSON.parse(payload) + } catch (err) { + logger.warn('Failed to parse JSON from memory extractor response:', text) + return null + } +} + +function formatEntry (entry) { + let str = '' + try { + if (typeof entry === 'string') { + str = entry + } else { + str = JSON.stringify(entry) + } + } catch (err) { + str = String(entry) + } + const limit = 200 + return str.length > limit ? str.slice(0, limit) + '…' : str +} + +function injectMessagesIntoTemplate (template, body) { + if (!template || typeof template !== 'string') { + return body + } + const placeholders = ['${messages}', '{messages}', '{{messages}}'] + let result = template + let replaced = false + for (const placeholder of placeholders) { + if (result.includes(placeholder)) { + result = result.split(placeholder).join(body) + replaced = true + } + } + if (!replaced) { + const trimmed = result.trim() + if (!trimmed) { + return body + } + if (/\n\s*$/.test(result)) { + return `${result}${body}` + } + return `${result}\n${body}` + } + return result +} + +async function resolvePresetSendMessageOption (presetId, scope) { + if (!presetId) { + return null + } + try { + const chaite = Chaite.getInstance?.() + if (!chaite) { + logger.warn(`[Memory] ${scope} extraction preset ${presetId} configured but Chaite is not initialized`) + return null + } + const presetManager = chaite.getChatPresetManager?.() + if (!presetManager) { + logger.warn(`[Memory] ${scope} extraction preset ${presetId} configured but preset manager unavailable`) + return null + } + const preset = await presetManager.getInstance(presetId) + if (!preset) { + logger.warn(`[Memory] ${scope} extraction preset ${presetId} not found`) + return null + } + logger.debug(`[Memory] using ${scope} extraction preset ${presetId}`) + return { + preset, + sendMessageOption: JSON.parse(JSON.stringify(preset.sendMessageOption || {})) + } + } catch (err) { + logger.error(`[Memory] failed to load ${scope} extraction preset ${presetId}:`, err) + return null + } +} + +function resolveGroupExtractionPrompts (presetSendMessageOption) { + const config = ChatGPTConfig.memory?.group || {} + const system = config.extractionSystemPrompt || presetSendMessageOption?.systemOverride || `You are a knowledge extraction assistant that specialises in summarising long-term facts from group chat transcripts. +Read the provided conversation and identify statements that should be stored as long-term knowledge for the group. +Return a JSON array. Each element must contain: +{ + "fact": 事实内容,必须完整包含事件的各个要素而不能是简单的短语(比如谁参与了事件、做了什么事情、背景时间是什么)(同一件事情尽可能整合为同一条而非拆分,以便利于检索), + "topic": 主题关键词,字符串,如 "活动"、"成员信息", + "importance": 一个介于0和1之间的小数,数值越大表示越重要, + "source_message_ids": 原始消息ID数组, + "source_messages": 对应原始消息的简要摘录或合并文本, + "involved_users": 出现或相关的用户ID数组 +} +Only include meaningful, verifiable group-specific information that is useful for future conversations. Do not record incomplete information. Do not include general knowledge or unrelated facts. Do not wrap the JSON array in code fences.` + const userTemplate = config.extractionUserPrompt || `以下是群聊中的一些消息,请根据系统说明提取值得长期记忆的事实,以JSON数组形式返回,不要输出额外说明。 + +\${messages}` + return { system, userTemplate } +} + +function buildGroupUserPrompt (messages, template) { + const joined = messages.map(msg => { + const sender = msg.nickname || msg.user_id || '未知用户' + return `${sender}: ${msg.text}` + }).join('\n') + return injectMessagesIntoTemplate(template, joined) +} + +function buildExistingMemorySection (existingMemories = []) { + if (!existingMemories || existingMemories.length === 0) { + return '当前没有任何已知的长期记忆。' + } + const lines = existingMemories.map((item, idx) => `${idx + 1}. ${item}`) + return `以下是关于用户的已知长期记忆,请在提取新记忆时参考,避免重复已有事实,并在信息变更时更新描述:\n${lines.join('\n')}` +} + +function resolveUserExtractionPrompts (existingMemories = [], presetSendMessageOption) { + const config = ChatGPTConfig.memory?.user || {} + const systemTemplate = config.extractionSystemPrompt || presetSendMessageOption?.systemOverride || `You are an assistant that extracts long-term personal preferences or persona details about a user. +Given a conversation snippet between the user and the bot, identify durable information such as preferences, nicknames, roles, speaking style, habits, or other facts that remain valid over time. +Return a JSON array of **strings**, and nothing else, without any other characters including \`\`\` or \`\`\`json. Each string must be a short sentence (in the same language as the conversation) describing one piece of long-term memory. Do not include keys, JSON objects, or additional metadata. Ignore temporary topics or uncertain information.` + const userTemplate = config.extractionUserPrompt || `下面是用户与机器人的对话,请根据系统提示提取可长期记忆的个人信息。 + +\${messages}` + return { + system: `${systemTemplate} + +${buildExistingMemorySection(existingMemories)}`, + userTemplate + } +} + +function buildUserPrompt (messages, template) { + const body = messages.map(msg => { + const prefix = msg.role === 'assistant' ? '机器人' : (msg.nickname || msg.user_id || '用户') + return `${prefix}: ${msg.text}` + }).join('\n') + return injectMessagesIntoTemplate(template, body) +} + +async function callModel ({ prompt, systemPrompt, model, maxToken = 4096, temperature = 0.2, sendMessageOption }) { + const options = sendMessageOption + ? JSON.parse(JSON.stringify(sendMessageOption)) + : {} + options.model = model || options.model + if (!options.model) { + throw new Error('No model available for memory extraction call') + } + const resolvedModel = options.model + const { client } = await getClientForModel(resolvedModel) + const response = await client.sendMessage({ + role: 'user', + content: [ + { + type: 'text', + text: prompt + } + ] + }, SendMessageOption.create({ + ...options, + model: options.model, + temperature: options.temperature ?? temperature, + maxToken: options.maxToken ?? maxToken, + systemOverride: systemPrompt ?? options.systemOverride, + disableHistoryRead: true, + disableHistorySave: true, + stream: false + })) + return collectTextFromResponse(response) +} + +function resolveGroupExtractionModel (presetSendMessageOption) { + const config = ChatGPTConfig.memory?.group + if (config?.extractionModel) { + return config.extractionModel + } + if (presetSendMessageOption?.model) { + return presetSendMessageOption.model + } + if (ChatGPTConfig.llm?.defaultModel) { + return ChatGPTConfig.llm.defaultModel + } + return '' +} + +function resolveUserExtractionModel (presetSendMessageOption) { + const config = ChatGPTConfig.memory?.user + if (config?.extractionModel) { + return config.extractionModel + } + if (presetSendMessageOption?.model) { + return presetSendMessageOption.model + } + if (ChatGPTConfig.llm?.defaultModel) { + return ChatGPTConfig.llm.defaultModel + } + return '' +} + +export async function extractGroupFacts (messages) { + if (!messages || messages.length === 0) { + return [] + } + const groupConfig = ChatGPTConfig.memory?.group || {} + const presetInfo = await resolvePresetSendMessageOption(groupConfig.extractionPresetId, 'group') + const presetOptions = presetInfo?.sendMessageOption + const model = resolveGroupExtractionModel(presetOptions) + if (!model) { + logger.warn('No model configured for group memory extraction') + return [] + } + try { + const prompts = resolveGroupExtractionPrompts(presetOptions) + logger.debug(`[Memory] start group fact extraction, messages=${messages.length}, model=${model}${presetInfo?.preset ? `, preset=${presetInfo.preset.id}` : ''}`) + const text = await callModel({ + prompt: buildGroupUserPrompt(messages, prompts.userTemplate), + systemPrompt: prompts.system, + model, + sendMessageOption: presetOptions + }) + const parsed = parseJSON(text) + if (Array.isArray(parsed)) { + logger.info(`[Memory] extracted ${parsed.length} group facts`) + parsed.slice(0, 10).forEach((item, idx) => { + logger.debug(`[Memory] group fact[${idx}] ${formatEntry(item)}`) + }) + return parsed + } + logger.debug('[Memory] group fact extraction returned non-array content') + return [] + } catch (err) { + logger.error('Failed to extract group facts:', err) + return [] + } +} + +export async function extractUserMemories (messages, existingMemories = []) { + if (!messages || messages.length === 0) { + return [] + } + const userConfig = ChatGPTConfig.memory?.user || {} + const presetInfo = await resolvePresetSendMessageOption(userConfig.extractionPresetId, 'user') + const presetOptions = presetInfo?.sendMessageOption + const model = resolveUserExtractionModel(presetOptions) + if (!model) { + logger.warn('No model configured for user memory extraction') + return [] + } + try { + const prompts = resolveUserExtractionPrompts(existingMemories, presetOptions) + logger.debug(`[Memory] start user memory extraction, snippets=${messages.length}, existing=${existingMemories.length}, model=${model}${presetInfo?.preset ? `, preset=${presetInfo.preset.id}` : ''}`) + const text = await callModel({ + prompt: buildUserPrompt(messages, prompts.userTemplate), + systemPrompt: prompts.system, + model, + sendMessageOption: presetOptions + }) + const parsed = parseJSON(text) + if (Array.isArray(parsed)) { + const sentences = parsed.map(item => { + if (typeof item === 'string') { + return item.trim() + } + if (item && typeof item === 'object') { + const possible = item.sentence || item.text || item.value || item.fact + if (possible) { + return String(possible).trim() + } + } + return '' + }).filter(Boolean) + logger.info(`[Memory] extracted ${sentences.length} user memories`) + sentences.slice(0, 10).forEach((item, idx) => { + logger.debug(`[Memory] user memory[${idx}] ${formatEntry(item)}`) + }) + return sentences + } + logger.debug('[Memory] user memory extraction returned non-array content') + return [] + } catch (err) { + logger.error('Failed to extract user memories:', err) + return [] + } +} diff --git a/models/memory/groupHistoryCursorStore.js b/models/memory/groupHistoryCursorStore.js new file mode 100644 index 0000000..b570e82 --- /dev/null +++ b/models/memory/groupHistoryCursorStore.js @@ -0,0 +1,61 @@ +import { getMemoryDatabase } from './database.js' + +function normaliseGroupId (groupId) { + if (groupId === null || groupId === undefined) { + return null + } + const str = String(groupId).trim() + return str || null +} + +export class GroupHistoryCursorStore { + constructor (db = getMemoryDatabase()) { + this.resetDatabase(db) + } + + resetDatabase (db = getMemoryDatabase()) { + this.db = db + this.selectStmt = this.db.prepare(` + SELECT last_message_id, last_timestamp + FROM group_history_cursor + WHERE group_id = ? + `) + this.upsertStmt = this.db.prepare(` + INSERT INTO group_history_cursor (group_id, last_message_id, last_timestamp) + VALUES (@group_id, @last_message_id, @last_timestamp) + ON CONFLICT(group_id) DO UPDATE SET + last_message_id = excluded.last_message_id, + last_timestamp = excluded.last_timestamp + `) + } + + ensureDb () { + if (!this.db || this.db.open === false) { + logger?.debug?.('[Memory] refreshing group history cursor database connection') + this.resetDatabase() + } + return this.db + } + + getCursor (groupId) { + const gid = normaliseGroupId(groupId) + if (!gid) return null + this.ensureDb() + return this.selectStmt.get(gid) || null + } + + updateCursor (groupId, { lastMessageId = null, lastTimestamp = null } = {}) { + const gid = normaliseGroupId(groupId) + if (!gid) return false + this.ensureDb() + const payload = { + group_id: gid, + last_message_id: lastMessageId ? String(lastMessageId) : null, + last_timestamp: (typeof lastTimestamp === 'number' && Number.isFinite(lastTimestamp)) ? Math.floor(lastTimestamp) : null + } + this.upsertStmt.run(payload) + return true + } +} + +export const groupHistoryCursorStore = new GroupHistoryCursorStore() diff --git a/models/memory/groupMemoryStore.js b/models/memory/groupMemoryStore.js new file mode 100644 index 0000000..459c3c0 --- /dev/null +++ b/models/memory/groupMemoryStore.js @@ -0,0 +1,515 @@ +import { getMemoryDatabase, getVectorDimension, getGroupMemoryFtsConfig, resetVectorTableDimension, sanitiseFtsQueryInput } from './database.js' +import ChatGPTConfig from '../../config/config.js' +import { embedTexts } from '../chaite/vectorizer.js' + +function toJSONString (value) { + if (!value) { + return '[]' + } + if (Array.isArray(value)) { + return JSON.stringify(value) + } + return typeof value === 'string' ? value : JSON.stringify(value) +} + +function toVectorBuffer (vector) { + if (!vector) { + return null + } + if (vector instanceof Float32Array) { + return Buffer.from(vector.buffer) + } + if (ArrayBuffer.isView(vector)) { + return Buffer.from(new Float32Array(vector).buffer) + } + return Buffer.from(new Float32Array(vector).buffer) +} + +function normaliseEmbeddingVector (vector) { + if (!vector) { + return null + } + if (Array.isArray(vector)) { + return vector + } + if (ArrayBuffer.isView(vector)) { + return Array.from(vector) + } + if (typeof vector === 'object') { + if (Array.isArray(vector.embedding)) { + return vector.embedding + } + if (ArrayBuffer.isView(vector.embedding)) { + return Array.from(vector.embedding) + } + if (Array.isArray(vector.vector)) { + return vector.vector + } + if (ArrayBuffer.isView(vector.vector)) { + return Array.from(vector.vector) + } + } + return null +} + +function normaliseGroupId (groupId) { + return groupId === null || groupId === undefined ? null : String(groupId) +} + +export class GroupMemoryStore { + constructor (db = getMemoryDatabase()) { + this.resetDatabase(db) + } + + resetDatabase (db = getMemoryDatabase()) { + this.db = db + this.insertFactStmt = this.db.prepare(` + INSERT INTO group_facts (group_id, fact, topic, importance, source_message_ids, source_messages, involved_users) + VALUES (@group_id, @fact, @topic, @importance, @source_message_ids, @source_messages, @involved_users) + ON CONFLICT(group_id, fact) DO UPDATE SET + topic = excluded.topic, + importance = excluded.importance, + source_message_ids = excluded.source_message_ids, + source_messages = excluded.source_messages, + involved_users = excluded.involved_users, + created_at = CASE + WHEN excluded.importance > group_facts.importance THEN datetime('now') + ELSE group_facts.created_at + END + `) + this.prepareVectorStatements() + this.loadFactByIdStmt = this.db.prepare('SELECT * FROM group_facts WHERE id = ?') + } + + prepareVectorStatements () { + try { + this.deleteVecStmt = this.db.prepare('DELETE FROM vec_group_facts WHERE rowid = ?') + this.insertVecStmt = this.db.prepare('INSERT INTO vec_group_facts(rowid, embedding) VALUES (?, ?)') + } catch (err) { + this.deleteVecStmt = null + this.insertVecStmt = null + logger?.debug?.('[Memory] vector table not ready, postpone statement preparation') + } + } + + ensureDb () { + if (!this.db || this.db.open === false) { + logger?.debug?.('[Memory] refreshing group memory database connection') + this.resetDatabase() + } + return this.db + } + + get embeddingModel () { + return ChatGPTConfig.llm?.embeddingModel || '' + } + + get retrievalMode () { + const mode = ChatGPTConfig.memory?.group?.retrievalMode || 'hybrid' + const lowered = String(mode).toLowerCase() + if (['vector', 'keyword', 'hybrid'].includes(lowered)) { + return lowered + } + return 'hybrid' + } + + get hybridPrefer () { + const prefer = ChatGPTConfig.memory?.group?.hybridPrefer || 'vector-first' + return prefer === 'keyword-first' ? 'keyword-first' : 'vector-first' + } + + isVectorEnabled () { + return Boolean(this.embeddingModel) + } + + get vectorDistanceThreshold () { + const value = Number(ChatGPTConfig.memory?.group?.vectorMaxDistance) + if (Number.isFinite(value) && value > 0) { + return value + } + return null + } + + get bm25Threshold () { + const value = Number(ChatGPTConfig.memory?.group?.textMaxBm25Score) + if (Number.isFinite(value) && value > 0) { + return value + } + return null + } + + async saveFacts (groupId, facts) { + if (!facts || facts.length === 0) { + return [] + } + this.ensureDb() + const normGroupId = normaliseGroupId(groupId) + const filteredFacts = facts + .map(f => { + const rawFact = typeof f.fact === 'string' ? f.fact : (Array.isArray(f.fact) ? f.fact.join(' ') : String(f.fact || '')) + const rawTopic = typeof f.topic === 'string' ? f.topic : (f.topic === undefined || f.topic === null ? '' : String(f.topic)) + const rawSourceMessages = f.source_messages ?? f.sourceMessages ?? '' + const sourceMessages = Array.isArray(rawSourceMessages) + ? rawSourceMessages.map(item => (item === null || item === undefined) ? '' : String(item)).filter(Boolean).join('\n') + : (typeof rawSourceMessages === 'string' ? rawSourceMessages : String(rawSourceMessages || '')) + return { + fact: rawFact.trim(), + topic: rawTopic.trim(), + importance: typeof f.importance === 'number' ? f.importance : Number(f.importance) || 0.5, + source_message_ids: toJSONString(f.source_message_ids || f.sourceMessages), + source_messages: sourceMessages, + involved_users: toJSONString(f.involved_users || f.involvedUsers || []) + } + }) + .filter(item => item.fact) + + if (filteredFacts.length === 0) { + return [] + } + + let vectors = [] + let tableDimension = getVectorDimension() || 0 + const configuredDimension = Number(ChatGPTConfig.llm?.dimensions || 0) + if (this.isVectorEnabled()) { + try { + const preferredDimension = configuredDimension > 0 + ? configuredDimension + : (tableDimension > 0 ? tableDimension : undefined) + vectors = await embedTexts(filteredFacts.map(f => f.fact), this.embeddingModel, preferredDimension) + vectors = vectors.map(normaliseEmbeddingVector) + const mismatchVector = vectors.find(vec => { + if (!vec) return false + if (Array.isArray(vec)) return vec.length > 0 + if (ArrayBuffer.isView(vec) && typeof vec.length === 'number') { + return vec.length > 0 + } + return false + }) + const actualDimension = mismatchVector ? mismatchVector.length : 0 + if (actualDimension && actualDimension !== tableDimension) { + const expectedDimension = tableDimension || preferredDimension || configuredDimension || 'unknown' + logger.warn(`[Memory] embedding dimension mismatch, expected=${expectedDimension}, actual=${actualDimension}. Recreating vector table.`) + try { + resetVectorTableDimension(actualDimension) + this.prepareVectorStatements() + tableDimension = actualDimension + } catch (resetErr) { + logger.error('Failed to reset vector table dimension:', resetErr) + vectors = [] + } + } else if (actualDimension && tableDimension <= 0) { + try { + resetVectorTableDimension(actualDimension) + this.prepareVectorStatements() + tableDimension = actualDimension + } catch (resetErr) { + logger.error('Failed to initialise vector table dimension:', resetErr) + vectors = [] + } + } + } catch (err) { + logger.error('Failed to embed group facts:', err) + vectors = [] + } + } + + const transaction = this.db.transaction((items, vectorList) => { + const saved = [] + for (let i = 0; i < items.length; i++) { + const payload = { + group_id: normGroupId, + ...items[i] + } + const info = this.insertFactStmt.run(payload) + let factId = Number(info.lastInsertRowid) + if (!factId) { + const existing = this.db.prepare('SELECT id FROM group_facts WHERE group_id = ? AND fact = ?').get(normGroupId, payload.fact) + factId = existing?.id + } + factId = Number.parseInt(String(factId ?? ''), 10) + if (!Number.isSafeInteger(factId)) { + logger.warn('[Memory] skip fact vector upsert due to invalid fact id', factId) + continue + } + if (!factId) { + continue + } + if (Array.isArray(vectorList) && vectorList[i]) { + if (!this.deleteVecStmt || !this.insertVecStmt) { + this.prepareVectorStatements() + } + if (!this.deleteVecStmt || !this.insertVecStmt) { + logger.warn('[Memory] vector table unavailable, skip vector upsert') + continue + } + try { + const vector = normaliseEmbeddingVector(vectorList[i]) + if (!vector) { + continue + } + let embeddingArray + if (ArrayBuffer.isView(vector)) { + if (vector instanceof Float32Array) { + embeddingArray = vector + } else { + embeddingArray = new Float32Array(vector.length) + for (let idx = 0; idx < vector.length; idx++) { + embeddingArray[idx] = Number(vector[idx]) + } + } + } else { + embeddingArray = Float32Array.from(vector) + } + const rowId = BigInt(factId) + logger.debug(`[Memory] upserting vector for fact ${factId}, rowIdType=${typeof rowId}`) + this.deleteVecStmt.run(rowId) + this.insertVecStmt.run(rowId, embeddingArray) + } catch (error) { + logger.error(`Failed to upsert vector for fact ${factId}:`, error) + } + } + saved.push(this.loadFactByIdStmt.get(factId)) + } + return saved + }) + + return transaction(filteredFacts, vectors) + } + + listFacts (groupId, limit = 50, offset = 0) { + return this.db.prepare(` + SELECT * FROM group_facts + WHERE group_id = ? + ORDER BY importance DESC, created_at DESC + LIMIT ? OFFSET ? + `).all(normaliseGroupId(groupId), limit, offset) + } + + deleteFact (groupId, factId) { + this.ensureDb() + const normGroupId = normaliseGroupId(groupId) + const fact = this.db.prepare('SELECT id FROM group_facts WHERE id = ? AND group_id = ?').get(factId, normGroupId) + if (!fact) { + return false + } + this.db.prepare('DELETE FROM group_facts WHERE id = ?').run(factId) + try { + this.deleteVecStmt.run(BigInt(factId)) + } catch (err) { + logger?.warn?.(`[Memory] failed to delete vector for fact ${factId}:`, err) + } + return true + } + + async vectorSearch (groupId, queryText, limit) { + this.ensureDb() + if (!this.isVectorEnabled()) { + return [] + } + try { + let tableDimension = getVectorDimension() || 0 + if (!tableDimension || tableDimension <= 0) { + logger.debug('[Memory] vector table dimension unavailable, attempting to infer from embedding') + } + const requestedDimension = tableDimension > 0 ? tableDimension : undefined + const [embedding] = await embedTexts([queryText], this.embeddingModel, requestedDimension) + if (!embedding) { + return [] + } + const embeddingVector = ArrayBuffer.isView(embedding) ? embedding : Float32Array.from(embedding) + const actualDimension = embeddingVector.length + if (!actualDimension) { + logger.debug('[Memory] vector search skipped: empty embedding returned') + return [] + } + if (tableDimension > 0 && actualDimension !== tableDimension) { + logger.warn(`[Memory] vector dimension mismatch detected during search, table=${tableDimension}, embedding=${actualDimension}. Rebuilding vector table.`) + try { + resetVectorTableDimension(actualDimension) + this.prepareVectorStatements() + tableDimension = actualDimension + } catch (resetErr) { + logger.error('Failed to reset vector table dimension during search:', resetErr) + return [] + } + logger.info('[Memory] vector table rebuilt; old vectors must be regenerated before vector search can return results') + return [] + } else if (tableDimension <= 0 && actualDimension > 0) { + try { + resetVectorTableDimension(actualDimension) + this.prepareVectorStatements() + tableDimension = actualDimension + } catch (resetErr) { + logger.error('Failed to initialise vector table dimension during search:', resetErr) + return [] + } + } + const rows = this.db.prepare(` + SELECT gf.*, vec_group_facts.distance AS distance + FROM vec_group_facts + JOIN group_facts gf ON gf.id = vec_group_facts.rowid + WHERE gf.group_id = ? + AND vec_group_facts.embedding MATCH ? + AND vec_group_facts.k = ${limit} + ORDER BY distance ASC + `).all(groupId, embeddingVector) + const threshold = this.vectorDistanceThreshold + if (!threshold) { + return rows + } + return rows.filter(row => typeof row?.distance === 'number' && row.distance <= threshold) + } catch (err) { + logger.warn('Vector search failed for group memory:', err) + return [] + } + } + + textSearch (groupId, queryText, limit) { + this.ensureDb() + if (!queryText || !queryText.trim()) { + return [] + } + const originalQuery = queryText.trim() + const ftsConfig = getGroupMemoryFtsConfig() + const matchQueryParam = sanitiseFtsQueryInput(originalQuery, ftsConfig) + const results = [] + const seen = new Set() + if (matchQueryParam) { + const matchExpression = ftsConfig.matchQuery ? `${ftsConfig.matchQuery}(?)` : '?' + try { + const rows = this.db.prepare(` + SELECT gf.*, bm25(group_facts_fts) AS bm25_score + FROM group_facts_fts + JOIN group_facts gf ON gf.id = group_facts_fts.rowid + WHERE gf.group_id = ? + AND group_facts_fts MATCH ${matchExpression} + ORDER BY bm25_score ASC + LIMIT ? + `).all(groupId, matchQueryParam, limit) + for (const row of rows) { + const bm25Threshold = this.bm25Threshold + if (bm25Threshold) { + const score = Number(row?.bm25_score) + if (!Number.isFinite(score) || score > bm25Threshold) { + continue + } + row.bm25_score = score + } + if (row && !seen.has(row.id)) { + results.push(row) + seen.add(row.id) + } + } + } catch (err) { + logger.warn('Text search failed for group memory:', err) + } + } else { + logger.debug('[Memory] group memory text search skipped MATCH due to empty query after sanitisation') + } + + if (results.length < limit) { + try { + const likeRows = this.db.prepare(` + SELECT * + FROM group_facts + WHERE group_id = ? + AND instr(fact, ?) > 0 + ORDER BY importance DESC, created_at DESC + LIMIT ? + `).all(groupId, originalQuery, Math.max(limit * 2, limit)) + for (const row of likeRows) { + if (row && !seen.has(row.id)) { + results.push(row) + seen.add(row.id) + if (results.length >= limit) { + break + } + } + } + } catch (err) { + logger.warn('LIKE fallback failed for group memory:', err) + } + } + + return results.slice(0, limit) + } + + importanceFallback (groupId, limit, minImportance, excludeIds = []) { + this.ensureDb() + const ids = excludeIds.filter(Boolean) + const notInClause = ids.length ? `AND id NOT IN (${ids.map(() => '?').join(',')})` : '' + const stmt = this.db.prepare(` + SELECT * FROM group_facts + WHERE group_id = ? + AND importance >= ? + ${notInClause} + ORDER BY importance DESC, created_at DESC + LIMIT ? + `) + const params = [groupId, minImportance] + if (ids.length) { + params.push(...ids) + } + params.push(limit) + return stmt.all(...params) + } + + /** + * 获取相关群记忆,支持向量/文本/混合检索 + * @param {string} groupId + * @param {string} queryText + * @param {{ limit?: number, minImportance?: number }} options + * @returns {Promise>} + */ + async queryRelevantFacts (groupId, queryText, options = {}) { + const { limit = 5, minImportance = 0 } = options + const normGroupId = normaliseGroupId(groupId) + if (!queryText) { + return this.listFacts(normGroupId, limit) + } + + const mode = this.retrievalMode + const combined = [] + const seen = new Set() + const append = rows => { + for (const row of rows) { + if (!row || seen.has(row.id)) { + continue + } + combined.push(row) + seen.add(row.id) + if (combined.length >= limit) { + break + } + } + } + + const preferVector = this.hybridPrefer !== 'keyword-first' + + if (mode === 'vector' || mode === 'hybrid') { + const vectorRows = await this.vectorSearch(normGroupId, queryText, limit) + if (mode === 'vector') { + append(vectorRows) + } else if (preferVector) { + append(vectorRows) + if (combined.length < limit) { + append(this.textSearch(normGroupId, queryText, limit)) + } + } else { + append(this.textSearch(normGroupId, queryText, limit)) + if (combined.length < limit) { + append(vectorRows) + } + } + } else if (mode === 'keyword') { + append(this.textSearch(normGroupId, queryText, limit)) + } + + if (combined.length < limit) { + const fallback = this.importanceFallback(normGroupId, limit - combined.length, minImportance, Array.from(seen)) + append(fallback) + } + + return combined.slice(0, limit) + } +} diff --git a/models/memory/prompt.js b/models/memory/prompt.js new file mode 100644 index 0000000..7093e26 --- /dev/null +++ b/models/memory/prompt.js @@ -0,0 +1,128 @@ +import ChatGPTConfig from '../../config/config.js' +import { memoryService } from './service.js' + +function renderTemplate (template, context = {}) { + if (!template) { + return '' + } + return template.replace(/\$\{(\w+)\}/g, (_, key) => { + const value = context[key] + return value === undefined || value === null ? '' : String(value) + }) +} + +function formatUserMemories (memories, config) { + if (!memories.length) { + return '' + } + const headerTemplate = config.promptHeader ?? '# 用户画像' + const itemTemplate = config.promptItemTemplate ?? '- ${value}' + const footerTemplate = config.promptFooter ?? '' + const segments = [] + const header = renderTemplate(headerTemplate, { count: memories.length }) + if (header) { + segments.push(header) + } + memories.forEach((item, index) => { + const timestamp = item.updated_at || item.created_at || '' + const timeSuffix = timestamp ? `(记录时间:${timestamp})` : '' + const context = { + index, + order: index + 1, + value: item.value || '', + importance: item.importance ?? '', + sourceMessageId: item.source_message_id || '', + sourceId: item.source_message_id || '', + groupId: item.group_id || '', + createdAt: item.created_at || '', + updatedAt: item.updated_at || '', + timestamp, + time: timestamp, + timeSuffix + } + const line = renderTemplate(itemTemplate, context) + if (line) { + segments.push(line) + } + }) + const footer = renderTemplate(footerTemplate, { count: memories.length }) + if (footer) { + segments.push(footer) + } + return segments.join('\n') +} + +function formatGroupFacts (facts, config) { + if (!facts.length) { + return '' + } + const headerTemplate = config.promptHeader ?? '# 群聊长期记忆' + const itemTemplate = config.promptItemTemplate ?? '- ${fact}${topicSuffix}' + const footerTemplate = config.promptFooter ?? '' + const segments = [] + const header = renderTemplate(headerTemplate, { count: facts.length }) + if (header) { + segments.push(header) + } + facts.forEach((item, index) => { + const topicSuffix = item.topic ? `(${item.topic})` : '' + const timestamp = item.updated_at || item.created_at || '' + const timeSuffix = timestamp ? `(记录时间:${timestamp})` : '' + const context = { + index, + order: index + 1, + fact: item.fact || '', + topic: item.topic || '', + topicSuffix, + importance: item.importance ?? '', + createdAt: item.created_at || '', + updatedAt: item.updated_at || '', + timestamp, + time: timestamp, + timeSuffix, + distance: item.distance ?? '', + bm25: item.bm25_score ?? '', + sourceMessages: item.source_messages || '', + sourceMessageIds: item.source_message_ids || '' + } + const line = renderTemplate(itemTemplate, context) + if (line) { + segments.push(line) + } + }) + const footer = renderTemplate(footerTemplate, { count: facts.length }) + if (footer) { + segments.push(footer) + } + return segments.join('\n') +} + +export async function buildMemoryPrompt ({ userId, groupId, queryText }) { + const segments = [] + const userConfig = ChatGPTConfig.memory?.user || {} + const groupConfig = ChatGPTConfig.memory?.group || {} + if (memoryService.isUserMemoryEnabled(userId)) { + const totalLimit = userConfig.maxItemsPerInjection || 5 + const searchLimit = Math.min(userConfig.maxRelevantItemsPerQuery || totalLimit, totalLimit) + const userMemories = memoryService.queryUserMemories(userId, groupId, queryText, { + totalLimit, + searchLimit, + minImportance: userConfig.minImportanceForInjection ?? 0 + }) + const userSegment = formatUserMemories(userMemories, userConfig) + if (userSegment) { + segments.push(userSegment) + } + } + if (groupId && memoryService.isGroupMemoryEnabled(groupId)) { + const facts = await memoryService.queryGroupFacts(groupId, queryText, { + limit: groupConfig.maxFactsPerInjection || 5, + minImportance: groupConfig.minImportanceForInjection || 0 + }) + const groupSegment = formatGroupFacts(facts, groupConfig) + if (groupSegment) { + segments.push(groupSegment) + } + } + return segments.join('\n\n').trim() +} diff --git a/models/memory/router.js b/models/memory/router.js new file mode 100644 index 0000000..0395013 --- /dev/null +++ b/models/memory/router.js @@ -0,0 +1,726 @@ +import express from 'express' +import fs from 'fs' +import os from 'os' +import path from 'path' +import https from 'https' +import { pipeline } from 'stream' +import { promisify } from 'util' +let AdmZip +try { + AdmZip = (await import('adm-zip')).default +} catch (e) { + logger.warn('Failed to load AdmZip, maybe you need to install it manually:', e) +} +import { execSync } from "child_process" +import { + Chaite, + ChaiteResponse, + FrontEndAuthHandler +} from 'chaite' +import ChatGPTConfig from '../../config/config.js' +import { memoryService } from './service.js' +import { + resetCachedDimension, + resetMemoryDatabaseInstance, + getSimpleExtensionState, + resolvePluginPath, + toPluginRelativePath, + resetVectorTableDimension +} from './database.js' + +const streamPipeline = promisify(pipeline) + +const SIMPLE_DOWNLOAD_BASE_URL = 'https://github.com/wangfenjin/simple/releases/latest/download' +const SIMPLE_ASSET_MAP = { + 'linux-x64': 'libsimple-linux-ubuntu-latest.zip', + 'linux-arm64': 'libsimple-linux-ubuntu-24.04-arm.zip', + 'linux-arm': 'libsimple-linux-ubuntu-24.04-arm.zip', + 'darwin-x64': 'libsimple-osx-x64.zip', + 'darwin-arm64': 'libsimple-osx-x64.zip', + 'win32-x64': 'libsimple-windows-x64.zip', + 'win32-ia32': 'libsimple-windows-x86.zip', + 'win32-arm64': 'libsimple-windows-arm64.zip' +} +const DEFAULT_SIMPLE_INSTALL_DIR = 'resources/simple' + +export function authenticateMemoryRequest (req, res, next) { + const bearer = req.header('Authorization') || '' + const token = bearer.replace(/^Bearer\s+/i, '').trim() + if (!token) { + res.status(401).json({ message: 'Access denied, token missing' }) + return + } + try { + const authKey = Chaite.getInstance()?.getGlobalConfig()?.getAuthKey() + if (authKey && FrontEndAuthHandler.validateJWT(authKey, token)) { + next() + return + } + res.status(401).json({ message: 'Invalid token' }) + } catch (error) { + res.status(401).json({ message: 'Invalid token format' }) + } +} + +function parsePositiveInt (value, fallback) { + const num = Number(value) + return Number.isInteger(num) && num >= 0 ? num : fallback +} + +function parseNumber (value, fallback) { + const num = Number(value) + return Number.isFinite(num) ? num : fallback +} + +function toStringArray (value) { + if (!Array.isArray(value)) { + return [] + } + return value + .map(item => { + if (item === undefined || item === null) { + return null + } + return String(item).trim() + }) + .filter(item => item) +} + +function parseOptionalStringParam (value) { + if (Array.isArray(value)) { + value = value[0] + } + if (value === undefined || value === null) { + return null + } + const trimmed = String(value).trim() + if (!trimmed || trimmed.toLowerCase() === 'null' || trimmed.toLowerCase() === 'undefined') { + return null + } + return trimmed +} + +function detectAssetKey (platform, arch) { + const normalizedArch = arch === 'arm64' ? 'arm64' : (arch === 'arm' ? 'arm' : (arch === 'ia32' ? 'ia32' : 'x64')) + const key = `${platform}-${normalizedArch}` + if (SIMPLE_ASSET_MAP[key]) { + return key + } + if (platform === 'darwin' && SIMPLE_ASSET_MAP['darwin-x64']) { + return 'darwin-x64' + } + if (platform === 'linux' && SIMPLE_ASSET_MAP['linux-x64']) { + return 'linux-x64' + } + if (platform === 'win32' && SIMPLE_ASSET_MAP['win32-x64']) { + return 'win32-x64' + } + return null +} + +function resolveSimpleAsset (requestedKey, requestedAsset) { + if (requestedAsset) { + return { + key: requestedKey || 'custom', + asset: requestedAsset + } + } + if (requestedKey && SIMPLE_ASSET_MAP[requestedKey]) { + return { + key: requestedKey, + asset: SIMPLE_ASSET_MAP[requestedKey] + } + } + const autoKey = detectAssetKey(process.platform, process.arch) + if (autoKey && SIMPLE_ASSET_MAP[autoKey]) { + return { key: autoKey, asset: SIMPLE_ASSET_MAP[autoKey] } + } + return { key: null, asset: null } +} + +function ensureDirectoryExists (dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }) + } +} + +async function downloadToFile (url, destination, redirectCount = 0) { + if (redirectCount > 5) { + throw new Error('Too many redirects while downloading extension') + } + await new Promise((resolve, reject) => { + const request = https.get(url, { + headers: { + 'User-Agent': 'chatgpt-plugin-memory-extension-downloader' + } + }, async res => { + if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { + res.resume() + try { + await downloadToFile(res.headers.location, destination, redirectCount + 1) + resolve() + } catch (err) { + reject(err) + } + return + } + if (res.statusCode !== 200) { + reject(new Error(`Failed to download extension: HTTP ${res.statusCode}`)) + res.resume() + return + } + const fileStream = fs.createWriteStream(destination) + streamPipeline(res, fileStream).then(resolve).catch(reject) + }) + request.on('error', error => reject(error)) + }) +} + +function removeDirectoryIfExists (dirPath) { + if (fs.existsSync(dirPath)) { + fs.rmSync(dirPath, { recursive: true, force: true }) + } +} + +function findLibraryFile (rootDir) { + const entries = fs.readdirSync(rootDir, { withFileTypes: true }) + for (const entry of entries) { + const fullPath = path.join(rootDir, entry.name) + if (entry.isDirectory()) { + const found = findLibraryFile(fullPath) + if (found) { + return found + } + } else if (/simple\.(so|dylib|dll)$/i.test(entry.name) || /^libsimple/i.test(entry.name)) { + return fullPath + } + } + return null +} + +function findDictDirectory (rootDir) { + const directDictPath = path.join(rootDir, 'dict') + if (fs.existsSync(directDictPath) && fs.statSync(directDictPath).isDirectory()) { + return directDictPath + } + const entries = fs.readdirSync(rootDir, { withFileTypes: true }) + for (const entry of entries) { + if (entry.isDirectory()) { + const match = findDictDirectory(path.join(rootDir, entry.name)) + if (match) { + return match + } + } + } + return null +} + +async function downloadSimpleExtensionArchive ({ assetKey, assetName, targetDir }) { + if (!assetName) { + throw new Error('Simple extension asset name is required.') + } + const downloadUrl = `${SIMPLE_DOWNLOAD_BASE_URL}/${assetName}` + const tempFile = path.join(os.tmpdir(), `libsimple-${Date.now()}-${Math.random().toString(16).slice(2)}.zip`) + ensureDirectoryExists(path.dirname(tempFile)) + await downloadToFile(downloadUrl, tempFile) + removeDirectoryIfExists(targetDir) + ensureDirectoryExists(targetDir) + if (AdmZip) { + try { + const zip = new AdmZip(tempFile) + zip.extractAllTo(targetDir, true) + } finally { + if (fs.existsSync(tempFile)) { + fs.unlinkSync(tempFile) + } + } + } else { + // 尝试使用 unzip 命令解压 + try { + execSync(`unzip "${tempFile}" -d "${targetDir}"`, { stdio: 'inherit' }) + } catch (error) { + throw new Error(`Failed to extract zip file: ${error.message}. Please install adm-zip manually: pnpm i`) + } finally { + if (fs.existsSync(tempFile)) { + fs.unlinkSync(tempFile) + } + } + } + + const libraryFile = findLibraryFile(targetDir) + if (!libraryFile) { + throw new Error('Downloaded extension package does not contain libsimple library.') + } + const dictDir = findDictDirectory(targetDir) + if (!ChatGPTConfig.memory.extensions) { + ChatGPTConfig.memory.extensions = {} + } + if (!ChatGPTConfig.memory.extensions.simple) { + ChatGPTConfig.memory.extensions.simple = { + enable: false, + libraryPath: '', + dictPath: '', + useJieba: false + } + } + const relativeLibraryPath = toPluginRelativePath(libraryFile) + const relativeDictPath = dictDir ? toPluginRelativePath(dictDir) : '' + ChatGPTConfig.memory.extensions.simple.libraryPath = relativeLibraryPath + ChatGPTConfig.memory.extensions.simple.dictPath = relativeDictPath + return { + assetKey, + assetName, + installDir: toPluginRelativePath(targetDir), + libraryPath: relativeLibraryPath, + dictPath: ChatGPTConfig.memory.extensions.simple.dictPath + } +} + +function updateMemoryConfig (payload = {}) { + const current = ChatGPTConfig.memory || {} + const previousDatabase = current.database + const previousDimension = current.vectorDimensions + + const nextConfig = { + ...current, + group: { + ...(current.group || {}) + }, + user: { + ...(current.user || {}) + }, + extensions: { + ...(current.extensions || {}), + simple: { + ...(current.extensions?.simple || {}) + } + } + } + const previousSimpleConfig = JSON.stringify(current.extensions?.simple || {}) + + if (Object.prototype.hasOwnProperty.call(payload, 'database') && typeof payload.database === 'string') { + nextConfig.database = payload.database.trim() + } + if (Object.prototype.hasOwnProperty.call(payload, 'vectorDimensions')) { + const dimension = parsePositiveInt(payload.vectorDimensions, current.vectorDimensions || 1536) + if (dimension > 0) { + nextConfig.vectorDimensions = dimension + } + } + + if (payload.group && typeof payload.group === 'object') { + const incomingGroup = payload.group + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'enable')) { + nextConfig.group.enable = Boolean(incomingGroup.enable) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'enabledGroups')) { + nextConfig.group.enabledGroups = toStringArray(incomingGroup.enabledGroups) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'extractionModel') && typeof incomingGroup.extractionModel === 'string') { + nextConfig.group.extractionModel = incomingGroup.extractionModel.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'extractionPresetId') && typeof incomingGroup.extractionPresetId === 'string') { + nextConfig.group.extractionPresetId = incomingGroup.extractionPresetId.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'minMessageCount')) { + nextConfig.group.minMessageCount = parsePositiveInt(incomingGroup.minMessageCount, nextConfig.group.minMessageCount || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'maxMessageWindow')) { + nextConfig.group.maxMessageWindow = parsePositiveInt(incomingGroup.maxMessageWindow, nextConfig.group.maxMessageWindow || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'retrievalMode')) { + const mode = String(incomingGroup.retrievalMode || '').toLowerCase() + if (['vector', 'keyword', 'hybrid'].includes(mode)) { + nextConfig.group.retrievalMode = mode + } + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'hybridPrefer')) { + const prefer = String(incomingGroup.hybridPrefer || '').toLowerCase() + if (prefer === 'keyword-first') { + nextConfig.group.hybridPrefer = 'keyword-first' + } else if (prefer === 'vector-first') { + nextConfig.group.hybridPrefer = 'vector-first' + } + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'historyPollInterval')) { + nextConfig.group.historyPollInterval = parsePositiveInt(incomingGroup.historyPollInterval, + nextConfig.group.historyPollInterval || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'historyBatchSize')) { + nextConfig.group.historyBatchSize = parsePositiveInt(incomingGroup.historyBatchSize, + nextConfig.group.historyBatchSize || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'promptHeader') && typeof incomingGroup.promptHeader === 'string') { + nextConfig.group.promptHeader = incomingGroup.promptHeader + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'promptItemTemplate') && typeof incomingGroup.promptItemTemplate === 'string') { + nextConfig.group.promptItemTemplate = incomingGroup.promptItemTemplate + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'promptFooter') && typeof incomingGroup.promptFooter === 'string') { + nextConfig.group.promptFooter = incomingGroup.promptFooter + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'vectorMaxDistance')) { + const distance = parseNumber(incomingGroup.vectorMaxDistance, + nextConfig.group.vectorMaxDistance ?? 0) + nextConfig.group.vectorMaxDistance = distance + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'textMaxBm25Score')) { + const bm25 = parseNumber(incomingGroup.textMaxBm25Score, + nextConfig.group.textMaxBm25Score ?? 0) + nextConfig.group.textMaxBm25Score = bm25 + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'maxFactsPerInjection')) { + nextConfig.group.maxFactsPerInjection = parsePositiveInt(incomingGroup.maxFactsPerInjection, + nextConfig.group.maxFactsPerInjection || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingGroup, 'minImportanceForInjection')) { + const importance = parseNumber(incomingGroup.minImportanceForInjection, + nextConfig.group.minImportanceForInjection ?? 0) + nextConfig.group.minImportanceForInjection = importance + } + } + + if (payload.user && typeof payload.user === 'object') { + const incomingUser = payload.user + if (Object.prototype.hasOwnProperty.call(incomingUser, 'enable')) { + nextConfig.user.enable = Boolean(incomingUser.enable) + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'whitelist')) { + nextConfig.user.whitelist = toStringArray(incomingUser.whitelist) + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'blacklist')) { + nextConfig.user.blacklist = toStringArray(incomingUser.blacklist) + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'extractionModel') && typeof incomingUser.extractionModel === 'string') { + nextConfig.user.extractionModel = incomingUser.extractionModel.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'extractionPresetId') && typeof incomingUser.extractionPresetId === 'string') { + nextConfig.user.extractionPresetId = incomingUser.extractionPresetId.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'maxItemsPerInjection')) { + nextConfig.user.maxItemsPerInjection = parsePositiveInt(incomingUser.maxItemsPerInjection, + nextConfig.user.maxItemsPerInjection || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'maxRelevantItemsPerQuery')) { + nextConfig.user.maxRelevantItemsPerQuery = parsePositiveInt(incomingUser.maxRelevantItemsPerQuery, + nextConfig.user.maxRelevantItemsPerQuery || 0) + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'minImportanceForInjection')) { + const importance = parseNumber(incomingUser.minImportanceForInjection, + nextConfig.user.minImportanceForInjection ?? 0) + nextConfig.user.minImportanceForInjection = importance + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'promptHeader') && typeof incomingUser.promptHeader === 'string') { + nextConfig.user.promptHeader = incomingUser.promptHeader + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'promptItemTemplate') && typeof incomingUser.promptItemTemplate === 'string') { + nextConfig.user.promptItemTemplate = incomingUser.promptItemTemplate + } + if (Object.prototype.hasOwnProperty.call(incomingUser, 'promptFooter') && typeof incomingUser.promptFooter === 'string') { + nextConfig.user.promptFooter = incomingUser.promptFooter + } + } + + if (payload.extensions && typeof payload.extensions === 'object' && !Array.isArray(payload.extensions)) { + const incomingExtensions = payload.extensions + if (incomingExtensions.simple && typeof incomingExtensions.simple === 'object' && !Array.isArray(incomingExtensions.simple)) { + const incomingSimple = incomingExtensions.simple + if (Object.prototype.hasOwnProperty.call(incomingSimple, 'enable')) { + nextConfig.extensions.simple.enable = Boolean(incomingSimple.enable) + } + if (Object.prototype.hasOwnProperty.call(incomingSimple, 'libraryPath') && typeof incomingSimple.libraryPath === 'string') { + nextConfig.extensions.simple.libraryPath = incomingSimple.libraryPath.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingSimple, 'dictPath') && typeof incomingSimple.dictPath === 'string') { + nextConfig.extensions.simple.dictPath = incomingSimple.dictPath.trim() + } + if (Object.prototype.hasOwnProperty.call(incomingSimple, 'useJieba')) { + nextConfig.extensions.simple.useJieba = Boolean(incomingSimple.useJieba) + } + } else if (Object.prototype.hasOwnProperty.call(incomingExtensions, 'simple')) { + logger.warn('[Memory] Unexpected value for extensions.simple, ignoring:', incomingExtensions.simple) + } + } + + ChatGPTConfig.memory.database = nextConfig.database + ChatGPTConfig.memory.vectorDimensions = nextConfig.vectorDimensions + if (!ChatGPTConfig.memory.group) ChatGPTConfig.memory.group = {} + if (!ChatGPTConfig.memory.user) ChatGPTConfig.memory.user = {} + if (!ChatGPTConfig.memory.extensions) ChatGPTConfig.memory.extensions = {} + if (!ChatGPTConfig.memory.extensions.simple) { + ChatGPTConfig.memory.extensions.simple = { + enable: false, + libraryPath: '', + dictPath: '', + useJieba: false + } + } + Object.assign(ChatGPTConfig.memory.group, nextConfig.group) + Object.assign(ChatGPTConfig.memory.user, nextConfig.user) + Object.assign(ChatGPTConfig.memory.extensions.simple, nextConfig.extensions.simple) + + if (nextConfig.vectorDimensions !== previousDimension) { + resetCachedDimension() + const targetDimension = Number(nextConfig.vectorDimensions) + if (Number.isFinite(targetDimension) && targetDimension > 0) { + try { + resetVectorTableDimension(targetDimension) + } catch (err) { + logger?.error?.('[Memory] failed to apply vector dimension change:', err) + } + } + } + const currentSimpleConfig = JSON.stringify(ChatGPTConfig.memory.extensions?.simple || {}) + + if (nextConfig.database !== previousDatabase) { + resetMemoryDatabaseInstance() + } else if (currentSimpleConfig !== previousSimpleConfig) { + resetMemoryDatabaseInstance() + } + + if (typeof ChatGPTConfig._triggerSave === 'function') { + ChatGPTConfig._triggerSave('memory') + } + + return ChatGPTConfig.memory +} + +export const MemoryRouter = (() => { + const router = express.Router() + + router.get('/config', (_req, res) => { + res.status(200).json(ChaiteResponse.ok(ChatGPTConfig.memory)) + }) + + router.post('/config', (req, res) => { + try { + const updated = updateMemoryConfig(req.body || {}) + res.status(200).json(ChaiteResponse.ok(updated)) + } catch (error) { + logger.error('Failed to update memory config:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to update memory config')) + } + }) + + router.get('/group/:groupId/facts', (req, res) => { + const { groupId } = req.params + const limit = parsePositiveInt(req.query.limit, 50) + const offset = parsePositiveInt(req.query.offset, 0) + try { + const facts = memoryService.listGroupFacts(groupId, limit, offset) + res.status(200).json(ChaiteResponse.ok(facts)) + } catch (error) { + logger.error('Failed to fetch group facts:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to fetch group facts')) + } + }) + + router.get('/extensions/simple/status', (_req, res) => { + try { + logger?.debug?.('[Memory] simple extension status requested') + const state = getSimpleExtensionState() + const simpleConfig = ChatGPTConfig.memory?.extensions?.simple || {} + const libraryPath = simpleConfig.libraryPath || state.libraryPath || '' + const dictPath = simpleConfig.dictPath || state.dictPath || '' + const resolvedLibraryPath = libraryPath ? resolvePluginPath(libraryPath) : '' + const resolvedDictPath = dictPath ? resolvePluginPath(dictPath) : '' + res.status(200).json(ChaiteResponse.ok({ + ...state, + enabled: Boolean(simpleConfig.enable), + libraryPath, + dictPath, + platform: process.platform, + arch: process.arch, + resolvedLibraryPath, + libraryExists: resolvedLibraryPath ? fs.existsSync(resolvedLibraryPath) : false, + resolvedDictPath, + dictExists: resolvedDictPath ? fs.existsSync(resolvedDictPath) : false + })) + } catch (error) { + logger.error('Failed to read simple extension status:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to read simple extension status')) + } + }) + + router.post('/extensions/simple/download', async (req, res) => { + const { assetKey, assetName, installDir } = req.body || {} + try { + const resolvedAsset = resolveSimpleAsset(assetKey, assetName) + if (!resolvedAsset.asset) { + res.status(400).json(ChaiteResponse.fail(null, '无法确定当前平台的扩展文件,请手动指定 assetName。')) + return + } + logger?.info?.('[Memory] downloading simple extension asset=%s (key=%s)', resolvedAsset.asset, resolvedAsset.key) + const targetRelativeDir = installDir || path.join(DEFAULT_SIMPLE_INSTALL_DIR, resolvedAsset.key || 'downloaded') + const targetDir = resolvePluginPath(targetRelativeDir) + const result = await downloadSimpleExtensionArchive({ + assetKey: resolvedAsset.key || assetKey || 'custom', + assetName: resolvedAsset.asset, + targetDir + }) + resetMemoryDatabaseInstance() + logger?.info?.('[Memory] simple extension downloaded and memory DB scheduled for reload') + res.status(200).json(ChaiteResponse.ok({ + ...result, + assetName: resolvedAsset.asset, + assetKey: resolvedAsset.key || assetKey || 'custom' + })) + } catch (error) { + logger.error('Failed to download simple extension:', error) + res.status(500).json(ChaiteResponse.fail(null, error?.message || 'Failed to download simple extension')) + } + }) + + router.post('/group/:groupId/facts', async (req, res) => { + const { groupId } = req.params + const facts = Array.isArray(req.body?.facts) ? req.body.facts : [] + if (facts.length === 0) { + res.status(400).json(ChaiteResponse.fail(null, 'facts is required')) + return + } + try { + const saved = await memoryService.saveGroupFacts(groupId, facts) + res.status(200).json(ChaiteResponse.ok(saved)) + } catch (error) { + logger.error('Failed to save group facts:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to save group facts')) + } + }) + + router.post('/group/:groupId/query', async (req, res) => { + const { groupId } = req.params + const { query, limit, minImportance } = req.body || {} + if (!query || typeof query !== 'string') { + res.status(400).json(ChaiteResponse.fail(null, 'query is required')) + return + } + try { + const facts = await memoryService.queryGroupFacts(groupId, query, { + limit: parsePositiveInt(limit, undefined), + minImportance: minImportance !== undefined ? parseNumber(minImportance, undefined) : undefined + }) + res.status(200).json(ChaiteResponse.ok(facts)) + } catch (error) { + logger.error('Failed to query group memory:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to query group memory')) + } + }) + + router.delete('/group/:groupId/facts/:factId', (req, res) => { + const { groupId, factId } = req.params + try { + const removed = memoryService.deleteGroupFact(groupId, factId) + if (!removed) { + res.status(404).json(ChaiteResponse.fail(null, 'Fact not found')) + return + } + res.status(200).json(ChaiteResponse.ok({ removed })) + } catch (error) { + logger.error('Failed to delete group fact:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to delete group fact')) + } + }) + + router.get('/user/memories', (req, res) => { + const userId = parseOptionalStringParam(req.query.userId) + const groupId = parseOptionalStringParam(req.query.groupId) + const limit = parsePositiveInt(req.query.limit, 50) + const offset = parsePositiveInt(req.query.offset, 0) + try { + const memories = memoryService.listUserMemories(userId, groupId, limit, offset) + res.status(200).json(ChaiteResponse.ok(memories)) + } catch (error) { + logger.error('Failed to fetch user memories:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to fetch user memories')) + } + }) + + router.get('/user/:userId/memories', (req, res) => { + const { userId } = req.params + const groupId = req.query.groupId ?? null + const limit = parsePositiveInt(req.query.limit, 50) + const offset = parsePositiveInt(req.query.offset, 0) + try { + const memories = memoryService.listUserMemories(userId, groupId, limit, offset) + res.status(200).json(ChaiteResponse.ok(memories)) + } catch (error) { + logger.error('Failed to fetch user memories:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to fetch user memories')) + } + }) + + router.post('/user/:userId/query', (req, res) => { + const { userId } = req.params + const groupId = req.body?.groupId ?? req.query.groupId ?? null + const query = req.body?.query + const totalLimit = parsePositiveInt(req.body?.totalLimit, undefined) + const searchLimit = parsePositiveInt(req.body?.searchLimit, undefined) + const minImportance = req.body?.minImportance !== undefined + ? parseNumber(req.body.minImportance, undefined) + : undefined + if (!query || typeof query !== 'string') { + res.status(400).json(ChaiteResponse.fail(null, 'query is required')) + return + } + try { + const memories = memoryService.queryUserMemories(userId, groupId, query, { + totalLimit, + searchLimit, + minImportance + }) + res.status(200).json(ChaiteResponse.ok(memories)) + } catch (error) { + logger.error('Failed to query user memory:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to query user memory')) + } + }) + + router.post('/user/:userId/memories', (req, res) => { + const { userId } = req.params + const groupId = req.body?.groupId ?? null + const memories = Array.isArray(req.body?.memories) ? req.body.memories : [] + if (memories.length === 0) { + res.status(400).json(ChaiteResponse.fail(null, 'memories is required')) + return + } + try { + const updated = memoryService.upsertUserMemories(userId, groupId, memories) + res.status(200).json(ChaiteResponse.ok({ updated })) + } catch (error) { + logger.error('Failed to upsert user memories:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to upsert user memories')) + } + }) + + router.delete('/user/:userId/memories/:memoryId', (req, res) => { + const { userId, memoryId } = req.params + try { + const removed = memoryService.deleteUserMemory(memoryId, userId) + if (!removed) { + res.status(404).json(ChaiteResponse.fail(null, 'Memory not found')) + return + } + res.status(200).json(ChaiteResponse.ok({ removed })) + } catch (error) { + logger.error('Failed to delete user memory:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to delete user memory')) + } + }) + + router.delete('/memories/:memoryId', (req, res) => { + const { memoryId } = req.params + try { + const removed = memoryService.deleteUserMemory(memoryId) + if (!removed) { + res.status(404).json(ChaiteResponse.fail(null, 'Memory not found')) + return + } + res.status(200).json(ChaiteResponse.ok({ removed })) + } catch (error) { + logger.error('Failed to delete memory:', error) + res.status(500).json(ChaiteResponse.fail(null, 'Failed to delete memory')) + } + }) + + return router +})() diff --git a/models/memory/service.js b/models/memory/service.js new file mode 100644 index 0000000..b9eb315 --- /dev/null +++ b/models/memory/service.js @@ -0,0 +1,194 @@ +import ChatGPTConfig from '../../config/config.js' +import { getMemoryDatabase } from './database.js' +import { GroupMemoryStore } from './groupMemoryStore.js' +import { UserMemoryStore } from './userMemoryStore.js' + +function normaliseId (id) { + if (id === null || id === undefined) { + return '' + } + return String(id) +} + +function formatEntry (entry) { + let str = '' + try { + str = JSON.stringify(entry) + } catch (err) { + str = String(entry) + } + const limit = 200 + return str.length > limit ? str.slice(0, limit) + '…' : str +} + +function normalisePersonalMemory (entry) { + if (!entry) return null + let text = '' + let importance = typeof entry?.importance === 'number' ? entry.importance : 0.6 + let sourceId = entry?.source_message_id ? String(entry.source_message_id) : null + if (typeof entry === 'string') { + text = entry.trim() + } else if (typeof entry === 'object') { + const value = entry.value || entry.text || entry.fact || entry.sentence + if (Array.isArray(value)) { + text = value.join(', ').trim() + } else if (value) { + text = String(value).trim() + } + if (entry.importance !== undefined) { + importance = Number(entry.importance) + } + if (entry.source_message_id) { + sourceId = String(entry.source_message_id) + } + } + if (!text) { + return null + } + if (Number.isNaN(importance) || importance <= 0) { + importance = 0.6 + } + return { text, importance, sourceId } +} + +class MemoryService { + constructor () { + const db = getMemoryDatabase() + this.groupStore = new GroupMemoryStore(db) + this.userStore = new UserMemoryStore(db) + } + + isGroupMemoryEnabled (groupId) { + const config = ChatGPTConfig.memory?.group + if (!config?.enable) { + return false + } + const enabledGroups = (config.enabledGroups || []).map(normaliseId) + if (enabledGroups.length === 0) { + return false + } + return enabledGroups.includes(normaliseId(groupId)) + } + + isUserMemoryEnabled (userId) { + const config = ChatGPTConfig.memory?.user + if (!config?.enable) { + return false + } + const uid = normaliseId(userId) + const whitelist = (config.whitelist || []).map(normaliseId).filter(Boolean) + const blacklist = (config.blacklist || []).map(normaliseId).filter(Boolean) + if (whitelist.length > 0) { + return whitelist.includes(uid) + } + if (blacklist.length > 0) { + return !blacklist.includes(uid) + } + return true + } + + async saveGroupFacts (groupId, facts) { + if (!this.isGroupMemoryEnabled(groupId)) { + return [] + } + try { + const saved = await this.groupStore.saveFacts(groupId, facts) + if (saved.length > 0) { + logger.info(`[Memory] group=${groupId} stored ${saved.length} facts`) + saved.slice(0, 10).forEach((item, idx) => { + logger.debug(`[Memory] group stored fact[${idx}] ${formatEntry(item)}`) + }) + } + return saved + } catch (err) { + logger.error('Failed to save group facts:', err) + return [] + } + } + + async queryGroupFacts (groupId, queryText, options = {}) { + if (!this.isGroupMemoryEnabled(groupId)) { + return [] + } + const { maxFactsPerInjection = 5, minImportanceForInjection = 0 } = ChatGPTConfig.memory?.group || {} + const limit = options.limit || maxFactsPerInjection + const minImportance = options.minImportance ?? minImportanceForInjection + try { + return await this.groupStore.queryRelevantFacts(groupId, queryText, { limit, minImportance }) + } catch (err) { + logger.error('Failed to query group memory:', err) + return [] + } + } + + listGroupFacts (groupId, limit = 50, offset = 0) { + return this.groupStore.listFacts(groupId, limit, offset) + } + + deleteGroupFact (groupId, factId) { + return this.groupStore.deleteFact(groupId, factId) + } + + upsertUserMemories (userId, groupId, memories) { + if (!this.isUserMemoryEnabled(userId)) { + return 0 + } + try { + const prepared = (memories || []) + .map(normalisePersonalMemory) + .filter(item => item && item.text) + .map(item => ({ + value: item.text, + importance: item.importance, + source_message_id: item.sourceId + })) + if (prepared.length === 0) { + return 0 + } + const changed = this.userStore.upsertMemories(userId, groupId, prepared) + if (changed > 0) { + logger.info(`[Memory] user=${userId} updated ${changed} personal memories${groupId ? ` in group=${groupId}` : ''}`) + prepared.slice(0, 10).forEach((item, idx) => { + logger.debug(`[Memory] user memory upsert[${idx}] ${formatEntry(item)}`) + }) + } + return changed + } catch (err) { + logger.error('Failed to upsert user memories:', err) + return 0 + } + } + + queryUserMemories (userId, groupId = null, queryText = '', options = {}) { + if (!this.isUserMemoryEnabled(userId)) { + return [] + } + const userConfig = ChatGPTConfig.memory?.user || {} + const totalLimit = options.totalLimit ?? userConfig.maxItemsPerInjection ?? 5 + const searchLimit = options.searchLimit ?? userConfig.maxRelevantItemsPerQuery ?? totalLimit + const minImportance = options.minImportance ?? userConfig.minImportanceForInjection ?? 0 + if (!totalLimit || totalLimit <= 0) { + return [] + } + try { + return this.userStore.queryMemories(userId, groupId, queryText, { + limit: searchLimit, + fallbackLimit: totalLimit, + minImportance + }) + } catch (err) { + logger.error('Failed to query user memories:', err) + return [] + } + } + + listUserMemories (userId, groupId = null, limit = 50, offset = 0) { + return this.userStore.listUserMemories(userId, groupId, limit, offset) + } + + deleteUserMemory (memoryId, userId = null) { + return this.userStore.deleteMemoryById(memoryId, userId) + } +} + +export const memoryService = new MemoryService() diff --git a/models/memory/userMemoryManager.js b/models/memory/userMemoryManager.js new file mode 100644 index 0000000..18dd1a3 --- /dev/null +++ b/models/memory/userMemoryManager.js @@ -0,0 +1,129 @@ +import { Chaite } from 'chaite' +import * as crypto from 'node:crypto' +import { extractUserMemories } from './extractor.js' +import { memoryService } from './service.js' + +const USER_MEMORY_CONTEXT_LIMIT = 6 + +export function extractTextFromContents (contents) { + if (!Array.isArray(contents)) { + return '' + } + return contents + .filter(item => item && item.type === 'text') + .map(item => item.text || '') + .join('\n') + .trim() +} + +export function extractTextFromUserMessage (userMessage) { + if (!userMessage?.content) { + return '' + } + return userMessage.content + .filter(item => item.type === 'text') + .map(item => item.text || '') + .join('\n') + .trim() +} + +function normaliseMemoriesInput (memories, sourceId) { + return (memories || []).map(mem => { + if (typeof mem === 'string') { + return { + value: mem, + source_message_id: sourceId + } + } + if (mem && typeof mem === 'object') { + const cloned = { ...mem } + if (!cloned.source_message_id && sourceId) { + cloned.source_message_id = sourceId + } + if (!cloned.value && cloned.fact) { + cloned.value = cloned.fact + } + if (!cloned.value && cloned.text) { + cloned.value = cloned.text + } + return cloned + } + return { + value: String(mem), + source_message_id: sourceId + } + }) +} + +export async function processUserMemory ({ event, userMessage, userText, conversationId, assistantContents, assistantMessageId }) { + const e = event + if (!memoryService.isUserMemoryEnabled(e.sender.user_id)) { + return + } + const snippets = [] + const userMessageId = e.message_id || e.seq || userMessage?.id || crypto.randomUUID() + const senderName = e.sender?.card || e.sender?.nickname || String(e.sender?.user_id || '') + + try { + const historyManager = Chaite.getInstance()?.getHistoryManager?.() + if (historyManager && conversationId) { + const history = await historyManager.getHistory(null, conversationId) + const filtered = (history || []) + .filter(msg => ['user', 'assistant'].includes(msg.role)) + .map(msg => ({ + role: msg.role, + text: extractTextFromContents(msg.content), + nickname: msg.role === 'user' ? senderName : '机器人', + message_id: msg.id + })) + .filter(item => item.text) + if (filtered.length > 0) { + const limited = filtered.slice(-USER_MEMORY_CONTEXT_LIMIT * 2) + snippets.push(...limited) + } + } + } catch (err) { + logger.warn('Failed to collect user memory context:', err) + } + + if (assistantContents) { + const assistantText = extractTextFromContents(assistantContents) + if (assistantText) { + snippets.push({ + role: 'assistant', + text: assistantText, + nickname: '机器人', + message_id: assistantMessageId || crypto.randomUUID() + }) + } + } + + if (userText && !snippets.some(item => item.message_id === userMessageId)) { + snippets.push({ + role: 'user', + text: userText, + nickname: senderName, + message_id: userMessageId + }) + } + + if (snippets.length === 0) { + return + } + + const existingRecords = memoryService.listUserMemories(e.sender.user_id, e.isGroup ? e.group_id : null, 50) + const existingTexts = existingRecords.map(record => record.value).filter(Boolean) + const memories = await extractUserMemories(snippets, existingTexts) + if (!memories || memories.length === 0) { + return + } + + const enriched = normaliseMemoriesInput(memories, userMessageId) + memoryService.upsertUserMemories( + e.sender.user_id, + e.isGroup ? e.group_id : null, + enriched + ) +} + +export { USER_MEMORY_CONTEXT_LIMIT } diff --git a/models/memory/userMemoryStore.js b/models/memory/userMemoryStore.js new file mode 100644 index 0000000..9511bae --- /dev/null +++ b/models/memory/userMemoryStore.js @@ -0,0 +1,335 @@ +import { getMemoryDatabase, getUserMemoryFtsConfig, sanitiseFtsQueryInput } from './database.js' +import { md5 } from '../../utils/common.js' + +function normaliseId (value) { + if (value === null || value === undefined) { + return null + } + const str = String(value).trim() + if (!str || str.toLowerCase() === 'null' || str.toLowerCase() === 'undefined') { + return null + } + return str +} + +function toMemoryPayload (entry) { + if (entry === null || entry === undefined) { + return null + } + if (typeof entry === 'string') { + const text = entry.trim() + return text ? { value: text, importance: 0.5 } : null + } + if (typeof entry === 'object') { + const rawValue = entry.value ?? entry.text ?? entry.fact ?? '' + const value = typeof rawValue === 'string' ? rawValue.trim() : String(rawValue || '').trim() + if (!value) { + return null + } + const importance = typeof entry.importance === 'number' ? entry.importance : 0.5 + const sourceId = entry.source_message_id ? String(entry.source_message_id) : null + const providedKey = entry.key ? String(entry.key).trim() : '' + return { + value, + importance, + source_message_id: sourceId, + providedKey + } + } + const value = String(entry).trim() + return value ? { value, importance: 0.5 } : null +} + +function deriveKey (value, providedKey = '') { + const trimmedProvided = providedKey?.trim?.() || '' + if (trimmedProvided) { + return trimmedProvided + } + if (!value) { + return null + } + return `fact:${md5(String(value))}` +} + +function stripKey (row) { + if (!row || typeof row !== 'object') { + return row + } + const { key, ...rest } = row + return rest +} + +function appendRows (target, rows, seen) { + if (!Array.isArray(rows)) { + return + } + for (const row of rows) { + if (!row || seen.has(row.id)) { + continue + } + target.push(stripKey(row)) + seen.add(row.id) + } +} + +export class UserMemoryStore { + constructor (db = getMemoryDatabase()) { + this.resetDatabase(db) + } + + resetDatabase (db = getMemoryDatabase()) { + this.db = db + this.upsertStmt = this.db.prepare(` + INSERT INTO user_memory (user_id, group_id, key, value, importance, source_message_id, created_at, updated_at) + VALUES (@user_id, @group_id, @key, @value, @importance, @source_message_id, datetime('now'), datetime('now')) + ON CONFLICT(user_id, coalesce(group_id, ''), key) DO UPDATE SET + value = excluded.value, + importance = excluded.importance, + source_message_id = excluded.source_message_id, + updated_at = datetime('now') + `) + } + + ensureDb () { + if (!this.db || this.db.open === false) { + logger?.debug?.('[Memory] refreshing user memory database connection') + this.resetDatabase() + } + return this.db + } + + upsertMemories (userId, groupId, memories) { + if (!memories || memories.length === 0) { + return 0 + } + this.ensureDb() + const normUserId = normaliseId(userId) + const normGroupId = normaliseId(groupId) + const prepared = (memories || []) + .map(toMemoryPayload) + .filter(item => item && item.value) + .map(item => { + const key = deriveKey(item.value, item.providedKey) + if (!key) { + return null + } + return { + user_id: normUserId, + group_id: normGroupId, + key, + value: String(item.value), + importance: typeof item.importance === 'number' ? item.importance : 0.5, + source_message_id: item.source_message_id ? String(item.source_message_id) : null + } + }) + .filter(Boolean) + if (!prepared.length) { + return 0 + } + const transaction = this.db.transaction(items => { + let changes = 0 + for (const item of items) { + const info = this.upsertStmt.run(item) + changes += info.changes + } + return changes + }) + return transaction(prepared) + } + + listUserMemories (userId = null, groupId = null, limit = 50, offset = 0) { + this.ensureDb() + const normUserId = normaliseId(userId) + const normGroupId = normaliseId(groupId) + const params = [] + let query = ` + SELECT * FROM user_memory + WHERE 1 = 1 + ` + if (normUserId) { + query += ' AND user_id = ?' + params.push(normUserId) + } + if (normGroupId) { + if (normUserId) { + query += ' AND (group_id = ? OR group_id IS NULL)' + } else { + query += ' AND group_id = ?' + } + params.push(normGroupId) + } + query += ` + ORDER BY importance DESC, updated_at DESC + LIMIT ? OFFSET ? + ` + params.push(limit, offset) + const rows = this.db.prepare(query).all(...params) + return rows.map(stripKey) + } + + deleteMemoryById (memoryId, userId = null) { + this.ensureDb() + if (userId) { + const result = this.db.prepare('DELETE FROM user_memory WHERE id = ? AND user_id = ?').run(memoryId, normaliseId(userId)) + return result.changes > 0 + } + const result = this.db.prepare('DELETE FROM user_memory WHERE id = ?').run(memoryId) + return result.changes > 0 + } + + listRecentMemories (userId, groupId = null, limit = 50, excludeIds = [], minImportance = 0) { + this.ensureDb() + const normUserId = normaliseId(userId) + const normGroupId = normaliseId(groupId) + const filteredExclude = (excludeIds || []).filter(Boolean) + const params = [normUserId] + let query = ` + SELECT * FROM user_memory + WHERE user_id = ? + AND importance >= ? + ` + params.push(minImportance) + if (normGroupId) { + query += ' AND (group_id = ? OR group_id IS NULL)' + params.push(normGroupId) + } + if (filteredExclude.length) { + query += ` AND id NOT IN (${filteredExclude.map(() => '?').join(',')})` + params.push(...filteredExclude) + } + query += ` + ORDER BY updated_at DESC + LIMIT ? + ` + params.push(limit) + return this.db.prepare(query).all(...params).map(stripKey) + } + + textSearch (userId, groupId = null, queryText, limit = 5, excludeIds = []) { + if (!queryText || !queryText.trim()) { + return [] + } + this.ensureDb() + const normUserId = normaliseId(userId) + const normGroupId = normaliseId(groupId) + const filteredExclude = (excludeIds || []).filter(Boolean) + const originalQuery = queryText.trim() + const ftsConfig = getUserMemoryFtsConfig() + const matchQueryParam = sanitiseFtsQueryInput(originalQuery, ftsConfig) + const results = [] + const seen = new Set(filteredExclude) + if (matchQueryParam) { + const matchExpression = ftsConfig.matchQuery ? `${ftsConfig.matchQuery}(?)` : '?' + const params = [normUserId, matchQueryParam] + let query = ` + SELECT um.*, bm25(user_memory_fts) AS bm25_score + FROM user_memory_fts + JOIN user_memory um ON um.id = user_memory_fts.rowid + WHERE um.user_id = ? + AND user_memory_fts MATCH ${matchExpression} + ` + if (normGroupId) { + query += ' AND (um.group_id = ? OR um.group_id IS NULL)' + params.push(normGroupId) + } + if (filteredExclude.length) { + query += ` AND um.id NOT IN (${filteredExclude.map(() => '?').join(',')})` + params.push(...filteredExclude) + } + query += ` + ORDER BY bm25_score ASC, um.updated_at DESC + LIMIT ? + ` + params.push(limit) + try { + const ftsRows = this.db.prepare(query).all(...params) + appendRows(results, ftsRows, seen) + } catch (err) { + logger?.warn?.('User memory text search failed:', err) + } + } else { + logger?.debug?.('[Memory] user memory text search skipped MATCH due to empty query after sanitisation') + } + + if (results.length < limit) { + const likeParams = [normUserId, originalQuery] + let likeQuery = ` + SELECT um.* + FROM user_memory um + WHERE um.user_id = ? + AND instr(um.value, ?) > 0 + ` + if (normGroupId) { + likeQuery += ' AND (um.group_id = ? OR um.group_id IS NULL)' + likeParams.push(normGroupId) + } + if (filteredExclude.length) { + likeQuery += ` AND um.id NOT IN (${filteredExclude.map(() => '?').join(',')})` + likeParams.push(...filteredExclude) + } + likeQuery += ` + ORDER BY um.importance DESC, um.updated_at DESC + LIMIT ? + ` + likeParams.push(Math.max(limit * 2, limit)) + try { + const likeRows = this.db.prepare(likeQuery).all(...likeParams) + appendRows(results, likeRows, seen) + } catch (err) { + logger?.warn?.('User memory LIKE search failed:', err) + } + } + + return results.slice(0, limit) + } + + queryMemories (userId, groupId = null, queryText = '', options = {}) { + const normUserId = normaliseId(userId) + if (!normUserId) { + return [] + } + this.ensureDb() + const { + limit = 3, + fallbackLimit, + minImportance = 0 + } = options + const totalLimit = Math.max(0, fallbackLimit ?? limit ?? 0) + if (totalLimit === 0) { + return [] + } + const searchLimit = limit > 0 ? Math.min(limit, totalLimit) : totalLimit + const results = [] + const seen = new Set() + const append = rows => { + for (const row of rows || []) { + if (!row || seen.has(row.id)) { + continue + } + results.push(row) + seen.add(row.id) + if (results.length >= totalLimit) { + break + } + } + } + + if (queryText && searchLimit > 0) { + const searched = this.textSearch(userId, groupId, queryText, searchLimit) + append(searched) + } + + if (results.length < totalLimit) { + const recent = this.listRecentMemories( + userId, + groupId, + Math.max(totalLimit * 2, totalLimit), + Array.from(seen), + minImportance + ) + append(recent) + } + + return results.slice(0, totalLimit) + } +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 989bbd1..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6946 +0,0 @@ -{ - "name": "chatgpt-plugin", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "chatgpt-plugin", - "dependencies": { - "@fastify/cookie": "^8.3.0", - "@fastify/cors": "^8.2.0", - "@fastify/static": "^6.9.0", - "@fastify/websocket": "^8.1.0", - "@slack/bolt": "^3.13.0", - "@waylaidwanderer/chatgpt-api": "^1.36.0", - "asn1.js": "^5.0.0", - "chatgpt": "^5.2.4", - "delay": "^5.0.0", - "diff": "^5.1.0", - "emoji-strip": "^1.0.1", - "eventsource": "^2.0.2", - "eventsource-parser": "^1.0.0", - "fastify": "^4.16.0", - "form-data": "^4.0.0", - "https-proxy-agent": "5.0.1", - "js-tiktoken": "^1.0.5", - "keyv": "^4.5.2", - "keyv-file": "^0.2.0", - "microsoft-cognitiveservices-speech-sdk": "^1.27.0", - "node-fetch": "^3.3.1", - "openai": "^3.2.1", - "p-timeout": "^6.1.2", - "quick-lru": "6.1.1", - "random": "^4.1.0", - "undici": "^5.21.0", - "uuid": "^9.0.0", - "ws": "^8.13.0" - }, - "devDependencies": { - "ts-node": "^10.9.1", - "ts-node-register": "^1.0.0" - }, - "optionalDependencies": { - "@node-rs/jieba": "^1.6.2", - "jimp": "^0.22.7", - "node-silk": "^0.1.0", - "puppeteer-extra": "^3.3.6", - "puppeteer-extra-plugin-recaptcha": "^3.6.8", - "puppeteer-extra-plugin-stealth": "^2.11.2", - "sharp": "^0.31.3" - } - }, - "../../node_modules/.pnpm/axios@1.4.0/node_modules/axios": { - "extraneous": true - }, - "../../node_modules/.pnpm/fastify@4.13.0/node_modules/fastify": { - "extraneous": true - }, - "../../node_modules/.pnpm/md5-node@1.0.1/node_modules/md5-node": { - "extraneous": true - }, - "../../node_modules/.pnpm/undici@5.21.0/node_modules/undici": { - "extraneous": true - }, - "../../node_modules/.pnpm/uuid@9.0.0/node_modules/uuid": { - "extraneous": true - }, - "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "dependencies": { - "@babel/highlight": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@dqbd/tiktoken": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/@dqbd/tiktoken/-/tiktoken-1.0.7.tgz", - "integrity": "sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==" - }, - "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", - "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.5.0", - "resolved": "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", - "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/cookie": { - "version": "8.3.0", - "resolved": "https://registry.npmmirror.com/@fastify/cookie/-/cookie-8.3.0.tgz", - "integrity": "sha512-P9hY9GO11L20TnZ33XN3i0bt+3x0zaT7S0ohAzWO950E9PB2xnNhLYzPFJIGFi5AVN0yr5+/iZhWxeYvR6KCzg==", - "dependencies": { - "cookie": "^0.5.0", - "fastify-plugin": "^4.0.0" - } - }, - "node_modules/@fastify/cors": { - "version": "8.3.0", - "resolved": "https://registry.npmmirror.com/@fastify/cors/-/cors-8.3.0.tgz", - "integrity": "sha512-oj9xkka2Tg0MrwuKhsSUumcAkfp2YCnKxmFEusi01pjk1YrdDsuSYTHXEelWNW+ilSy/ApZq0c2SvhKrLX0H1g==", - "dependencies": { - "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.5" - } - }, - "node_modules/@fastify/deepmerge": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", - "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" - }, - "node_modules/@fastify/error": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/@fastify/error/-/error-3.2.1.tgz", - "integrity": "sha512-scZVbcpPNWw/yyFmzzO7cf1daTeJp53spN2n7dBTHZd+cV7791fcWJCPP1Tfhdbre+8vDiCyQyqqXfQnYMntYQ==" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", - "dependencies": { - "fast-json-stringify": "^5.7.0" - } - }, - "node_modules/@fastify/send": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/@fastify/send/-/send-2.1.0.tgz", - "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" - } - }, - "node_modules/@fastify/static": { - "version": "6.10.2", - "resolved": "https://registry.npmmirror.com/@fastify/static/-/static-6.10.2.tgz", - "integrity": "sha512-UoaMvIHSBLCZBYOVZwFRYqX2ufUhd7FFMYGDeSf0Z+D8jhYtwljjmuQGuanUP8kS4y/ZEV1a8mfLha3zNwsnnQ==", - "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "glob": "^8.0.1", - "p-limit": "^3.1.0", - "readable-stream": "^4.0.0" - } - }, - "node_modules/@fastify/websocket": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@fastify/websocket/-/websocket-8.1.0.tgz", - "integrity": "sha512-7Tk+ODgvS/S9IyPXuXDU1v5tpchAeSWFH0B3Ldz+cnBKy5+lEmuj4ncLhVUzXgn9w3ycX/Vhf1mEEkcemBFUIQ==", - "dependencies": { - "fastify-plugin": "^4.0.0", - "ws": "^8.0.0" - } - }, - "node_modules/@jimp/bmp": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/bmp/-/bmp-0.22.8.tgz", - "integrity": "sha512-JEMKgM1AEvvWfn9ZCHn62nK+QCE3Pb/ZhPdL3NF0ZgKNww6pqOmo6KqXzqY18JLB7c0epuTp4GPDPDhOh/ou1g==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "bmp-js": "^0.1.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/core": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/core/-/core-0.22.8.tgz", - "integrity": "sha512-vkN28aFikzQieA6bGxN+qe20pseCAemCyUI0YmRkJIArlb6OujtAwWAKyokv2lylV56bq8EQGIz+Y30OXUnRqg==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "any-base": "^1.1.0", - "buffer": "^5.2.0", - "exif-parser": "^0.1.12", - "file-type": "^16.5.4", - "isomorphic-fetch": "^3.0.0", - "mkdirp": "^2.1.3", - "pixelmatch": "^4.0.2", - "tinycolor2": "^1.6.0" - } - }, - "node_modules/@jimp/core/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/@jimp/custom": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/custom/-/custom-0.22.8.tgz", - "integrity": "sha512-u6lP9x/HNeGHB0Oojv4c2mhuDvn7G0ikzYbK4IKLsH4HzHxt62faMjBzQMcFhKJhR6UiiKE/jiHrhGvBT/fMkw==", - "optional": true, - "dependencies": { - "@jimp/core": "^0.22.8" - } - }, - "node_modules/@jimp/gif": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/gif/-/gif-0.22.8.tgz", - "integrity": "sha512-I0l6koS67IPU40RPxCJTD1NvePEd8vUIHTejx1ly0jrjGnumbqdarAlBUkDrKfPPc+Fnqp84hBbSN1w5hNPT6w==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "gifwrap": "^0.9.2", - "omggif": "^1.0.9" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/jpeg": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/jpeg/-/jpeg-0.22.8.tgz", - "integrity": "sha512-hLXrQ7/0QiUhAVAF10dfGCSq3hvyqjKltlpu/87b3wqMDKe9KdvhX1AJHiUUrAbJv1fAcnOmQGTyXGuySa1D6A==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "jpeg-js": "^0.4.4" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-blit": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-blit/-/plugin-blit-0.22.8.tgz", - "integrity": "sha512-rQ19txVCKIwo74HtgFodFt4//0ATPCJK+f24riqzb+nx+1JaOo1xRvpJqg4moirHwKR2fhwdDxmY7KX20kCeYA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-blur": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-blur/-/plugin-blur-0.22.8.tgz", - "integrity": "sha512-GWbNK3YW6k2EKiGJdpAFEr0jezPBtiVxj2wG/lCPuWJz7KmzSSN99hQjIy73xQxoBCRdALfJlkhe3leFNRueSQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-circle": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-circle/-/plugin-circle-0.22.8.tgz", - "integrity": "sha512-qPCw8XFW8opT89ciFDuvs+eB3EB1mZIJWVajD2qAlprHiE7YGr34TkM7N5MNr3qZ1pJgkYdW6+HbBrJwBaonqw==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-color": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-color/-/plugin-color-0.22.8.tgz", - "integrity": "sha512-ogkbg6rpDVH/mMLgAQKg17z3oZE0VN7ZWxNoH12fUHchqKz1I57zpa65fxZe2I8T5Xz97HR3x+7V7oI8qQGdSA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "tinycolor2": "^1.6.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-contain": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-contain/-/plugin-contain-0.22.8.tgz", - "integrity": "sha512-oiaPLdJt9Dk+XEEhM/OU3lFemM51mA9NgMCAdburSCjDzKacJYBGFSHjTOhXzcxOie/ZDpOYN/UzFGKy8Dgl9A==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5", - "@jimp/plugin-scale": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-cover": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-cover/-/plugin-cover-0.22.8.tgz", - "integrity": "sha512-mO68w1m/LhfuHU8LKHY05a4/hhWnY4t+T+8JCw9t+5yfzA4+LofBZZKtFtWgwf/QGe1y3X2rtUU/avAzDUKyyA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-crop": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5", - "@jimp/plugin-scale": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-crop": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-crop/-/plugin-crop-0.22.8.tgz", - "integrity": "sha512-ns4oH0h0gezYsbuH8RThcMLY5uTLk/vnqOVjWCehMHEzxi0DHMWCmpcb6bC//vJ+XFNhtVGn1ALN7+ROmPrj+A==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-displace": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-displace/-/plugin-displace-0.22.8.tgz", - "integrity": "sha512-Cj8nHYgsdFynOIx3dbbiVwRuZn3xO+RVfwkTRy0JBye+K2AU8SQJS+hSFNMQFTZt5djivh6kh0TzvR/6LkOd1w==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-dither": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-dither/-/plugin-dither-0.22.8.tgz", - "integrity": "sha512-oE0Us/6bEgrgEg56plU3jSBzvB9iGhweKUHmxYMWnQbFCHP4mNCtPAs8+Fmq6c+m98ZgBgRcrJTnC7lphHkGyw==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-fisheye": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-fisheye/-/plugin-fisheye-0.22.8.tgz", - "integrity": "sha512-bWvYY/nfMcKclWEaRyAir+YsT6C5St823HUQAsewZowTrJmme+w4U2a6InsryTHUL01BBcV5BLH0aDHuV3StvA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-flip": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-flip/-/plugin-flip-0.22.8.tgz", - "integrity": "sha512-0NFTNzjsdmOQkaIkNjZqO3/yU4SQb9nnWQXsLS1fFo+9QrIL5v8vVkXpk/rhiND6PyTj2mMTNjOa76GuZcC+iQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-rotate": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-gaussian": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.22.8.tgz", - "integrity": "sha512-E/f14aLzCS50QAM7K+InI9V61KVy/Zx52vy7Jjfo1h7qKhQHss3PYaydaH0N6qlXRNeXgh+4/32P9JfieLMcdw==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-invert": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-invert/-/plugin-invert-0.22.8.tgz", - "integrity": "sha512-UauP39FF2cwbA5VU+Tz9VlNa9rtULPSHZb0Huwcjqjm9/G/xVN69VJ8+RKiFC4zM1/kYAUp/6IRwPa6qdKJpSw==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-mask": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-mask/-/plugin-mask-0.22.8.tgz", - "integrity": "sha512-bhg5+3i8x1CmYj6cjvPBQZLwZEI3iK3gJWF25ZHF+12d3cqDuJngtr8oRQOQLlAgvKmrj9FXIiEPDczUI9cnWQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-normalize": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-normalize/-/plugin-normalize-0.22.8.tgz", - "integrity": "sha512-Yg5nreAR1JYuSObu3ExlgaLxVeW6VvjVL5qFwiPFxSNlG8JIwL1Ir3K3ChSnnvymyZvJMHb6YKTYNfXKw5Da6g==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-print": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-print/-/plugin-print-0.22.8.tgz", - "integrity": "sha512-86O5ejCDi543IYl0TykSmNWErzAjEYhiAxNQb2F7rFRT38WJYNVsvJ6QhxhDQHKxSmF5iwmqbk0jYk5Wp2Z1kw==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "load-bmfont": "^1.4.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-resize": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-resize/-/plugin-resize-0.22.8.tgz", - "integrity": "sha512-kg8ArQRPqv/iU3DWNXCa8kcVIhoq64Ze0aGCAeFLKlAq/59f5pzAci6m6vV4L/uOVdYmUa9/kYwIFY6RWKpfzQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-rotate": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-rotate/-/plugin-rotate-0.22.8.tgz", - "integrity": "sha512-9a+VPZWMN/Cks76wf8LjM5RVA3ntP9+NAdsS1SZhhXel7U3Re/dWMouIEbo3QTt6K+igRo4txUCdZiw4ZucvkQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blit": ">=0.3.5", - "@jimp/plugin-crop": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-scale": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-scale/-/plugin-scale-0.22.8.tgz", - "integrity": "sha512-dQS4pG6DX6endu8zUpvBBOEtGC+ljDDDNw0scSXY71TxyQdNo5Ro0apfsppjmuAr8rNotRkfyxbITKkXQDRUDQ==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-shadow": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-shadow/-/plugin-shadow-0.22.8.tgz", - "integrity": "sha512-HyAhr7OblTQh+BoKHQg4qbS9MweNlH77yfpBqUEyDtfyjI5r06+5chf1ZdLRIPEWv/BdCfdI/g81Wv69muCMwA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-blur": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" - } - }, - "node_modules/@jimp/plugin-threshold": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugin-threshold/-/plugin-threshold-0.22.8.tgz", - "integrity": "sha512-ZmkfH0PtjvF1UcKsjw0H7V6r+LC0yKzEfg76Jhs2nIqIgsxsSOVfHwS7z0/1IWnyXxSw36m+NjCAotNHRILGmA==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-color": ">=0.8.0", - "@jimp/plugin-resize": ">=0.8.0" - } - }, - "node_modules/@jimp/plugins": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/plugins/-/plugins-0.22.8.tgz", - "integrity": "sha512-ieI2+kCpmIfjwVlT7B67ULCzxMizfj7LspJh9HnIZCDXQB9GBOZ9KImLYc75Krae0dP/3FR7FglLiSI7fkOHbw==", - "optional": true, - "dependencies": { - "@jimp/plugin-blit": "^0.22.8", - "@jimp/plugin-blur": "^0.22.8", - "@jimp/plugin-circle": "^0.22.8", - "@jimp/plugin-color": "^0.22.8", - "@jimp/plugin-contain": "^0.22.8", - "@jimp/plugin-cover": "^0.22.8", - "@jimp/plugin-crop": "^0.22.8", - "@jimp/plugin-displace": "^0.22.8", - "@jimp/plugin-dither": "^0.22.8", - "@jimp/plugin-fisheye": "^0.22.8", - "@jimp/plugin-flip": "^0.22.8", - "@jimp/plugin-gaussian": "^0.22.8", - "@jimp/plugin-invert": "^0.22.8", - "@jimp/plugin-mask": "^0.22.8", - "@jimp/plugin-normalize": "^0.22.8", - "@jimp/plugin-print": "^0.22.8", - "@jimp/plugin-resize": "^0.22.8", - "@jimp/plugin-rotate": "^0.22.8", - "@jimp/plugin-scale": "^0.22.8", - "@jimp/plugin-shadow": "^0.22.8", - "@jimp/plugin-threshold": "^0.22.8", - "timm": "^1.6.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/png": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/png/-/png-0.22.8.tgz", - "integrity": "sha512-XOj11kcCr8zKg24QSwlRfH9k4hbV6rkMGUVxMS3puRzzB0FBSQy42NBYEfYf2XlY2QJSAByPl4AYerOtKb805w==", - "optional": true, - "dependencies": { - "@jimp/utils": "^0.22.8", - "pngjs": "^6.0.0" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/tiff": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/tiff/-/tiff-0.22.8.tgz", - "integrity": "sha512-K0hYUVW5MLgwq3jiHVHa6LvP05J1rXOlRCC+5dMTUnAXVwi45+MKsqA/8lzzwhHYJ65CNhZwy6D3+ZNzM9SIBQ==", - "optional": true, - "dependencies": { - "utif2": "^4.0.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/types": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/types/-/types-0.22.8.tgz", - "integrity": "sha512-9+xc+mzuYwu0i+6dsnhXiUgfcS+Ktqn5q2jczoKyyBT0cOKgsk+57EIeFLgpTfVGRKRR0y/UIdHByeCzGguF3A==", - "optional": true, - "dependencies": { - "@jimp/bmp": "^0.22.8", - "@jimp/gif": "^0.22.8", - "@jimp/jpeg": "^0.22.8", - "@jimp/png": "^0.22.8", - "@jimp/tiff": "^0.22.8", - "timm": "^1.6.1" - }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" - } - }, - "node_modules/@jimp/utils": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/@jimp/utils/-/utils-0.22.8.tgz", - "integrity": "sha512-AaqjfqDeLzSFzrbGRKHMXg/ntiWKvoG9tpVgWzgOx5/gPWj/IyGfztojLTTvY8HqZCr25z8z91u2lAQD2v46Jw==", - "optional": true, - "dependencies": { - "regenerator-runtime": "^0.13.3" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/@lukeed/ms/-/ms-2.0.1.tgz", - "integrity": "sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@node-rs/jieba": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba/-/jieba-1.7.0.tgz", - "integrity": "sha512-Hm1JIlejxkWe1FSFZRns/g1j5hZmp357n+0n2BluABA4KLZ8EraHfPmPRmVMW6vbdMZObTYIVu5aVrPnUfBOxg==", - "optional": true, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@node-rs/jieba-android-arm-eabi": "1.7.0", - "@node-rs/jieba-android-arm64": "1.7.0", - "@node-rs/jieba-darwin-arm64": "1.7.0", - "@node-rs/jieba-darwin-x64": "1.7.0", - "@node-rs/jieba-freebsd-x64": "1.7.0", - "@node-rs/jieba-linux-arm-gnueabihf": "1.7.0", - "@node-rs/jieba-linux-arm64-gnu": "1.7.0", - "@node-rs/jieba-linux-arm64-musl": "1.7.0", - "@node-rs/jieba-linux-x64-gnu": "1.7.0", - "@node-rs/jieba-linux-x64-musl": "1.7.0", - "@node-rs/jieba-win32-arm64-msvc": "1.7.0", - "@node-rs/jieba-win32-ia32-msvc": "1.7.0", - "@node-rs/jieba-win32-x64-msvc": "1.7.0" - } - }, - "node_modules/@node-rs/jieba-android-arm-eabi": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-1.7.0.tgz", - "integrity": "sha512-XF4OYcZCyDiBK+jm1Zmt2o+xEO7K2K5OvUC3MTc9jd3Lwvy3EdHp8tpGvEp8PxfVFe2/JxNzX4OQQQP3Dhmk9A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-android-arm64/-/jieba-android-arm64-1.7.0.tgz", - "integrity": "sha512-9oWwFVr/37T89WC+jjiI9A6u0zUJNTJl5ZC4CMxX45MVMokWI7bBXU7t7qBmMdFBzj+OFwDd3sm1fh4vl7NSWA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-9gBuxJCNITNI/gU5l8eeVGQ9MAf0BV86lfeo9TeU61vJCy6sqyx26wFMLODQgLNdiMP+q/fZme/G0hfZUjfPVA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-1.7.0.tgz", - "integrity": "sha512-FFUSMY4tl0Prpxa1SHy7Yzze2KfV/bZzccpO5nd+a8zCKbiX6gVkJ89FfxSAD2QrXUGkZvJYiPmu5nkZItqRZQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-QFz2pz0Br+621QbKkgQPqTn90j1kcCD9jaI++qTLNHJGlWLRn6sFoAjb+jQEQEy9aE7VqfIV56eaVcCoU5VO2w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-kHJxO2sd7gMKqI1YS5DjABEcRwRemaCtgbKSuUqEaHGmUz9nAaUF6FSY8U4rXwr7HXt+kQa4NgyYDjgz+Pscrw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-3qoCV9pF6llPBGDMu7K8JdHjI10WPkrq6P2gpZESqekcE4DatV6DcU9FWR+QL7MK/7meoE3/Zhjm7OK+qBd8gg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-xv6hvzOV7iTCq7mM8SWhC3zEk6CqmBwhOSlfbb3gvPkc4U1UA1hmvcrD7oO5Qn+U+nuswysGCdVU6Z5AypLDfg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-NpelWidMSNLoFTw+ov3y5jhJZjapHwEnh0Fyfm/7mvqkdwzVyedqNj22etRGum+nsAosMotCUWUznIMAD075gQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-yG4F8sy+fW4RbhyKXmEMT/JGuQuKH0TGymCEGYgT0km2I60iys63jWf2VTzCtrx583wxN5XoHv5HN60nhtIBtw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-R6l/BSMs6R6BwpZS6DIDZuAEjUIPdAHgyi+xptP3mICjm6U+GMsvsRTeZkIJ7a/yzYUfqvz54VpQsfE5f0psBQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-FwibbuizEjzom02K2JM2T8tL0VlxW5xGDDy3L3dgx46xIGE85PwGYjgju+eDt4UODgxDsxGC4DUMMZf3XvCc7A==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/jieba-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-pJv7nluB6azhsOWvJB86Dyfg/M7n9k49bs9Bwmsylz9uhdZX9QnEShDW934RdmnjPYQ5aPgsSFrY6NXP/aovUA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@slack/bolt": { - "version": "3.13.1", - "resolved": "https://registry.npmmirror.com/@slack/bolt/-/bolt-3.13.1.tgz", - "integrity": "sha512-ifWmlgW2pmtVfbb2YLuicKJu0PQEY0ZncAXTBxIgTP+EIl9okZnY5uIfCqFWzR9LkU0JWGdxMrHVKTptHV3YRQ==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/oauth": "^2.6.1", - "@slack/socket-mode": "^1.3.0", - "@slack/types": "^2.7.0", - "@slack/web-api": "^6.7.1", - "@types/express": "^4.16.1", - "@types/node": "18.16.0", - "@types/promise.allsettled": "^1.0.3", - "@types/tsscmp": "^1.0.0", - "axios": "^0.27.2", - "express": "^4.16.4", - "path-to-regexp": "^6.2.1", - "please-upgrade-node": "^3.2.0", - "promise.allsettled": "^1.0.2", - "raw-body": "^2.3.3", - "tsscmp": "^1.0.6" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/logger": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/@slack/logger/-/logger-3.0.0.tgz", - "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", - "dependencies": { - "@types/node": ">=12.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/oauth": { - "version": "2.6.1", - "resolved": "https://registry.npmmirror.com/@slack/oauth/-/oauth-2.6.1.tgz", - "integrity": "sha512-Qm8LI+W9gtC5YQz/3yq7b6Qza7SSIJ9jVIgbkrY3AGwT4E0P6mUFV5gKHadvDEfTGG3ZiWuKMyC06ZpexZsQgg==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/web-api": "^6.3.0", - "@types/jsonwebtoken": "^8.3.7", - "@types/node": ">=12", - "jsonwebtoken": "^9.0.0", - "lodash.isstring": "^4.0.1" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/socket-mode": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/@slack/socket-mode/-/socket-mode-1.3.2.tgz", - "integrity": "sha512-6LiwYE6k4DNbnctZZSLfERiOzWngAvXogxQEYzUkxeZgh2GC6EdmRq6OEbZXOBe71/K66YVx05VfR7B4b1ScTQ==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/web-api": "^6.2.3", - "@types/node": ">=12.0.0", - "@types/p-queue": "^2.3.2", - "@types/ws": "^7.4.7", - "eventemitter3": "^3.1.0", - "finity": "^0.5.4", - "p-cancelable": "^1.1.0", - "p-queue": "^2.4.2", - "ws": "^7.5.3" - }, - "engines": { - "node": ">=12.13.0", - "npm": ">=6.12.0" - } - }, - "node_modules/@slack/socket-mode/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@slack/types": { - "version": "2.8.0", - "resolved": "https://registry.npmmirror.com/@slack/types/-/types-2.8.0.tgz", - "integrity": "sha512-ghdfZSF0b4NC9ckBA8QnQgC9DJw2ZceDq0BIjjRSv6XAZBXJdWgxIsYz0TYnWSiqsKZGH2ZXbj9jYABZdH3OSQ==", - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/web-api": { - "version": "6.8.1", - "resolved": "https://registry.npmmirror.com/@slack/web-api/-/web-api-6.8.1.tgz", - "integrity": "sha512-eMPk2S99S613gcu7odSw/LV+Qxr8A+RXvBD0GYW510wJuTERiTjP5TgCsH8X09+lxSumbDE88wvWbuFuvGa74g==", - "dependencies": { - "@slack/logger": "^3.0.0", - "@slack/types": "^2.0.0", - "@types/is-stream": "^1.1.0", - "@types/node": ">=12.0.0", - "axios": "^0.27.2", - "eventemitter3": "^3.1.0", - "form-data": "^2.5.0", - "is-electron": "2.2.0", - "is-stream": "^1.1.0", - "p-queue": "^6.6.1", - "p-retry": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } - }, - "node_modules/@slack/web-api/node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@slack/web-api/node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmmirror.com/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@slack/web-api/node_modules/p-queue/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/@slack/web-api/node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@timefox/bic-sydney": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@timefox/bic-sydney/-/bic-sydney-1.1.4.tgz", - "integrity": "sha512-ONeS0weT+ZoE471TDdzPqkKRk+VFr7sEL5+qEq1nIur6XMuVZ8cvlBicUNHfhYKIavkOM8xmBnk2dfVFQ54aiQ==", - "dependencies": { - "fetch-undici": "^3.0.1", - "undici": "^5.22.1" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "optional": true - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", - "optional": true, - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@types/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.9", - "resolved": "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", - "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", - "optional": true - }, - "node_modules/@types/node": { - "version": "18.16.0", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.16.0.tgz", - "integrity": "sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/p-queue": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/@types/p-queue/-/p-queue-2.3.2.tgz", - "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==" - }, - "node_modules/@types/promise.allsettled": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/@types/promise.allsettled/-/promise.allsettled-1.0.3.tgz", - "integrity": "sha512-b/IFHHTkYkTqu41IH9UtpICwqrpKj2oNlb4KHPzFQDMiz+h1BgAeATeO0/XTph4+UkH9W2U0E4B4j64KWOovag==" - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", - "dependencies": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/tsscmp": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/@types/tsscmp/-/tsscmp-1.0.0.tgz", - "integrity": "sha512-rj18XR6c4Ohds86Lq8MI1NMRrXes4eLo4H06e5bJyKucE1rXGsfBBbFGD2oDC+DSufQCpnU3TTW7QAiwLx+7Yw==" - }, - "node_modules/@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@waylaidwanderer/chatgpt-api": { - "version": "1.37.0", - "resolved": "https://registry.npmmirror.com/@waylaidwanderer/chatgpt-api/-/chatgpt-api-1.37.0.tgz", - "integrity": "sha512-fJfNvfZliuzRlTEY2kb3u6psfAaFI88YSU1RJ6J865BRk90Km3k62br/npCGLCtbREklTR18bMYhNXVM/BuFow==", - "dependencies": { - "@dqbd/tiktoken": "^1.0.2", - "@fastify/cors": "^8.2.0", - "@timefox/bic-sydney": "^1.1.2", - "@waylaidwanderer/fastify-sse-v2": "^3.1.0", - "@waylaidwanderer/fetch-event-source": "^3.0.1", - "boxen": "^7.0.1", - "clipboardy": "^3.0.0", - "dotenv": "^16.0.3", - "fastify": "^4.11.0", - "fetch-undici": "^3.0.1", - "https-proxy-agent": "^7.0.0", - "inquirer": "^9.1.4", - "inquirer-autocomplete-prompt": "^3.0.0", - "keyv": "^4.5.2", - "keyv-file": "^0.2.0", - "ora": "^6.1.2", - "undici": "^5.20.0", - "ws": "^8.12.0" - }, - "bin": { - "chatgpt-api": "bin/server.js", - "chatgpt-cli": "bin/cli.js" - } - }, - "node_modules/@waylaidwanderer/chatgpt-api/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@waylaidwanderer/chatgpt-api/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@waylaidwanderer/chatgpt-api/node_modules/https-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz", - "integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@waylaidwanderer/chatgpt-api/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@waylaidwanderer/fastify-sse-v2": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/@waylaidwanderer/fastify-sse-v2/-/fastify-sse-v2-3.1.0.tgz", - "integrity": "sha512-R6/VT14+iGZmyp7Jih7FYZuWr0B0gJ9uym1xoVPlKjZBngzFS2bL8yvZyEIPbMrTjrC8syZY2z2WuMHsipRfpw==", - "dependencies": { - "fastify-plugin": "^4.3.0", - "it-pushable": "^1.4.2", - "it-to-stream": "^1.0.0" - }, - "peerDependencies": { - "fastify": ">=4" - } - }, - "node_modules/@waylaidwanderer/fetch-event-source": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/@waylaidwanderer/fetch-event-source/-/fetch-event-source-3.0.1.tgz", - "integrity": "sha512-gkc7vmBW9uulRj7tY30/1D8iBrpcgphBpI+e7LP744x/hAzaQxUuyF+n4O5dctKx+dE3i4BFuCWMEz9fAx2jlQ==", - "engines": { - "node": ">=16.15" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - } - }, - "node_modules/any-base": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/any-base/-/any-base-1.1.0.tgz", - "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "optional": true - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "optional": true - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==" - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array.prototype.map": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/array.prototype.map/-/array.prototype.map-1.0.5.tgz", - "integrity": "sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmmirror.com/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/atomically": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/atomically/-/atomically-2.0.1.tgz", - "integrity": "sha512-sxBhVZUFBFhqSAsYMM3X2oaUi2NVDJ8U026FsIusM8gYXls9AYs/eXzgGrufs1Qjpkxi9zunds+75QUFz+m7UQ==", - "dependencies": { - "stubborn-fs": "^1.2.4", - "when-exit": "^2.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/avvio": { - "version": "8.2.1", - "resolved": "https://registry.npmmirror.com/avvio/-/avvio-8.2.1.tgz", - "integrity": "sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==", - "dependencies": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" - } - }, - "node_modules/avvio/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/avvio/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "node_modules/bent": { - "version": "7.3.12", - "resolved": "https://registry.npmmirror.com/bent/-/bent-7.3.12.tgz", - "integrity": "sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==", - "dependencies": { - "bytesish": "^0.4.1", - "caseless": "~0.12.0", - "is-stream": "^2.0.0" - } - }, - "node_modules/bent/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bmp-js": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/bmp-js/-/bmp-js-0.1.0.tgz", - "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", - "optional": true - }, - "node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/boxen": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/boxen/-/boxen-7.1.0.tgz", - "integrity": "sha512-ScG8CDo8dj7McqCZ5hz4dIBp20xj4unQ2lXIDa7ff6RcZElCpuNzutdwzKVvRikfNjm7CFAlR3HJHcoHkDOExQ==", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytesish": { - "version": "0.4.4", - "resolved": "https://registry.npmmirror.com/bytesish/-/bytesish-0.4.4.tgz", - "integrity": "sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ==" - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmmirror.com/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/chatgpt": { - "version": "5.2.5", - "resolved": "https://registry.npmmirror.com/chatgpt/-/chatgpt-5.2.5.tgz", - "integrity": "sha512-DNhBzPb2zTDjJADY44XfngMvsvrvHRq1md2VPXLmnKeP1UCeA1B6pV3s9ZRwlcgjVT0RyM77fRj1xj5V11Vctg==", - "dependencies": { - "cac": "^6.7.14", - "conf": "^11.0.1", - "eventsource-parser": "^1.0.0", - "js-tiktoken": "^1.0.5", - "keyv": "^4.5.2", - "p-timeout": "^6.1.1", - "quick-lru": "^6.1.1", - "read-pkg-up": "^9.1.0", - "uuid": "^9.0.0" - }, - "bin": { - "chatgpt": "bin/cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-width": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/cli-width/-/cli-width-4.0.0.tgz", - "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/clipboardy": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-3.0.0.tgz", - "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", - "dependencies": { - "arch": "^2.2.0", - "execa": "^5.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-0.2.4.tgz", - "integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==", - "optional": true, - "dependencies": { - "for-own": "^0.1.3", - "is-plain-object": "^2.0.1", - "kind-of": "^3.0.2", - "lazy-cache": "^1.0.3", - "shallow-clone": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "optional": true - }, - "node_modules/conf": { - "version": "11.0.1", - "resolved": "https://registry.npmmirror.com/conf/-/conf-11.0.1.tgz", - "integrity": "sha512-WlLiQboEjKx0bYx2IIRGedBgNjLAxtwPaCSnsjWPST5xR0DB4q8lcsO/bEH9ZRYNcj63Y9vj/JG/5Fg6uWzI0Q==", - "dependencies": { - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "atomically": "^2.0.0", - "debounce-fn": "^5.1.2", - "dot-prop": "^7.2.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debounce-fn": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-5.1.2.tgz", - "integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "optional": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delay": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/delay/-/delay-5.0.0.tgz", - "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", - "optional": true - }, - "node_modules/dot-prop": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-7.2.0.tgz", - "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", - "dependencies": { - "type-fest": "^2.11.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.3.0", - "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.3.0.tgz", - "integrity": "sha512-tHB+hmf8MRCkT3VVivGiG8kq9HiGTmQ3FzOKgztfpJQH1IWuZTOvKSJmHNnQPowecAmkCJhLrxdPhOr06LLqIQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/emoji-regex": { - "version": "6.5.1", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-6.5.1.tgz", - "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==" - }, - "node_modules/emoji-strip": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/emoji-strip/-/emoji-strip-1.0.1.tgz", - "integrity": "sha512-1iU1Bz98ziALBnMTzQHKB104vntCokqgdmJIP9p1eM0Musc3WQ4y8C5noIckorxJ1vihZCTv2lyXMTM1UsemPg==", - "dependencies": { - "emoji-regex": "^6.1.3" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-1.0.0.tgz", - "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==", - "engines": { - "node": ">=14.18" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/execa/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/exif-parser": { - "version": "0.1.12", - "resolved": "https://registry.npmmirror.com/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", - "optional": true - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmmirror.com/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-content-type-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz", - "integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==" - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-fifo": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/fast-fifo/-/fast-fifo-1.2.0.tgz", - "integrity": "sha512-NcvQXt7Cky1cNau15FWy64IjuO8X0JijhTBBrJj1YlxlDfRkJXNaK9RFUjwpfDPzMdv7wB38jr53l9tkNLxnWg==" - }, - "node_modules/fast-json-stringify": { - "version": "5.7.0", - "resolved": "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-5.7.0.tgz", - "integrity": "sha512-sBVPTgnAZseLu1Qgj6lUbQ0HfjFhZWXAmpZ5AaSGkyLh5gAXBga/uPJjQPHpDFjC9adWIpdOcCLSDTgrZ7snoQ==", - "dependencies": { - "@fastify/deepmerge": "^1.0.0", - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/fast-redact/-/fast-redact-3.2.0.tgz", - "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-uri": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-2.2.0.tgz", - "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" - }, - "node_modules/fastify": { - "version": "4.18.0", - "resolved": "https://registry.npmmirror.com/fastify/-/fastify-4.18.0.tgz", - "integrity": "sha512-L5o/2GEkBastQ3HV0dtKo7SUZ497Z1+q4fcqAoPyq6JCQ/8zdk1JQEoTQwnBWCp+EmA7AQa6mxNqSAEhzP0RwQ==", - "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.2.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.2.1", - "fast-content-type-parse": "^1.0.0", - "fast-json-stringify": "^5.7.0", - "find-my-way": "^7.6.0", - "light-my-request": "^5.9.1", - "pino": "^8.12.0", - "process-warning": "^2.2.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.5.0", - "semver": "^7.5.0", - "tiny-lru": "^11.0.1" - } - }, - "node_modules/fastify-plugin": { - "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/fastify-plugin/-/fastify-plugin-4.5.0.tgz", - "integrity": "sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/fetch-undici": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/fetch-undici/-/fetch-undici-3.0.1.tgz", - "integrity": "sha512-UHHu1HqW22ZhK6C/1Zmjf7mQpOwPwLYZ+xcsOgpzistONU8QqvCop6Od29p/kw1GUVoq2Ihu6ItpKLtlojx4FQ==", - "dependencies": { - "undici": "^5.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmmirror.com/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "optional": true, - "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-my-way": { - "version": "7.6.2", - "resolved": "https://registry.npmmirror.com/find-my-way/-/find-my-way-7.6.2.tgz", - "integrity": "sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/finity": { - "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/finity/-/finity-0.5.4.tgz", - "integrity": "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA==" - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmmirror.com/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", - "optional": true, - "dependencies": { - "for-in": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true - }, - "node_modules/fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true - }, - "node_modules/gauge/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "node_modules/get-iterator": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/get-iterator/-/get-iterator-1.0.2.tgz", - "integrity": "sha512-v+dm9bNVfOYsY1OrhaCrmyOcYoSeVvbt+hHZ0Au+T+p1y+0Uyj9aMaGIeUTT6xdpRbWzDeYKvfOslPhggQMcsg==" - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gifwrap": { - "version": "0.9.4", - "resolved": "https://registry.npmmirror.com/gifwrap/-/gifwrap-0.9.4.tgz", - "integrity": "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==", - "optional": true, - "dependencies": { - "image-q": "^4.0.0", - "omggif": "^1.0.10" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/global": { - "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "optional": true, - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dependencies": { - "get-intrinsic": "^1.1.1" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "node_modules/image-q": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/image-q/-/image-q-4.0.0.tgz", - "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", - "optional": true, - "dependencies": { - "@types/node": "16.9.1" - } - }, - "node_modules/image-q/node_modules/@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", - "optional": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true - }, - "node_modules/inquirer": { - "version": "9.2.7", - "resolved": "https://registry.npmmirror.com/inquirer/-/inquirer-9.2.7.tgz", - "integrity": "sha512-Bf52lnfvNxGPJPltiNO2tLBp3zC339KNlGMqOkW+dsvNikBhcVDK5kqU2lVX2FTPzuXUFX5WJDlsw//w3ZwoTw==", - "dependencies": { - "ansi-escapes": "^4.3.2", - "chalk": "^5.2.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.0.0", - "external-editor": "^3.0.3", - "figures": "^5.0.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/inquirer-autocomplete-prompt": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-3.0.0.tgz", - "integrity": "sha512-nsPWllBQB3qhvpVgV1UIJN4xo3yz7Qv8y1+zrNVpJUNPxtUZ7btCum/4UCAs5apPCe/FVhKH1V6Wx0cAwkreyg==", - "dependencies": { - "ansi-escapes": "^6.0.0", - "figures": "^5.0.0", - "picocolors": "^1.0.0", - "run-async": "^2.4.1", - "rxjs": "^7.5.6" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "inquirer": "^9.1.0" - } - }, - "node_modules/inquirer-autocomplete-prompt/node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/inquirer-autocomplete-prompt/node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/inquirer-autocomplete-prompt/node_modules/type-fest": { - "version": "3.12.0", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.12.0.tgz", - "integrity": "sha512-qj9wWsnFvVEMUDbESiilKeXeHL7FwwiFcogfhfyjmvT968RXSvnl23f1JOClTHYItsi7o501C/7qVllscUP3oA==", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/inquirer/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/inquirer/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "optional": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-electron": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/is-electron/-/is-electron-2.2.0.tgz", - "integrity": "sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q==" - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "optional": true - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "optional": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "optional": true, - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/isomorphic-fetch/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/it-pushable": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/it-pushable/-/it-pushable-1.4.2.tgz", - "integrity": "sha512-vVPu0CGRsTI8eCfhMknA7KIBqqGFolbRx+1mbQ6XuZ7YCz995Qj7L4XUviwClFunisDq96FdxzF5FnAbw15afg==", - "dependencies": { - "fast-fifo": "^1.0.0" - } - }, - "node_modules/it-to-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/it-to-stream/-/it-to-stream-1.0.0.tgz", - "integrity": "sha512-pLULMZMAB/+vbdvbZtebC0nWBTbG581lk6w8P7DfIIIKUfa8FbY7Oi0FxZcFPbxvISs7A9E+cMpLDBc1XhpAOA==", - "dependencies": { - "buffer": "^6.0.3", - "fast-fifo": "^1.0.0", - "get-iterator": "^1.0.2", - "p-defer": "^3.0.0", - "p-fifo": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "node_modules/it-to-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iterate-iterator": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz", - "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==" - }, - "node_modules/iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dependencies": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, - "node_modules/jimp": { - "version": "0.22.8", - "resolved": "https://registry.npmmirror.com/jimp/-/jimp-0.22.8.tgz", - "integrity": "sha512-pBbrooJMX7795sDcxx1XpwNZC8B/ITyDV+JK2/1qNbQl/1UWqWeh5Dq7qQpMZl5jLdcFDv5IVTM+OhpafSqSFA==", - "optional": true, - "dependencies": { - "@jimp/custom": "^0.22.8", - "@jimp/plugins": "^0.22.8", - "@jimp/types": "^0.22.8", - "regenerator-runtime": "^0.13.3" - } - }, - "node_modules/jpeg-js": { - "version": "0.4.4", - "resolved": "https://registry.npmmirror.com/jpeg-js/-/jpeg-js-0.4.4.tgz", - "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", - "optional": true - }, - "node_modules/js-tiktoken": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/js-tiktoken/-/js-tiktoken-1.0.7.tgz", - "integrity": "sha512-biba8u/clw7iesNEWLOLwrNGoBP2lA+hTaBLs/D45pJdUPFXyxD6nhcDVtADChghv4GgyAiMKYMiRx7x6h7Biw==", - "dependencies": { - "base64-js": "^1.5.1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-schema-typed": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/json-schema-typed/-/json-schema-typed-8.0.1.tgz", - "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "dependencies": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/keyv-file": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/keyv-file/-/keyv-file-0.2.0.tgz", - "integrity": "sha512-zUQ11eZRmilEUpV1gJSj8mBAHjyXpleQo1iCS0khb+GFRhiPfwavWgn4eDUKNlOyMZzmExnISl8HE1hNbim0gw==", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^4.0.1", - "tslib": "^1.9.3" - } - }, - "node_modules/keyv-file/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/keyv-file/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "optional": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/light-my-request": { - "version": "5.10.0", - "resolved": "https://registry.npmmirror.com/light-my-request/-/light-my-request-5.10.0.tgz", - "integrity": "sha512-ZU2D9GmAcOUculTTdH9/zryej6n8TzT+fNGdNtm6SDp5MMMpHrJJkvAdE3c6d8d2chE9i+a//dS9CWZtisknqA==", - "dependencies": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/load-bmfont": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/load-bmfont/-/load-bmfont-1.4.1.tgz", - "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", - "optional": true, - "dependencies": { - "buffer-equal": "0.0.1", - "mime": "^1.3.4", - "parse-bmfont-ascii": "^1.0.3", - "parse-bmfont-binary": "^1.0.5", - "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", - "xhr": "^2.0.1", - "xtend": "^4.0.0" - } - }, - "node_modules/load-bmfont/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "optional": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "optional": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-deep": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/merge-deep/-/merge-deep-3.0.3.tgz", - "integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==", - "optional": true, - "dependencies": { - "arr-union": "^3.1.0", - "clone-deep": "^0.2.4", - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/microsoft-cognitiveservices-speech-sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmmirror.com/microsoft-cognitiveservices-speech-sdk/-/microsoft-cognitiveservices-speech-sdk-1.29.0.tgz", - "integrity": "sha512-/NaDni70OR5x0FPG6LD/aOkmfLIsOwqpw0UsijcHbH+U0q6FPAX1VZVlv6ZMvkObw3k/FJv0N9CFNsN1P4M7kA==", - "dependencies": { - "agent-base": "^6.0.1", - "bent": "^7.3.12", - "https-proxy-agent": "^4.0.0", - "uuid": "^9.0.0", - "ws": "^7.5.6" - } - }, - "node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/https-proxy-agent/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/microsoft-cognitiveservices-speech-sdk/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", - "optional": true, - "dependencies": { - "dom-walk": "^0.1.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "optional": true - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==", - "optional": true, - "dependencies": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-object/node_modules/for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "optional": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true - }, - "node_modules/mnemonist": { - "version": "0.39.5", - "resolved": "https://registry.npmmirror.com/mnemonist/-/mnemonist-0.39.5.tgz", - "integrity": "sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==", - "dependencies": { - "obliterator": "^2.0.1" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmmirror.com/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", - "optional": true - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.45.0", - "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.45.0.tgz", - "integrity": "sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ==", - "optional": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "optional": true - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/node-silk": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/node-silk/-/node-silk-0.1.0.tgz", - "integrity": "sha512-z3zl66E1S1aOOhr9Sa0C957QBi39DqM5GzRalSXRYer52Aqp0cSv74DdMEDBXr9sn2AV5M7O78UZ4ppg/NVelg==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.9", - "nan": "^2.15.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "optional": true, - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, - "node_modules/omggif": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/omggif/-/omggif-1.0.10.tgz", - "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", - "optional": true - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/onetime/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/openai": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/openai/-/openai-3.3.0.tgz", - "integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==", - "dependencies": { - "axios": "^0.26.0", - "form-data": "^4.0.0" - } - }, - "node_modules/openai/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "^1.14.8" - } - }, - "node_modules/ora": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/ora/-/ora-6.3.1.tgz", - "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", - "dependencies": { - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-defer": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/p-defer/-/p-defer-3.0.0.tgz", - "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-fifo": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/p-fifo/-/p-fifo-1.0.0.tgz", - "integrity": "sha512-IjoCxXW48tqdtDFz6fqo5q1UfFVjjVZe8TC1QRflvNUJtNfCUhxOUw6MOVZhDPjqhSzc26xKdugsO17gmzd5+A==", - "dependencies": { - "fast-fifo": "^1.0.0", - "p-defer": "^3.0.0" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/p-locate/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-queue": { - "version": "2.4.2", - "resolved": "https://registry.npmmirror.com/p-queue/-/p-queue-2.4.2.tgz", - "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", - "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "optional": true - }, - "node_modules/parse-bmfont-ascii": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", - "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", - "optional": true - }, - "node_modules/parse-bmfont-binary": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", - "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", - "optional": true - }, - "node_modules/parse-bmfont-xml": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", - "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", - "optional": true, - "dependencies": { - "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" - } - }, - "node_modules/parse-headers": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz", - "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", - "optional": true - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" - }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/phin": { - "version": "2.9.3", - "resolved": "https://registry.npmmirror.com/phin/-/phin-2.9.3.tgz", - "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", - "optional": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/pino": { - "version": "8.14.1", - "resolved": "https://registry.npmmirror.com/pino/-/pino-8.14.1.tgz", - "integrity": "sha512-8LYNv7BKWXSfS+k6oEc6occy5La+q2sPwU3q2ljTX5AZk7v+5kND2o5W794FyRaqha6DJajmkNRsWtPpFyMUdw==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.1.0", - "thread-stream": "^2.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", - "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz", - "integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ==" - }, - "node_modules/pixelmatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/pixelmatch/-/pixelmatch-4.0.2.tgz", - "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", - "optional": true, - "dependencies": { - "pngjs": "^3.0.0" - }, - "bin": { - "pixelmatch": "bin/pixelmatch" - } - }, - "node_modules/pixelmatch/node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "node_modules/pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "optional": true, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-warning": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-2.2.0.tgz", - "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" - }, - "node_modules/promise.allsettled": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/promise.allsettled/-/promise.allsettled-1.0.6.tgz", - "integrity": "sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==", - "dependencies": { - "array.prototype.map": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "iterate-value": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/puppeteer-extra": { - "version": "3.3.6", - "resolved": "https://registry.npmmirror.com/puppeteer-extra/-/puppeteer-extra-3.3.6.tgz", - "integrity": "sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A==", - "optional": true, - "dependencies": { - "@types/debug": "^4.1.0", - "debug": "^4.1.1", - "deepmerge": "^4.2.2" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "@types/puppeteer": "*", - "puppeteer": "*", - "puppeteer-core": "*" - }, - "peerDependenciesMeta": { - "@types/puppeteer": { - "optional": true - }, - "puppeteer": { - "optional": true - }, - "puppeteer-core": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin": { - "version": "3.2.3", - "resolved": "https://registry.npmmirror.com/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz", - "integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==", - "optional": true, - "dependencies": { - "@types/debug": "^4.1.0", - "debug": "^4.1.1", - "merge-deep": "^3.0.1" - }, - "engines": { - "node": ">=9.11.2" - }, - "peerDependencies": { - "playwright-extra": "*", - "puppeteer-extra": "*" - }, - "peerDependenciesMeta": { - "playwright-extra": { - "optional": true - }, - "puppeteer-extra": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-recaptcha": { - "version": "3.6.8", - "resolved": "https://registry.npmmirror.com/puppeteer-extra-plugin-recaptcha/-/puppeteer-extra-plugin-recaptcha-3.6.8.tgz", - "integrity": "sha512-AY2HG1ZFlSi4xs+Hy84LtRJ95DIfnbjR3Az64dJGVW8gr/hBAGEWRlXTMzea7YOmxO3Nc8Ak3CcUgjgp1gIu1w==", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "merge-deep": "^3.0.2", - "puppeteer-extra-plugin": "^3.2.3" - }, - "engines": { - "node": ">=9.11.2" - }, - "peerDependencies": { - "playwright-extra": "*", - "puppeteer-extra": "*" - }, - "peerDependenciesMeta": { - "playwright-extra": { - "optional": true - }, - "puppeteer-extra": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-recaptcha/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-recaptcha/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/puppeteer-extra-plugin-stealth": { - "version": "2.11.2", - "resolved": "https://registry.npmmirror.com/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz", - "integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "puppeteer-extra-plugin": "^3.2.3", - "puppeteer-extra-plugin-user-preferences": "^2.4.1" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "playwright-extra": "*", - "puppeteer-extra": "*" - }, - "peerDependenciesMeta": { - "playwright-extra": { - "optional": true - }, - "puppeteer-extra": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-stealth/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-stealth/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/puppeteer-extra-plugin-user-data-dir": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz", - "integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^10.0.0", - "puppeteer-extra-plugin": "^3.2.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "playwright-extra": "*", - "puppeteer-extra": "*" - }, - "peerDependenciesMeta": { - "playwright-extra": { - "optional": true - }, - "puppeteer-extra": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-user-data-dir/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-user-data-dir/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "optional": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/puppeteer-extra-plugin-user-data-dir/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "optional": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/puppeteer-extra-plugin-user-data-dir/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/puppeteer-extra-plugin-user-data-dir/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "optional": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/puppeteer-extra-plugin-user-preferences": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz", - "integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==", - "optional": true, - "dependencies": { - "debug": "^4.1.1", - "deepmerge": "^4.2.2", - "puppeteer-extra-plugin": "^3.2.3", - "puppeteer-extra-plugin-user-data-dir": "^2.4.1" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "playwright-extra": "*", - "puppeteer-extra": "*" - }, - "peerDependenciesMeta": { - "playwright-extra": { - "optional": true - }, - "puppeteer-extra": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-user-preferences/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin-user-preferences/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/puppeteer-extra-plugin/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/puppeteer-extra/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/puppeteer-extra/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/quick-lru": { - "version": "6.1.1", - "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-6.1.1.tgz", - "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==", - "engines": { - "node": ">=12" - } - }, - "node_modules/random": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/random/-/random-4.1.0.tgz", - "integrity": "sha512-6Ajb7XmMSE9EFAMGC3kg9mvE7fGlBip25mYYuSMzw/uUSrmGilvZo2qwX3RnTRjwXkwkS+4swse9otZ92VjAtQ==", - "dependencies": { - "seedrandom": "^3.0.5" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/read-pkg": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-7.1.0.tgz", - "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^2.0.0" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/read-pkg-up": { - "version": "9.1.0", - "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-9.1.0.tgz", - "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", - "dependencies": { - "find-up": "^6.3.0", - "read-pkg": "^7.1.0", - "type-fest": "^2.5.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "optional": true, - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "optional": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "dependencies": { - "ret": "~0.2.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "optional": true - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmmirror.com/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, - "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmmirror.com/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "optional": true - }, - "node_modules/set-cookie-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shallow-clone": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==", - "optional": true, - "dependencies": { - "is-extendable": "^0.1.1", - "kind-of": "^2.0.1", - "lazy-cache": "^0.2.3", - "mixin-object": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shallow-clone/node_modules/kind-of": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==", - "optional": true, - "dependencies": { - "is-buffer": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shallow-clone/node_modules/lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmmirror.com/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sharp": { - "version": "0.31.3", - "resolved": "https://registry.npmmirror.com/sharp/-/sharp-0.31.3.tgz", - "integrity": "sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.8", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=14.15.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "optional": true - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "optional": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "optional": true - }, - "node_modules/sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", - "dependencies": { - "bl": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "optional": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stubborn-fs": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/stubborn-fs/-/stubborn-fs-1.2.4.tgz", - "integrity": "sha512-KRa4nIRJ8q6uApQbPwYZVhOof8979fw4xbajBWa5kPJFa4nyY3aFaMWVyIVCDnkNCCG/3HLipUZ4QaNlYsmX1w==" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmmirror.com/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "optional": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/tar-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/thread-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/thread-stream/-/thread-stream-2.3.0.tgz", - "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/timm": { - "version": "1.7.1", - "resolved": "https://registry.npmmirror.com/timm/-/timm-1.7.1.tgz", - "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==", - "optional": true - }, - "node_modules/tiny-lru": { - "version": "11.0.1", - "resolved": "https://registry.npmmirror.com/tiny-lru/-/tiny-lru-11.0.1.tgz", - "integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/tinycolor2": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "optional": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "optional": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "optional": true - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node-register": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/ts-node-register/-/ts-node-register-1.0.0.tgz", - "integrity": "sha512-0i5qFowh0t1phylWB0Y0l6+K2bv1He0ncYhM+jLSzOWGYM37qjDQJQV5yER63+97q7VFETC5mkPXZH+4JgZlng==", - "dev": true, - "dependencies": { - "ts-node": ">=0.9.0" - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "engines": { - "node": ">=0.6.x" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "node_modules/undici": { - "version": "5.22.1", - "resolved": "https://registry.npmmirror.com/undici/-/undici-5.22.1.tgz", - "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==", - "dependencies": { - "busboy": "^1.6.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utif2": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/utif2/-/utif2-4.1.0.tgz", - "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", - "optional": true, - "dependencies": { - "pako": "^1.0.11" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "optional": true - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", - "optional": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "optional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/when-exit": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/when-exit/-/when-exit-2.1.0.tgz", - "integrity": "sha512-H85ulNwUBU1e6PGxkWUDgxnbohSXD++ah6Xw1VHAN7CtypcbZaC4aYjQ+C2PMVaDkURDuOinNAT+Lnz3utWXxQ==" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wide-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmmirror.com/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", - "optional": true, - "dependencies": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/xml-parse-from-string": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", - "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", - "optional": true - }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "optional": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "optional": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "optional": true, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - } - } - } -} diff --git a/package.json b/package.json index 3af5d51..7a4e110 100644 --- a/package.json +++ b/package.json @@ -1,48 +1,21 @@ { "name": "chatgpt-plugin", + "version": "3.0.0", "type": "module", "author": "ikechan8370", "dependencies": { - "@fastify/cookie": "^8.3.0", - "@fastify/cors": "^8.2.0", - "@fastify/static": "^6.9.0", - "@fastify/websocket": "^8.1.0", - "@slack/bolt": "^3.13.0", - "@waylaidwanderer/chatgpt-api": "^1.36.0", - "asn1.js": "^5.0.0", - "chatgpt": "^5.2.4", - "delay": "^5.0.0", - "diff": "^5.1.0", - "emoji-strip": "^1.0.1", - "eventsource": "^2.0.2", - "eventsource-parser": "^1.0.0", - "fastify": "^4.16.0", - "form-data": "^4.0.0", - "https-proxy-agent": "5.0.1", - "js-tiktoken": "^1.0.5", - "keyv": "^4.5.2", - "keyv-file": "^0.2.0", - "microsoft-cognitiveservices-speech-sdk": "^1.27.0", - "node-fetch": "^3.3.1", - "openai": "^3.2.1", - "p-timeout": "^6.1.2", - "quick-lru": "6.1.1", - "random": "^4.1.0", - "undici": "^5.21.0", - "uuid": "^9.0.0", - "ws": "^8.13.0" + "better-sqlite3": "^9.4.3", + "adm-zip": "^0.5.10", + "chaite": "^1.8.2", + "js-yaml": "^4.1.0", + "keyv": "^5.3.1", + "keyv-file": "^5.1.2", + "lowdb": "^7.0.1", + "sqlite-vec": "^0.1.7-alpha.2", + "vectra": "^0.9.0" }, - "optionalDependencies": { - "@node-rs/jieba": "^1.6.2", - "jimp": "^0.22.7", - "node-silk": "^0.1.0", - "puppeteer-extra": "^3.3.6", - "puppeteer-extra-plugin-recaptcha": "^3.6.8", - "puppeteer-extra-plugin-stealth": "^2.11.2", - "sharp": "^0.31.3" + "peerDependencies": { + "sqlite3": ">=5.1.6" }, - "devDependencies": { - "ts-node": "^10.9.1", - "ts-node-register": "^1.0.0" - } + "pnpm": {} } diff --git a/prompts/.gitkeep b/prompts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/resources/content/Bing/644b614f82934cd2a7833c108ceb941b.css b/resources/content/Bing/644b614f82934cd2a7833c108ceb941b.css deleted file mode 100644 index d5215a7..0000000 --- a/resources/content/Bing/644b614f82934cd2a7833c108ceb941b.css +++ /dev/null @@ -1,270 +0,0 @@ -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/resources/content/Bing/a0020bf6401d4e99884e2be5f5402f24.css b/resources/content/Bing/a0020bf6401d4e99884e2be5f5402f24.css deleted file mode 100644 index 21ce389..0000000 --- a/resources/content/Bing/a0020bf6401d4e99884e2be5f5402f24.css +++ /dev/null @@ -1,630 +0,0 @@ -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0udc1uaw.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0ddc1uaw.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0vdc1uaw.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0adc1uaw.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0wdc1uaw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0xdc1uaw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0zdc0.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwj0bbck.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufuz0bbck.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwz0bbck.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufvp0bbck.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwp0bbck.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufw50bbck.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufvz0b.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/resources/content/Bing/index.html b/resources/content/Bing/index.html deleted file mode 100644 index e011f45..0000000 --- a/resources/content/Bing/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
-
-
-
-

New Bing

- {{if cache.file != ''}} -

{{cache.file}}

- {{/if}} -
-
-
-
-
-
-

{{senderName}}

-
-
-

- -
-
-
-
-
-
- {{if style === 'Sydney'}} -

Sydney

- {{else}} -

必应

- {{/if}} -
-
-

- -
-
- {{if mood != ''}} -
-
- -
-
- {{/if}} -
- {{if quote}} -
-
-
-

引用

-
-
- {{each quotes item}} -

{{item.text}} - {{item.url}}

- {{/each}} - -
-
-
- {{/if}} - {{if cache.file != ''}} -
- {{/if}} -
-
- -
-
-
-
-
-
- -
-
-
-
- - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/content/Bing/style.css b/resources/content/Bing/style.css deleted file mode 100644 index a53a94f..0000000 --- a/resources/content/Bing/style.css +++ /dev/null @@ -1,3608 +0,0 @@ -@charset "UTF-8"; -/*----------------------------------------------------------------------------------- - - Template Name: Aiden - Creative Portfolio HTML5 Template - Template URI: site.com - Description: Aiden - Creative Portfolio HTML5 Template - Author: Rs_Theme - Version: 1.0 - ------------------------------------------------------------------------------------ - - CSS INDEX - =================== - 01. Theme default css - 02. header css - 03. Navigation css - 04. hero css - 05. about css - 06. service css - 07. work CSS - 08. testimonial css - 09. brand css - 10. blog css - 11. contact css - 12. breadcrumb css - 13. cta css - 14. contact-page css - 15. blog-page css - 16. footer css - ------------------------------------------------------------------------------------*/ -/* reset css start */ -@import url("644b614f82934cd2a7833c108ceb941b.css"); -@import url("a0020bf6401d4e99884e2be5f5402f24.css"); -html { - scroll-behavior: smooth; -} - -body { - font-family: "Microsoft YaHei", "微软雅黑", "Josefin Sans", sans-serif; - font-size: 18px; - padding: 0; - margin: 0; - font-weight: 400; - position: relative; - line-height: 26px; - background: #f5feff; -} - -img { - max-width: 100%; - height: auto; -} - -ul, ol { - padding: 0; - margin: 0; - list-style: none; -} - -button { - cursor: pointer; -} - -*:focus { - outline: none; -} - -button { - border: none; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} - -button:focus { - outline: none; -} - -a { - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -a:hover { - text-decoration: none; - color: #242B58; -} - -table { - width: 100%; -} - -p, li, span { - color: #242B58; - margin-bottom: 0; -} - -/* reset css end */ -/* global css start */ -.nice-select { - background-color: transparent; - height: 40px !important; - line-height: 40px !important; - min-height: 40px !important; - padding: 0 30px; -} -.nice-select span { - color: #242B58; -} -.nice-select .list { - box-shadow: 0px -5px 26px -5px #cdd4e7; -} -.nice-select .list li { - margin-right: 0 !important; -} -.nice-select .list .option { - color: #242B58; -} -.nice-select .list .option.selected, .nice-select .list .option:hover { - border: none !important; -} - -/* global css end */ -.bg_img { - background-position: center center; - background-size: cover; - background-repeat: no-repeat; - width: 100%; - height: 100%; -} - -.grey-bg { - background: #B0B0B2; -} - -.white { - color: #ffffff; -} - -.f-right { - float: right; -} - -.height-50 { - height: 40px; - display: block; -} - -.height-40 { - height: 40px; - display: block; -} - -.height-35 { - height: 35px; - display: block; -} - -.height-25 { - height: 20px; - display: block; -} - -.pt-225 { - padding-top: 225px; -} - -.pt-240 { - padding-top: 240px; -} - -.pt-210 { - padding-top: 210px; -} - -.pt-232 { - padding-top: 232px; -} - -.psr { - position: relative; -} - -.site-btn { - font-size: 14px; - text-transform: capitalize; - color: #ffffff; - background: #6B84FF; - padding: 8.5px 45px; - display: inline-block; - border-radius: 5px; - border: 1px solid #6B84FF; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; - z-index: 2; - position: relative; - font-family: "Microsoft YaHei", "微软雅黑", "Josefin Sans", sans-serif; -} -@media (max-width: 767px) { - .site-btn { - padding: 7.5px 25px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .site-btn { - padding: 8.5px 45px; - } -} -.site-btn:hover { - border-color: #6B84FF; - background: transparent; - color: #ffffff; -} -.site-btn__borderd { - background: transparent; - color: #B0B0B2; - margin-left: 35px; -} -@media (max-width: 767px) { - .site-btn__borderd { - margin-left: 15px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .site-btn__borderd { - margin-left: 35px; - } -} -.site-btn__borderd:hover { - background: #6B84FF; -} - -.section-heading { - position: relative; -} -.section-heading h2 { - font-size: 42px; - line-height: 51px; - display: inline-block; - position: relative; - padding-right: 15px; - color: rgb(0 123 255 / 25%); - text-transform: uppercase; -} -@media (max-width: 767px) { - .section-heading h2 { - font-size: 36px; - line-height: 45px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .section-heading h2 { - font-size: 42px; - line-height: 51px; - } -} -.section-heading h2::after { - width: 160px; - height: 100px; - right: 0px; - content: ""; - position: absolute; - bottom: 0px; - background: #b8dee094; - z-index: -1; -} -.section-heading__left h2 { - padding-right: 0px; - padding-left: 15px; -} -.section-heading__left h2::after { - right: auto; - left: 0px; -} - -.inline-btn { - font-size: 20px; - color: #6B84FF; - font-family: "Josefin Sans", sans-serif; - position: relative; - display: inline-block; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -@media (max-width: 767px) { - .inline-btn { - font-size: 16px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .inline-btn { - font-size: 20px; - } -} -.inline-btn::after { - position: absolute; - content: ""; - left: 0; - width: 50px; - height: 2px; - background: #ffffff; - left: -70px; - -webkit-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); - top: 50%; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -@media (max-width: 991px) { - .inline-btn::after { - left: auto; - right: -70px; - } -} -.inline-btn:hover { - color: #6B84FF; -} -.inline-btn:hover::after { - left: -65px; - background: #6B84FF; -} - -.side-shape { - position: absolute; - left: 0; - top: -150px; -} -@media (max-width: 1650px) { - .side-shape { - left: -100px; - } -} -.side-shape__right { - left: auto; - right: 0; - top: -80px; -} -@media (max-width: 1650px) { - .side-shape__right { - right: -100px; - } -} -.side-shape__contact { - top: 200px; - left: -95px; - right: auto; -} -@media (max-width: 1650px) { - .side-shape__contact { - left: -125px; - } -} -@media (max-width: 1300px) { - .side-shape__contact { - width: 170px; - } -} -.side-shape__about { - top: 290px; -} -@media (max-width: 767px) { - .side-shape__about { - width: 150px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .side-shape__about { - width: 200px; - } -} - -/*-- - - Margin & Padding ------------------------------------------*/ -/*-- Margin Top --*/ -.mt-none-5 { - margin-top: -5px; -} - -.mt-none-10 { - margin-top: -10px; -} - -.mt-none-15 { - margin-top: -15px; -} - -.mt-none-20 { - margin-top: -20px; -} - -.mt-none-25 { - margin-top: -25px; -} - -.mt-none-30 { - margin-top: -30px; -} - -.mt-none-35 { - margin-top: -35px; -} - -.mt-none-40 { - margin-top: -40px; -} - -.mt-none-45 { - margin-top: -45px; -} - -.mt-none-50 { - margin-top: -50px; -} - -.mt-none-55 { - margin-top: -55px; -} - -.mt-none-60 { - margin-top: -60px; -} - -.mt-none-65 { - margin-top: -65px; -} - -.mt-none-70 { - margin-top: -70px; -} - -.mt-none-75 { - margin-top: -75px; -} - -.mt-none-80 { - margin-top: -80px; -} - -.mt-none-85 { - margin-top: -85px; -} - -.mt-none-90 { - margin-top: -90px; -} - -.mt-none-95 { - margin-top: -95px; -} - -.mt-none-100 { - margin-top: -100px; -} - -/*-- Margin Top --*/ -.mt-5 { - margin-top: 5px; -} - -.mt-10 { - margin-top: 10px; -} - -.mt-15 { - margin-top: 15px; -} - -.mt-20 { - margin-top: 20px; -} - -.mt-25 { - margin-top: 25px; -} - -.mt-30 { - margin-top: 30px; -} - -.mt-35 { - margin-top: 35px; -} - -.mt-40 { - margin-top: 40px; -} - -.mt-45 { - margin-top: 45px; -} - -.mt-50 { - margin-top: 50px; -} - -.mt-55 { - margin-top: 55px; -} - -.mt-60 { - margin-top: 60px; -} - -.mt-65 { - margin-top: 65px; -} - -.mt-70 { - margin-top: 70px; -} - -.mt-75 { - margin-top: 75px; -} - -.mt-80 { - margin-top: 80px; -} - -.mt-85 { - margin-top: 85px; -} - -.mt-90 { - margin-top: 90px; -} - -.mt-95 { - margin-top: 95px; -} - -.mt-100 { - margin-top: 100px; -} - -.mt-105 { - margin-top: 105px; -} - -.mt-110 { - margin-top: 110px; -} - -.mt-115 { - margin-top: 115px; -} - -.mt-120 { - margin-top: 120px; -} - -.mt-125 { - margin-top: 125px; -} - -.mt-130 { - margin-top: 130px; -} - -.mt-135 { - margin-top: 135px; -} - -.mt-140 { - margin-top: 140px; -} - -.mt-145 { - margin-top: 145px; -} - -.mt-150 { - margin-top: 150px; -} - -.mt-155 { - margin-top: 155px; -} - -.mt-160 { - margin-top: 160px; -} - -.mt-165 { - margin-top: 165px; -} - -.mt-170 { - margin-top: 170px; -} - -.mt-175 { - margin-top: 175px; -} - -.mt-180 { - margin-top: 180px; -} - -.mt-185 { - margin-top: 185px; -} - -.mt-190 { - margin-top: 190px; -} - -.mt-195 { - margin-top: 195px; -} - -.mt-200 { - margin-top: 200px; -} - -/*-- Margin Bottom --*/ -.mb-5 { - margin-bottom: 5px; -} - -.mb-10 { - margin-bottom: 10px; -} - -.mb-15 { - margin-bottom: 15px; -} - -.mb-20 { - margin-bottom: 20px; -} - -.mb-25 { - margin-bottom: 25px; -} - -.mb-30 { - margin-bottom: 30px; -} - -.mb-35 { - margin-bottom: 35px; -} - -.mb-40 { - margin-bottom: 40px; -} - -.mb-45 { - margin-bottom: 45px; -} - -.mb-50 { - margin-bottom: 50px; -} - -.mb-55 { - margin-bottom: 55px; -} - -.mb-60 { - margin-bottom: 60px; -} - -.mb-65 { - margin-bottom: 65px; -} - -.mb-70 { - margin-bottom: 70px; -} - -.mb-75 { - margin-bottom: 75px; -} - -.mb-80 { - margin-bottom: 80px; -} - -.mb-85 { - margin-bottom: 85px; -} - -.mb-90 { - margin-bottom: 90px; -} - -.mb-95 { - margin-bottom: 95px; -} - -.mb-100 { - margin-bottom: 100px; -} - -.mb-105 { - margin-bottom: 105px; -} - -.mb-110 { - margin-bottom: 110px; -} - -.mb-115 { - margin-bottom: 115px; -} - -.mb-120 { - margin-bottom: 120px; -} - -.mb-125 { - margin-bottom: 125px; -} - -.mb-130 { - margin-bottom: 130px; -} - -.mb-135 { - margin-bottom: 135px; -} - -.mb-140 { - margin-bottom: 140px; -} - -.mb-145 { - margin-bottom: 145px; -} - -.mb-150 { - margin-bottom: 150px; -} - -.mb-155 { - margin-bottom: 155px; -} - -.mb-160 { - margin-bottom: 160px; -} - -.mb-165 { - margin-bottom: 165px; -} - -.mb-170 { - margin-bottom: 170px; -} - -.mb-175 { - margin-bottom: 175px; -} - -.mb-180 { - margin-bottom: 180px; -} - -.mb-185 { - margin-bottom: 185px; -} - -.mb-190 { - margin-bottom: 190px; -} - -.mb-195 { - margin-bottom: 195px; -} - -.mb-200 { - margin-bottom: 200px; -} - -/*-- Margin Left --*/ -.ml-5 { - margin-left: 5px; -} - -.ml-10 { - margin-left: 10px; -} - -.ml-15 { - margin-left: 15px; -} - -.ml-20 { - margin-left: 20px; -} - -.ml-25 { - margin-left: 25px; -} - -.ml-30 { - margin-left: 30px; -} - -.ml-35 { - margin-left: 35px; -} - -.ml-40 { - margin-left: 40px; -} - -.ml-45 { - margin-left: 45px; -} - -.ml-50 { - margin-left: 50px; -} - -.ml-55 { - margin-left: 55px; -} - -.ml-60 { - margin-left: 60px; -} - -.ml-65 { - margin-left: 65px; -} - -.ml-70 { - margin-left: 70px; -} - -.ml-75 { - margin-left: 75px; -} - -.ml-80 { - margin-left: 80px; -} - -.ml-85 { - margin-left: 85px; -} - -.ml-90 { - margin-left: 90px; -} - -.ml-95 { - margin-left: 95px; -} - -.ml-100 { - margin-left: 100px; -} - -.ml-105 { - margin-left: 105px; -} - -.ml-110 { - margin-left: 110px; -} - -.ml-115 { - margin-left: 115px; -} - -.ml-120 { - margin-left: 120px; -} - -.ml-125 { - margin-left: 125px; -} - -.ml-130 { - margin-left: 130px; -} - -.ml-135 { - margin-left: 135px; -} - -.ml-140 { - margin-left: 140px; -} - -.ml-145 { - margin-left: 145px; -} - -.ml-150 { - margin-left: 150px; -} - -.ml-155 { - margin-left: 155px; -} - -.ml-160 { - margin-left: 160px; -} - -.ml-165 { - margin-left: 165px; -} - -.ml-170 { - margin-left: 170px; -} - -.ml-175 { - margin-left: 175px; -} - -.ml-180 { - margin-left: 180px; -} - -.ml-185 { - margin-left: 185px; -} - -.ml-190 { - margin-left: 190px; -} - -.ml-195 { - margin-left: 195px; -} - -.ml-200 { - margin-left: 200px; -} - -/*-- Margin Right --*/ -.mr-5 { - margin-right: 5px; -} - -.mr-10 { - margin-right: 10px; -} - -.mr-15 { - margin-right: 15px; -} - -.mr-20 { - margin-right: 20px; -} - -.mr-25 { - margin-right: 25px; -} - -.mr-30 { - margin-right: 30px; -} - -.mr-35 { - margin-right: 35px; -} - -.mr-40 { - margin-right: 40px; -} - -.mr-45 { - margin-right: 45px; -} - -.mr-50 { - margin-right: 50px; -} - -.mr-55 { - margin-right: 55px; -} - -.mr-60 { - margin-right: 60px; -} - -.mr-65 { - margin-right: 65px; -} - -.mr-70 { - margin-right: 70px; -} - -.mr-75 { - margin-right: 75px; -} - -.mr-80 { - margin-right: 80px; -} - -.mr-85 { - margin-right: 85px; -} - -.mr-90 { - margin-right: 90px; -} - -.mr-95 { - margin-right: 95px; -} - -.mr-100 { - margin-right: 100px; -} - -.mr-105 { - margin-right: 105px; -} - -.mr-110 { - margin-right: 110px; -} - -.mr-115 { - margin-right: 115px; -} - -.mr-120 { - margin-right: 120px; -} - -.mr-125 { - margin-right: 125px; -} - -.mr-130 { - margin-right: 130px; -} - -.mr-135 { - margin-right: 135px; -} - -.mr-140 { - margin-right: 140px; -} - -.mr-145 { - margin-right: 145px; -} - -.mr-150 { - margin-right: 150px; -} - -.mr-155 { - margin-right: 155px; -} - -.mr-160 { - margin-right: 160px; -} - -.mr-165 { - margin-right: 165px; -} - -.mr-170 { - margin-right: 170px; -} - -.mr-175 { - margin-right: 175px; -} - -.mr-180 { - margin-right: 180px; -} - -.mr-185 { - margin-right: 185px; -} - -.mr-190 { - margin-right: 190px; -} - -.mr-195 { - margin-right: 195px; -} - -.mr-200 { - margin-right: 200px; -} - -/*-- Padding Top --*/ -.pt-5 { - padding-top: 5px; -} - -.pt-10 { - padding-top: 10px; -} - -.pt-15 { - padding-top: 15px; -} - -.pt-20 { - padding-top: 20px; -} - -.pt-25 { - padding-top: 25px; -} - -.pt-30 { - padding-top: 30px; -} - -.pt-35 { - padding-top: 35px; -} - -.pt-40 { - padding-top: 40px; -} - -.pt-45 { - padding-top: 45px; -} - -.pt-50 { - padding-top: 50px; -} - -.pt-55 { - padding-top: 55px; -} - -.pt-60 { - padding-top: 60px; -} - -.pt-65 { - padding-top: 65px; -} - -.pt-70 { - padding-top: 70px; -} - -.pt-75 { - padding-top: 75px; -} - -.pt-80 { - padding-top: 80px; -} - -.pt-85 { - padding-top: 85px; -} - -.pt-90 { - padding-top: 90px; -} - -.pt-95 { - padding-top: 95px; -} - -.pt-100 { - padding-top: 100px; -} - -.pt-105 { - padding-top: 105px; -} - -.pt-110 { - padding-top: 110px; -} - -.pt-115 { - padding-top: 115px; -} - -.pt-120 { - padding-top: 120px; -} - -.pt-125 { - padding-top: 125px; -} - -.pt-130 { - padding-top: 130px; -} - -.pt-135 { - padding-top: 135px; -} - -.pt-140 { - padding-top: 140px; -} - -.pt-145 { - padding-top: 145px; -} - -.pt-150 { - padding-top: 150px; -} - -.pt-155 { - padding-top: 155px; -} - -.pt-160 { - padding-top: 160px; -} - -.pt-165 { - padding-top: 165px; -} - -.pt-170 { - padding-top: 170px; -} - -.pt-175 { - padding-top: 175px; -} - -.pt-180 { - padding-top: 180px; -} - -.pt-185 { - padding-top: 185px; -} - -.pt-190 { - padding-top: 190px; -} - -.pt-195 { - padding-top: 195px; -} - -.pt-200 { - padding-top: 200px; -} - -/*-- Padding Bottom --*/ -.pb-5 { - padding-bottom: 5px; -} - -.pb-10 { - padding-bottom: 10px; -} - -.pb-15 { - padding-bottom: 15px; -} - -.pb-20 { - padding-bottom: 20px; -} - -.pb-25 { - padding-bottom: 25px; -} - -.pb-30 { - padding-bottom: 30px; -} - -.pb-35 { - padding-bottom: 35px; -} - -.pb-40 { - padding-bottom: 40px; -} - -.pb-45 { - padding-bottom: 45px; -} - -.pb-50 { - padding-bottom: 50px; -} - -.pb-55 { - padding-bottom: 55px; -} - -.pb-60 { - padding-bottom: 60px; -} - -.pb-65 { - padding-bottom: 65px; -} - -.pb-70 { - padding-bottom: 70px; -} - -.pb-75 { - padding-bottom: 75px; -} - -.pb-80 { - padding-bottom: 80px; -} - -.pb-85 { - padding-bottom: 85px; -} - -.pb-90 { - padding-bottom: 90px; -} - -.pb-95 { - padding-bottom: 95px; -} - -.pb-100 { - padding-bottom: 100px; -} - -.pb-105 { - padding-bottom: 105px; -} - -.pb-110 { - padding-bottom: 110px; -} - -.pb-115 { - padding-bottom: 115px; -} - -.pb-120 { - padding-bottom: 120px; -} - -.pb-125 { - padding-bottom: 125px; -} - -.pb-130 { - padding-bottom: 130px; -} - -.pb-135 { - padding-bottom: 135px; -} - -.pb-140 { - padding-bottom: 140px; -} - -.pb-145 { - padding-bottom: 145px; -} - -.pb-150 { - padding-bottom: 150px; -} - -.pb-155 { - padding-bottom: 155px; -} - -.pb-160 { - padding-bottom: 160px; -} - -.pb-165 { - padding-bottom: 165px; -} - -.pb-170 { - padding-bottom: 170px; -} - -.pb-175 { - padding-bottom: 175px; -} - -.pb-180 { - padding-bottom: 180px; -} - -.pb-185 { - padding-bottom: 185px; -} - -.pb-190 { - padding-bottom: 190px; -} - -.pb-195 { - padding-bottom: 195px; -} - -.pb-200 { - padding-bottom: 200px; -} - -/*-- Padding Left --*/ -.pl-5 { - padding-left: 5px; -} - -.pl-10 { - padding-left: 10px; -} - -.pl-15 { - padding-left: 15px; -} - -.pl-20 { - padding-left: 20px; -} - -.pl-25 { - padding-left: 25px; -} - -.pl-30 { - padding-left: 30px; -} - -.pl-35 { - padding-left: 35px; -} - -.pl-40 { - padding-left: 40px; -} - -.pl-45 { - padding-left: 45px; -} - -.pl-50 { - padding-left: 50px; -} - -.pl-55 { - padding-left: 55px; -} - -.pl-60 { - padding-left: 60px; -} - -.pl-65 { - padding-left: 65px; -} - -.pl-70 { - padding-left: 70px; -} - -.pl-75 { - padding-left: 75px; -} - -.pl-80 { - padding-left: 80px; -} - -.pl-85 { - padding-left: 85px; -} - -.pl-90 { - padding-left: 90px; -} - -.pl-95 { - padding-left: 95px; -} - -.pl-100 { - padding-left: 100px; -} - -.pl-105 { - padding-left: 105px; -} - -.pl-110 { - padding-left: 110px; -} - -.pl-115 { - padding-left: 115px; -} - -.pl-120 { - padding-left: 120px; -} - -.pl-125 { - padding-left: 125px; -} - -.pl-130 { - padding-left: 130px; -} - -.pl-135 { - padding-left: 135px; -} - -.pl-140 { - padding-left: 140px; -} - -.pl-145 { - padding-left: 145px; -} - -.pl-150 { - padding-left: 150px; -} - -.pl-155 { - padding-left: 155px; -} - -.pl-160 { - padding-left: 160px; -} - -.pl-165 { - padding-left: 165px; -} - -.pl-170 { - padding-left: 170px; -} - -.pl-175 { - padding-left: 175px; -} - -.pl-180 { - padding-left: 180px; -} - -.pl-185 { - padding-left: 185px; -} - -.pl-190 { - padding-left: 190px; -} - -.pl-195 { - padding-left: 195px; -} - -.pl-200 { - padding-left: 200px; -} - -/*-- Padding Right --*/ -.pr-5 { - padding-right: 5px; -} - -.pr-10 { - padding-right: 10px; -} - -.pr-15 { - padding-right: 15px; -} - -.pr-20 { - padding-right: 20px; -} - -.pr-25 { - padding-right: 25px; -} - -.pr-30 { - padding-right: 30px; -} - -.pr-35 { - padding-right: 35px; -} - -.pr-40 { - padding-right: 40px; -} - -.pr-45 { - padding-right: 45px; -} - -.pr-50 { - padding-right: 50px; -} - -.pr-55 { - padding-right: 55px; -} - -.pr-60 { - padding-right: 60px; -} - -.pr-65 { - padding-right: 65px; -} - -.pr-70 { - padding-right: 70px; -} - -.pr-75 { - padding-right: 75px; -} - -.pr-80 { - padding-right: 80px; -} - -.pr-85 { - padding-right: 85px; -} - -.pr-90 { - padding-right: 90px; -} - -.pr-95 { - padding-right: 95px; -} - -.pr-100 { - padding-right: 100px; -} - -.pr-105 { - padding-right: 105px; -} - -.pr-110 { - padding-right: 110px; -} - -.pr-115 { - padding-right: 115px; -} - -.pr-120 { - padding-right: 120px; -} - -.pr-125 { - padding-right: 125px; -} - -.pr-130 { - padding-right: 130px; -} - -.pr-135 { - padding-right: 135px; -} - -.pr-140 { - padding-right: 140px; -} - -.pr-145 { - padding-right: 145px; -} - -.pr-150 { - padding-right: 150px; -} - -.pr-155 { - padding-right: 155px; -} - -.pr-160 { - padding-right: 160px; -} - -.pr-165 { - padding-right: 165px; -} - -.pr-170 { - padding-right: 170px; -} - -.pr-175 { - padding-right: 175px; -} - -.pr-180 { - padding-right: 180px; -} - -.pr-185 { - padding-right: 185px; -} - -.pr-190 { - padding-right: 190px; -} - -.pr-195 { - padding-right: 195px; -} - -.pr-200 { - padding-right: 200px; -} - -/* typography css start */ -h1, h2, h3, h4, h5, h6 { - font-weight: 700; - color: #242B58; - margin: 0; - line-height: 1.4; - font-family: "Josefin Sans", sans-serif; -} - -h2 { - font-size: 32px; -} - -h3 { - font-size: 22px; -} - -h4 { - font-size: 20px; -} - -h5 { - font-size: 18px; -} - -h6 { - font-size: 16px; -} - -/* typography css end */ -/*-- - - Overlay -------------------------------------------*/ -[data-overlay] { - position: relative; - background-size: cover; - background-repeat: no-repeat; - background-position: center center; -} -[data-overlay]::before { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - content: ""; - width: 100%; - height: 100%; -} - -/*-- Overlay Color --*/ -[data-overlay=light]::before { - background-color: #ffffff; -} - -[data-overlay=dark]::before { - background-color: #242B58; -} - -/*-- Overlay Opacity --*/ -[data-opacity="1"]::before { - opacity: 0.1; -} - -[data-opacity="2"]::before { - opacity: 0.2; -} - -[data-opacity="3"]::before { - opacity: 0.3; -} - -[data-opacity="4"]::before { - opacity: 0.4; -} - -[data-opacity="5"]::before { - opacity: 0.5; -} - -[data-opacity="6"]::before { - opacity: 0.6; -} - -[data-opacity="7"]::before { - opacity: 0.7; -} - -[data-opacity="8"]::before { - opacity: 0.8; -} - -[data-opacity="9"]::before { - opacity: 0.9; -} - -/* Heder css start*/ -.site-header { - margin-top: 15px; - padding: 25px 0px; - position: absolute; - width: 100%; - z-index: 2; -} - -/* Navigation css */ -.menu-area { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - justify-content: flex-end; - align-items: center; -} -.menu-area .site-btn { - margin-left: 40px; -} -@media (max-width: 991px) { - .menu-area .site-btn { - margin-left: 0px; - margin-right: 80px; - } -} -@media (max-width: 767px) { - .menu-area .site-btn { - margin-right: 70px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .menu-area .site-btn { - margin-right: 80px; - } -} - -.main-menu ul { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - justify-content: flex-end; -} -.main-menu ul li a { - color: #ffffff; - text-transform: uppercase; - padding: 9px 20px; - display: block; - font-family: "Josefin Sans", sans-serif; -} -.main-menu ul li a:hover { - color: #6B84FF; -} - -.mean-container a.meanmenu-reveal { - margin-top: -48px; - width: 48px; - height: 48px; - color: #6B84FF; - border-color: #6B84FF; -} -.mean-container a.meanmenu-reveal span { - background: #6B84FF; -} - -/* form css start */ -label { - font-size: fsize("14px"); -} - -input:not([type=radio]), -textarea { - padding: 10px 20px; - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - -o-border-radius: 5px; - background-color: transparent; - font-size: fsize("14px") !important; -} -input:not([type=radio])::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio])::-moz-placeholder, -textarea::-moz-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio]):-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio]):-moz-placeholder, -textarea:-moz-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} - -select { - padding: 8px 10px; - cursor: pointer; - color: #242B58; - background-color: transparent; - border-radius: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; -} - -textarea { - min-height: 100px; - width: 100%; -} -textarea.resize--none { - resize: none; -} - -input[type=radio], -input[type=range], -input[type=checkbox] { - height: auto !important; - padding: 0; -} - -input.form-control { - height: 38px; -} -input.form-control-xl { - height: calc(2rem + 1rem + 2px); - font-size: fsize("18px") !important; -} -input.form-control-lg { - height: calc(1.8rem + 1rem + 2px); - font-size: fsize("16px") !important; -} -input.form-control-sm { - height: calc(1rem + 1rem + 2px); - font-size: fsize("13px") !important; -} - -.label--text { - font-size: fsize("14px"); - margin-bottom: 0.5rem; -} - -.select2-container { - width: 100% !important; -} -.select2-container .select2-selection--single { - height: 34px; -} - -.select2-container--default .select2-selection--single .select2-selection__rendered { - line-height: 34px; -} - -.select2-container--default .select2-selection--single .select2-selection__arrow { - height: 34px; -} - -/* form css end */ -/* hero area css*/ -.hero-area { - position: relative; - overflow: hidden; -} -.hero-area .shape { - position: absolute; -} -.hero-area .shape__left-top { - left: -80px; - top: -130px; -} -@media (max-width: 1650px) { - .hero-area .shape__left-top { - left: -145px; - top: -190px; - } -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .hero-area .shape__left-top { - left: -35px; - top: -40px; - width: 125px; - } -} -@media (max-width: 1199px) { - .hero-area .shape__left-top { - left: -60px; - top: -50px; - width: 125px; - } -} -.hero-area .shape__left-bottom { - bottom: 0; - left: 220px; -} -@media (max-width: 1650px) { - .hero-area .shape__left-bottom { - left: 100px; - } -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .hero-area .shape__left-bottom { - left: 50px; - width: 135px; - } -} -@media (max-width: 1199px) { - .hero-area .shape__left-bottom { - left: 10px; - width: 100px; - } -} -@media (max-width: 991px) { - .hero-area .shape__left-bottom { - bottom: 80px; - } -} -.hero-area .shape__right-bottom { - right: 400px; - bottom: 0px; -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .hero-area .shape__right-bottom { - right: 265px; - bottom: 0px; - width: 250px; - } -} -@media (max-width: 1199px) { - .hero-area .shape__right-bottom { - right: 150px; - bottom: 0px; - width: 200px; - } -} -@media (max-width: 991px) { - .hero-area .shape__right-bottom { - display: none; - } -} -.hero-area .hero-avater { - margin-top: 330px; -} -@media (max-width: 1199px) { - .hero-area .hero-avater { - margin-top: 300px; - } -} -@media (max-width: 991px) { - .hero-area .hero-avater { - display: none; - } -} -.hero-area .hero-avater img { - max-width: inherit; - -webkit-transform: translateX(-145px); - -ms-transform: translateX(-145px); - transform: translateX(-145px); -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .hero-area .hero-avater img { - max-width: 480px; - transform: translateX(-120px); - } -} -@media (max-width: 1300px) { - .hero-area .hero-avater img { - transform: translateX(-175px); - } -} -@media (max-width: 1199px) { - .hero-area .hero-avater img { - transform: translateX(-245px); - max-width: 470px; - } -} -.hero-area .social-links { - flex-direction: column; - position: absolute; - right: 40px; - top: 50%; - -webkit-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} -@media (max-width: 767px) { - .hero-area .social-links { - right: 20px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .hero-area .social-links { - right: 40px; - } -} -.hero-area .social-links a { - margin-right: 0px; - position: relative; -} -.hero-area .social-links a::after { - position: absolute; - right: -60px; - width: 60px; - height: 2px; - content: ""; - border: 2px dashed #6B84FF; - z-index: -1; -} -@media (max-width: 767px) { - .hero-area .social-links a::after { - right: -30px; - width: 30px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .hero-area .social-links a::after { - right: -60px; - width: 60px; - } -} -.hero-area .social-links a:not(:last-child) { - margin-bottom: 15px; -} - -.hero-content { - position: relative; - margin-top: 30px; - margin-bottom: 25px; -} -.hero-content .shape { - right: 0; - top: -45px; -} -.hero-content h4 { - position: relative; - font-size: 24px; - font-weight: 600; - color: #12152f; - padding-left: 65px; - line-height: 29px; -} -@media (max-width: 1199px) { - .hero-content h4 { - font-size: 18px; - line-height: 23px; - } -} -@media (max-width: 767px) { - .hero-content h4 { - font-size: 16px; - line-height: 21px; - } -} -.hero-content h4::after { - width: 50px; - height: 2px; - background: #6B84FF; - left: 0px; - top: 50%; - -webkit-transition: translateY(-50%); - -o-transition: translateY(-50%); - transition: translateY(-50%); - content: ""; - position: absolute; -} -.hero-content h1 { - margin-top: 20px; - font-size: 82px; - line-height: 99px; - font-weight: 700; - color: #ffffff; - margin-bottom: 30px; - position: relative; - z-index: 2; -} -@media (max-width: 1650px) { - .hero-content h1 { - font-size: 70px; - line-height: 87px; - } -} -@media (max-width: 1199px) { - .hero-content h1 { - font-size: 45px; - line-height: 62px; - } -} -@media (max-width: 767px) { - .hero-content h1 { - font-size: 30px; - line-height: 47px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .hero-content h1 { - font-size: 45px; - line-height: 62px; - } -} -.hero-content h1 span { - font-weight: 300; - display: block; - color: #ffffff; -} -.hero-content p { - color: #B0B0B2; - line-height: 24px; -} -.hero-content .hero-btns { - margin-top: 55px; -} - -.social-links { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; -} -.social-links a { - width: 35px; - height: 35px; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - justify-content: center; - align-items: center; - color: #242B58; - font-size: 14px; - background: #ffffff; - border-radius: 50%; -} -.social-links a:not(:last-child) { - margin-right: 15px; -} -@media (max-width: 1650px) { - .social-links a:not(:last-child) { - margin-right: 0px; - } -} -.social-links a:hover { - color: #ffffff; - background: #6B84FF; -} - -/* About css */ -.about-area { - position: relative; - padding-top: 40px; -} - -.about-content h6 { - color: #12152f; - font-size: 14px; - font-weight: 400; - line-height: 24px; - margin-bottom: 10px; -} -.about-content h2 { - color: #12152f; - font-size: 28px; -} -.about-content p { - color: #12152f; - line-height: 24px; -} - -/* skill cass */ -.skill-wrap { - overflow: hidden; -} - -.skill__box h6 { - font-size: 14px; - color: #E2E2E3; - font-weight: 400; - margin-bottom: 15px; -} -.skill__box .progress { - height: 10px; - border-radius: 5px; - background: #1B2044; - overflow: visible; -} -.skill__box .progress .progress-bar { - background: #6B84FF; - border-radius: 5px; -} -.skill__box .progress .progress-bar span { - color: #E2E2E3; - margin-top: -60px; - text-align: right; - z-index: 2; -} - -/* service css*/ -.service-area { - position: relative; -} -@media (max-width: 991px) { - .service-area .text-right { - text-align: left !important; - } -} -@media (max-width: 991px) { - .service-area .text-right .inline-btn { - -webkit-transform: translateY(-60px); - -ms-transform: translateY(-60px); - transform: translateY(-60px); - } -} -.service-area .shape { - position: absolute; -} -.service-area .shape__right-bottom { - right: -8%; - bottom: -15%; - width: 250px; -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .service-area .shape__right-bottom { - right: -2%; - bottom: -12%; - width: 200px; - } -} -@media (max-width: 1260px) { - .service-area .shape__right-bottom { - right: 0%; - } -} -@media (max-width: 1199px) { - .service-area .shape__right-bottom { - width: 150px; - bottom: -5%; - } -} -@media (max-width: 991px) { - .service-area .shape__right-bottom { - width: 150px; - bottom: -3%; - } -} - -.service-shape { - position: absolute; - right: -115px; - top: -180px; -} -@media (max-width: 1650px) { - .service-shape { - right: -40px; - } -} -@media (max-width: 1300px) { - .service-shape { - right: -15px; - width: 160px; - } -} - -.service-item { - background: #12152F; - padding: 50px 30px 70px 30px; - border-radius: 10px; - backface-visibility: hidden; - z-index: 2; - position: relative; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -.service-item__icon { - display: inline-block; -} -.service-item h2 { - font-size: 28px; - color: #6B84FF; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -.service-item p { - color: #B0B0B2; -} -.service-item:hover { - -webkit-transform: translateY(-20px); - -ms-transform: translateY(-20px); - transform: translateY(-20px); - background: #242B58; -} -.service-item:hover h2 { - color: #ffffff; -} - -/* work css */ -.service-shape__left { - right: auto; - left: -20px; -} - -.work-area { - position: relative; -} -.work-area .shape { - position: absolute; -} -.work-area .shape__right-bottom { - right: -8%; - bottom: -5%; - width: 200px; -} -@media only screen and (min-width: 1200px) and (max-width: 1500px) { - .work-area .shape__right-bottom { - right: -2%; - bottom: -12%; - width: 200px; - } -} -@media (max-width: 1260px) { - .work-area .shape__right-bottom { - right: 0%; - } -} -@media (max-width: 1199px) { - .work-area .shape__right-bottom { - width: 150px; - bottom: -5%; - } -} -@media (max-width: 991px) { - .work-area .shape__right-bottom { - width: 150px; - bottom: -1%; - } -} - -.work-top { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; -} -@media (max-width: 991px) { - .work-top { - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - } -} -@media (max-width: 767px) { - .work-top { - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - margin-top: -20px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .work-top { - margin-top: 0px; - } -} -.work-top ul { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; -} -.work-top ul li { - padding: 7px 20px; - display: block; - border-radius: 5px; - color: #ffffff; - background: #242B58; - font-size: 14px; - font-family: "Josefin Sans", sans-serif; - cursor: pointer; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -@media (max-width: 767px) { - .work-top ul li { - margin-top: 20px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .work-top ul li { - margin-top: 0px; - } -} -.work-top ul li:not(:last-child) { - margin-right: 30px; -} -.work-top ul li:hover { - color: #6B84FF; -} -.work-top ul .active { - color: #6B84FF; -} -@media (max-width: 991px) { - .work-top .inline-btn { - margin-top: 50px; - } -} - -.work-item .thumb { - overflow: hidden; - border-radius: 10px; -} -.work-item .thumb img { - width: 100%; - border-radius: 10px; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -.work-item .content h3 { - color: #ffffff; - font-size: 24px; - margin-bottom: 0px; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -.work-item .content span { - position: relative; - display: inline-block; - padding-left: 40px; - color: #B0B0B2; - font-size: 14px; - font-family: "Josefin Sans", sans-serif; -} -.work-item .content span::after { - background: #ffffff; - position: absolute; - left: 0; - -webkit-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); - top: 50%; - width: 30px; - height: 2px; - content: ""; -} -.work-item:hover .thumb img { - -webkit-transform: scale(1.1); - -ms-transform: scale(1.1); - transform: scale(1.1); -} -.work-item:hover h3 { - color: #6B84FF; -} - -/* testimonial css */ -.testimonial { - background: #12152F; - padding: 50px 65px 50px 65px; - border-radius: 10px; - position: relative; - z-index: inherit; -} -@media (max-width: 1300px) { - .testimonial { - margin-left: -25px; - } -} -@media (max-width: 1199px) { - .testimonial { - margin-left: 0px; - } -} -@media (max-width: 767px) { - .testimonial { - padding: 30px 20px 30px 20px; - width: calc(100% - 30px); - margin-left: 15px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .testimonial { - padding: 50px 65px 50px 65px; - width: calc(100% - 00px); - margin-left: 0px; - } -} -.testimonial::after { - position: absolute; - right: -30px; - bottom: -30px; - content: ""; - border: 1px dashed #6B84FF; - width: 100%; - height: 100%; - z-index: -1; - border-radius: 10px; -} -@media (max-width: 767px) { - .testimonial::after { - right: -10px; - bottom: -10px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .testimonial::after { - right: -30px; - bottom: -30px; - } -} -.testimonial::before { - bottom: 80px; - left: -15px; - border-top: 15px solid transparent; - border-right: 20px solid #12152F; - border-bottom: 15px solid transparent; - content: ""; - position: absolute; -} -.testimonial__item { - text-align: right; -} -.testimonial__item p { - text-align: left; - color: #B0B0B2; -} -.testimonial__bottom { - margin-top: 50px; - display: inline-block; - text-align: left; -} -.testimonial__bottom h4 { - position: relative; - font-size: 20px; - color: #ffffff; - z-index: 1; - margin-bottom: 0px; - line-height: 24px; -} -.testimonial__bottom h4::after { - position: absolute; - content: ""; - width: 95px; - height: 20px; - left: -30px; - background: #242b58; - z-index: -1; - top: -5px; -} -.testimonial__bottom span { - font-size: 12px; - font-family: "Open Sans", sans-serif; - font-style: italic; - color: #6B84FF; - text-transform: capitalize; - margin-bottom: -5px; - display: block; -} - -.testimonial-area { - position: relative; - overflow: hidden; -} -@media (max-width: 1199px) { - .testimonial-area { - padding-bottom: 40px; - } -} - -@media (max-width: 1199px) { - .testimonial-shape { - margin-bottom: 80px; - text-align: center; - } -} - -/* brand css */ -@media (max-width: 1199px) { - .brand-area { - margin-top: 180px; - } -} -.brand-area .shape { - position: absolute; - top: -160px; - left: 30px; -} -@media (max-width: 767px) { - .brand-area .shape { - width: 200px; - } -} - -.brand__item { - background: #242B58; - min-height: 150px; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - align-items: center; - justify-content: center; - border-radius: 10px; -} -.brand__item img { - width: auto !important; -} - -/* blog css */ -.blog-area { - position: relative; -} -.blog-area__single-page .side-shape { - right: 0; - bottom: -50px; - position: absolute; - left: auto; - top: auto; -} -@media (max-width: 767px) { - .blog-area__single-page .side-shape { - display: none; - } -} - -.blog-item { - background: #12152F; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; - backface-visibility: hidden; - border-radius: 10px; -} -.blog-item .thumb { - border-radius: 10px 10px 20px 20px; - overflow: hidden; -} -.blog-item .thumb img { - border-radius: 10px 10px 20px 20px; - width: 100%; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -.blog-item__meta { - margin-top: 30px; - padding-left: 15px; - padding-right: 15px; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - justify-content: space-between; - align-items: center; -} -.blog-item__meta span { - font-size: 12px; -} -.blog-item__meta .author { - color: #ffffff; -} -.blog-item__meta .date { - color: #6B84FF; - position: relative; -} -.blog-item__meta .date::after { - position: absolute; - width: 30px; - height: 2px; - left: -40px; - background: #ffffff; - content: ""; - top: 50%; - -webkit-transform: translateY(-50%); - -ms-transform: translateY(-50%); - transform: translateY(-50%); -} -.blog-item__meta--2 { - justify-content: center; - margin-top: 35px; -} -.blog-item__meta--2 .author { - padding-right: 90px; -} -@media (max-width: 767px) { - .blog-item__meta--2 .author { - padding-right: 55px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__meta--2 .author { - padding-right: 80px; - } -} -.blog-item__content { - padding-left: 15px; - padding-right: 15px; - text-align: center; - padding-bottom: 30px; -} -.blog-item__content p { - color: #B0B0B2; - font-family: "Josefin Sans", sans-serif; -} -.blog-item__content .read-more { - font-family: "Josefin Sans", sans-serif; - font-size: 14px; - color: #6B84FF; - display: inline-block; - margin-top: 20px; -} -.blog-item__content--2 { - padding-left: 60px; - padding-right: 60px; - padding-bottom: 35px; -} -@media (max-width: 767px) { - .blog-item__content--2 { - padding-left: 30px; - padding-right: 30px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__content--2 { - padding-left: 45px; - padding-right: 45px; - } -} -.blog-item__content--single-page { - padding-left: 70px; - padding-right: 70px; - padding-bottom: 0px; -} -@media (max-width: 767px) { - .blog-item__content--single-page { - padding-left: 15px; - padding-right: 15px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__content--single-page { - padding-left: 50px; - padding-right: 50px; - } -} -.blog-item__content--single-page p { - font-size: 14px; -} -.blog-item__content--single-page blockquote { - position: relative; - font-size: 15px; - font-family: "Open Sans", sans-serif; - line-height: 30px; - font-weight: 500; - font-style: italic; - color: #ffffff; - margin-bottom: 0px; -} -.blog-item__content--single-page blockquote::after, .blog-item__content--single-page blockquote::before { - position: absolute; - left: -35px; - top: -5px; - font-family: "Font Awesome 5 Pro"; - content: ""; - font-style: normal; - color: #12152F; - font-size: 80px; - z-index: -1; - font-weight: 700; -} -@media (max-width: 767px) { - .blog-item__content--single-page blockquote::after, .blog-item__content--single-page blockquote::before { - left: -15px; - font-size: 45px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__content--single-page blockquote::after, .blog-item__content--single-page blockquote::before { - left: -25px; - font-size: 65px; - } -} -.blog-item__content--single-page blockquote::before { - left: auto; - top: auto; - right: -35px; - bottom: -5px; - content: ""; -} -@media (max-width: 767px) { - .blog-item__content--single-page blockquote::before { - right: -15px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__content--single-page blockquote::before { - right: -25px; - } -} -.blog-item__title { - text-align: center; - margin-top: 25px; - margin-bottom: 10px; -} -.blog-item__title a { - font-size: 20px; - color: #ffffff; - font-weight: 600; - font-family: "Josefin Sans", sans-serif; - line-height: 30px; -} -.blog-item__title--2 { - font-size: 28px; - color: #ffffff; -} -@media (max-width: 767px) { - .blog-item__title--2 { - font-size: 20px; - line-height: 26px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__title--2 { - font-size: 26px; - line-height: 32px; - } -} -.blog-item__title--2 a { - font-size: 28px; - line-height: 34px; -} -@media (max-width: 767px) { - .blog-item__title--2 a { - font-size: 20px; - line-height: 26px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-item__title--2 a { - font-size: 26px; - line-height: 32px; - } -} -.blog-item:hover { - -webkit-transform: translateY(-30px); - -ms-transform: translateY(-30px); - transform: translateY(-30px); -} -.blog-item:hover a { - color: #6B84FF; -} -.blog-item__2 .thumb { - max-height: 525px; -} -.blog-item__single-page { - background: transparent; -} -.blog-item__single-page:hover { - -webkit-transform: translateY(0px); - -ms-transform: translateY(0px); - transform: translateY(0px); -} - -/* contact css */ -.contact-area { - position: relative; - overflow: hidden; -} - -.contact-form, .comment-form { - padding-right: 70px; -} -@media (max-width: 991px) { - .contact-form, .comment-form { - padding-right: 0px; - } -} -.contact-form input, .contact-form textarea, .comment-form input, .comment-form textarea { - padding-left: 0px; - padding-top: 0px; - padding-bottom: 9px; - border: none; - border-bottom: 2px solid #12152F; - width: 100%; - font-family: "Josefin Sans", sans-serif; - color: #707070; - font-size: 14px; - border-radius: 0px; -} -.contact-form input::-webkit-input-placeholder, .contact-form textarea::-webkit-input-placeholder, .comment-form input::-webkit-input-placeholder, .comment-form textarea::-webkit-input-placeholder { - color: #707070 !important; - opacity: 1; -} -.contact-form input::-moz-placeholder, .contact-form textarea::-moz-placeholder, .comment-form input::-moz-placeholder, .comment-form textarea::-moz-placeholder { - color: #707070 !important; - opacity: 1; -} -.contact-form input:-ms-input-placeholder, .contact-form textarea:-ms-input-placeholder, .comment-form input:-ms-input-placeholder, .comment-form textarea:-ms-input-placeholder { - color: #707070 !important; - opacity: 1; -} -.contact-form input:-moz-placeholder, .contact-form textarea:-moz-placeholder, .comment-form input:-moz-placeholder, .comment-form textarea:-moz-placeholder { - color: #707070 !important; - opacity: 1; -} -.contact-form textarea, .comment-form textarea { - min-height: 70px; -} -.contact-form .site-btn, .comment-form .site-btn { - padding: 8.5px 30px; -} -.contact-form__2, .comment-form__2 { - padding-top: 90px; -} - -@media (max-width: 1199px) { - .contact-shape { - margin-bottom: 100px; - text-align: center; - } -} - -/* breadcrumb css */ -.breadcrumb-area { - background: #12152F; - padding-top: 220px; - position: relative; - padding-bottom: 108px; -} -.breadcrumb-area .shape { - position: absolute; -} -.breadcrumb-area .shape__left-top { - left: 70px; - z-index: -1; - width: 300px; - bottom: -180px; -} -@media (max-width: 1199px) { - .breadcrumb-area .shape__left-top { - left: 20px; - z-index: -1; - width: 150px; - bottom: -80px; - } -} -@media (max-width: 767px) { - .breadcrumb-area .shape__left-top { - left: 10px; - z-index: -1; - width: 80px; - bottom: -50px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .breadcrumb-area .shape__left-top { - left: 20px; - z-index: -1; - width: 150px; - bottom: -80px; - } -} -.breadcrumb-area .shape__right-bottom { - right: 95px; - bottom: -98px; -} -@media (max-width: 1199px) { - .breadcrumb-area .shape__right-bottom { - right: 20px; - bottom: -55px; - width: 300px; - } -} -@media (max-width: 767px) { - .breadcrumb-area .shape__right-bottom { - right: 10px; - bottom: -40px; - width: 170px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .breadcrumb-area .shape__right-bottom { - right: 20px; - bottom: -55px; - width: 300px; - } -} - -.breadcrumb-nav { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - flex-wrap: wrap; - justify-content: center; - align-items: center; -} -.breadcrumb-nav li { - font-size: 60px; - font-family: "Josefin Sans", sans-serif; - font-weight: 700; - color: #ffffff; - line-height: 72px; -} -@media (max-width: 767px) { - .breadcrumb-nav li { - font-size: 26px; - line-height: 38px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .breadcrumb-nav li { - font-size: 36px; - line-height: 48px; - } -} -.breadcrumb-nav li:not(:last-child) { - margin-right: 25px; -} -@media (max-width: 767px) { - .breadcrumb-nav li:not(:last-child) { - margin-right: 15px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .breadcrumb-nav li:not(:last-child) { - margin-right: 20px; - } -} -.breadcrumb-nav li a { - font-family: "Josefin Sans", sans-serif; - color: #242B58; -} - -/* cta css */ -.cta-area { - position: relative; -} -.cta-area .shape { - position: absolute; - top: -160px; - left: 30px; -} -@media (max-width: 767px) { - .cta-area .shape { - width: 200px; - } -} - -.cta-wrap { - background: #12152F; - padding: 50px; - padding-bottom: 55px; - border-radius: 10px; -} -@media (max-width: 767px) { - .cta-wrap { - padding: 20px; - padding-bottom: 25px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .cta-wrap { - padding: 50px; - padding-bottom: 55px; - } -} -.cta-wrap h2 { - color: #ffffff; - font-size: 42px; - line-height: 54px; - padding-right: 150px; -} -@media (max-width: 991px) { - .cta-wrap h2 { - padding-right: 50px; - } -} -@media (max-width: 767px) { - .cta-wrap h2 { - padding-right: 0px; - font-size: 26px; - line-height: 38px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .cta-wrap h2 { - padding-right: 0px; - font-size: 36px; - line-height: 48px; - } -} -.cta-wrap p { - color: #B0B0B2; -} -.cta-wrap .inline-btn { - margin-right: 100px; -} -@media (max-width: 1199px) { - .cta-wrap .text-right { - text-align: left !important; - margin-top: 30px; - } -} - -/* contact page css */ -.contact-info-head { - padding-right: 70px; -} -@media (max-width: 991px) { - .contact-info-head { - padding-right: 0px; - } -} -.contact-info-head span { - font-size: 14px; - font-family: "Josefin Sans", sans-serif; - color: #ffffff; - background: #6B84FF; - display: inline-block; - padding: 3px 21px; - margin-bottom: 20px; -} -.contact-info-head h2 { - font-size: 42px; - line-height: 51px; - color: #ffffff; - text-transform: uppercase; - margin-bottom: 20px; -} -@media (max-width: 767px) { - .contact-info-head h2 { - font-size: 32px; - line-height: 41px; - } -} -.contact-info-head p { - font-size: 14px; - font-family: "Josefin Sans", sans-serif; - color: #B0B0B2; -} - -.contact-info__item h5 { - color: #ffffff; - font-size: 20px; - margin-bottom: 10px; -} -.contact-info__item ul li { - color: #B0B0B2; - font-size: 14px; - font-family: "Josefin Sans", sans-serif; -} -.contact-info__item ul li a { - display: block; - color: #B0B0B2; -} - -/* blog page css */ -.blog-wrap { - padding: 0px 25px; -} -.blog-wrap .blog-item::after { - height: 1px; - width: calc(100% - 200px); - position: absolute; - left: 50%; - -webkit-transform: translateX(-50%); - -ms-transform: translateX(-50%); - transform: translateX(-50%); - content: ""; - background: #1B2044; - bottom: -60px; -} -@media (max-width: 767px) { - .blog-wrap .blog-item::after { - width: calc(100% - 100px); - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-wrap .blog-item::after { - width: calc(100% - 150px); - } -} -.blog-wrap .col-xl-12:last-child .blog-item::after { - display: none; -} - -.blog-nav { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - flex-wrap: wrap; - justify-content: center; - align-items: center; -} -.blog-nav li { - font-family: "Josefin Sans", sans-serif; - font-size: 28px; - line-height: 34px; -} -@media (max-width: 767px) { - .blog-nav li { - font-size: 24px; - line-height: 30px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .blog-nav li { - font-size: 28px; - line-height: 34px; - } -} -.blog-nav li:not(:last-child) { - margin-right: 15px; -} -.blog-nav li a { - color: #1B2044; - font-weight: 700; -} -.blog-nav li a:hover { - color: #6B84FF; -} -.blog-nav li .active { - color: #6B84FF; -} -.blog-nav li i { - -webkit-transform: translateY(2px); - -ms-transform: translateY(2px); - transform: translateY(2px); -} - -/* single blog page css */ -.author-box { - background: #12152F; - padding: 35px 85px; - border-radius: 10px; - border: 2px solid #1B2044; -} -@media (max-width: 767px) { - .author-box { - padding: 35px 10px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .author-box { - padding: 35px 45px; - } -} -.author-box .thumb { - margin-bottom: 12px; -} -.author-box .thumb img { - border-radius: 50%; -} -.author-box .name { - font-size: 14px; - color: #ffffff; - font-weight: 700; - margin-bottom: 0px; -} -.author-box .designation { - color: #707070; - font-family: "Josefin Sans", sans-serif; - margin-top: -4px; - margin-bottom: 2px; -} -.author-box p { - color: #B0B0B2; - font-family: "Josefin Sans", sans-serif; -} - -/* comment css */ -.comment-wrap .title { - font-size: 28px; - line-height: 34px; - position: relative; - color: #ffffff; - margin-bottom: 45px; - margin-left: 10px; -} -.comment-wrap .title::after { - height: 50px; - width: 50px; - bottom: -5px; - content: ""; - position: absolute; - left: -10px; - background: #1B2044; - z-index: -1; -} - -.comment-lists li { - position: relative; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - align-items: flex-start; -} -.comment-lists li .thumb { - flex: 0 0 60px; - -ms-flex: 0 0 60px; - max-width: 60px; - border: 2px solid #1B2044; - border-radius: 50%; -} -.comment-lists li .thumb img { - border-radius: 50%; -} -.comment-lists li .content { - padding-left: 23px; - flex: 0 0 90%; - -ms-flex: 0 0 90%; - max-width: 90%; -} -@media (max-width: 767px) { - .comment-lists li .content { - flex: 0 0 100%; - -ms-flex: 0 0 100%; - max-width: 100%; - padding-left: 0px; - padding-top: 25px; - } -} -.comment-lists li .content p { - font-family: "Josefin Sans", sans-serif; - color: #B0B0B2; -} -.comment-lists li .content .view-reply { - color: #B0B0B2; - font-size: 12px; - font-family: "Josefin Sans", sans-serif; -} -.comment-lists li .content .view-reply i { - color: #707070; - padding-right: 5px; -} -.comment-lists li .name-hour { - margin-bottom: 5px; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - align-items: flex-end; -} -.comment-lists li .name-hour .name { - padding-right: 25px; - font-size: 20px; - color: #ffffff; -} -.comment-lists li .name-hour span { - color: #707070; - font-size: 14px; - font-family: "Josefin Sans", sans-serif; -} -.comment-lists li:not(:last-child) { - margin-bottom: 35px; -} -.comment-lists li ul li { - padding-left: 80px; - margin-top: 35px; -} - -.comment-form textarea { - min-height: 100px; -} - -/*Footer css*/ -.site-footer { - border-top: 1px solid #007bff; - padding: 30px 0px; -} -@media (max-width: 767px) { - .site-footer .social-links { - justify-content: center; - } -} -.site-footer .social-links a:not(:last-child) { - margin-right: 15px; -} - -@media (max-width: 767px) { - .copyright-text { - text-align: center; - margin-top: 20px; - } -} -.copyright-text p { - color: #17a2b8; -} - -/* Sydney */ -.body-Sydney { - background: #b4afed29; -} -.section-heading-Sydney h2 { - color: rgba(26, 0, 170, 0.25); -} -.section-heading-Sydney h2::after { - background: #a5aae96a; -} -.hero-content-Sydney h4::after { - background: #6e6bff; -} -.site-footer-Sydney { - border-top: 1px solid #4842c1; -} -.copyright-text-Sydney p { - color: #6217b8; -} - -/* precise */ -.body-precise { - background: #20c9970f; -} -.section-heading-precise h2 { - color: rgba(0, 167, 170, 0.25); -} -.section-heading-precise h2::after { - background: #a5e9ca6a; -} -.hero-content-precise h4::after { - background: #33ccb0; -} -.site-footer-precise { - border-top: 1px solid #42c19d; -} -.copyright-text-precise p { - color: #17b8a0; -} - -/* creative */ -.body-creative { - background: #c5afed29; -} -.section-heading-creative h2 { - color: rgb(97 0 170 / 25%); -} -.section-heading-creative h2::after { - background: #caa5e96a; -} -.hero-content-creative h4::after { - background: #ad6bff; -} -.site-footer-creative { - border-top: 1px solid #6f42c1; -} -.copyright-text-creative p { - color: #9c17b8; -} - -/*# sourceMappingURL=style.css.map */ diff --git a/resources/content/ChatGPT/644b614f82934cd2a7833c108ceb941b.css b/resources/content/ChatGPT/644b614f82934cd2a7833c108ceb941b.css deleted file mode 100644 index d5215a7..0000000 --- a/resources/content/ChatGPT/644b614f82934cd2a7833c108ceb941b.css +++ /dev/null @@ -1,270 +0,0 @@ -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* vietnamese */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Josefin Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/resources/content/ChatGPT/a0020bf6401d4e99884e2be5f5402f24.css b/resources/content/ChatGPT/a0020bf6401d4e99884e2be5f5402f24.css deleted file mode 100644 index 21ce389..0000000 --- a/resources/content/ChatGPT/a0020bf6401d4e99884e2be5f5402f24.css +++ /dev/null @@ -1,630 +0,0 @@ -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwyv9hriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0udc1uaw.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0ddc1uaw.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0vdc1uaw.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0adc1uaw.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0wdc1uaw.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0xdc1uaw.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem6yags126mizpba-ufuk0zdc0.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukxgudhriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukwiunhriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hmiqojjg.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hviqojjg.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hniqojjg.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hoiqojjg.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hkiqojjg.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hliqojjg.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url(../fonts/memnyags126mizpba-ufukw-u9hriqm.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8oxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un_r8ouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwj0bbck.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufuz0bbck.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwz0bbck.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufvp0bbck.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufwp0bbck.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufw50bbck.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(../fonts/mem8yags126mizpba-ufvz0b.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-unirkouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un7rgouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsox-hpoqc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsovuhpoqc.woff2) format('woff2'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxuhpoqc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsouehpoqc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxehpoqc.woff2) format('woff2'); - unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; -} -/* latin-ext */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsoxohpoqc.woff2) format('woff2'); - unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url(../fonts/mem5yags126mizpba-un8rsouuhp.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/resources/content/ChatGPT/index.html b/resources/content/ChatGPT/index.html deleted file mode 100644 index aaae9a7..0000000 --- a/resources/content/ChatGPT/index.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
-
-
-
-

Open AI

- {{if cache.file != ''}} -

{{cache.file}}

- {{/if}} -
-
-
-
-
-
-

{{senderName}}

-
-
-

- -
-
-
-
-
-
-

ChatGPT

-
-
-

- -
-
-
- {{if cache.file != ''}} -
- {{/if}} -
-
- -
-
-
-
-
-
- -
-
-
-
- - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/content/ChatGPT/style.css b/resources/content/ChatGPT/style.css deleted file mode 100644 index f65c498..0000000 --- a/resources/content/ChatGPT/style.css +++ /dev/null @@ -1,429 +0,0 @@ -@charset "UTF-8"; -/*----------------------------------------------------------------------------------- - - Template Name: Aiden - Creative Portfolio HTML5 Template - Template URI: site.com - Description: Aiden - Creative Portfolio HTML5 Template - Author: Rs_Theme - Version: 1.0 - ------------------------------------------------------------------------------------ - - CSS INDEX - =================== - 01. Theme default css - 02. header css - 03. Navigation css - 04. hero css - 05. about css - 06. service css - 07. work CSS - 08. testimonial css - 09. brand css - 10. blog css - 11. contact css - 12. breadcrumb css - 13. cta css - 14. contact-page css - 15. blog-page css - 16. footer css - ------------------------------------------------------------------------------------*/ -/* reset css start */ -@import url("644b614f82934cd2a7833c108ceb941b.css"); -@import url("a0020bf6401d4e99884e2be5f5402f24.css"); -html { - scroll-behavior: smooth; -} - -body { - font-family: "Microsoft YaHei", "微软雅黑", "Josefin Sans", sans-serif; - font-size: 18px; - padding: 0; - margin: 0; - font-weight: 400; - position: relative; - line-height: 26px; - background: #f5feff; -} - -img { - max-width: 100%; - height: auto; -} - -ul, ol { - padding: 0; - margin: 0; - list-style: none; -} - -button { - cursor: pointer; -} - -*:focus { - outline: none; -} - -button { - border: none; - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} - -button:focus { - outline: none; -} - -a { - -webkit-transition: 0.3s; - -o-transition: 0.3s; - transition: 0.3s; -} -a:hover { - text-decoration: none; - color: #242B58; -} - -table { - width: 100%; -} - -p, li, span { - color: #242B58; - margin-bottom: 0; -} - -/* reset css end */ -/* global css start */ - -/* global css end */ - -.height-50 { - height: 40px; - display: block; -} - -.pt-232 { - padding-top: 232px; -} - -.section-heading { - position: relative; -} -.section-heading h2 { - font-size: 42px; - line-height: 51px; - display: inline-block; - position: relative; - padding-right: 15px; - color: rgb(255 115 0 / 25%); - text-transform: uppercase; -} -@media (max-width: 767px) { - .section-heading h2 { - font-size: 36px; - line-height: 45px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .section-heading h2 { - font-size: 42px; - line-height: 51px; - } -} -.section-heading h2::after { - width: 160px; - height: 100px; - right: 0px; - content: ""; - position: absolute; - bottom: 0px; - background: #b8dee094; - z-index: -1; -} - -/*-- - - Margin & Padding ------------------------------------------*/ -/*-- Margin Top --*/ - -/*-- Margin Top --*/ - -.mt-175 { - margin-top: 175px; -} - -/*-- Margin Bottom --*/ - -/*-- Margin Left --*/ - -/*-- Margin Right --*/ - -/*-- Padding Top --*/ - -/*-- Padding Bottom --*/ - -/*-- Padding Left --*/ - -/*-- Padding Right --*/ - -/* typography css start */ -h1, h2, h3, h4, h5, h6 { - font-weight: 700; - color: #242B58; - margin: 0; - line-height: 1.4; - font-family: "Microsoft YaHei", "微软雅黑", "Josefin Sans", sans-serif; -} - -h2 { - font-size: 32px; -} - -h3 { - font-size: 22px; -} - -h4 { - font-size: 20px; -} - -h5 { - font-size: 18px; -} - -h6 { - font-size: 16px; -} - -/* typography css end */ -/*-- - - Overlay -------------------------------------------*/ - -/*-- Overlay Color --*/ - -/*-- Overlay Opacity --*/ - -/* Heder css start*/ -.site-header { - margin-top: 15px; - padding: 25px 0px; - position: absolute; - width: 100%; - z-index: 2; -} - -/* Navigation css */ - -/* form css start */ -label { - font-size: fsize("14px"); -} - -input:not([type=radio]), -textarea { - padding: 10px 20px; - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - -o-border-radius: 5px; - background-color: transparent; - font-size: fsize("14px") !important; -} -input:not([type=radio])::-webkit-input-placeholder, -textarea::-webkit-input-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio])::-moz-placeholder, -textarea::-moz-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio]):-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} -input:not([type=radio]):-moz-placeholder, -textarea:-moz-placeholder { - color: #c9c9c9 !important; - opacity: 1; -} - -select { - padding: 8px 10px; - cursor: pointer; - color: #242B58; - background-color: transparent; - border-radius: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - -o-border-radius: 0; -} - -textarea { - min-height: 100px; - width: 100%; -} - -input[type=radio], -input[type=range], -input[type=checkbox] { - height: auto !important; - padding: 0; -} - -/* form css end */ -/* hero area css*/ - -.hero-content { - position: relative; - margin-top: 30px; - margin-bottom: 25px; -} -.hero-content h4 { - position: relative; - font-size: 24px; - font-weight: 600; - color: #12152f; - padding-left: 65px; - line-height: 29px; -} -@media (max-width: 1199px) { - .hero-content h4 { - font-size: 18px; - line-height: 23px; - } -} -@media (max-width: 767px) { - .hero-content h4 { - font-size: 16px; - line-height: 21px; - } -} -.hero-content h4::after { - width: 50px; - height: 2px; - background: #5beeb1; - left: 0px; - top: 50%; - -webkit-transition: translateY(-50%); - -o-transition: translateY(-50%); - transition: translateY(-50%); - content: ""; - position: absolute; -} -.hero-content h1 { - margin-top: 20px; - font-size: 82px; - line-height: 99px; - font-weight: 700; - color: #ffffff; - margin-bottom: 30px; - position: relative; - z-index: 2; -} -@media (max-width: 1650px) { - .hero-content h1 { - font-size: 70px; - line-height: 87px; - } -} -@media (max-width: 1199px) { - .hero-content h1 { - font-size: 45px; - line-height: 62px; - } -} -@media (max-width: 767px) { - .hero-content h1 { - font-size: 30px; - line-height: 47px; - } -} -@media only screen and (min-width: 576px) and (max-width: 767px) { - .hero-content h1 { - font-size: 45px; - line-height: 62px; - } -} -.hero-content h1 span { - font-weight: 300; - display: block; - color: #ffffff; -} -.hero-content p { - color: #B0B0B2; - line-height: 24px; -} - -/* About css */ -.about-area { - position: relative; - padding-top: 40px; -} - -.about-content h6 { - color: #12152f; - font-size: 14px; - font-weight: 400; - line-height: 24px; - margin-bottom: 10px; -} -.about-content h2 { - color: #12152f; - font-size: 28px; -} -.about-content p { - color: #12152f; - line-height: 24px; -} - -/* skill cass */ - -/* service css*/ - -/* work css */ - -/* testimonial css */ - -/* brand css */ - -/* blog css */ - -/* contact css */ - -/* breadcrumb css */ - -/* cta css */ - -/* contact page css */ - -/* blog page css */ - -/* single blog page css */ - -/* comment css */ - -/*Footer css*/ -.site-footer { - border-top: 1px solid #00ff8c; - padding: 30px 0px; -} - -@media (max-width: 767px) { - .copyright-text { - text-align: center; - margin-top: 20px; - } -} -.copyright-text p { - color: #17b874; -} diff --git a/resources/content/History/index.html b/resources/content/History/index.html deleted file mode 100644 index c8080c2..0000000 --- a/resources/content/History/index.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - 聊天记录 - - -
-
- 聊天记录 {{user.name}}【User】 & {{bot.name}}【Bot】 -
- {{each chat val}} -
-
- -
-
- {{val.prompt}} -
-
-
-
- {{val.response}} -
-
- -
-
- {{/each}} - -
- - - \ No newline at end of file diff --git a/resources/content/static/css/bootstrap.min.css b/resources/content/static/css/bootstrap.min.css deleted file mode 100644 index 357a4f2..0000000 --- a/resources/content/static/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v4.2.1 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.blockquote{margin-bottom:1rem;font-size:1.25rem}.figure{display:inline-block}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529;background:#dee2e6db}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1200px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.col,.col-4,.col-lg-3,.col-lg-5,.col-md-3,.col-md-9,.col-xl-12,.col-xl-5{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}@media (min-width:768px){.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}}@media (min-width:992px){.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}}@media (min-width:1200px){.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.collapse:not(.show){display:none}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.my-auto{margin-top:auto!important}.my-auto{margin-bottom:auto!important}.text-justify{text-align:justify!important}.text-right{text-align:right!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}} \ No newline at end of file diff --git a/resources/content/static/css/font-awesome.min.css b/resources/content/static/css/font-awesome.min.css deleted file mode 100644 index c657a7a..0000000 --- a/resources/content/static/css/font-awesome.min.css +++ /dev/null @@ -1 +0,0 @@ -.fa{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;font-display:auto;src:url(../fonts/fa-brands-400.eot);src:url(../fonts/fa-brands-400.eot) format("embedded-opentype"),url(../fonts/fa-brands-400.woff2) format("woff2"),url(../fonts/fa-brands-400.woff) format("woff"),url(../fonts/fa-brands-400.ttf) format("truetype"),url(../fonts/fa-brands-400.svg#fontawesome) format("svg")}@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:300;font-display:auto;src:url(../fonts/fa-light-300.eot);src:url(../fonts/fa-light-300.eot) format("embedded-opentype"),url(../fonts/fa-light-300.woff2) format("woff2"),url(../fonts/fa-light-300.woff) format("woff"),url(../fonts/fa-light-300.ttf) format("truetype"),url(../fonts/fa-light-300.svg#fontawesome) format("svg")}@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:400;font-display:auto;src:url(../fonts/fa-regular-400.eot);src:url(../fonts/fa-regular-400.eot) format("embedded-opentype"),url(../fonts/fa-regular-400.woff2) format("woff2"),url(../fonts/fa-regular-400.woff) format("woff"),url(../fonts/fa-regular-400.ttf) format("truetype"),url(../fonts/fa-regular-400.svg#fontawesome) format("svg")}@font-face{font-family:"Font Awesome 5 Pro";font-style:normal;font-weight:900;font-display:auto;src:url(../fonts/fa-solid-900.eot);src:url(../fonts/fa-solid-900.eot) format("embedded-opentype"),url(../fonts/fa-solid-900.woff2) format("woff2"),url(../fonts/fa-solid-900.woff) format("woff"),url(../fonts/fa-solid-900.ttf) format("truetype"),url(../fonts/fa-solid-900.svg#fontawesome) format("svg")}.fa{font-family:"Font Awesome 5 Pro";font-weight:900} \ No newline at end of file diff --git a/resources/content/static/css/hljs.css b/resources/content/static/css/hljs.css deleted file mode 100644 index 3071b24..0000000 --- a/resources/content/static/css/hljs.css +++ /dev/null @@ -1,108 +0,0 @@ -/*! - Theme: Default - Description: Original highlight.js style - Author: (c) Ivan Sagalaev - Maintainer: @highlightjs/core-team - Website: https://highlightjs.org/ - License: see project LICENSE - Touched: 2021 -*/ -pre { - white-space: pre-wrap; /* 允许换行 */ - word-wrap: break-word; /* 允许在单词内换行 */ - max-width: 100%; /* 最大宽度为父容器的宽度 */ - overflow: auto; /* 添加滚动条 */ -} - -pre code.hljs { - display: block; - overflow-x: auto; - padding: 1em -} - -code.hljs { - padding: 3px 5px -} - -.hljs { - background: #f3f3f3; - color: #444 -} - -.hljs-comment { - color: #697070 -} - -.hljs-punctuation, -.hljs-tag { - color: #444a -} - -.hljs-tag .hljs-attr, -.hljs-tag .hljs-name { - color: #444 -} - -.hljs-attribute, -.hljs-doctag, -.hljs-keyword, -.hljs-meta .hljs-keyword, -.hljs-name, -.hljs-selector-tag { - font-weight: 700 -} - -.hljs-deletion, -.hljs-number, -.hljs-quote, -.hljs-selector-class, -.hljs-selector-id, -.hljs-string, -.hljs-template-tag, -.hljs-type { - color: #800 -} - -.hljs-section, -.hljs-title { - color: #800; - font-weight: 700 -} - -.hljs-link, -.hljs-operator, -.hljs-regexp, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-symbol, -.hljs-template-variable, -.hljs-variable { - color: #ab5656 -} - -.hljs-literal { - color: #695 -} - -.hljs-addition, -.hljs-built_in, -.hljs-bullet, -.hljs-code { - color: #397300 -} - -.hljs-meta { - color: #1f7199 -} - -.hljs-meta .hljs-string { - color: #38a -} - -.hljs-emphasis { - font-style: italic -} - -.hljs-strong { - font-weight: 700 -} \ No newline at end of file diff --git a/resources/content/static/fonts/fa-brands-400.woff2 b/resources/content/static/fonts/fa-brands-400.woff2 deleted file mode 100644 index 52ba071..0000000 Binary files a/resources/content/static/fonts/fa-brands-400.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/fa-regular-400.woff2 b/resources/content/static/fonts/fa-regular-400.woff2 deleted file mode 100644 index f7e04c6..0000000 Binary files a/resources/content/static/fonts/fa-regular-400.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/fa-solid-900.woff2 b/resources/content/static/fonts/fa-solid-900.woff2 deleted file mode 100644 index aad28a0..0000000 Binary files a/resources/content/static/fonts/fa-solid-900.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/lightcase.woff b/resources/content/static/fonts/lightcase.woff deleted file mode 100644 index 375017c..0000000 Binary files a/resources/content/static/fonts/lightcase.woff and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgouehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgouehpoqc.woff2 deleted file mode 100644 index eff71c0..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgouehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgouuhp.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgouuhp.woff2 deleted file mode 100644 index b251084..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgouuhp.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgovuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgovuhpoqc.woff2 deleted file mode 100644 index 43d5a7c..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgovuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgox-hpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgox-hpoqc.woff2 deleted file mode 100644 index f1ae685..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgox-hpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgoxehpoqc.woff2 deleted file mode 100644 index f1b2ddb..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxohpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgoxohpoqc.woff2 deleted file mode 100644 index c805bc0..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxohpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un7rgoxuhpoqc.woff2 deleted file mode 100644 index 1302cd5..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un7rgoxuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsouehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsouehpoqc.woff2 deleted file mode 100644 index 8a419a1..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsouehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsouuhp.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsouuhp.woff2 deleted file mode 100644 index caa87bf..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsouuhp.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsovuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsovuhpoqc.woff2 deleted file mode 100644 index 9c199fd..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsovuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsox-hpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsox-hpoqc.woff2 deleted file mode 100644 index 4975c2d..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsox-hpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsoxehpoqc.woff2 deleted file mode 100644 index e950aa0..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxohpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsoxohpoqc.woff2 deleted file mode 100644 index 1476f0f..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxohpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un8rsoxuhpoqc.woff2 deleted file mode 100644 index 6ec620f..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un8rsoxuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8ouehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8ouehpoqc.woff2 deleted file mode 100644 index 456c550..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8ouehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8ouuhp.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8ouuhp.woff2 deleted file mode 100644 index f3c046d..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8ouuhp.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8ovuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8ovuhpoqc.woff2 deleted file mode 100644 index d3bbaba..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8ovuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8ox-hpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8ox-hpoqc.woff2 deleted file mode 100644 index 09d388e..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8ox-hpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8oxehpoqc.woff2 deleted file mode 100644 index f799825..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxohpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8oxohpoqc.woff2 deleted file mode 100644 index 1ed5f4f..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxohpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-un_r8oxuhpoqc.woff2 deleted file mode 100644 index 7c251dc..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-un_r8oxuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkouehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkouehpoqc.woff2 deleted file mode 100644 index 59855dc..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkouehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkouuhp.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkouuhp.woff2 deleted file mode 100644 index 301f31d..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkouuhp.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkovuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkovuhpoqc.woff2 deleted file mode 100644 index f8a4ea3..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkovuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkox-hpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkox-hpoqc.woff2 deleted file mode 100644 index 4587c68..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkox-hpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkoxehpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkoxehpoqc.woff2 deleted file mode 100644 index 85ea2fa..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkoxehpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkoxohpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkoxohpoqc.woff2 deleted file mode 100644 index 83b1803..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkoxohpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem5yags126mizpba-unirkoxuhpoqc.woff2 b/resources/content/static/fonts/mem5yags126mizpba-unirkoxuhpoqc.woff2 deleted file mode 100644 index 9a9a5c6..0000000 Binary files a/resources/content/static/fonts/mem5yags126mizpba-unirkoxuhpoqc.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0adc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0adc1uaw.woff2 deleted file mode 100644 index 04139ef..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0adc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0ddc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0ddc1uaw.woff2 deleted file mode 100644 index a7b01fa..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0ddc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0udc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0udc1uaw.woff2 deleted file mode 100644 index b4d9f61..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0udc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0vdc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0vdc1uaw.woff2 deleted file mode 100644 index 3426d9b..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0vdc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0wdc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0wdc1uaw.woff2 deleted file mode 100644 index 8f6c5d4..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0wdc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0xdc1uaw.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0xdc1uaw.woff2 deleted file mode 100644 index ca1b2b1..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0xdc1uaw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem6yags126mizpba-ufuk0zdc0.woff2 b/resources/content/static/fonts/mem6yags126mizpba-ufuk0zdc0.woff2 deleted file mode 100644 index caac24c..0000000 Binary files a/resources/content/static/fonts/mem6yags126mizpba-ufuk0zdc0.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufuz0bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufuz0bbck.woff2 deleted file mode 100644 index 4c5a6f0..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufuz0bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufvp0bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufvp0bbck.woff2 deleted file mode 100644 index 3770a39..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufvp0bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufvz0b.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufvz0b.woff2 deleted file mode 100644 index c813f9c..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufvz0b.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufw50bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufw50bbck.woff2 deleted file mode 100644 index 4e74089..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufw50bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufwj0bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufwj0bbck.woff2 deleted file mode 100644 index 1f2f588..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufwj0bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufwp0bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufwp0bbck.woff2 deleted file mode 100644 index 156e746..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufwp0bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/mem8yags126mizpba-ufwz0bbck.woff2 b/resources/content/static/fonts/mem8yags126mizpba-ufwz0bbck.woff2 deleted file mode 100644 index 1675bab..0000000 Binary files a/resources/content/static/fonts/mem8yags126mizpba-ufwz0bbck.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hkiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hkiqojjg.woff2 deleted file mode 100644 index defb593..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hkiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hliqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hliqojjg.woff2 deleted file mode 100644 index ec3e64b..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hliqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hmiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hmiqojjg.woff2 deleted file mode 100644 index d1afcdd..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hmiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hniqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hniqojjg.woff2 deleted file mode 100644 index 5160843..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hniqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hoiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hoiqojjg.woff2 deleted file mode 100644 index 4762fc4..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hoiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hriqm.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hriqm.woff2 deleted file mode 100644 index f2182ac..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hriqm.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hviqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hviqojjg.woff2 deleted file mode 100644 index 6c277d7..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukw-u9hviqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhkiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhkiqojjg.woff2 deleted file mode 100644 index b0c7541..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhkiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhliqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhliqojjg.woff2 deleted file mode 100644 index 5b3484f..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhliqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhmiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhmiqojjg.woff2 deleted file mode 100644 index 9854e0e..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhmiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhniqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhniqojjg.woff2 deleted file mode 100644 index 8a4187c..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhniqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhoiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhoiqojjg.woff2 deleted file mode 100644 index 45362f1..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhoiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhriqm.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhriqm.woff2 deleted file mode 100644 index 1cd173e..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhriqm.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhviqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwiunhviqojjg.woff2 deleted file mode 100644 index 0307dec..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwiunhviqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hkiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hkiqojjg.woff2 deleted file mode 100644 index a0dbb14..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hkiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hliqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hliqojjg.woff2 deleted file mode 100644 index 8f5da52..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hliqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hmiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hmiqojjg.woff2 deleted file mode 100644 index be29757..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hmiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hniqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hniqojjg.woff2 deleted file mode 100644 index ccbc460..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hniqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hoiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hoiqojjg.woff2 deleted file mode 100644 index e44fa88..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hoiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hriqm.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hriqm.woff2 deleted file mode 100644 index e21a6b0..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hriqm.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hviqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hviqojjg.woff2 deleted file mode 100644 index d91cb0b..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukwyv9hviqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhkiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhkiqojjg.woff2 deleted file mode 100644 index ec44ba0..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhkiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhliqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhliqojjg.woff2 deleted file mode 100644 index ccf4acb..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhliqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhmiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhmiqojjg.woff2 deleted file mode 100644 index 1dd821c..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhmiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhniqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhniqojjg.woff2 deleted file mode 100644 index c2f5b87..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhniqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhoiqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhoiqojjg.woff2 deleted file mode 100644 index 6868066..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhoiqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhriqm.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhriqm.woff2 deleted file mode 100644 index 3c24fb5..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhriqm.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhviqojjg.woff2 b/resources/content/static/fonts/memnyags126mizpba-ufukxgudhviqojjg.woff2 deleted file mode 100644 index 1a1fa02..0000000 Binary files a/resources/content/static/fonts/memnyags126mizpba-ufukxgudhviqojjg.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2 b/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2 deleted file mode 100644 index bd14753..0000000 Binary files a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eua3x4rhw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2 b/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2 deleted file mode 100644 index bfb0f4f..0000000 Binary files a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5euanx4rhw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2 b/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2 deleted file mode 100644 index 73f31fc..0000000 Binary files a/resources/content/static/fonts/qw3azqnved7rkgkxtqiqx5eudxx4.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2 b/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2 deleted file mode 100644 index cd81c3a..0000000 Binary files a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex0xhgciw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2 b/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2 deleted file mode 100644 index c91af5e..0000000 Binary files a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex1xhgciw.woff2 and /dev/null differ diff --git a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2 b/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2 deleted file mode 100644 index 89278e0..0000000 Binary files a/resources/content/static/fonts/qw3ezqnved7rkgkxtqiqx5eucex6xhg.woff2 and /dev/null differ diff --git a/resources/content/static/js/base64.min.js b/resources/content/static/js/base64.min.js deleted file mode 100644 index fe00df0..0000000 --- a/resources/content/static/js/base64.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Minified by jsDelivr using Terser v5.15.1. - * Original file: /npm/js-base64@3.7.5/base64.js - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -!function(t,n){var r,e;"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(r=t.Base64,(e=n()).noConflict=function(){return t.Base64=r,e},t.Meteor&&(Base64=e),t.Base64=e)}("undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this,(function(){"use strict";var t,n="3.7.5",r="function"==typeof atob,e="function"==typeof btoa,o="function"==typeof Buffer,u="function"==typeof TextDecoder?new TextDecoder:void 0,i="function"==typeof TextEncoder?new TextEncoder:void 0,f=Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="),c=(t={},f.forEach((function(n,r){return t[n]=r})),t),a=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,d=String.fromCharCode.bind(String),s="function"==typeof Uint8Array.from?Uint8Array.from.bind(Uint8Array):function(t){return new Uint8Array(Array.prototype.slice.call(t,0))},l=function(t){return t.replace(/=/g,"").replace(/[+\/]/g,(function(t){return"+"==t?"-":"_"}))},h=function(t){return t.replace(/[^A-Za-z0-9\+\/]/g,"")},p=function(t){for(var n,r,e,o,u="",i=t.length%3,c=0;c255||(e=t.charCodeAt(c++))>255||(o=t.charCodeAt(c++))>255)throw new TypeError("invalid character found");u+=f[(n=r<<16|e<<8|o)>>18&63]+f[n>>12&63]+f[n>>6&63]+f[63&n]}return i?u.slice(0,i-3)+"===".substring(i):u},y=e?function(t){return btoa(t)}:o?function(t){return Buffer.from(t,"binary").toString("base64")}:p,A=o?function(t){return Buffer.from(t).toString("base64")}:function(t){for(var n=[],r=0,e=t.length;r>>6)+d(128|63&n):d(224|n>>>12&15)+d(128|n>>>6&63)+d(128|63&n);var n=65536+1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320);return d(240|n>>>18&7)+d(128|n>>>12&63)+d(128|n>>>6&63)+d(128|63&n)},B=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,x=function(t){return t.replace(B,g)},C=o?function(t){return Buffer.from(t,"utf8").toString("base64")}:i?function(t){return A(i.encode(t))}:function(t){return y(x(t))},m=function(t,n){return void 0===n&&(n=!1),n?l(C(t)):C(t)},v=function(t){return m(t,!0)},U=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,F=function(t){switch(t.length){case 4:var n=((7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3))-65536;return d(55296+(n>>>10))+d(56320+(1023&n));case 3:return d((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return d((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},w=function(t){return t.replace(U,F)},S=function(t){if(t=t.replace(/\s+/g,""),!a.test(t))throw new TypeError("malformed base64.");t+="==".slice(2-(3&t.length));for(var n,r,e,o="",u=0;u>16&255):64===e?d(n>>16&255,n>>8&255):d(n>>16&255,n>>8&255,255&n);return o},E=r?function(t){return atob(h(t))}:o?function(t){return Buffer.from(t,"base64").toString("binary")}:S,D=o?function(t){return s(Buffer.from(t,"base64"))}:function(t){return s(E(t).split("").map((function(t){return t.charCodeAt(0)})))},R=function(t){return D(T(t))},z=o?function(t){return Buffer.from(t,"base64").toString("utf8")}:u?function(t){return u.decode(D(t))}:function(t){return w(E(t))},T=function(t){return h(t.replace(/[-_]/g,(function(t){return"-"==t?"+":"/"})))},Z=function(t){return z(T(t))},j=function(t){return{value:t,enumerable:!1,writable:!0,configurable:!0}},I=function(){var t=function(t,n){return Object.defineProperty(String.prototype,t,j(n))};t("fromBase64",(function(){return Z(this)})),t("toBase64",(function(t){return m(this,t)})),t("toBase64URI",(function(){return m(this,!0)})),t("toBase64URL",(function(){return m(this,!0)})),t("toUint8Array",(function(){return R(this)}))},O=function(){var t=function(t,n){return Object.defineProperty(Uint8Array.prototype,t,j(n))};t("toBase64",(function(t){return b(this,t)})),t("toBase64URI",(function(){return b(this,!0)})),t("toBase64URL",(function(){return b(this,!0)}))},P={version:n,VERSION:"3.7.5",atob:E,atobPolyfill:S,btoa:y,btoaPolyfill:p,fromBase64:Z,toBase64:m,encode:m,encodeURI:v,encodeURL:v,utob:x,btou:w,decode:Z,isValid:function(t){if("string"!=typeof t)return!1;var n=t.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(n)||!/[^\s0-9a-zA-Z\-_]/.test(n)},fromUint8Array:b,toUint8Array:R,extendString:I,extendUint8Array:O,extendBuiltins:function(){I(),O()},Base64:{}};return Object.keys(P).forEach((function(t){return P.Base64[t]=P[t]})),P})); -//# sourceMappingURL=/sm/555281732ed54ee1693a772f485329378d8ea5052ffa4370e9c2e9947eb42d22.map \ No newline at end of file diff --git a/resources/content/static/js/highlight.min.js b/resources/content/static/js/highlight.min.js deleted file mode 100644 index 43841d3..0000000 --- a/resources/content/static/js/highlight.min.js +++ /dev/null @@ -1,1202 +0,0 @@ -/*! - Highlight.js v11.7.0 (git: 82688fad18) - (c) 2006-2022 undefined and other contributors - License: BSD-3-Clause - */ - var hljs=function(){"use strict";var e={exports:{}};function n(e){ - return e instanceof Map?e.clear=e.delete=e.set=()=>{ - throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{ - throw Error("set is read-only") - }),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((t=>{var a=e[t] - ;"object"!=typeof a||Object.isFrozen(a)||n(a)})),e} - e.exports=n,e.exports.default=n;class t{constructor(e){ - void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} - ignoreMatch(){this.isMatchIgnored=!0}}function a(e){ - return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") - }function i(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] - ;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t} - const r=e=>!!e.scope||e.sublanguage&&e.language;class s{constructor(e,n){ - this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ - this.buffer+=a(e)}openNode(e){if(!r(e))return;let n="" - ;n=e.sublanguage?"language-"+e.language:((e,{prefix:n})=>{if(e.includes(".")){ - const t=e.split(".") - ;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") - }return`${n}${e}`})(e.scope,{prefix:this.classPrefix}),this.span(n)} - closeNode(e){r(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ - this.buffer+=``}}const o=(e={})=>{const n={children:[]} - ;return Object.assign(n,e),n};class l{constructor(){ - this.rootNode=o(),this.stack=[this.rootNode]}get top(){ - return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ - this.top.children.push(e)}openNode(e){const n=o({scope:e}) - ;this.add(n),this.stack.push(n)}closeNode(){ - if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ - for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} - walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ - return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), - n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ - "string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ - l._collapse(e)})))}}class c extends l{constructor(e){super(),this.options=e} - addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())} - addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root - ;t.sublanguage=!0,t.language=n,this.add(t)}toHTML(){ - return new s(this,this.options).value()}finalize(){return!0}}function d(e){ - return e?"string"==typeof e?e:e.source:null}function g(e){return m("(?=",e,")")} - function u(e){return m("(?:",e,")*")}function b(e){return m("(?:",e,")?")} - function m(...e){return e.map((e=>d(e))).join("")}function p(...e){const n=(e=>{ - const n=e[e.length-1] - ;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} - })(e);return"("+(n.capture?"":"?:")+e.map((e=>d(e))).join("|")+")"} - function _(e){return RegExp(e.toString()+"|").exec("").length-1} - const h=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ - ;function f(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t - ;let a=d(e),i="";for(;a.length>0;){const e=h.exec(a);if(!e){i+=a;break} - i+=a.substring(0,e.index), - a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], - "("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} - const E="[a-zA-Z]\\w*",y="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",v="\\b(0b[01]+)",O={ - begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'", - illegal:"\\n",contains:[O]},x={scope:"string",begin:'"',end:'"',illegal:"\\n", - contains:[O]},M=(e,n,t={})=>{const a=i({scope:"comment",begin:e,end:n, - contains:[]},t);a.contains.push({scope:"doctag", - begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", - end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) - ;const r=p("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) - ;return a.contains.push({begin:m(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),a - },S=M("//","$"),A=M("/\\*","\\*/"),C=M("#","$");var T=Object.freeze({ - __proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:E,UNDERSCORE_IDENT_RE:y, - NUMBER_RE:w,C_NUMBER_RE:N,BINARY_NUMBER_RE:v, - RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", - SHEBANG:(e={})=>{const n=/^#![ ]*\// - ;return e.binary&&(e.begin=m(n,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:n, - end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, - BACKSLASH_ESCAPE:O,APOS_STRING_MODE:k,QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:{ - begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ - },COMMENT:M,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:A,HASH_COMMENT_MODE:C, - NUMBER_MODE:{scope:"number",begin:w,relevance:0},C_NUMBER_MODE:{scope:"number", - begin:N,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:v,relevance:0}, - REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//, - end:/\/[gimuy]*/,illegal:/\n/,contains:[O,{begin:/\[/,end:/\]/,relevance:0, - contains:[O]}]}]},TITLE_MODE:{scope:"title",begin:E,relevance:0}, - UNDERSCORE_TITLE_MODE:{scope:"title",begin:y,relevance:0},METHOD_GUARD:{ - begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ - "on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ - n.data._beginMatch!==e[1]&&n.ignoreMatch()}})});function R(e,n){ - "."===e.input[e.index-1]&&n.ignoreMatch()}function D(e,n){ - void 0!==e.className&&(e.scope=e.className,delete e.className)}function I(e,n){ - n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", - e.__beforeBegin=R,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, - void 0===e.relevance&&(e.relevance=0))}function L(e,n){ - Array.isArray(e.illegal)&&(e.illegal=p(...e.illegal))}function B(e,n){ - if(e.match){ - if(e.begin||e.end)throw Error("begin & end are not supported with match") - ;e.begin=e.match,delete e.match}}function $(e,n){ - void 0===e.relevance&&(e.relevance=1)}const z=(e,n)=>{if(!e.beforeMatch)return - ;if(e.starts)throw Error("beforeMatch cannot be used with starts") - ;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] - })),e.keywords=t.keywords,e.begin=m(t.beforeMatch,g(t.begin)),e.starts={ - relevance:0,contains:[Object.assign(t,{endsParent:!0})] - },e.relevance=0,delete t.beforeMatch - },F=["of","and","for","in","not","or","if","then","parent","list","value"] - ;function U(e,n,t="keyword"){const a=Object.create(null) - ;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ - Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ - n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") - ;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ - return n?Number(n):(e=>F.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ - console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ - P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) - },Z=Error();function G(e,n,{key:t}){let a=0;const i=e[t],r={},s={} - ;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=_(n[e-1]) - ;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ - e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, - delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ - _wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope - }),(e=>{if(Array.isArray(e.begin)){ - if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), - Z - ;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), - Z;G(e,e.begin,{key:"beginScope"}),e.begin=f(e.begin,{joinWith:""})}})(e),(e=>{ - if(Array.isArray(e.end)){ - if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), - Z - ;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), - Z;G(e,e.end,{key:"endScope"}),e.end=f(e.end,{joinWith:""})}})(e)}function Q(e){ - function n(n,t){ - return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) - }class t{constructor(){ - this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} - addRule(e,n){ - n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), - this.matchAt+=_(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) - ;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(f(e,{joinWith:"|" - }),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex - ;const n=this.matcherRe.exec(e);if(!n)return null - ;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] - ;return n.splice(0,t),Object.assign(n,a)}}class a{constructor(){ - this.rules=[],this.multiRegexes=[], - this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ - if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t - ;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), - n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ - return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ - this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ - const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex - ;let t=n.exec(e) - ;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ - const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} - return t&&(this.regexIndex+=t.position+1, - this.regexIndex===this.count&&this.considerAll()),t}} - if(e.compilerExtensions||(e.compilerExtensions=[]), - e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") - ;return e.classNameAliases=i(e.classNameAliases||{}),function t(r,s){const o=r - ;if(r.isCompiled)return o - ;[D,B,W,z].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), - r.__beforeBegin=null,[I,L,$].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null - ;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), - l=r.keywords.$pattern, - delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), - o.keywordPatternRe=n(l,!0), - s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), - r.end&&(o.endRe=n(o.end)), - o.terminatorEnd=d(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), - r.illegal&&(o.illegalRe=n(r.illegal)), - r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>i(e,{ - variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?i(e,{ - starts:e.starts?i(e.starts):null - }):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) - })),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new a - ;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" - }))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" - }),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ - return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ - constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} - const J=a,Y=i,ee=Symbol("nomatch");var ne=(n=>{ - const a=Object.create(null),i=Object.create(null),r=[];let s=!0 - ;const o="Could not find the language '{}', did you forget to load/include a language module?",l={ - disableAutodetect:!0,name:"Plain text",contains:[]};let d={ - ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, - languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", - cssSelector:"pre code",languages:null,__emitter:c};function _(e){ - return d.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" - ;"object"==typeof n?(a=e, - t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), - q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), - i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) - ;const s=r.result?r.result:f(r.language,r.code,t) - ;return s.code=r.code,x("after:highlight",s),s}function f(e,n,i,r){ - const l=Object.create(null);function c(){if(!k.keywords)return void M.addText(S) - ;let e=0;k.keywordPatternRe.lastIndex=0;let n=k.keywordPatternRe.exec(S),t="" - ;for(;n;){t+=S.substring(e,n.index) - ;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,k.keywords[a]);if(r){ - const[e,a]=r - ;if(M.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(A+=a),e.startsWith("_"))t+=n[0];else{ - const t=w.classNameAliases[e]||e;M.addKeyword(n[0],t)}}else t+=n[0] - ;e=k.keywordPatternRe.lastIndex,n=k.keywordPatternRe.exec(S)}var a - ;t+=S.substring(e),M.addText(t)}function g(){null!=k.subLanguage?(()=>{ - if(""===S)return;let e=null;if("string"==typeof k.subLanguage){ - if(!a[k.subLanguage])return void M.addText(S) - ;e=f(k.subLanguage,S,!0,x[k.subLanguage]),x[k.subLanguage]=e._top - }else e=E(S,k.subLanguage.length?k.subLanguage:null) - ;k.relevance>0&&(A+=e.relevance),M.addSublanguage(e._emitter,e.language) - })():c(),S=""}function u(e,n){let t=1;const a=n.length-1;for(;t<=a;){ - if(!e._emit[t]){t++;continue}const a=w.classNameAliases[e[t]]||e[t],i=n[t] - ;a?M.addKeyword(i,a):(S=i,c(),S=""),t++}}function b(e,n){ - return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope), - e.beginScope&&(e.beginScope._wrap?(M.addKeyword(S,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), - S=""):e.beginScope._multi&&(u(e.beginScope,n),S="")),k=Object.create(e,{parent:{ - value:k}}),k}function m(e,n,a){let i=((e,n)=>{const t=e&&e.exec(n) - ;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new t(e) - ;e["on:end"](n,a),a.isMatchIgnored&&(i=!1)}if(i){ - for(;e.endsParent&&e.parent;)e=e.parent;return e}} - if(e.endsWithParent)return m(e.parent,n,a)}function p(e){ - return 0===k.matcher.regexIndex?(S+=e[0],1):(R=!0,0)}function _(e){ - const t=e[0],a=n.substring(e.index),i=m(k,e,a);if(!i)return ee;const r=k - ;k.endScope&&k.endScope._wrap?(g(), - M.addKeyword(t,k.endScope._wrap)):k.endScope&&k.endScope._multi?(g(), - u(k.endScope,e)):r.skip?S+=t:(r.returnEnd||r.excludeEnd||(S+=t), - g(),r.excludeEnd&&(S=t));do{ - k.scope&&M.closeNode(),k.skip||k.subLanguage||(A+=k.relevance),k=k.parent - }while(k!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:t.length} - let h={};function y(a,r){const o=r&&r[0];if(S+=a,null==o)return g(),0 - ;if("begin"===h.type&&"end"===r.type&&h.index===r.index&&""===o){ - if(S+=n.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) - ;throw n.languageName=e,n.badRule=h.rule,n}return 1} - if(h=r,"begin"===r.type)return(e=>{ - const n=e[0],a=e.rule,i=new t(a),r=[a.__beforeBegin,a["on:begin"]] - ;for(const t of r)if(t&&(t(e,i),i.isMatchIgnored))return p(n) - ;return a.skip?S+=n:(a.excludeBegin&&(S+=n), - g(),a.returnBegin||a.excludeBegin||(S=n)),b(a,e),a.returnBegin?0:n.length})(r) - ;if("illegal"===r.type&&!i){ - const e=Error('Illegal lexeme "'+o+'" for mode "'+(k.scope||"")+'"') - ;throw e.mode=k,e}if("end"===r.type){const e=_(r);if(e!==ee)return e} - if("illegal"===r.type&&""===o)return 1 - ;if(T>1e5&&T>3*r.index)throw Error("potential infinite loop, way more iterations than matches") - ;return S+=o,o.length}const w=v(e) - ;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') - ;const N=Q(w);let O="",k=r||N;const x={},M=new d.__emitter(d);(()=>{const e=[] - ;for(let n=k;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) - ;e.forEach((e=>M.openNode(e)))})();let S="",A=0,C=0,T=0,R=!1;try{ - for(k.matcher.considerAll();;){ - T++,R?R=!1:k.matcher.considerAll(),k.matcher.lastIndex=C - ;const e=k.matcher.exec(n);if(!e)break;const t=y(n.substring(C,e.index),e) - ;C=e.index+t} - return y(n.substring(C)),M.closeAllNodes(),M.finalize(),O=M.toHTML(),{ - language:e,value:O,relevance:A,illegal:!1,_emitter:M,_top:k}}catch(t){ - if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n), - illegal:!0,relevance:0,_illegalBy:{message:t.message,index:C, - context:n.slice(C-100,C+100),mode:t.mode,resultSoFar:O},_emitter:M};if(s)return{ - language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:k} - ;throw t}}function E(e,n){n=n||d.languages||Object.keys(a);const t=(e=>{ - const n={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new d.__emitter(d)} - ;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) - ;i.unshift(t);const r=i.sort(((e,n)=>{ - if(e.relevance!==n.relevance)return n.relevance-e.relevance - ;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 - ;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,c=s - ;return c.secondBest=o,c}function y(e){let n=null;const t=(e=>{ - let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" - ;const t=d.languageDetectRe.exec(n);if(t){const n=v(t[1]) - ;return n||(H(o.replace("{}",t[1])), - H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} - return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return - ;if(x("before:highlightElement",{el:e,language:t - }),e.children.length>0&&(d.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), - console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), - console.warn("The element with unescaped HTML:"), - console.warn(e)),d.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) - ;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) - ;e.innerHTML=r.value,((e,n,t)=>{const a=n&&i[n]||t - ;e.classList.add("hljs"),e.classList.add("language-"+a) - })(e,t,r.language),e.result={language:r.language,re:r.relevance, - relevance:r.relevance},r.secondBest&&(e.secondBest={ - language:r.secondBest.language,relevance:r.secondBest.relevance - }),x("after:highlightElement",{el:e,result:r,text:a})}let w=!1;function N(){ - "loading"!==document.readyState?document.querySelectorAll(d.cssSelector).forEach(y):w=!0 - }function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} - function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ - i[e.toLowerCase()]=n}))}function k(e){const n=v(e) - ;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ - e[t]&&e[t](n)}))} - "undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ - w&&N()}),!1),Object.assign(n,{highlight:h,highlightAuto:E,highlightAll:N, - highlightElement:y, - highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), - q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{d=Y(d,e)}, - initHighlighting:()=>{ - N(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, - initHighlightingOnLoad:()=>{ - N(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") - },registerLanguage:(e,t)=>{let i=null;try{i=t(n)}catch(n){ - if(K("Language definition for '{}' could not be registered.".replace("{}",e)), - !s)throw n;K(n),i=l} - i.name||(i.name=e),a[e]=i,i.rawDefinition=t.bind(null,n),i.aliases&&O(i.aliases,{ - languageName:e})},unregisterLanguage:e=>{delete a[e] - ;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, - listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, - autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ - e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ - e["before:highlightBlock"](Object.assign({block:n.el},n)) - }),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ - e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)} - }),n.debugMode=()=>{s=!1},n.safeMode=()=>{s=!0 - },n.versionString="11.7.0",n.regex={concat:m,lookahead:g,either:p,optional:b, - anyNumberOfTimes:u};for(const n in T)"object"==typeof T[n]&&e.exports(T[n]) - ;return Object.assign(n,T),n})({});const te=e=>({IMPORTANT:{scope:"meta", - begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ - scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, - FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, - ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", - contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ - scope:"number", - begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", - relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z][A-Za-z0-9_-]*/} - }),ae=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],ie=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],re=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],se=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],oe=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),le=re.concat(se) - ;var ce="\\.([0-9](_*[0-9])*)",de="[0-9a-fA-F](_*[0-9a-fA-F])*",ge={ - className:"number",variants:[{ - begin:`(\\b([0-9](_*[0-9])*)((${ce})|\\.)?|(${ce}))[eE][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` - },{begin:`\\b([0-9](_*[0-9])*)((${ce})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ - begin:`(${ce})[fFdD]?\\b`},{begin:"\\b([0-9](_*[0-9])*)[fFdD]\\b"},{ - begin:`\\b0[xX]((${de})\\.?|(${de})?\\.(${de}))[pP][+-]?([0-9](_*[0-9])*)[fFdD]?\\b` - },{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${de})[lL]?\\b`},{ - begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], - relevance:0};function ue(e,n,t){return-1===t?"":e.replace(n,(a=>ue(e,n,t-1)))} - const be="[A-Za-z$_][0-9A-Za-z$_]*",me=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],pe=["true","false","null","undefined","NaN","Infinity"],_e=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],he=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],fe=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],Ee=["arguments","this","super","console","window","document","localStorage","module","global"],ye=[].concat(fe,_e,he) - ;function we(e){const n=e.regex,t=be,a={begin:/<[A-Za-z0-9\\._:-]+/, - end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ - const t=e[0].length+e.index,a=e.input[t] - ;if("<"===a||","===a)return void n.ignoreMatch();let i - ;">"===a&&(((e,{after:n})=>{const t="",k={ - match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(O)], - keywords:"async",className:{1:"keyword",3:"title.function"},contains:[_]} - ;return{name:"Javascript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ - PARAMS_CONTAINS:p,CLASS_REFERENCE:f},illegal:/#(?![$_A-z])/, - contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ - label:"use_strict",className:"meta",relevance:10, - begin:/^\s*['"]use (strict|asm)['"]/ - },e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,c,d,g,u,{match:/\$\d+/},o,f,{ - className:"attr",begin:t+n.lookahead(":"),relevance:0},k,{ - begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", - keywords:"return throw case",relevance:0,contains:[u,e.REGEXP_MODE,{ - className:"function",begin:O,returnBegin:!0,end:"\\s*=>",contains:[{ - className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ - className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, - excludeEnd:!0,keywords:i,contains:p}]}]},{begin:/,/,relevance:0},{match:/\s+/, - relevance:0},{variants:[{begin:"<>",end:""},{ - match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, - "on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ - begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},E,{ - beginKeywords:"while if switch catch for"},{ - begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", - returnBegin:!0,label:"func.def",contains:[_,e.inherit(e.TITLE_MODE,{begin:t, - className:"title.function"})]},{match:/\.\.\./,relevance:0},N,{match:"\\$"+t, - relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, - contains:[_]},y,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, - className:"variable.constant"},h,v,{match:/\$[(.]/}]}} - const Ne=e=>m(/\b/,e,/\w$/.test(e)?/\b/:/\B/),ve=["Protocol","Type"].map(Ne),Oe=["init","self"].map(Ne),ke=["Any","Self"],xe=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","distributed","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Me=["false","nil","true"],Se=["assignment","associativity","higherThan","left","lowerThan","none","right"],Ae=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],Ce=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Te=p(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Re=p(Te,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),De=m(Te,Re,"*"),Ie=p(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),Le=p(Ie,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Be=m(Ie,Le,"*"),$e=m(/[A-Z]/,Le,"*"),ze=["autoclosure",m(/convention\(/,p("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",m(/objc\(/,Be,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","testable","UIApplicationMain","unknown","usableFromInline"],Fe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] - ;var Ue=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ - begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} - ;Object.assign(t,{className:"variable",variants:[{ - begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ - className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ - begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, - end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, - contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, - end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] - },l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 - }),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, - contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ - name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, - keyword:["if","then","else","elif","fi","for","while","in","do","done","case","esac","function"], - literal:["true","false"], - built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] - },contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ - className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},t]}}, - grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] - }),a="[a-zA-Z_]\\w*::",i="(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={ - className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ - match:/\batomic_[a-z]{3,6}\b/}]},s={className:"string",variants:[{ - begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ - begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", - end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ - begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ - className:"number",variants:[{begin:"\\b(0b[01']+)"},{ - begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" - },{ - begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" - }],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ - keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" - },contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{ - className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={ - className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0 - },d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={ - keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], - type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], - literal:"true false NULL", - built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" - },u=[l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],b={variants:[{begin:/=/,end:/;/},{ - begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], - keywords:g,contains:u.concat([{begin:/\(/,end:/\)/,keywords:g, - contains:u.concat(["self"]),relevance:0}]),relevance:0},m={ - begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, - keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)", - keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[e.inherit(c,{ - className:"title.function"})],relevance:0},{relevance:0,match:/,/},{ - className:"params",begin:/\(/,end:/\)/,keywords:g,relevance:0, - contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/,keywords:g, - relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}] - },r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C",aliases:["h"],keywords:g, - disableAutodetect:!0,illegal:"=]/,contains:[{ - beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:l, - strings:s,keywords:g}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ - contains:[{begin:/\\\n/}] - }),a="[a-zA-Z_]\\w*::",i="(?!struct)(decltype\\(auto\\)|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",r={ - className:"type",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",variants:[{ - begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ - begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", - end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ - begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ - className:"number",variants:[{begin:"\\b(0b[01']+)"},{ - begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" - },{ - begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" - }],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ - keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" - },contains:[{begin:/\\\n/,relevance:0},e.inherit(s,{className:"string"}),{ - className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},c={ - className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0 - },d=n.optional(a)+e.IDENT_RE+"\\s*\\(",g={ - type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], - keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], - literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], - _type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] - },u={className:"function.dispatch",relevance:0,keywords:{ - _hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] - }, - begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) - },b=[u,l,r,t,e.C_BLOCK_COMMENT_MODE,o,s],m={variants:[{begin:/=/,end:/;/},{ - begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], - keywords:g,contains:b.concat([{begin:/\(/,end:/\)/,keywords:g, - contains:b.concat(["self"]),relevance:0}]),relevance:0},p={className:"function", - begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, - keywords:g,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)", - keywords:g,relevance:0},{begin:d,returnBegin:!0,contains:[c],relevance:0},{ - begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[s,o]},{ - relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:g, - relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,s,o,r,{begin:/\(/,end:/\)/, - keywords:g,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,s,o,r]}] - },r,t,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++", - aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:g,illegal:"",keywords:g,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:g},{ - match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], - className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ - keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), - built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], - literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ - begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ - begin:"\\b(0b[01']+)"},{ - begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ - begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" - }],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] - },r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, - keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, - end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ - },e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ - begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, - contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) - ;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], - o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ - illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] - },u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] - },b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ - begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], - keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, - contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ - begin:"\x3c!--|--\x3e"},{begin:""}]}] - }),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", - end:"$",keywords:{ - keyword:"if else elif endif define undef warning error line region endregion pragma checksum" - }},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, - illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" - },t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", - relevance:0,end:/[{;=]/,illegal:/[^\s:]/, - contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ - beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, - contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", - begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ - className:"string",begin:/"/,end:/"/}]},{ - beginKeywords:"new return throw await else",relevance:0},{className:"function", - begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, - end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ - beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", - relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, - contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", - begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, - contains:[g,a,e.C_BLOCK_COMMENT_MODE] - },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ - const n=e.regex,t=te(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ - name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ - keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, - contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ - },t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 - },{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 - },t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ - begin:":("+re.join("|")+")"},{begin:":(:)?("+se.join("|")+")"}] - },t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b"},{ - begin:/:/,end:/[;}{]/, - contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ - begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" - },contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, - excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", - relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ - },{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ - $pattern:/[a-z-]+/,keyword:"and or not only",attribute:ie.join(" ")},contains:[{ - begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ - className:"selector-tag",begin:"\\b("+ae.join("|")+")\\b"}]}},grmr_diff:e=>{ - const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ - className:"meta",relevance:10, - match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) - },{className:"comment",variants:[{ - begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), - end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ - className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, - end:/$/}]}},grmr_go:e=>{const n={ - keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], - type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], - literal:["true","false","iota","nil"], - built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] - };return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], - case_insensitive:!0,disableAutodetect:!1,keywords:{ - keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], - literal:["true","false","null"]}, - contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ - scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", - begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, - end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ - scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), - relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ - className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ - begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, - end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ - begin:/\$\{(.*?)\}/}]},r={className:"literal", - begin:/\bon|off|true|false|yes|no\b/},s={className:"string", - contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ - begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] - },o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 - },l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ - name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, - contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ - begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), - className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ - const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+ue("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ - keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], - literal:["false","true","null"], - type:["char","boolean","long","float","int","byte","short","double"], - built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ - begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, - end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} - ;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, - contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, - relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ - begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 - },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, - className:"string",contains:[e.BACKSLASH_ESCAPE] - },e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ - match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ - 1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ - begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", - 3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", - 3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ - beginKeywords:"new throw return else",relevance:0},{ - begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ - 2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, - end:/\)/,keywords:i,relevance:0, - contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,ge,e.C_BLOCK_COMMENT_MODE] - },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},ge,r]}},grmr_javascript:we, - grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", - beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ - className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ - match:/[{}[\],:]/,className:"punctuation",relevance:0 - },e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], - illegal:"\\S"}},grmr_kotlin:e=>{const n={ - keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", - built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", - literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" - },a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ - className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", - variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", - illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, - contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ - className:"meta", - begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" - },o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, - end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] - },l=ge,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ - variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, - contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], - {name:"Kotlin",aliases:["kt","kts"],keywords:n, - contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", - begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", - begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", - begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", - returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ - begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, - contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, - keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, - endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, - endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 - },e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ - begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ - 3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, - illegal:"extends implements",contains:[{ - beginKeywords:"public protected internal private constructor" - },e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, - excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, - excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", - end:"$",illegal:"\n"},l]}},grmr_less:e=>{ - const n=te(e),t=le,a="([\\w-]+|@\\{[\\w-]+\\})",i=[],r=[],s=e=>({ - className:"string",begin:"~?"+e+".*?"+e}),o=(e,n,t)=>({className:e,begin:n, - relevance:t}),l={$pattern:/[a-z-]+/,keyword:"and or not only", - attribute:ie.join(" ")},c={begin:"\\(",end:"\\)",contains:r,keywords:l, - relevance:0} - ;r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s("'"),s('"'),n.CSS_NUMBER_MODE,{ - begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", - excludeEnd:!0} - },n.HEXCOLOR,c,o("variable","@@?[\\w-]+",10),o("variable","@\\{[\\w-]+\\}"),o("built_in","~?`[^`]*?`"),{ - className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0 - },n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const d=r.concat({ - begin:/\{/,end:/\}/,contains:i}),g={beginKeywords:"when",endsWithParent:!0, - contains:[{beginKeywords:"and not"}].concat(r)},u={begin:a+"\\s*:", - returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ - },n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+oe.join("|")+")\\b", - end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:r}}] - },b={className:"keyword", - begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", - starts:{end:"[;{}]",keywords:l,returnEnd:!0,contains:r,relevance:0}},m={ - className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{ - begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:d}},p={variants:[{ - begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:a,end:/\{/}],returnBegin:!0, - returnEnd:!0,illegal:"[<='$\"]",relevance:0, - contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,o("keyword","all\\b"),o("variable","@\\{[\\w-]+\\}"),{ - begin:"\\b("+ae.join("|")+")\\b",className:"selector-tag" - },n.CSS_NUMBER_MODE,o("selector-tag",a,0),o("selector-id","#"+a),o("selector-class","\\."+a,0),o("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ - className:"selector-pseudo",begin:":("+re.join("|")+")"},{ - className:"selector-pseudo",begin:":(:)?("+se.join("|")+")"},{begin:/\(/, - end:/\)/,relevance:0,contains:d},{begin:"!important"},n.FUNCTION_DISPATCH]},_={ - begin:`[\\w-]+:(:)?(${t.join("|")})`,returnBegin:!0,contains:[p]} - ;return i.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,b,m,_,u,p,g,n.FUNCTION_DISPATCH), - {name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:i}}, - grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] - },i=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[",t,{contains:[a], - relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, - literal:"true false nil", - keyword:"and break do else elseif end for goto if in local not or repeat return then until while", - built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" - },contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", - contains:[e.inherit(e.TITLE_MODE,{ - begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", - begin:"\\(",endsWithParent:!0,contains:i}].concat(i) - },e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", - begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ - className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", - contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ - const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ - className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, - contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] - },r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ - className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ - endsWithParent:!0,illegal:/`]+/}]}]}]};return{ - name:"HTML, XML", - aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], - case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ - className:"meta",begin://,contains:[i,r,o,s]}]}] - },e.COMMENT(//,{relevance:10}),{begin://, - relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, - relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", - begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ - end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", - begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ - end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ - className:"tag",begin:/<>|<\/>/},{className:"tag", - begin:n.concat(//,/>/,/\s/)))), - end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ - className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ - className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} - },grmr_markdown:e=>{const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml", - relevance:0},t={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ - begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, - relevance:2},{ - begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), - relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ - begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ - },{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, - returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", - excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", - end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], - variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] - },i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ - begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] - }),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) - ;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) - })),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ - className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ - begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", - contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", - end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, - end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ - begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ - begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", - contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ - begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ - className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ - className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ - const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, - keyword:["@interface","@class","@protocol","@implementation"]};return{ - name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], - keywords:{"variable.language":["this","super"],$pattern:n, - keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], - literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], - built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], - type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] - },illegal:"/,end:/$/,illegal:"\\n" - },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", - begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, - contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, - relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ - $pattern:/[\w.]+/, - keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" - },i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, - end:/\}/},s={variants:[{begin:/\$\d/},{ - begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") - },{begin:/[$%@][^\s\w{]/,relevance:0}] - },o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ - const r="\\1"===i?i:n.concat(i,a) - ;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) - },d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ - endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ - begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", - end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ - begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", - relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", - contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", - contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ - begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", - begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", - relevance:0},{ - begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", - keywords:"split return print reverse grep",relevance:0, - contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ - begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ - begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ - className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ - begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 - }),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ - begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", - end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ - begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", - subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] - }];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, - contains:g}},grmr_php:e=>{ - const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ - scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ - begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null - }),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ - illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s) - }),o,e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/, - contains:e.QUOTE_STRING_MODE.contains.concat(s)})]},d={scope:"number", - variants:[{begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{ - begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ - begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ - begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" - }],relevance:0 - },g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ - keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ - n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) - })),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ - match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ - 1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ - match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" - }},{match:[/::/,/class/],scope:{2:"variable.language"}},{ - match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", - 3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], - scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", - 3:"variable.language"}}]},E={scope:"attr", - match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, - begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] - },w={relevance:0, - match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], - scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(w) - ;const N=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, - keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, - endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ - begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, - contains:["self",...N]},...N,{scope:"meta",match:i}] - },e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ - scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, - keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, - contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ - begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ - begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,w,f,{ - match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ - scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, - excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" - },e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", - begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, - contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ - beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", - illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ - beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ - beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, - contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ - beginKeywords:"use",relevance:0,end:";",contains:[{ - match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} - },grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ - begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", - end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 - },e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, - skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, - contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", - aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ - const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ - $pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, - built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], - literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], - type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] - },r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, - end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ - className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ - begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, - contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ - begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, - contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ - begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, - contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, - end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, - relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ - begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, - end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, - contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, - contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] - },c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ - className:"number",relevance:0,variants:[{ - begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ - begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ - begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` - },{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` - }]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, - contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ - className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, - end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, - contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ - name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, - illegal:/(<\/|->|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", - relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ - 1:"keyword",3:"title.function"},contains:[m]},{variants:[{ - match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], - scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ - className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, - grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", - starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ - begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ - const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) - ;return{name:"R",keywords:{$pattern:t, - keyword:"function if in break next repeat else for while", - literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", - built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" - },contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, - starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), - endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ - scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 - }]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] - }),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], - variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ - }),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ - }),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ - }),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ - }),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ - }),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', - relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ - 1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, - match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ - 2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, - match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ - match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", - contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ - const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ - "variable.constant":["__FILE__","__LINE__","__ENCODING__"], - "variable.language":["self","super"], - keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], - built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], - literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ - begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] - }),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 - }),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, - end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], - variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ - begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ - begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, - end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ - begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ - begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ - begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ - begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ - begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), - contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, - contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", - relevance:0,variants:[{ - begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ - begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" - },{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ - begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ - begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ - className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, - keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ - match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", - 4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ - 2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ - 1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, - className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ - match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ - begin:e.IDENT_RE+"::"},{className:"symbol", - begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", - begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", - begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ - className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, - relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", - keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], - illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ - begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", - end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) - ;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} - },{className:"meta.prompt", - begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", - starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", - aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, - contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, - grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, - begin:n.concat(/\b/,/(?!let\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) - },a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] - ;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, - keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], - literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, - grmr_scss:e=>{const n=te(e),t=se,a=re,i="@[a-z-]+",r={className:"variable", - begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", - case_insensitive:!0,illegal:"[=/|']", - contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ - className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ - className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 - },n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", - begin:"\\b("+ae.join("|")+")\\b",relevance:0},{className:"selector-pseudo", - begin:":("+a.join("|")+")"},{className:"selector-pseudo", - begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, - contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", - begin:"\\b("+oe.join("|")+")\\b"},{ - begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" - },{begin:/:/,end:/[;}{]/,relevance:0, - contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] - },{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ - begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, - keyword:"and or not only",attribute:ie.join(" ")},contains:[{begin:i, - className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" - },r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] - },n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", - aliases:["console","shellsession"],contains:[{className:"meta.prompt", - begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, - subLanguage:"bash"}}]}),grmr_sql:e=>{ - const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ - begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} - ;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ - $pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t - ;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) - })(l,{when:e=>e.length<3}),literal:a,type:i, - built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] - },contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, - keyword:l.concat(s),literal:a,type:i}},{className:"type", - begin:n.either("double precision","large object","with timezone","without timezone") - },c,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{ - begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{ - begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{className:"operator", - begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}, - grmr_swift:e=>{const n={match:/\s+/,relevance:0},t=e.COMMENT("/\\*","\\*/",{ - contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={match:[/\./,p(...ve,...Oe)], - className:{2:"keyword"}},r={match:m(/\./,p(...xe)),relevance:0 - },s=xe.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ - className:"keyword", - match:p(...xe.filter((e=>"string"!=typeof e)).concat(ke).map(Ne),...Oe)}]},l={ - $pattern:p(/\b\w+/,/#\w+/),keyword:s.concat(Ae),literal:Me},c=[i,r,o],d=[{ - match:m(/\./,p(...Ce)),relevance:0},{className:"built_in", - match:m(/\b/,p(...Ce),/(?=\()/)}],u={match:/->/,relevance:0},b=[u,{ - className:"operator",relevance:0,variants:[{match:De},{match:`\\.(\\.|${Re})+`}] - }],_="([0-9a-fA-F]_*)+",h={className:"number",relevance:0,variants:[{ - match:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{ - match:`\\b0x(${_})(\\.(${_}))?([pP][+-]?(([0-9]_*)+))?\\b`},{ - match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},f=(e="")=>({ - className:"subst",variants:[{match:m(/\\/,e,/[0\\tnr"']/)},{ - match:m(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),E=(e="")=>({className:"subst", - match:m(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),y=(e="")=>({className:"subst", - label:"interpol",begin:m(/\\/,e,/\(/),end:/\)/}),w=(e="")=>({begin:m(e,/"""/), - end:m(/"""/,e),contains:[f(e),E(e),y(e)]}),N=(e="")=>({begin:m(e,/"/), - end:m(/"/,e),contains:[f(e),y(e)]}),v={className:"string", - variants:[w(),w("#"),w("##"),w("###"),N(),N("#"),N("##"),N("###")]},O={ - match:m(/`/,Be,/`/)},k=[O,{className:"variable",match:/\$\d+/},{ - className:"variable",match:`\\$${Le}+`}],x=[{match:/(@|#(un)?)available/, - className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:Fe, - contains:[...b,h,v]}]}},{className:"keyword",match:m(/@/,p(...ze))},{ - className:"meta",match:m(/@/,Be)}],M={match:g(/\b[A-Z]/),relevance:0,contains:[{ - className:"type", - match:m(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Le,"+") - },{className:"type",match:$e,relevance:0},{match:/[?!]+/,relevance:0},{ - match:/\.\.\./,relevance:0},{match:m(/\s+&\s+/,g($e)),relevance:0}]},S={ - begin://,keywords:l,contains:[...a,...c,...x,u,M]};M.contains.push(S) - ;const A={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ - match:m(Be,/\s*:/),keywords:"_|0",relevance:0 - },...a,...c,...d,...b,h,v,...k,...x,M]},C={begin://,contains:[...a,M] - },T={begin:/\(/,end:/\)/,keywords:l,contains:[{ - begin:p(g(m(Be,/\s*:/)),g(m(Be,/\s+/,Be,/\s*:/))),end:/:/,relevance:0, - contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:Be}] - },...a,...c,...b,h,v,...x,M,A],endsParent:!0,illegal:/["']/},R={ - match:[/func/,/\s+/,p(O.match,Be,De)],className:{1:"keyword",3:"title.function" - },contains:[C,T,n],illegal:[/\[/,/%/]},D={ - match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, - contains:[C,T,n],illegal:/\[|%/},I={match:[/operator/,/\s+/,De],className:{ - 1:"keyword",3:"title"}},L={begin:[/precedencegroup/,/\s+/,$e],className:{ - 1:"keyword",3:"title"},contains:[M],keywords:[...Se,...Me],end:/}/} - ;for(const e of v.variants){const n=e.contains.find((e=>"interpol"===e.label)) - ;n.keywords=l;const t=[...c,...d,...b,h,v,...k];n.contains=[...t,{begin:/\(/, - end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, - contains:[...a,R,D,{beginKeywords:"struct protocol class extension enum actor", - end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ - className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] - },I,L,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 - },...c,...d,...b,h,v,...k,...x,M,A]}},grmr_typescript:e=>{ - const n=we(e),t=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],a={ - beginKeywords:"namespace",end:/\{/,excludeEnd:!0, - contains:[n.exports.CLASS_REFERENCE]},i={beginKeywords:"interface",end:/\{/, - excludeEnd:!0,keywords:{keyword:"interface extends",built_in:t}, - contains:[n.exports.CLASS_REFERENCE]},r={$pattern:be, - keyword:me.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), - literal:pe,built_in:ye.concat(t),"variable.language":Ee},s={className:"meta", - begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},o=(e,n,t)=>{ - const a=e.contains.findIndex((e=>e.label===n)) - ;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} - ;return Object.assign(n.keywords,r), - n.exports.PARAMS_CONTAINS.push(s),n.contains=n.contains.concat([s,a,i]), - o(n,"shebang",e.SHEBANG()),o(n,"use_strict",{className:"meta",relevance:10, - begin:/^\s*['"]use strict['"]/ - }),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ - name:"TypeScript",aliases:["ts","tsx"]}),n},grmr_vbnet:e=>{ - const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ - className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ - begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ - begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] - },o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] - }),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) - ;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, - classNameAliases:{label:"symbol"},keywords:{ - keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", - built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", - type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", - literal:"true false nothing"}, - illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ - className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, - end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, - variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ - },{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ - begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ - className:"label",begin:/^\w+:/},o,l,{className:"meta", - begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, - end:/$/,keywords:{ - keyword:"const disable else elseif enable end externalsource if region then"}, - contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) - ;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, - keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] - },contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], - className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ - match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ - begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", - 3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, - className:"type"},{className:"keyword", - match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ - },{className:"number",relevance:0, - match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ - }]}},grmr_yaml:e=>{ - const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ - className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ - },{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", - variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ - variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ - end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, - end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", - contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ - begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ - begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", - relevance:10},{className:"string", - begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ - begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, - relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", - begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t - },{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", - begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", - relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ - className:"number", - begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" - },{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] - ;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, - aliases:["yml"],contains:l}}});const je=ne;for(const e of Object.keys(Ue)){ - const n=e.replace("grmr_","").replace("_","-");je.registerLanguage(n,Ue[e])} - return je}() - ;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/resources/content/static/js/jquery-2.2.4.min.js b/resources/content/static/js/jquery-2.2.4.min.js deleted file mode 100644 index 4024b66..0000000 --- a/resources/content/static/js/jquery-2.2.4.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v2.2.4 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; -}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("