From 50422aac145ac43a93aeeb579a339589fc517a50 Mon Sep 17 00:00:00 2001 From: rianli Date: Sat, 31 Jan 2026 19:49:27 +0800 Subject: [PATCH 01/14] =?UTF-8?q?feat(qqbot):=20=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E8=BE=93=E5=87=BA=E4=B8=8E=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **流式消息** - 新增 StreamSender 类,支持流式消息分片发送 - 实现消息队列异步处理,防止阻塞心跳 - 支持 C2C/Group 流式消息 **架构重构** - 移除 clawdbot/moltbot 旧配置,统一为 qqbot - 新增 upgrade-and-run.sh 一键升级脚本 - 重构 api/channel/gateway/outbound 模块 - 新增富媒体消息发送接口 --- README.md | 212 ---- clawdbot.plugin.json | 9 - index.ts | 11 +- moltbot.plugin.json | 9 - package-lock.json | 2789 +++++++++++++++++++++++++++--------------- package.json | 19 +- scripts/upgrade.sh | 132 +- src/api.ts | 220 +++- src/channel.ts | 202 ++- src/config.ts | 44 +- src/gateway.ts | 309 ++++- src/onboarding.ts | 44 +- src/outbound.ts | 353 +++++- src/runtime.ts | 2 +- src/types.ts | 38 + upgrade-and-run.sh | 41 + 16 files changed, 2933 insertions(+), 1501 deletions(-) delete mode 100644 README.md delete mode 100644 clawdbot.plugin.json delete mode 100644 moltbot.plugin.json create mode 100755 upgrade-and-run.sh diff --git a/README.md b/README.md deleted file mode 100644 index 7cc6cde..0000000 --- a/README.md +++ /dev/null @@ -1,212 +0,0 @@ -# QQ Bot Channel Plugin for Moltbot - -QQ 开放平台Bot API 的 Moltbot 渠道插件,支持 C2C 私聊、群聊 @消息、频道消息。 - -## 功能特性 - -- **多场景支持**:C2C 单聊、QQ 群 @消息、频道公开消息、频道私信 -- **自动重连**:WebSocket 断连后自动重连,支持 Session Resume -- **消息去重**:自动管理 `msg_seq`,支持对同一消息多次回复 -- **系统提示词**:可配置自定义系统提示词注入到 AI 请求 -- **错误提示**:AI 无响应时自动提示用户检查配置 - -## 使用示例: -image - -## 版本更新 -Clipboard_Screenshot_1769739939 - -### 1.3.0(即将更新) -- 支持回复图片等功能 - -### 1.2.2 -- 支持发送文件 -- 支持openclaw、moltbot命令行 -- 修复[health]检查提示: [health] refresh failed: Cannot read properties of undefined (reading 'appId')的问题(不影响使用) -- 修复文件发送后clawdbot无法读取的问题 - -### 1.2.1 -- 解决了长时间使用会断联的问题 -- 解决了频繁重连的问题 -- 增加了大模型调用失败后的提示消息 - - -### 1.1.0 -- 解决了一些url会被拦截的问题 -- 解决了多轮消息会发送失败的问题 -- 修复了部分图片无法接受的问题 -- 增加支持onboard的方式配置AppId 和 AppSecret - - -## 安装 - -在插件目录下执行: - -```bash -git clone https://github.com/sliverp/qqbot.git && cd qqbot -clawdbot plugins install . # 这一步会有点久,需要安装一些依赖。稍微耐心等待一下,尤其是小内存机器 -``` - -## 配置 - -### 1. 获取 QQ 机器人凭证 - -1. 访问 [QQ 开放平台](https://q.qq.com/) -2. 创建机器人应用 -3. 获取 `AppID` 和 `AppSecret`(ClientSecret) -4. Token 格式为 `AppID:AppSecret`,例如 `102146862:Xjv7JVhu7KXkxANbp3HVjxCRgvAPeuAQ` - -### 2. 添加配置 - -#### 方式一:交互式配置 - -```bash -clawdbot channels add -# 选择 qqbot,按提示输入 Token -``` - -#### 方式二:命令行配置 - -```bash -clawdbot channels add --channel qqbot --token "AppID:AppSecret" -``` - -示例: - -```bash -clawdbot channels add --channel qqbot --token "102146862:xxxxxxxx" -``` - -### 3. 手动编辑配置(可选) - -也可以直接编辑 `~/.clawdbot/clawdbot.json`: - -```json -{ - "channels": { - "qqbot": { - "enabled": true, - "appId": "你的AppID", - "clientSecret": "你的AppSecret", - "systemPrompt": "你是一个友好的助手" - } - } -} -``` - - - -## 配置项说明 - -| 配置项 | 类型 | 必填 | 说明 | -|--------|------|------|------| -| `appId` | string | 是 | QQ 机器人 AppID | -| `clientSecret` | string | 是* | AppSecret,与 `clientSecretFile` 二选一 | -| `clientSecretFile` | string | 是* | AppSecret 文件路径 | -| `enabled` | boolean | 否 | 是否启用,默认 `true` | -| `name` | string | 否 | 账户显示名称 | -| `systemPrompt` | string | 否 | 自定义系统提示词 | - -## 支持的消息类型 - -| 事件类型 | 说明 | Intent | -|----------|------|--------| -| `C2C_MESSAGE_CREATE` | C2C 单聊消息 | `1 << 25` | -| `GROUP_AT_MESSAGE_CREATE` | 群聊 @机器人消息 | `1 << 25` | -| `AT_MESSAGE_CREATE` | 频道 @机器人消息 | `1 << 30` | -| `DIRECT_MESSAGE_CREATE` | 频道私信 | `1 << 12` | - -## 使用 - -### 启动 - -后台启动 -```bash -clawdbot gateway restart -``` - -前台启动, 方便试试查看日志 -```bash -clawdbot gateway --port 18789 --verbose -``` - -### CLI 配置向导 - -```bash -clawdbot onboard -# 选择 QQ Bot 进行交互式配置 -``` - -## 注意事项 - -1. **消息回复限制**:QQ 官方 API 限制每条消息最多回复 5 次,超时 60 分钟 -2. **URL 限制**:QQ 平台不允许消息中包含 URL,插件已内置提示词限制 -3. **群消息**:需要在群内 @机器人 才能触发回复 -4. **沙箱模式**:新创建的机器人默认在沙箱模式,需要添加测试用户 - -## 升级 - -如果需要升级插件,先运行升级脚本清理旧版本: - -```bash -git clone https://github.com/sliverp/qqbot.git && cd qqbot - -# 运行升级脚本(清理旧版本和配置) -bash ./scripts/upgrade.sh - -# 重新安装插件 -clawdbot plugins install . # 这一步会有点久,需要安装一些依赖。稍微耐心等待一下,尤其是小内存机器 - -# 重新配置 -clawdbot channels add --channel qqbot --token "AppID:AppSecret" - -# 重启网关 -clawdbot gateway restart -``` - -升级脚本会自动: -- 删除 `~/.clawdbot/extensions/qqbot` 目录 -- 清理 `clawdbot.json` 中的 qqbot 相关配置 - -## 开发 - -```bash -# 安装依赖 -npm install - -# 编译 -npm run build - -# 监听模式 -npm run dev -``` - -## 文件结构 - -``` -qqbot/ -├── index.ts # 入口文件 -├── src/ -│ ├── api.ts # QQ Bot API 封装 -│ ├── channel.ts # Channel Plugin 定义 -│ ├── config.ts # 配置解析 -│ ├── gateway.ts # WebSocket 网关 -│ ├── onboarding.ts # CLI 配置向导 -│ ├── outbound.ts # 出站消息处理 -│ ├── runtime.ts # 运行时状态 -│ └── types.ts # 类型定义 -├── scripts/ -│ └── upgrade.sh # 升级脚本 -├── package.json -└── tsconfig.json -``` - -## 相关链接 - -- [QQ 机器人官方文档](https://bot.q.qq.com/wiki/) -- [QQ 开放平台](https://q.qq.com/) -- [API v2 文档](https://bot.q.qq.com/wiki/develop/api-v2/) - -## License - -MIT diff --git a/clawdbot.plugin.json b/clawdbot.plugin.json deleted file mode 100644 index 32017b4..0000000 --- a/clawdbot.plugin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "qqbot", - "channels": ["qqbot"], - "configSchema": { - "type": "object", - "additionalProperties": false, - "properties": {} - } -} diff --git a/index.ts b/index.ts index 8b602fc..fd5ec4a 100644 --- a/index.ts +++ b/index.ts @@ -1,12 +1,15 @@ -import type { MoltbotPluginApi } from "clawdbot/plugin-sdk"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; +import { emptyPluginConfigSchema } from "openclaw/plugin-sdk"; + import { qqbotPlugin } from "./src/channel.js"; import { setQQBotRuntime } from "./src/runtime.js"; const plugin = { id: "qqbot", - name: "QQ Bot", - description: "QQ Bot channel plugin", - register(api: MoltbotPluginApi) { + name: "QQ Bot (Stream)", + description: "QQ Bot channel plugin with streaming message support", + configSchema: emptyPluginConfigSchema(), + register(api: OpenClawPluginApi) { setQQBotRuntime(api.runtime); api.registerChannel({ plugin: qqbotPlugin }); }, diff --git a/moltbot.plugin.json b/moltbot.plugin.json deleted file mode 100644 index 32017b4..0000000 --- a/moltbot.plugin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "qqbot", - "channels": ["qqbot"], - "configSchema": { - "type": "object", - "additionalProperties": false, - "properties": {} - } -} diff --git a/package-lock.json b/package-lock.json index 220688c..5d66079 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "qqbot", - "version": "1.2.3", + "name": "@openclaw/qqbot", + "version": "2026.1.31", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "qqbot", - "version": "1.2.2", + "name": "@openclaw/qqbot", + "version": "2026.1.31", "bundleDependencies": [ "ws" ], @@ -16,28 +16,28 @@ "devDependencies": { "@types/node": "^20.0.0", "@types/ws": "^8.5.0", - "typescript": "^5.0.0" + "typescript": "^5.9.3" }, "peerDependencies": { - "clawdbot": "*", - "moltbot": "*", "openclaw": "*" } }, "node_modules/@agentclientprotocol/sdk": { "version": "0.13.1", - "resolved": "https://mirrors.tencent.com/npm/@agentclientprotocol/sdk/-/sdk-0.13.1.tgz", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.13.1.tgz", "integrity": "sha512-6byvu+F/xc96GBkdAx4hq6/tB3vT63DSBO4i3gYCz8nuyZMerVFna2Gkhm8EHNpZX0J9DjUxzZCW+rnHXUg0FA==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "node_modules/@anthropic-ai/sdk": { "version": "0.71.2", - "resolved": "https://mirrors.tencent.com/npm/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.71.2.tgz", "integrity": "sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==", "license": "MIT", + "peer": true, "dependencies": { "json-schema-to-ts": "^3.1.1" }, @@ -55,9 +55,10 @@ }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -69,9 +70,10 @@ }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -84,9 +86,10 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -96,9 +99,10 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -109,9 +113,10 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { "version": "2.3.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -122,9 +127,10 @@ }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -136,18 +142,20 @@ }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" } }, "node_modules/@aws-crypto/util": { "version": "5.2.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-crypto/util/-/util-5.2.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -156,9 +164,10 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -168,9 +177,10 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -181,9 +191,10 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { "version": "2.3.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -194,9 +205,10 @@ }, "node_modules/@aws-sdk/client-bedrock": { "version": "3.978.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/client-bedrock/-/client-bedrock-3.978.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock/-/client-bedrock-3.978.0.tgz", "integrity": "sha512-nyKE6MsusYw8BZRd4pzkRM5ZMtPtg4rbzbFu2ruz8dEFLW0InVeGE8OJnVFSGdZIGFFMsAG4vpu/e0qO4D1f4w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -245,9 +257,10 @@ }, "node_modules/@aws-sdk/client-bedrock-runtime": { "version": "3.978.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.978.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.978.0.tgz", "integrity": "sha512-N5zRvcpq/brl46Mnn3J1N4RFDeiOKU4hFNISFVawt3eT+WBPc74hbbd5b7aky+sreapkfeZ+MAXrQLdevZyMBw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -303,9 +316,10 @@ }, "node_modules/@aws-sdk/client-sso": { "version": "3.975.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/client-sso/-/client-sso-3.975.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.975.0.tgz", "integrity": "sha512-HpgJuleH7P6uILxzJKQOmlHdwaCY+xYC6VgRDzlwVEqU/HXjo4m2gOAyjUbpXlBOCWfGgMUzfBlNJ9z3MboqEQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -352,9 +366,10 @@ }, "node_modules/@aws-sdk/core": { "version": "3.973.4", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/core/-/core-3.973.4.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.4.tgz", "integrity": "sha512-8Rk+kPP74YiR47x54bxYlKZswsaSh0a4XvvRUMLvyS/koNawhsGu/+qSZxREqUeTO+GkKpFvSQIsAZR+deUP+g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/xml-builder": "^3.972.2", @@ -376,9 +391,10 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.2.tgz", "integrity": "sha512-wzH1EdrZsytG1xN9UHaK12J9+kfrnd2+c8y0LVoS4O4laEjPoie1qVK3k8/rZe7KOtvULzyMnO3FT4Krr9Z0Dg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.2", "@aws-sdk/types": "^3.973.1", @@ -392,9 +408,10 @@ }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.4", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.4.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.4.tgz", "integrity": "sha512-OC7F3ipXV12QfDEWybQGHLzoeHBlAdx/nLzPfHP0Wsabu3JBffu5nlzSaJNf7to9HGtOW8Bpu8NX0ugmDrCbtw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.4", "@aws-sdk/types": "^3.973.1", @@ -413,9 +430,10 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.2.tgz", "integrity": "sha512-Jrb8sLm6k8+L7520irBrvCtdLxNtrG7arIxe9TCeMJt/HxqMGJdbIjw8wILzkEHLMIi4MecF2FbXCln7OT1Tag==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.2", "@aws-sdk/credential-provider-env": "^3.972.2", @@ -438,9 +456,10 @@ }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.2.tgz", "integrity": "sha512-mlaw2aiI3DrimW85ZMn3g7qrtHueidS58IGytZ+mbFpsYLK5wMjCAKZQtt7VatLMtSBG/dn/EY4njbnYXIDKeQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.2", "@aws-sdk/nested-clients": "3.975.0", @@ -456,13 +475,14 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.2.tgz", - "integrity": "sha512-Lz1J5IZdTjLYTVIcDP5DVDgi1xlgsF3p1cnvmbfKbjCRhQpftN2e2J4NFfRRvPD54W9+bZ8l5VipPXtTYK7aEg==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.3.tgz", + "integrity": "sha512-iu+JwWHM7tHowKqE+8wNmI3sM6mPEiI9Egscz2BEV7adyKmV95oR9tBO4VIOl72FGDi7X9mXg19VtqIpSkEEsA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.2", - "@aws-sdk/credential-provider-http": "^3.972.3", + "@aws-sdk/credential-provider-http": "^3.972.4", "@aws-sdk/credential-provider-ini": "^3.972.2", "@aws-sdk/credential-provider-process": "^3.972.2", "@aws-sdk/credential-provider-sso": "^3.972.2", @@ -480,9 +500,10 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.2.tgz", "integrity": "sha512-NLKLTT7jnUe9GpQAVkPTJO+cs2FjlQDt5fArIYS7h/Iw/CvamzgGYGFRVD2SE05nOHCMwafUSi42If8esGFV+g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.2", "@aws-sdk/types": "^3.973.1", @@ -497,9 +518,10 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.2.tgz", "integrity": "sha512-YpwDn8g3gCGUl61cCV0sRxP2pFIwg+ZsMfWQ/GalSyjXtRkctCMFA+u0yPb/Q4uTfNEiya1Y4nm0C5rIHyPW5Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/client-sso": "3.975.0", "@aws-sdk/core": "^3.973.2", @@ -516,9 +538,10 @@ }, "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { "version": "3.975.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/token-providers/-/token-providers-3.975.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.975.0.tgz", "integrity": "sha512-AWQt64hkVbDQ+CmM09wnvSk2mVyH4iRROkmYkr3/lmUtFNbE2L/fnw26sckZnUcFCsHPqbkQrcsZAnTcBLbH4w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.1", "@aws-sdk/nested-clients": "3.975.0", @@ -534,9 +557,10 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.2.tgz", "integrity": "sha512-x9DAiN9Qz+NjJ99ltDiVQ8d511M/tuF/9MFbe2jUgo7HZhD6+x4S3iT1YcP07ndwDUjmzKGmeOEgE24k4qvfdg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.2", "@aws-sdk/nested-clients": "3.975.0", @@ -552,9 +576,10 @@ }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.2.tgz", "integrity": "sha512-bYYftGahAQv90qJci/MvE/baqlxqUJ3urY0WpEux0Nd2bl2mh0t2M7mtnHa6pxU95UW2BeKSL6/LV6zLo00o4Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/eventstream-codec": "^4.2.8", @@ -567,9 +592,10 @@ }, "node_modules/@aws-sdk/middleware-eventstream": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.2.tgz", "integrity": "sha512-cUxOy8hXPgNkKw0G0avq4nxJ2kyROTmBKaO8B4G84HguV3orxMMdwq7bdKkv4xV8RZr/Bd8lJDoVtgRjSBq83Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", @@ -582,9 +608,10 @@ }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.2.tgz", "integrity": "sha512-42hZ8jEXT2uR6YybCzNq9OomqHPw43YIfRfz17biZjMQA4jKSQUaHIl6VvqO2Ddl5904pXg2Yd/ku78S0Ikgog==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/protocol-http": "^5.3.8", @@ -597,9 +624,10 @@ }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-logger/-/middleware-logger-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.2.tgz", "integrity": "sha512-iUzdXKOgi4JVDDEG/VvoNw50FryRCEm0qAudw12DcZoiNJWl0rN6SYVLcL1xwugMfQncCXieK5UBlG6mhH7iYA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", @@ -611,9 +639,10 @@ }, "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.2.tgz", "integrity": "sha512-/mzlyzJDtngNFd/rAYvqx29a2d0VuiYKN84Y/Mu9mGw7cfMOCyRK+896tb9wV6MoPRHUX7IXuKCIL8nzz2Pz5A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws/lambda-invoke-store": "^0.2.2", @@ -627,9 +656,10 @@ }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.972.4", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.4.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.4.tgz", "integrity": "sha512-6sU8jrSJvY/lqSnU6IYsa8SrCKwOZ4Enl6O4xVJo8RCq9Bdr5Giuw2eUaJAk9GPcpr4OFcmSFv3JOLhpKGeRZA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.4", "@aws-sdk/types": "^3.973.1", @@ -645,9 +675,10 @@ }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.2.tgz", "integrity": "sha512-D4fFifl48BJ7fSGz33zJPrbKQ4DFD5mR73xTEs1JoxgsyskV/bR7h+QidE+Kyeps5GX7D1E4TKHimpoGSqAlRg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-format-url": "^3.972.2", @@ -666,9 +697,10 @@ }, "node_modules/@aws-sdk/nested-clients": { "version": "3.975.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/nested-clients/-/nested-clients-3.975.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.975.0.tgz", "integrity": "sha512-OkeFHPlQj2c/Y5bQGkX14pxhDWUGUFt3LRHhjcDKsSCw6lrxKcxN3WFZN0qbJwKNydP+knL5nxvfgKiCLpTLRA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -715,9 +747,10 @@ }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.2.tgz", "integrity": "sha512-/7vRBsfmiOlg2X67EdKrzzQGw5/SbkXb7ALHQmlQLkZh8qNgvS2G2dDC6NtF3hzFlpP3j2k+KIEtql/6VrI6JA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", @@ -731,9 +764,10 @@ }, "node_modules/@aws-sdk/token-providers": { "version": "3.978.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/token-providers/-/token-providers-3.978.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.978.0.tgz", "integrity": "sha512-WASLAiZAzGCIsr9Yqf0RW8Jpuwfw9RP6Dpuev1J+ewLKl5nr6JL5Y9PIfnRi7asG0QUxgu5smadfSuPDMEbJjA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.4", "@aws-sdk/nested-clients": "3.978.0", @@ -749,9 +783,10 @@ }, "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/nested-clients": { "version": "3.978.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/nested-clients/-/nested-clients-3.978.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.978.0.tgz", "integrity": "sha512-FyFiPp1SPt2JnspHlPO0LJyRwfYLBA27ToAoJAsbJIcd/Ytt9mFkRQ4kqUJQwnSl5JpdYzotNRqoRFCmx3uUfA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -798,9 +833,10 @@ }, "node_modules/@aws-sdk/types": { "version": "3.973.1", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/types/-/types-3.973.1.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.1.tgz", "integrity": "sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -811,9 +847,10 @@ }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.972.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/util-endpoints/-/util-endpoints-3.972.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.972.0.tgz", "integrity": "sha512-6JHsl1V/a1ZW8D8AFfd4R52fwZPnZ5H4U6DS8m/bWT8qad72NvbOFAC7U2cDtFs2TShqUO3TEiX/EJibtY3ijg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "3.972.0", "@smithy/types": "^4.12.0", @@ -827,9 +864,10 @@ }, "node_modules/@aws-sdk/util-endpoints/node_modules/@aws-sdk/types": { "version": "3.972.0", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/types/-/types-3.972.0.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.972.0.tgz", "integrity": "sha512-U7xBIbLSetONxb2bNzHyDgND3oKGoIfmknrEVnoEU4GUSs+0augUOIn9DIWGUO2ETcRFdsRUnmx9KhPT9Ojbug==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -840,9 +878,10 @@ }, "node_modules/@aws-sdk/util-format-url": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/util-format-url/-/util-format-url-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.2.tgz", "integrity": "sha512-RCd8eur5wzDLgFBvbBhoFQ1bw1wxHJiN88MQ82IiJBs6OGXTWaf0oFgLbK06qJvnVUqL13t3jEnlYPHPNdgBWw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/querystring-builder": "^4.2.8", @@ -855,9 +894,10 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.4", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", "integrity": "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -867,9 +907,10 @@ }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.2.tgz", "integrity": "sha512-gz76bUyebPZRxIsBHJUd/v+yiyFzm9adHbr8NykP2nm+z/rFyvQneOHajrUejtmnc5tTBeaDPL4X25TnagRk4A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.1", "@smithy/types": "^4.12.0", @@ -879,9 +920,10 @@ }, "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.2.tgz", "integrity": "sha512-vnxOc4C6AR7hVbwyFo1YuH0GB6dgJlWt8nIOOJpnzJAWJPkUMPJ9Zv2lnKsSU7TTZbhP2hEO8OZ4PYH59XFv8Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.3", "@aws-sdk/types": "^3.973.1", @@ -903,9 +945,10 @@ }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.2", - "resolved": "https://mirrors.tencent.com/npm/@aws-sdk/xml-builder/-/xml-builder-3.972.2.tgz", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.2.tgz", "integrity": "sha512-jGOOV/bV1DhkkUhHiZ3/1GZ67cZyOXaDb7d1rYD6ZiXf5V9tBNOcgqXwRRPvrCbYaFRa1pPMFb3ZjqjWpR3YfA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", @@ -917,27 +960,30 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.3", - "resolved": "https://mirrors.tencent.com/npm/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=18.0.0" } }, "node_modules/@babel/runtime": { "version": "7.28.6", - "resolved": "https://mirrors.tencent.com/npm/@babel/runtime/-/runtime-7.28.6.tgz", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@borewit/text-codec": { "version": "0.2.1", - "resolved": "https://mirrors.tencent.com/npm/@borewit/text-codec/-/text-codec-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" @@ -945,9 +991,10 @@ }, "node_modules/@buape/carbon": { "version": "0.14.0", - "resolved": "https://mirrors.tencent.com/npm/@buape/carbon/-/carbon-0.14.0.tgz", + "resolved": "https://registry.npmjs.org/@buape/carbon/-/carbon-0.14.0.tgz", "integrity": "sha512-mavllPK2iVpRNRtC4C8JOUdJ1hdV0+LDelFW+pjpJaM31MBLMfIJ+f/LlYTIK5QrEcQsXOC+6lU2e0gmgjWhIQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "^25.0.9", "discord-api-types": "0.38.37" @@ -963,23 +1010,37 @@ }, "node_modules/@buape/carbon/node_modules/@types/node": { "version": "25.1.0", - "resolved": "https://mirrors.tencent.com/npm/@types/node/-/node-25.1.0.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, + "node_modules/@buape/carbon/node_modules/discord-api-types": { + "version": "0.38.37", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.37.tgz", + "integrity": "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==", + "license": "MIT", + "peer": true, + "workspaces": [ + "scripts/actions/documentation" + ] + }, "node_modules/@buape/carbon/node_modules/undici-types": { "version": "7.16.0", - "resolved": "https://mirrors.tencent.com/npm/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT", + "peer": true }, "node_modules/@cacheable/memory": { "version": "2.0.7", - "resolved": "https://mirrors.tencent.com/npm/@cacheable/memory/-/memory-2.0.7.tgz", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.7.tgz", "integrity": "sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==", "license": "MIT", + "peer": true, "dependencies": { "@cacheable/utils": "^2.3.3", "@keyv/bigmap": "^1.3.0", @@ -989,9 +1050,10 @@ }, "node_modules/@cacheable/node-cache": { "version": "1.7.6", - "resolved": "https://mirrors.tencent.com/npm/@cacheable/node-cache/-/node-cache-1.7.6.tgz", + "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.7.6.tgz", "integrity": "sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==", "license": "MIT", + "peer": true, "dependencies": { "cacheable": "^2.3.1", "hookified": "^1.14.0", @@ -1003,9 +1065,10 @@ }, "node_modules/@cacheable/utils": { "version": "2.3.3", - "resolved": "https://mirrors.tencent.com/npm/@cacheable/utils/-/utils-2.3.3.tgz", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.3.tgz", "integrity": "sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==", "license": "MIT", + "peer": true, "dependencies": { "hashery": "^1.3.0", "keyv": "^5.5.5" @@ -1013,9 +1076,10 @@ }, "node_modules/@clack/core": { "version": "0.5.0", - "resolved": "https://mirrors.tencent.com/npm/@clack/core/-/core-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz", "integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==", "license": "MIT", + "peer": true, "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" @@ -1023,9 +1087,10 @@ }, "node_modules/@clack/prompts": { "version": "0.11.0", - "resolved": "https://mirrors.tencent.com/npm/@clack/prompts/-/prompts-0.11.0.tgz", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz", "integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==", "license": "MIT", + "peer": true, "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", @@ -1034,17 +1099,19 @@ }, "node_modules/@cloudflare/workers-types": { "version": "4.20260120.0", - "resolved": "https://mirrors.tencent.com/npm/@cloudflare/workers-types/-/workers-types-4.20260120.0.tgz", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260120.0.tgz", "integrity": "sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==", "license": "MIT OR Apache-2.0", - "optional": true + "optional": true, + "peer": true }, "node_modules/@discordjs/voice": { "version": "0.19.0", - "resolved": "https://mirrors.tencent.com/npm/@discordjs/voice/-/voice-0.19.0.tgz", + "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.0.tgz", "integrity": "sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "@types/ws": "^8.18.1", "discord-api-types": "^0.38.16", @@ -1061,19 +1128,21 @@ }, "node_modules/@emnapi/runtime": { "version": "1.8.1", - "resolved": "https://mirrors.tencent.com/npm/@emnapi/runtime/-/runtime-1.8.1.tgz", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@google/genai": { "version": "1.34.0", - "resolved": "https://mirrors.tencent.com/npm/@google/genai/-/genai-1.34.0.tgz", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.34.0.tgz", "integrity": "sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" @@ -1092,9 +1161,10 @@ }, "node_modules/@grammyjs/runner": { "version": "2.0.3", - "resolved": "https://mirrors.tencent.com/npm/@grammyjs/runner/-/runner-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/@grammyjs/runner/-/runner-2.0.3.tgz", "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", "license": "MIT", + "peer": true, "dependencies": { "abort-controller": "^3.0.0" }, @@ -1107,9 +1177,10 @@ }, "node_modules/@grammyjs/transformer-throttler": { "version": "1.2.1", - "resolved": "https://mirrors.tencent.com/npm/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", "integrity": "sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==", "license": "MIT", + "peer": true, "dependencies": { "bottleneck": "^2.0.0" }, @@ -1122,30 +1193,34 @@ }, "node_modules/@grammyjs/types": { "version": "3.23.0", - "resolved": "https://mirrors.tencent.com/npm/@grammyjs/types/-/types-3.23.0.tgz", + "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.23.0.tgz", "integrity": "sha512-D3jQ4UWERPsyR3op/YFudMMIPNTU47vy7L51uO9/73tMELmjO/+LX5N36/Y0CG5IQfIsz43MxiHI5rgsK0/k+g==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@hapi/boom": { "version": "9.1.4", - "resolved": "https://mirrors.tencent.com/npm/@hapi/boom/-/boom-9.1.4.tgz", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@hapi/hoek": "9.x.x" } }, "node_modules/@hapi/hoek": { "version": "9.3.0", - "resolved": "https://mirrors.tencent.com/npm/@hapi/hoek/-/hoek-9.3.0.tgz", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@homebridge/ciao": { "version": "1.3.4", - "resolved": "https://mirrors.tencent.com/npm/@homebridge/ciao/-/ciao-1.3.4.tgz", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.4.tgz", "integrity": "sha512-qK6ZgGx0wwOubq/MY6eTbhApQHBUQCvCOsTYpQE01uLvfA2/Prm6egySHlZouKaina1RPuDwfLhCmsRCxwHj3Q==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.1", "fast-deep-equal": "^3.1.3", @@ -1158,10 +1233,11 @@ }, "node_modules/@hono/node-server": { "version": "1.19.9", - "resolved": "https://mirrors.tencent.com/npm/@hono/node-server/-/node-server-1.19.9.tgz", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18.14.1" }, @@ -1171,26 +1247,28 @@ }, "node_modules/@huggingface/jinja": { "version": "0.5.4", - "resolved": "https://mirrors.tencent.com/npm/@huggingface/jinja/-/jinja-0.5.4.tgz", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.4.tgz", "integrity": "sha512-VoQJywjpjy2D88Oj0BTHRuS8JCbUgoOg5t1UGgbtGh2fRia9Dx/k6Wf8FqrEWIvWK9fAkfJeeLB9fcSpCNPCpw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" } }, "node_modules/@img/colour": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/@img/colour/-/colour-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" @@ -1200,6 +1278,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1212,7 +1291,7 @@ }, "node_modules/@img/sharp-darwin-x64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" @@ -1222,6 +1301,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1234,7 +1314,7 @@ }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" @@ -1244,13 +1324,14 @@ "os": [ "darwin" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-x64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" @@ -1260,13 +1341,14 @@ "os": [ "darwin" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" @@ -1276,13 +1358,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" @@ -1292,13 +1375,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-ppc64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" @@ -1308,13 +1392,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-riscv64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", "cpu": [ "riscv64" @@ -1324,13 +1409,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-s390x": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" @@ -1340,13 +1426,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" @@ -1356,13 +1443,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" @@ -1372,13 +1460,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { "version": "1.2.4", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" @@ -1388,13 +1477,14 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-linux-arm": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" @@ -1404,6 +1494,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1416,7 +1507,7 @@ }, "node_modules/@img/sharp-linux-arm64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" @@ -1426,6 +1517,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1438,7 +1530,7 @@ }, "node_modules/@img/sharp-linux-ppc64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ "ppc64" @@ -1448,6 +1540,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1460,7 +1553,7 @@ }, "node_modules/@img/sharp-linux-riscv64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", "cpu": [ "riscv64" @@ -1470,6 +1563,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1482,7 +1576,7 @@ }, "node_modules/@img/sharp-linux-s390x": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" @@ -1492,6 +1586,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1504,7 +1599,7 @@ }, "node_modules/@img/sharp-linux-x64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" @@ -1514,6 +1609,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1526,7 +1622,7 @@ }, "node_modules/@img/sharp-linuxmusl-arm64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" @@ -1536,6 +1632,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1548,7 +1645,7 @@ }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" @@ -1558,6 +1655,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1570,13 +1668,14 @@ }, "node_modules/@img/sharp-wasm32": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, + "peer": true, "dependencies": { "@emnapi/runtime": "^1.7.0" }, @@ -1589,7 +1688,7 @@ }, "node_modules/@img/sharp-win32-arm64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" @@ -1599,6 +1698,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1608,7 +1708,7 @@ }, "node_modules/@img/sharp-win32-ia32": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" @@ -1618,6 +1718,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1627,7 +1728,7 @@ }, "node_modules/@img/sharp-win32-x64": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" @@ -1637,6 +1738,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -1646,18 +1748,20 @@ }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "license": "MIT", + "peer": true, "engines": { "node": "20 || >=22" } }, "node_modules/@isaacs/brace-expansion": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", "license": "MIT", + "peer": true, "dependencies": { "@isaacs/balanced-match": "^4.0.1" }, @@ -1667,9 +1771,10 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://mirrors.tencent.com/npm/@isaacs/cliui/-/cliui-8.0.2.tgz", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "license": "ISC", + "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1684,9 +1789,10 @@ }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "license": "ISC", + "peer": true, "dependencies": { "minipass": "^7.0.4" }, @@ -1696,9 +1802,10 @@ }, "node_modules/@keyv/bigmap": { "version": "1.3.1", - "resolved": "https://mirrors.tencent.com/npm/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", "license": "MIT", + "peer": true, "dependencies": { "hashery": "^1.4.0", "hookified": "^1.15.0" @@ -1712,32 +1819,36 @@ }, "node_modules/@keyv/serialize": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/@keyv/serialize/-/serialize-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "debug": "^4.1.1" } }, "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@line/bot-sdk": { "version": "10.6.0", - "resolved": "https://mirrors.tencent.com/npm/@line/bot-sdk/-/bot-sdk-10.6.0.tgz", + "resolved": "https://registry.npmjs.org/@line/bot-sdk/-/bot-sdk-10.6.0.tgz", "integrity": "sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@types/node": "^24.0.0" }, @@ -1750,23 +1861,27 @@ }, "node_modules/@line/bot-sdk/node_modules/@types/node": { "version": "24.10.9", - "resolved": "https://mirrors.tencent.com/npm/@types/node/-/node-24.10.9.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@line/bot-sdk/node_modules/undici-types": { "version": "7.16.0", - "resolved": "https://mirrors.tencent.com/npm/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT", + "peer": true }, "node_modules/@lydell/node-pty": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty/-/node-pty-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.3.tgz", "integrity": "sha512-ngGAItlRhmJXrhspxt8kX13n1dVFqzETOq0m/+gqSkO8NJBvNMwP7FZckMwps2UFySdr4yxCXNGu/bumg5at6A==", "license": "MIT", + "peer": true, "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.3", "@lydell/node-pty-darwin-x64": "1.2.0-beta.3", @@ -1778,7 +1893,7 @@ }, "node_modules/@lydell/node-pty-darwin-arm64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-owcv+e1/OSu3bf9ZBdUQqJsQF888KyuSIiPYFNn0fLhgkhm9F3Pvha76Kj5mCPnodf7hh3suDe7upw7GPRXftQ==", "cpu": [ "arm64" @@ -1787,11 +1902,12 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@lydell/node-pty-darwin-x64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.3.tgz", "integrity": "sha512-k38O+UviWrWdxtqZBBc/D8NJU11Rey8Y2YMwSWNxLv3eXZZdF5IVpbBkI/2RmLsV5nCcciqLPbukxeZnEfPlwA==", "cpu": [ "x64" @@ -1800,11 +1916,12 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@lydell/node-pty-linux-arm64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-HUwRpGu3O+4sv9DAQFKnyW5LYhyYu2SDUa/bdFO/t4dIFCM4uDJEq47wfRM7+aYtJTi1b3lakN8SlWeuFQqJQQ==", "cpu": [ "arm64" @@ -1813,11 +1930,12 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@lydell/node-pty-linux-x64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.3.tgz", "integrity": "sha512-+RRY0PoCUeQaCvPR7/UnkGbxulwbFtoTWJfe+o4T1RcNtngrgaI55I9nl8CD8uqhGrB3smKuyvPM5UtwGhASUw==", "cpu": [ "x64" @@ -1826,11 +1944,12 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@lydell/node-pty-win32-arm64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-UEDd9ASp2M3iIYpIzfmfBlpyn4+K1G4CAjYcHWStptCkefoSVXWTiUBIa1KjBjZi3/xmsHIDpBEYTkGWuvLt2Q==", "cpu": [ "arm64" @@ -1839,11 +1958,12 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@lydell/node-pty-win32-x64": { "version": "1.2.0-beta.3", - "resolved": "https://mirrors.tencent.com/npm/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.3.tgz", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.3.tgz", "integrity": "sha512-TpdqSFYx7/Rj+68tuP6F/lkRYrHCYAIJgaS1bx3SctTkb5QAQCFwOKHd4xlsivmEOMT2LdhkJggPxwX9PAO5pQ==", "cpu": [ "x64" @@ -1852,13 +1972,15 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@mariozechner/clipboard": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard/-/clipboard-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.0.tgz", "integrity": "sha512-tQrCRAtr58BLmWcvwCqlJo5GJgqBGb3zwOBFFBKCEKvRgD8y/EawhCyXsfOh9XOOde1NTAYsYuYyVOYw2tLnoQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10" }, @@ -1876,7 +1998,7 @@ }, "node_modules/@mariozechner/clipboard-darwin-arm64": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.0.tgz", "integrity": "sha512-7i4bitLzRSij0fj6q6tPmmf+JrwHqfBsBmf8mOcLVv0LVexD+4gEsyMait4i92exKYmCfna6uHKVS84G4nqehg==", "cpu": [ "arm64" @@ -1886,26 +2008,28 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-darwin-universal": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.0.tgz", "integrity": "sha512-FVZLGdIkmvqtPQjD0GQwKLVheL+zV7DjA6I5NcsHGjBeWpG2nACS6COuelNf8ruMoPxJFw7RoB4fjw6mmjT+Nw==", "license": "MIT", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-darwin-x64": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.0.tgz", "integrity": "sha512-KuurQYEqRhalvBji3CH5xIq1Ts23IgVRE3rjanhqFDI77luOhCnlNbDtqv3No5OxJhEBLykQNrAzfgjqPsPWdA==", "cpu": [ "x64" @@ -1915,13 +2039,14 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.0.tgz", "integrity": "sha512-nWpGMlk43bch7ztGfnALcSi5ZREVziPYzrFKjoJimbwaiULrfY0fGce0gWBynP9ak0nHgDLp0nSa7b4cCl+cIw==", "cpu": [ "arm64" @@ -1931,13 +2056,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.0.tgz", "integrity": "sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ==", "cpu": [ "riscv64" @@ -1947,13 +2073,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-x64-gnu": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.0.tgz", "integrity": "sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA==", "cpu": [ "x64" @@ -1963,13 +2090,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-x64-musl": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.0.tgz", "integrity": "sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA==", "cpu": [ "x64" @@ -1979,13 +2107,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.0.tgz", "integrity": "sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg==", "cpu": [ "arm64" @@ -1995,13 +2124,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-win32-x64-msvc": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.0.tgz", "integrity": "sha512-dfpHrUpKHl7ad3xVGE1+gIN3cEnjjPZa4I0BIYMuj2OKq07Gf1FKTXMypB41rDFv6XNzcfhYQnY+ZNgIu9FB8A==", "cpu": [ "x64" @@ -2011,15 +2141,17 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/jiti": { "version": "2.6.5", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/jiti/-/jiti-2.6.5.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", "license": "MIT", + "peer": true, "dependencies": { "std-env": "^3.10.0", "yoctocolors": "^2.1.2" @@ -2030,9 +2162,10 @@ }, "node_modules/@mariozechner/pi-agent-core": { "version": "0.49.3", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/pi-agent-core/-/pi-agent-core-0.49.3.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.49.3.tgz", "integrity": "sha512-YL3PrLA8//Cklx58GJBUyNBCVLIOtK+wpAgqimuR03EgToaGPkSM7B/1S4CP4pFkr7b3DTIsC6t++tK7LgfjQg==", "license": "MIT", + "peer": true, "dependencies": { "@mariozechner/pi-ai": "^0.49.3", "@mariozechner/pi-tui": "^0.49.3" @@ -2043,9 +2176,10 @@ }, "node_modules/@mariozechner/pi-ai": { "version": "0.49.3", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/pi-ai/-/pi-ai-0.49.3.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.49.3.tgz", "integrity": "sha512-FYck4TPrF7ps3WBKxLnBQdda9OXUWN6rukni0LgK8m/GpMAXGienHouDrWPn0XIgTwrz5r7SGI3sfsEYslCICA==", "license": "MIT", + "peer": true, "dependencies": { "@anthropic-ai/sdk": "0.71.2", "@aws-sdk/client-bedrock-runtime": "^3.966.0", @@ -2068,9 +2202,10 @@ }, "node_modules/@mariozechner/pi-coding-agent": { "version": "0.49.3", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.49.3.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.49.3.tgz", "integrity": "sha512-V/Fsq0PeYB5svmw5lZsbD/glmkXofegQcPSKecK2ab3VihKwQp/MQfYjyK6nY4b/B1HIugatcDWaH5WvPKIKwg==", "license": "MIT", + "peer": true, "dependencies": { "@mariozechner/clipboard": "^0.3.0", "@mariozechner/jiti": "^2.6.2", @@ -2097,9 +2232,10 @@ }, "node_modules/@mariozechner/pi-tui": { "version": "0.49.3", - "resolved": "https://mirrors.tencent.com/npm/@mariozechner/pi-tui/-/pi-tui-0.49.3.tgz", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.49.3.tgz", "integrity": "sha512-SyBtQ0B9A/8V4eX7z3l9Q7sEVAnSueNJ1gC6+nRakDBfBOSxuqA61vz6tic0C7Jj46NERRuvJKdQSmk1VP5sUA==", "license": "MIT", + "peer": true, "dependencies": { "@types/mime-types": "^2.1.4", "chalk": "^5.5.0", @@ -2113,8 +2249,9 @@ }, "node_modules/@mistralai/mistralai": { "version": "1.10.0", - "resolved": "https://mirrors.tencent.com/npm/@mistralai/mistralai/-/mistralai-1.10.0.tgz", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.10.0.tgz", "integrity": "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==", + "peer": true, "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" @@ -2122,28 +2259,31 @@ }, "node_modules/@mistralai/mistralai/node_modules/zod": { "version": "3.25.76", - "resolved": "https://mirrors.tencent.com/npm/zod/-/zod-3.25.76.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/@mozilla/readability": { "version": "0.6.0", - "resolved": "https://mirrors.tencent.com/npm/@mozilla/readability/-/readability-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14.0.0" } }, "node_modules/@napi-rs/canvas": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas/-/canvas-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.89.tgz", "integrity": "sha512-7GjmkMirJHejeALCqUnZY3QwID7bbumOiLrqq2LKgxrdjdmxWQBTc6rcASa2u8wuWrH7qo4/4n/VNrOwCoKlKg==", "license": "MIT", "optional": true, + "peer": true, "workspaces": [ "e2e/*" ], @@ -2170,7 +2310,7 @@ }, "node_modules/@napi-rs/canvas-android-arm64": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.89.tgz", "integrity": "sha512-CXxQTXsjtQqKGENS8Ejv9pZOFJhOPIl2goenS+aU8dY4DygvkyagDhy/I07D1YLqrDtPvLEX5zZHt8qUdnuIpQ==", "cpu": [ "arm64" @@ -2180,6 +2320,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2190,7 +2331,7 @@ }, "node_modules/@napi-rs/canvas-darwin-arm64": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.89.tgz", "integrity": "sha512-k29cR/Zl20WLYM7M8YePevRu2VQRaKcRedYr1V/8FFHkyIQ8kShEV+MPoPGi+znvmd17Eqjy2Pk2F2kpM2umVg==", "cpu": [ "arm64" @@ -2200,6 +2341,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2210,7 +2352,7 @@ }, "node_modules/@napi-rs/canvas-darwin-x64": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.89.tgz", "integrity": "sha512-iUragqhBrA5FqU13pkhYBDbUD1WEAIlT8R2+fj6xHICY2nemzwMUI8OENDhRh7zuL06YDcRwENbjAVxOmaX9jg==", "cpu": [ "x64" @@ -2220,6 +2362,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2230,7 +2373,7 @@ }, "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.89.tgz", "integrity": "sha512-y3SM9sfDWasY58ftoaI09YBFm35Ig8tosZqgahLJ2WGqawCusGNPV9P0/4PsrLOCZqGg629WxexQMY25n7zcvA==", "cpu": [ "arm" @@ -2240,6 +2383,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2250,7 +2394,7 @@ }, "node_modules/@napi-rs/canvas-linux-arm64-gnu": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.89.tgz", "integrity": "sha512-NEoF9y8xq5fX8HG8aZunBom1ILdTwt7ayBzSBIwrmitk7snj4W6Fz/yN/ZOmlM1iyzHDNX5Xn0n+VgWCF8BEdA==", "cpu": [ "arm64" @@ -2260,6 +2404,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2270,7 +2415,7 @@ }, "node_modules/@napi-rs/canvas-linux-arm64-musl": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.89.tgz", "integrity": "sha512-UQQkIEzV12/l60j1ziMjZ+mtodICNUbrd205uAhbyTw0t60CrC/EsKb5/aJWGq1wM0agvcgZV72JJCKfLS6+4w==", "cpu": [ "arm64" @@ -2280,6 +2425,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2290,7 +2436,7 @@ }, "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.89.tgz", "integrity": "sha512-1/VmEoFaIO6ONeeEMGoWF17wOYZOl5hxDC1ios2Bkz/oQjbJJ8DY/X22vWTmvuUKWWhBVlo63pxLGZbjJU/heA==", "cpu": [ "riscv64" @@ -2300,6 +2446,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2310,7 +2457,7 @@ }, "node_modules/@napi-rs/canvas-linux-x64-gnu": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.89.tgz", "integrity": "sha512-ebLuqkCuaPIkKgKH9q4+pqWi1tkPOfiTk5PM1LKR1tB9iO9sFNVSIgwEp+SJreTSbA2DK5rW8lQXiN78SjtcvA==", "cpu": [ "x64" @@ -2320,6 +2467,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2330,7 +2478,7 @@ }, "node_modules/@napi-rs/canvas-linux-x64-musl": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.89.tgz", "integrity": "sha512-w+5qxHzplvA4BkHhCaizNMLLXiI+CfP84YhpHm/PqMub4u8J0uOAv+aaGv40rYEYra5hHRWr9LUd6cfW32o9/A==", "cpu": [ "x64" @@ -2340,6 +2488,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2350,7 +2499,7 @@ }, "node_modules/@napi-rs/canvas-win32-arm64-msvc": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.89.tgz", "integrity": "sha512-DmyXa5lJHcjOsDC78BM3bnEECqbK3xASVMrKfvtT/7S7Z8NGQOugvu+L7b41V6cexCd34mBWgMOsjoEBceeB1Q==", "cpu": [ "arm64" @@ -2360,6 +2509,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2370,7 +2520,7 @@ }, "node_modules/@napi-rs/canvas-win32-x64-msvc": { "version": "0.1.89", - "resolved": "https://mirrors.tencent.com/npm/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.89.tgz", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.89.tgz", "integrity": "sha512-WMej0LZrIqIncQcx0JHaMXlnAG7sncwJh7obs/GBgp0xF9qABjwoRwIooMWCZkSansapKGNUHhamY6qEnFN7gA==", "cpu": [ "x64" @@ -2380,6 +2530,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" }, @@ -2390,7 +2541,7 @@ }, "node_modules/@node-llama-cpp/linux-arm64": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-arm64/-/linux-arm64-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-arm64/-/linux-arm64-3.15.0.tgz", "integrity": "sha512-IaHIllWlj6tGjhhCtyp1w6xA7AHaGJiVaXAZ+78hDs8X1SL9ySBN2Qceju8AQJALePtynbAfjgjTqjQ7Hyk+IQ==", "cpu": [ "arm64", @@ -2401,13 +2552,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-armv7l": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.15.0.tgz", "integrity": "sha512-ZuZ3q6mejQnEP4o22la7zBv7jNR+IZfgItDm3KjAl04HUXTKJ43HpNwjnf9GyYYd+dEgtoX0MESvWz4RnGH8Jw==", "cpu": [ "arm", @@ -2418,13 +2570,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-x64/-/linux-x64-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64/-/linux-x64-3.15.0.tgz", "integrity": "sha512-etUuTqSyNefRObqc5+JZviNTkuef2XEtHcQLaamEIWwjI1dj7nTD2YMZPBP7H3M3E55HSIY82vqCQ1bp6ZILiA==", "cpu": [ "x64" @@ -2434,13 +2587,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-cuda": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.15.0.tgz", "integrity": "sha512-mDjyVulCTRYilm9Emm3lDMx7dbI1vzGqk28Pj28shartjERTUu8aUNDYOmVKNMLpUKS1akw7vy0lMF8t4qswxQ==", "cpu": [ "x64" @@ -2450,13 +2604,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-cuda-ext": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-x64-cuda-ext/-/linux-x64-cuda-ext-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda-ext/-/linux-x64-cuda-ext-3.15.0.tgz", "integrity": "sha512-wQwgSl7Qm8vH56oBt7IuWWDNNsDECkVMS000C92wl3PkbzjwZFiWzehwa+kF8Lr2BBMiCJNkI5nEabhYH3RN2Q==", "cpu": [ "x64" @@ -2466,13 +2621,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-vulkan": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.15.0.tgz", "integrity": "sha512-htVIthQKq/rr8v5e7NiVtcHsstqTBAAC50kUymmDMbrzAu6d/EHacCJpNbU57b1UUa1nKN5cBqr6Jr+QqEalMA==", "cpu": [ "x64" @@ -2482,13 +2638,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/mac-arm64-metal": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.15.0.tgz", "integrity": "sha512-3Vkq6bpyQZaIzoaLLP7H2Tt8ty5BS0zxUY2pX0ox2S9P4fp8Au0CCJuUJF4V+EKi+/PTn70A6R1QCkRMfMQJig==", "cpu": [ "arm64", @@ -2499,13 +2656,14 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/mac-x64": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/mac-x64/-/mac-x64-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-x64/-/mac-x64-3.15.0.tgz", "integrity": "sha512-BUrmLu0ySveEYv2YzFIjqnWWAqjTZfRHuzoFLaZwqIJ86Jzycm9tzxJub4wfJCj6ixeuWyI1sUdNGIw4/2E03Q==", "cpu": [ "x64" @@ -2515,13 +2673,14 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-arm64": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/win-arm64/-/win-arm64-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-arm64/-/win-arm64-3.15.0.tgz", "integrity": "sha512-GwhqaPNpbtGDmw0Ex13hwq4jqzSZr7hw5QpRWhSKB1dHiYj6C1NLM1Vz5xiDZX+69WI/ndb+FEqGiIYFQpfmiQ==", "cpu": [ "arm64", @@ -2532,13 +2691,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/win-x64/-/win-x64-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64/-/win-x64-3.15.0.tgz", "integrity": "sha512-gWhtc8l3HOry5guO46YfFohLQnF0NfL4On0GAO8E27JiYYxHO9nHSCfFif4+U03+FfHquZXL0znJ1qPVOiwOPw==", "cpu": [ "x64" @@ -2548,13 +2708,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-cuda": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/win-x64-cuda/-/win-x64-cuda-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-cuda/-/win-x64-cuda-3.15.0.tgz", "integrity": "sha512-2Kyu1roDwXwFLaJgGZQISIXP9lCDZtJCx/DRcmrYRHcSUFCzo5ikOuAECyliSSQmRUAvvlRCuD+GrTcegbhojA==", "cpu": [ "x64" @@ -2564,13 +2725,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-cuda-ext": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/win-x64-cuda-ext/-/win-x64-cuda-ext-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-cuda-ext/-/win-x64-cuda-ext-3.15.0.tgz", "integrity": "sha512-KQoNH9KsVtqGVXaRdPrnHPrg5w3KOM7CfynPmG1m16gmjmDSIspdPg/Dbg6DgHBfkdAzt+duRZEBk8Bg8KbDHw==", "cpu": [ "x64" @@ -2580,13 +2742,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-vulkan": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/@node-llama-cpp/win-x64-vulkan/-/win-x64-vulkan-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-vulkan/-/win-x64-vulkan-3.15.0.tgz", "integrity": "sha512-sH+K7lO49WrUvCCC3RPddCBrn2ZQwKCXKL90P/NZicMRgxTPFZEVSU2jXR/bu1K8B+4lNN+z5OEbjSYs7cKEcA==", "cpu": [ "x64" @@ -2596,16 +2759,18 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@octokit/app": { "version": "16.1.2", - "resolved": "https://mirrors.tencent.com/npm/@octokit/app/-/app-16.1.2.tgz", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-16.1.2.tgz", "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/auth-app": "^8.1.2", "@octokit/auth-unauthenticated": "^7.0.3", @@ -2621,10 +2786,11 @@ }, "node_modules/@octokit/auth-app": { "version": "8.1.2", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-app/-/auth-app-8.1.2.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-8.1.2.tgz", "integrity": "sha512-db8VO0PqXxfzI6GdjtgEFHY9tzqUql5xMFXYA12juq8TeTgPAuiiP3zid4h50lwlIP457p5+56PnJOgd2GGBuw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.3", "@octokit/auth-oauth-user": "^6.0.2", @@ -2641,10 +2807,11 @@ }, "node_modules/@octokit/auth-oauth-app": { "version": "9.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/auth-oauth-user": "^6.0.2", @@ -2658,10 +2825,11 @@ }, "node_modules/@octokit/auth-oauth-device": { "version": "8.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", @@ -2674,10 +2842,11 @@ }, "node_modules/@octokit/auth-oauth-user": { "version": "6.0.2", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", @@ -2691,20 +2860,22 @@ }, "node_modules/@octokit/auth-token": { "version": "6.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-unauthenticated": { "version": "7.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0" @@ -2713,12 +2884,33 @@ "node": ">= 20" } }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/@octokit/endpoint": { "version": "11.0.2", - "resolved": "https://mirrors.tencent.com/npm/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" @@ -2729,10 +2921,11 @@ }, "node_modules/@octokit/graphql": { "version": "9.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/graphql/-/graphql-9.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", @@ -2744,10 +2937,11 @@ }, "node_modules/@octokit/oauth-app": { "version": "8.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.2", "@octokit/auth-oauth-user": "^6.0.1", @@ -2764,20 +2958,22 @@ }, "node_modules/@octokit/oauth-authorization-url": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@octokit/oauth-methods": { "version": "6.0.2", - "resolved": "https://mirrors.tencent.com/npm/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/oauth-authorization-url": "^8.0.0", "@octokit/request": "^10.0.6", @@ -2790,24 +2986,27 @@ }, "node_modules/@octokit/openapi-types": { "version": "27.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@octokit/openapi-webhooks-types": { "version": "12.1.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@octokit/plugin-paginate-graphql": { "version": "6.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 20" }, @@ -2817,10 +3016,11 @@ }, "node_modules/@octokit/plugin-paginate-rest": { "version": "14.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -2833,10 +3033,11 @@ }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "17.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -2849,10 +3050,11 @@ }, "node_modules/@octokit/plugin-retry": { "version": "8.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz", "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", @@ -2867,10 +3069,11 @@ }, "node_modules/@octokit/plugin-throttling": { "version": "11.0.3", - "resolved": "https://mirrors.tencent.com/npm/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" @@ -2884,10 +3087,11 @@ }, "node_modules/@octokit/request": { "version": "10.0.7", - "resolved": "https://mirrors.tencent.com/npm/@octokit/request/-/request-10.0.7.tgz", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", @@ -2901,10 +3105,11 @@ }, "node_modules/@octokit/request-error": { "version": "7.1.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/request-error/-/request-error-7.1.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -2914,20 +3119,22 @@ }, "node_modules/@octokit/types": { "version": "16.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/types/-/types-16.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@octokit/webhooks": { "version": "14.2.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/webhooks/-/webhooks-14.2.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-14.2.0.tgz", "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/openapi-webhooks-types": "12.1.0", "@octokit/request-error": "^7.0.0", @@ -2939,59 +3146,67 @@ }, "node_modules/@octokit/webhooks-methods": { "version": "6.0.0", - "resolved": "https://mirrors.tencent.com/npm/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@pinojs/redact": { "version": "0.4.0", - "resolved": "https://mirrors.tencent.com/npm/@pinojs/redact/-/redact-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://mirrors.tencent.com/npm/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/base64/-/base64-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -2999,40 +3214,46 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/float/-/float-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/path/-/path-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/pool/-/pool-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@reflink/reflink": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink/-/reflink-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink/-/reflink-0.1.19.tgz", "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 10" }, @@ -3049,7 +3270,7 @@ }, "node_modules/@reflink/reflink-darwin-arm64": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", "cpu": [ "arm64" @@ -3059,13 +3280,14 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-darwin-x64": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", "cpu": [ "x64" @@ -3075,13 +3297,14 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-arm64-gnu": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==", "cpu": [ "arm64" @@ -3091,13 +3314,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-arm64-musl": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==", "cpu": [ "arm64" @@ -3107,13 +3331,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-x64-gnu": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==", "cpu": [ "x64" @@ -3123,13 +3348,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-x64-musl": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==", "cpu": [ "x64" @@ -3139,13 +3365,14 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-win32-arm64-msvc": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==", "cpu": [ "arm64" @@ -3155,13 +3382,14 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-win32-x64-msvc": { "version": "0.1.19", - "resolved": "https://mirrors.tencent.com/npm/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==", "cpu": [ "x64" @@ -3171,27 +3399,31 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", - "resolved": "https://mirrors.tencent.com/npm/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@sinclair/typebox": { "version": "0.34.47", - "resolved": "https://mirrors.tencent.com/npm/@sinclair/typebox/-/typebox-0.34.47.tgz", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@slack/bolt": { "version": "4.6.0", - "resolved": "https://mirrors.tencent.com/npm/@slack/bolt/-/bolt-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.6.0.tgz", "integrity": "sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==", "license": "MIT", + "peer": true, "dependencies": { "@slack/logger": "^4.0.0", "@slack/oauth": "^3.0.4", @@ -3214,9 +3446,10 @@ }, "node_modules/@slack/logger": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/@slack/logger/-/logger-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": ">=18.0.0" }, @@ -3227,9 +3460,10 @@ }, "node_modules/@slack/oauth": { "version": "3.0.4", - "resolved": "https://mirrors.tencent.com/npm/@slack/oauth/-/oauth-3.0.4.tgz", + "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.4.tgz", "integrity": "sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==", "license": "MIT", + "peer": true, "dependencies": { "@slack/logger": "^4", "@slack/web-api": "^7.10.0", @@ -3244,9 +3478,10 @@ }, "node_modules/@slack/socket-mode": { "version": "2.0.5", - "resolved": "https://mirrors.tencent.com/npm/@slack/socket-mode/-/socket-mode-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-2.0.5.tgz", "integrity": "sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==", "license": "MIT", + "peer": true, "dependencies": { "@slack/logger": "^4", "@slack/web-api": "^7.10.0", @@ -3262,9 +3497,10 @@ }, "node_modules/@slack/types": { "version": "2.19.0", - "resolved": "https://mirrors.tencent.com/npm/@slack/types/-/types-2.19.0.tgz", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.19.0.tgz", "integrity": "sha512-7+QZ38HGcNh/b/7MpvPG6jnw7mliV6UmrquJLqgdxkzJgQEYUcEztvFWRU49z0x4vthF0ixL5lTK601AXrS8IA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 12.13.0", "npm": ">= 6.12.0" @@ -3272,9 +3508,10 @@ }, "node_modules/@slack/web-api": { "version": "7.13.0", - "resolved": "https://mirrors.tencent.com/npm/@slack/web-api/-/web-api-7.13.0.tgz", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.13.0.tgz", "integrity": "sha512-ERcExbWrnkDN8ovoWWe6Wgt/usanj1dWUd18dJLpctUI4mlPS0nKt81Joh8VI+OPbNnY1lIilVt9gdMBD9U2ig==", "license": "MIT", + "peer": true, "dependencies": { "@slack/logger": "^4.0.0", "@slack/types": "^2.18.0", @@ -3296,9 +3533,10 @@ }, "node_modules/@smithy/abort-controller": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3309,9 +3547,10 @@ }, "node_modules/@smithy/config-resolver": { "version": "4.4.6", - "resolved": "https://mirrors.tencent.com/npm/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", @@ -3326,9 +3565,10 @@ }, "node_modules/@smithy/core": { "version": "3.22.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/core/-/core-3.22.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.22.0.tgz", "integrity": "sha512-6vjCHD6vaY8KubeNw2Fg3EK0KLGQYdldG4fYgQmA0xSW0dJ8G2xFhSOdrlUakWVoP5JuWHtFODg3PNd/DN3FDA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", @@ -3347,9 +3587,10 @@ }, "node_modules/@smithy/credential-provider-imds": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", @@ -3363,9 +3604,10 @@ }, "node_modules/@smithy/eventstream-codec": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.12.0", @@ -3378,9 +3620,10 @@ }, "node_modules/@smithy/eventstream-serde-browser": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.8", "@smithy/types": "^4.12.0", @@ -3392,9 +3635,10 @@ }, "node_modules/@smithy/eventstream-serde-config-resolver": { "version": "4.3.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3405,9 +3649,10 @@ }, "node_modules/@smithy/eventstream-serde-node": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.8", "@smithy/types": "^4.12.0", @@ -3419,9 +3664,10 @@ }, "node_modules/@smithy/eventstream-serde-universal": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/eventstream-codec": "^4.2.8", "@smithy/types": "^4.12.0", @@ -3433,9 +3679,10 @@ }, "node_modules/@smithy/fetch-http-handler": { "version": "5.3.9", - "resolved": "https://mirrors.tencent.com/npm/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", @@ -3449,9 +3696,10 @@ }, "node_modules/@smithy/hash-node": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/hash-node/-/hash-node-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-buffer-from": "^4.2.0", @@ -3464,9 +3712,10 @@ }, "node_modules/@smithy/invalid-dependency": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3477,9 +3726,10 @@ }, "node_modules/@smithy/is-array-buffer": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3489,9 +3739,10 @@ }, "node_modules/@smithy/middleware-content-length": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", @@ -3503,9 +3754,10 @@ }, "node_modules/@smithy/middleware-endpoint": { "version": "4.4.12", - "resolved": "https://mirrors.tencent.com/npm/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.12.tgz", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.12.tgz", "integrity": "sha512-9JMKHVJtW9RysTNjcBZQHDwB0p3iTP6B1IfQV4m+uCevkVd/VuLgwfqk5cnI4RHcp4cPwoIvxQqN4B1sxeHo8Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/core": "^3.22.0", "@smithy/middleware-serde": "^4.2.9", @@ -3522,9 +3774,10 @@ }, "node_modules/@smithy/middleware-retry": { "version": "4.4.29", - "resolved": "https://mirrors.tencent.com/npm/@smithy/middleware-retry/-/middleware-retry-4.4.29.tgz", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.29.tgz", "integrity": "sha512-bmTn75a4tmKRkC5w61yYQLb3DmxNzB8qSVu9SbTYqW6GAL0WXO2bDZuMAn/GJSbOdHEdjZvWxe+9Kk015bw6Cg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", @@ -3542,9 +3795,10 @@ }, "node_modules/@smithy/middleware-serde": { "version": "4.2.9", - "resolved": "https://mirrors.tencent.com/npm/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", @@ -3556,9 +3810,10 @@ }, "node_modules/@smithy/middleware-stack": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3569,9 +3824,10 @@ }, "node_modules/@smithy/node-config-provider": { "version": "4.3.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", @@ -3584,9 +3840,10 @@ }, "node_modules/@smithy/node-http-handler": { "version": "4.4.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", "integrity": "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/protocol-http": "^5.3.8", @@ -3600,9 +3857,10 @@ }, "node_modules/@smithy/property-provider": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/property-provider/-/property-provider-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3613,9 +3871,10 @@ }, "node_modules/@smithy/protocol-http": { "version": "5.3.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3626,9 +3885,10 @@ }, "node_modules/@smithy/querystring-builder": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", @@ -3640,9 +3900,10 @@ }, "node_modules/@smithy/querystring-parser": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3653,9 +3914,10 @@ }, "node_modules/@smithy/service-error-classification": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0" }, @@ -3665,9 +3927,10 @@ }, "node_modules/@smithy/shared-ini-file-loader": { "version": "4.4.3", - "resolved": "https://mirrors.tencent.com/npm/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3678,9 +3941,10 @@ }, "node_modules/@smithy/signature-v4": { "version": "5.3.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.8", @@ -3697,9 +3961,10 @@ }, "node_modules/@smithy/smithy-client": { "version": "4.11.1", - "resolved": "https://mirrors.tencent.com/npm/@smithy/smithy-client/-/smithy-client-4.11.1.tgz", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.11.1.tgz", "integrity": "sha512-SERgNg5Z1U+jfR6/2xPYjSEHY1t3pyTHC/Ma3YQl6qWtmiL42bvNId3W/oMUWIwu7ekL2FMPdqAmwbQegM7HeQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/core": "^3.22.0", "@smithy/middleware-endpoint": "^4.4.12", @@ -3715,9 +3980,10 @@ }, "node_modules/@smithy/types": { "version": "4.12.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/types/-/types-4.12.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3727,9 +3993,10 @@ }, "node_modules/@smithy/url-parser": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/url-parser/-/url-parser-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/querystring-parser": "^4.2.8", "@smithy/types": "^4.12.0", @@ -3741,9 +4008,10 @@ }, "node_modules/@smithy/util-base64": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", @@ -3755,9 +4023,10 @@ }, "node_modules/@smithy/util-body-length-browser": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3767,9 +4036,10 @@ }, "node_modules/@smithy/util-body-length-node": { "version": "4.2.1", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3779,9 +4049,10 @@ }, "node_modules/@smithy/util-buffer-from": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" @@ -3792,9 +4063,10 @@ }, "node_modules/@smithy/util-config-provider": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3804,9 +4076,10 @@ }, "node_modules/@smithy/util-defaults-mode-browser": { "version": "4.3.28", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.28.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.28.tgz", "integrity": "sha512-/9zcatsCao9h6g18p/9vH9NIi5PSqhCkxQ/tb7pMgRFnqYp9XUOyOlGPDMHzr8n5ih6yYgwJEY2MLEobUgi47w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.11.1", @@ -3819,9 +4092,10 @@ }, "node_modules/@smithy/util-defaults-mode-node": { "version": "4.2.31", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.31.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.31.tgz", "integrity": "sha512-JTvoApUXA5kbpceI2vuqQzRjeTbLpx1eoa5R/YEZbTgtxvIB7AQZxFJ0SEyfCpgPCyVV9IT7we+ytSeIB3CyWA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", @@ -3837,9 +4111,10 @@ }, "node_modules/@smithy/util-endpoints": { "version": "3.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", @@ -3851,9 +4126,10 @@ }, "node_modules/@smithy/util-hex-encoding": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3863,9 +4139,10 @@ }, "node_modules/@smithy/util-middleware": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" @@ -3876,9 +4153,10 @@ }, "node_modules/@smithy/util-retry": { "version": "4.2.8", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-retry/-/util-retry-4.2.8.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/service-error-classification": "^4.2.8", "@smithy/types": "^4.12.0", @@ -3890,9 +4168,10 @@ }, "node_modules/@smithy/util-stream": { "version": "4.5.10", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-stream/-/util-stream-4.5.10.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz", "integrity": "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", @@ -3909,9 +4188,10 @@ }, "node_modules/@smithy/util-uri-escape": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3921,9 +4201,10 @@ }, "node_modules/@smithy/util-utf8": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" @@ -3934,9 +4215,10 @@ }, "node_modules/@smithy/uuid": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/@smithy/uuid/-/uuid-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -3946,10 +4228,11 @@ }, "node_modules/@tinyhttp/content-disposition": { "version": "2.2.3", - "resolved": "https://mirrors.tencent.com/npm/@tinyhttp/content-disposition/-/content-disposition-2.2.3.tgz", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.3.tgz", "integrity": "sha512-0nSvOgFHvq0a15+pZAdbAyHUk0+AGLX6oyo45b7fPdgWdPfHA19IfgUKRECYT0aw86ZP6ZDDLxGQ7FEA1fAVOg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=12.17.0" }, @@ -3960,9 +4243,10 @@ }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", - "resolved": "https://mirrors.tencent.com/npm/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" @@ -3977,22 +4261,25 @@ }, "node_modules/@tokenizer/token": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/@tokenizer/token/-/token-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/aws-lambda": { "version": "8.10.160", - "resolved": "https://mirrors.tencent.com/npm/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@types/body-parser": { "version": "1.19.6", - "resolved": "https://mirrors.tencent.com/npm/@types/body-parser/-/body-parser-1.19.6.tgz", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", + "peer": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -4000,27 +4287,30 @@ }, "node_modules/@types/bun": { "version": "1.3.6", - "resolved": "https://mirrors.tencent.com/npm/@types/bun/-/bun-1.3.6.tgz", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.6.tgz", "integrity": "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "bun-types": "1.3.6" } }, "node_modules/@types/connect": { "version": "3.4.38", - "resolved": "https://mirrors.tencent.com/npm/@types/connect/-/connect-3.4.38.tgz", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/express": { "version": "5.0.6", - "resolved": "https://mirrors.tencent.com/npm/@types/express/-/express-5.0.6.tgz", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", "peer": true, "dependencies": { "@types/body-parser": "*", @@ -4030,9 +4320,10 @@ }, "node_modules/@types/express-serve-static-core": { "version": "5.1.1", - "resolved": "https://mirrors.tencent.com/npm/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4042,15 +4333,17 @@ }, "node_modules/@types/http-errors": { "version": "2.0.5", - "resolved": "https://mirrors.tencent.com/npm/@types/http-errors/-/http-errors-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", - "resolved": "https://mirrors.tencent.com/npm/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", + "peer": true, "dependencies": { "@types/ms": "*", "@types/node": "*" @@ -4058,25 +4351,28 @@ }, "node_modules/@types/long": { "version": "4.0.2", - "resolved": "https://mirrors.tencent.com/npm/@types/long/-/long-4.0.2.tgz", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/mime-types": { "version": "2.1.4", - "resolved": "https://mirrors.tencent.com/npm/@types/mime-types/-/mime-types-2.1.4.tgz", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/@types/ms/-/ms-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/node": { "version": "20.19.30", - "resolved": "https://mirrors.tencent.com/npm/@types/node/-/node-20.19.30.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "license": "MIT", "dependencies": { @@ -4085,36 +4381,41 @@ }, "node_modules/@types/qs": { "version": "6.14.0", - "resolved": "https://mirrors.tencent.com/npm/@types/qs/-/qs-6.14.0.tgz", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://mirrors.tencent.com/npm/@types/range-parser/-/range-parser-1.2.7.tgz", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/retry": { "version": "0.12.0", - "resolved": "https://mirrors.tencent.com/npm/@types/retry/-/retry-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/send": { "version": "1.2.1", - "resolved": "https://mirrors.tencent.com/npm/@types/send/-/send-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/serve-static": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/@types/serve-static/-/serve-static-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*" @@ -4122,7 +4423,7 @@ }, "node_modules/@types/ws": { "version": "8.18.1", - "resolved": "https://mirrors.tencent.com/npm/@types/ws/-/ws-8.18.1.tgz", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", "dependencies": { @@ -4131,10 +4432,11 @@ }, "node_modules/@whiskeysockets/baileys": { "version": "7.0.0-rc.9", - "resolved": "https://mirrors.tencent.com/npm/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", + "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", "integrity": "sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@cacheable/node-cache": "^1.4.0", "@hapi/boom": "^9.1.3", @@ -4170,9 +4472,10 @@ }, "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { "version": "9.1.0", - "resolved": "https://mirrors.tencent.com/npm/p-queue/-/p-queue-9.1.0.tgz", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", "license": "MIT", + "peer": true, "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^7.0.0" @@ -4186,9 +4489,10 @@ }, "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { "version": "7.0.1", - "resolved": "https://mirrors.tencent.com/npm/p-timeout/-/p-timeout-7.0.1.tgz", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "license": "MIT", + "peer": true, "engines": { "node": ">=20" }, @@ -4198,9 +4502,10 @@ }, "node_modules/abort-controller": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/abort-controller/-/abort-controller-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", + "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -4210,8 +4515,10 @@ }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/accepts/-/accepts-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -4222,17 +4529,20 @@ }, "node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://mirrors.tencent.com/npm/agent-base/-/agent-base-7.1.4.tgz", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 14" } }, "node_modules/ajv": { "version": "8.17.1", - "resolved": "https://mirrors.tencent.com/npm/ajv/-/ajv-8.17.1.tgz", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4246,8 +4556,10 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", - "resolved": "https://mirrors.tencent.com/npm/ajv-formats/-/ajv-formats-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -4262,10 +4574,11 @@ }, "node_modules/ansi-escapes": { "version": "6.2.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14.16" }, @@ -4275,8 +4588,10 @@ }, "node_modules/ansi-regex": { "version": "6.2.2", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-6.2.2.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4286,9 +4601,10 @@ }, "node_modules/ansi-styles": { "version": "6.2.3", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-6.2.3.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4298,24 +4614,27 @@ }, "node_modules/any-promise": { "version": "1.3.0", - "resolved": "https://mirrors.tencent.com/npm/any-promise/-/any-promise-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/aproba": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/aproba/-/aproba-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/are-we-there-yet": { "version": "3.0.1", - "resolved": "https://mirrors.tencent.com/npm/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -4326,10 +4645,11 @@ }, "node_modules/are-we-there-yet/node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://mirrors.tencent.com/npm/readable-stream/-/readable-stream-3.6.2.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4341,49 +4661,55 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/argparse/-/argparse-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" + "license": "Python-2.0", + "peer": true }, "node_modules/async-mutex": { "version": "0.5.0", - "resolved": "https://mirrors.tencent.com/npm/async-mutex/-/async-mutex-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/async-retry": { "version": "1.3.3", - "resolved": "https://mirrors.tencent.com/npm/async-retry/-/async-retry-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "retry": "0.13.1" } }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://mirrors.tencent.com/npm/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/atomic-sleep": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=8.0.0" } }, "node_modules/axios": { "version": "1.13.4", - "resolved": "https://mirrors.tencent.com/npm/axios/-/axios-1.13.4.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -4392,13 +4718,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/balanced-match/-/balanced-match-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://mirrors.tencent.com/npm/base64-js/-/base64-js-1.5.1.tgz", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { @@ -4414,29 +4741,33 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/before-after-hook": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/before-after-hook/-/before-after-hook-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0", - "optional": true + "optional": true, + "peer": true }, "node_modules/bignumber.js": { "version": "9.3.1", - "resolved": "https://mirrors.tencent.com/npm/bignumber.js/-/bignumber.js-9.3.1.tgz", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", "license": "MIT", + "peer": true, "engines": { "node": "*" } }, "node_modules/body-parser": { "version": "2.2.2", - "resolved": "https://mirrors.tencent.com/npm/body-parser/-/body-parser-2.2.2.tgz", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", + "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -4458,66 +4789,76 @@ }, "node_modules/boolbase": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/boolbase/-/boolbase-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/bottleneck": { "version": "2.19.5", - "resolved": "https://mirrors.tencent.com/npm/bottleneck/-/bottleneck-2.19.5.tgz", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/bowser": { "version": "2.13.1", - "resolved": "https://mirrors.tencent.com/npm/bowser/-/bowser-2.13.1.tgz", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://mirrors.tencent.com/npm/brace-expansion/-/brace-expansion-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/buffer-from": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/buffer-from/-/buffer-from-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/bun-types": { "version": "1.3.6", - "resolved": "https://mirrors.tencent.com/npm/bun-types/-/bun-types-1.3.6.tgz", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.6.tgz", "integrity": "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://mirrors.tencent.com/npm/bytes/-/bytes-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/cacheable": { "version": "2.3.2", - "resolved": "https://mirrors.tencent.com/npm/cacheable/-/cacheable-2.3.2.tgz", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.2.tgz", "integrity": "sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==", "license": "MIT", + "peer": true, "dependencies": { "@cacheable/memory": "^2.0.7", "@cacheable/utils": "^2.3.3", @@ -4528,8 +4869,10 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -4540,8 +4883,10 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://mirrors.tencent.com/npm/call-bound/-/call-bound-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -4555,9 +4900,10 @@ }, "node_modules/chalk": { "version": "5.6.2", - "resolved": "https://mirrors.tencent.com/npm/chalk/-/chalk-5.6.2.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", + "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -4567,16 +4913,18 @@ }, "node_modules/chmodrp": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/chmodrp/-/chmodrp-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/chmodrp/-/chmodrp-1.0.2.tgz", "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/chokidar": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/chokidar/-/chokidar-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -4589,18 +4937,20 @@ }, "node_modules/chownr": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/chownr/-/chownr-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } }, "node_modules/chromium-bidi": { "version": "13.0.1", - "resolved": "https://mirrors.tencent.com/npm/chromium-bidi/-/chromium-bidi-13.0.1.tgz", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.0.1.tgz", "integrity": "sha512-c+RLxH0Vg2x2syS9wPw378oJgiJNXtYXUvnVAldUlt5uaHekn0CCU7gPksNgHjrH1qFhmjVXQj4esvuthuC7OQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" @@ -4611,16 +4961,17 @@ }, "node_modules/chromium-bidi/node_modules/zod": { "version": "3.25.76", - "resolved": "https://mirrors.tencent.com/npm/zod/-/zod-3.25.76.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/ci-info": { "version": "4.4.0", - "resolved": "https://mirrors.tencent.com/npm/ci-info/-/ci-info-4.4.0.tgz", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "funding": [ { @@ -4630,89 +4981,18 @@ ], "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/clawdbot": { - "version": "2026.1.24-3", - "resolved": "https://mirrors.tencent.com/npm/clawdbot/-/clawdbot-2026.1.24-3.tgz", - "integrity": "sha512-zt9BzhWXduq8ZZR4rfzQDurQWAgmijTTyPZCQGrn5ew6wCEwhxxEr2/NHG7IlCwcfRsKymsY4se9KMhoNz0JtQ==", - "hasInstallScript": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@agentclientprotocol/sdk": "0.13.1", - "@aws-sdk/client-bedrock": "^3.975.0", - "@buape/carbon": "0.14.0", - "@clack/prompts": "^0.11.0", - "@grammyjs/runner": "^2.0.3", - "@grammyjs/transformer-throttler": "^1.2.1", - "@homebridge/ciao": "^1.3.4", - "@line/bot-sdk": "^10.6.0", - "@lydell/node-pty": "1.2.0-beta.3", - "@mariozechner/pi-agent-core": "0.49.3", - "@mariozechner/pi-ai": "0.49.3", - "@mariozechner/pi-coding-agent": "0.49.3", - "@mariozechner/pi-tui": "0.49.3", - "@mozilla/readability": "^0.6.0", - "@sinclair/typebox": "0.34.47", - "@slack/bolt": "^4.6.0", - "@slack/web-api": "^7.13.0", - "@whiskeysockets/baileys": "7.0.0-rc.9", - "ajv": "^8.17.1", - "body-parser": "^2.2.2", - "chalk": "^5.6.2", - "chokidar": "^5.0.0", - "chromium-bidi": "13.0.1", - "cli-highlight": "^2.1.11", - "commander": "^14.0.2", - "croner": "^9.1.0", - "detect-libc": "^2.1.2", - "discord-api-types": "^0.38.37", - "dotenv": "^17.2.3", - "express": "^5.2.1", - "file-type": "^21.3.0", - "grammy": "^1.39.3", - "hono": "4.11.4", - "jiti": "^2.6.1", - "json5": "^2.2.3", - "jszip": "^3.10.1", - "linkedom": "^0.18.12", - "long": "5.3.2", - "markdown-it": "^14.1.0", - "node-edge-tts": "^1.2.9", - "osc-progress": "^0.3.0", - "pdfjs-dist": "^5.4.530", - "playwright-core": "1.58.0", - "proper-lockfile": "^4.1.2", - "qrcode-terminal": "^0.12.0", - "sharp": "^0.34.5", - "sqlite-vec": "0.1.7-alpha.2", - "tar": "7.5.4", - "tslog": "^4.10.2", - "undici": "^7.19.0", - "ws": "^8.19.0", - "yaml": "^2.8.2", - "zod": "^4.3.6" - }, - "bin": { - "clawdbot": "dist/entry.js" - }, - "engines": { - "node": ">=22.12.0" - }, - "optionalDependencies": { - "@napi-rs/canvas": "^0.1.88", - "node-llama-cpp": "3.15.0" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/cli-cursor/-/cli-cursor-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "restore-cursor": "^5.0.0" }, @@ -4725,9 +5005,10 @@ }, "node_modules/cli-highlight": { "version": "2.1.11", - "resolved": "https://mirrors.tencent.com/npm/cli-highlight/-/cli-highlight-2.1.11.tgz", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "license": "ISC", + "peer": true, "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", @@ -4746,9 +5027,10 @@ }, "node_modules/cli-highlight/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4761,9 +5043,10 @@ }, "node_modules/cli-highlight/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://mirrors.tencent.com/npm/chalk/-/chalk-4.1.2.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4777,9 +5060,11 @@ }, "node_modules/cli-spinners": { "version": "2.9.2", - "resolved": "https://mirrors.tencent.com/npm/cli-spinners/-/cli-spinners-2.9.2.tgz", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=6" }, @@ -4789,8 +5074,10 @@ }, "node_modules/cliui": { "version": "7.0.4", - "resolved": "https://mirrors.tencent.com/npm/cliui/-/cliui-7.0.4.tgz", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -4799,17 +5086,20 @@ }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4822,24 +5112,27 @@ }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4851,9 +5144,10 @@ }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4863,9 +5157,10 @@ }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4880,10 +5175,11 @@ }, "node_modules/cmake-js": { "version": "7.4.0", - "resolved": "https://mirrors.tencent.com/npm/cmake-js/-/cmake-js-7.4.0.tgz", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.4.0.tgz", "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "axios": "^1.6.5", "debug": "^4", @@ -4907,20 +5203,22 @@ }, "node_modules/cmake-js/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/cmake-js/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4933,20 +5231,22 @@ }, "node_modules/cmake-js/node_modules/chownr": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/chownr/-/chownr-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": ">=10" } }, "node_modules/cmake-js/node_modules/cliui": { "version": "8.0.1", - "resolved": "https://mirrors.tencent.com/npm/cliui/-/cliui-8.0.1.tgz", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4958,37 +5258,41 @@ }, "node_modules/cmake-js/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/cmake-js/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/cmake-js/node_modules/minipass": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/minipass/-/minipass-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/cmake-js/node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/minizlib/-/minizlib-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -4999,10 +5303,11 @@ }, "node_modules/cmake-js/node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://mirrors.tencent.com/npm/minipass/-/minipass-3.3.6.tgz", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -5012,10 +5317,11 @@ }, "node_modules/cmake-js/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5027,10 +5333,11 @@ }, "node_modules/cmake-js/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5040,10 +5347,12 @@ }, "node_modules/cmake-js/node_modules/tar": { "version": "6.2.1", - "resolved": "https://mirrors.tencent.com/npm/tar/-/tar-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -5058,10 +5367,11 @@ }, "node_modules/cmake-js/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5076,17 +5386,19 @@ }, "node_modules/cmake-js/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/yallist/-/yallist-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/cmake-js/node_modules/yargs": { "version": "17.7.2", - "resolved": "https://mirrors.tencent.com/npm/yargs/-/yargs-17.7.2.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -5102,19 +5414,21 @@ }, "node_modules/cmake-js/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://mirrors.tencent.com/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": ">=12" } }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/color-convert/-/color-convert-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5124,25 +5438,28 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://mirrors.tencent.com/npm/color-name/-/color-name-1.1.4.tgz", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/color-support": { "version": "1.1.3", - "resolved": "https://mirrors.tencent.com/npm/color-support/-/color-support-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "license": "ISC", "optional": true, + "peer": true, "bin": { "color-support": "bin.js" } }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://mirrors.tencent.com/npm/combined-stream/-/combined-stream-1.0.8.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", + "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -5152,23 +5469,28 @@ }, "node_modules/commander": { "version": "14.0.2", - "resolved": "https://mirrors.tencent.com/npm/commander/-/commander-14.0.2.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=20" } }, "node_modules/console-control-strings": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/content-disposition": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/content-disposition/-/content-disposition-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -5179,49 +5501,57 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://mirrors.tencent.com/npm/content-type/-/content-type-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://mirrors.tencent.com/npm/cookie/-/cookie-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://mirrors.tencent.com/npm/cookie-signature/-/cookie-signature-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "peer": true, "engines": { "node": ">=6.6.0" } }, "node_modules/core-util-is": { "version": "1.0.3", - "resolved": "https://mirrors.tencent.com/npm/core-util-is/-/core-util-is-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/croner": { "version": "9.1.0", - "resolved": "https://mirrors.tencent.com/npm/croner/-/croner-9.1.0.tgz", + "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz", "integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0" } }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://mirrors.tencent.com/npm/cross-spawn/-/cross-spawn-7.0.6.tgz", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5233,9 +5563,10 @@ }, "node_modules/css-select": { "version": "5.2.2", - "resolved": "https://mirrors.tencent.com/npm/css-select/-/css-select-5.2.2.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -5249,9 +5580,10 @@ }, "node_modules/css-what": { "version": "6.2.2", - "resolved": "https://mirrors.tencent.com/npm/css-what/-/css-what-6.2.2.tgz", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">= 6" }, @@ -5261,28 +5593,34 @@ }, "node_modules/cssom": { "version": "0.5.0", - "resolved": "https://mirrors.tencent.com/npm/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "license": "MIT", + "peer": true }, "node_modules/curve25519-js": { "version": "0.0.4", - "resolved": "https://mirrors.tencent.com/npm/curve25519-js/-/curve25519-js-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 12" } }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://mirrors.tencent.com/npm/debug/-/debug-4.4.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -5297,75 +5635,86 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://mirrors.tencent.com/npm/deep-extend/-/deep-extend-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=4.0.0" } }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=0.4.0" } }, "node_modules/delegates": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/delegates/-/delegates-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/depd/-/depd-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/detect-libc": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/detect-libc/-/detect-libc-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/devtools-protocol": { "version": "0.0.1575685", - "resolved": "https://mirrors.tencent.com/npm/devtools-protocol/-/devtools-protocol-0.0.1575685.tgz", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1575685.tgz", "integrity": "sha512-5dS9wazW8h5VoY3wx8GkZH1EJMv4y90WISi5OnYi6aWzSoC8kwXlxcnnETz95KdlPjNbOuL9TWBE7xDUVYTOew==", "license": "BSD-3-Clause", "peer": true }, "node_modules/diff": { "version": "8.0.3", - "resolved": "https://mirrors.tencent.com/npm/diff/-/diff-8.0.3.tgz", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.3.1" } }, "node_modules/discord-api-types": { - "version": "0.38.37", - "resolved": "https://mirrors.tencent.com/npm/discord-api-types/-/discord-api-types-0.38.37.tgz", - "integrity": "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==", + "version": "0.38.38", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.38.tgz", + "integrity": "sha512-7qcM5IeZrfb+LXW07HvoI5L+j4PQeMZXEkSm1htHAHh4Y9JSMXBWjy/r7zmUCOj4F7zNjMcm7IMWr131MT2h0Q==", "license": "MIT", + "peer": true, "workspaces": [ "scripts/actions/documentation" ] }, "node_modules/dom-serializer": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/dom-serializer/-/dom-serializer-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "license": "MIT", + "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -5377,7 +5726,7 @@ }, "node_modules/domelementtype": { "version": "2.3.0", - "resolved": "https://mirrors.tencent.com/npm/domelementtype/-/domelementtype-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { @@ -5385,13 +5734,15 @@ "url": "https://github.com/sponsors/fb55" } ], - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/domhandler": { "version": "5.0.3", - "resolved": "https://mirrors.tencent.com/npm/domhandler/-/domhandler-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -5404,9 +5755,10 @@ }, "node_modules/domutils": { "version": "3.2.2", - "resolved": "https://mirrors.tencent.com/npm/domutils/-/domutils-3.2.2.tgz", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -5418,9 +5770,10 @@ }, "node_modules/dotenv": { "version": "17.2.3", - "resolved": "https://mirrors.tencent.com/npm/dotenv/-/dotenv-17.2.3.tgz", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=12" }, @@ -5430,8 +5783,10 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/dunder-proto/-/dunder-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -5443,43 +5798,51 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://mirrors.tencent.com/npm/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://mirrors.tencent.com/npm/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "safe-buffer": "^5.0.1" } }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT", + "peer": true }, "node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-9.2.2.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/encodeurl/-/encodeurl-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/entities": { "version": "4.5.0", - "resolved": "https://mirrors.tencent.com/npm/entities/-/entities-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.12" }, @@ -5489,35 +5852,41 @@ }, "node_modules/env-var": { "version": "7.5.0", - "resolved": "https://mirrors.tencent.com/npm/env-var/-/env-var-7.5.0.tgz", + "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz", "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=10" } }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/es-define-property/-/es-define-property-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://mirrors.tencent.com/npm/es-errors/-/es-errors-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -5527,8 +5896,10 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -5541,44 +5912,54 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://mirrors.tencent.com/npm/escalade/-/escalade-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://mirrors.tencent.com/npm/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT", + "peer": true }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://mirrors.tencent.com/npm/etag/-/etag-1.8.1.tgz", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/event-target-shim/-/event-target-shim-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, "node_modules/eventemitter3": { "version": "5.0.4", - "resolved": "https://mirrors.tencent.com/npm/eventemitter3/-/eventemitter3-5.0.4.tgz", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://mirrors.tencent.com/npm/express/-/express-5.2.1.tgz", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5619,13 +6000,14 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://mirrors.tencent.com/npm/extend/-/extend-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-content-type-parse": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { @@ -5638,17 +6020,19 @@ } ], "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://mirrors.tencent.com/npm/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { "version": "3.1.0", - "resolved": "https://mirrors.tencent.com/npm/fast-uri/-/fast-uri-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { @@ -5660,11 +6044,12 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/fast-xml-parser": { "version": "5.2.5", - "resolved": "https://mirrors.tencent.com/npm/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", "funding": [ { @@ -5673,6 +6058,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "strnum": "^2.1.0" }, @@ -5682,7 +6068,7 @@ }, "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://mirrors.tencent.com/npm/fetch-blob/-/fetch-blob-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { @@ -5695,6 +6081,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -5705,9 +6092,10 @@ }, "node_modules/file-type": { "version": "21.3.0", - "resolved": "https://mirrors.tencent.com/npm/file-type/-/file-type-21.3.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", "license": "MIT", + "peer": true, "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", @@ -5723,10 +6111,11 @@ }, "node_modules/filename-reserved-regex": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -5736,10 +6125,11 @@ }, "node_modules/filenamify": { "version": "6.0.0", - "resolved": "https://mirrors.tencent.com/npm/filenamify/-/filenamify-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "filename-reserved-regex": "^3.0.0" }, @@ -5752,9 +6142,10 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://mirrors.tencent.com/npm/finalhandler/-/finalhandler-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -5773,7 +6164,7 @@ }, "node_modules/follow-redirects": { "version": "1.15.11", - "resolved": "https://mirrors.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.11.tgz", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { @@ -5781,6 +6172,8 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", + "peer": true, "engines": { "node": ">=4.0" }, @@ -5792,9 +6185,10 @@ }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://mirrors.tencent.com/npm/foreground-child/-/foreground-child-3.3.1.tgz", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", + "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -5808,8 +6202,10 @@ }, "node_modules/form-data": { "version": "4.0.5", - "resolved": "https://mirrors.tencent.com/npm/form-data/-/form-data-4.0.5.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5823,17 +6219,20 @@ }, "node_modules/form-data/node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://mirrors.tencent.com/npm/mime-db/-/mime-db-1.52.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/form-data/node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://mirrors.tencent.com/npm/mime-types/-/mime-types-2.1.35.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -5843,8 +6242,10 @@ }, "node_modules/formdata-polyfill": { "version": "4.0.10", - "resolved": "https://mirrors.tencent.com/npm/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "peer": true, "dependencies": { "fetch-blob": "^3.1.2" }, @@ -5854,27 +6255,31 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://mirrors.tencent.com/npm/forwarded/-/forwarded-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/fresh/-/fresh-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/fs-extra": { "version": "11.3.3", - "resolved": "https://mirrors.tencent.com/npm/fs-extra/-/fs-extra-11.3.3.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "license": "MIT", "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5886,10 +6291,11 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/fs-minipass/-/fs-minipass-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -5899,10 +6305,11 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://mirrors.tencent.com/npm/minipass/-/minipass-3.3.6.tgz", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -5912,26 +6319,30 @@ }, "node_modules/fs-minipass/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/yallist/-/yallist-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/function-bind/-/function-bind-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gauge": { "version": "4.0.4", - "resolved": "https://mirrors.tencent.com/npm/gauge/-/gauge-4.0.4.tgz", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -5948,44 +6359,49 @@ }, "node_modules/gauge/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/gauge/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/gauge/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/gauge/node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://mirrors.tencent.com/npm/signal-exit/-/signal-exit-3.0.7.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/gauge/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5997,10 +6413,11 @@ }, "node_modules/gauge/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6010,9 +6427,10 @@ }, "node_modules/gaxios": { "version": "7.1.3", - "resolved": "https://mirrors.tencent.com/npm/gaxios/-/gaxios-7.1.3.tgz", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -6025,9 +6443,10 @@ }, "node_modules/gcp-metadata": { "version": "8.1.2", - "resolved": "https://mirrors.tencent.com/npm/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", @@ -6039,18 +6458,20 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://mirrors.tencent.com/npm/get-caller-file/-/get-caller-file-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", + "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-east-asian-width": { "version": "1.4.0", - "resolved": "https://mirrors.tencent.com/npm/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -6060,8 +6481,10 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://mirrors.tencent.com/npm/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -6083,8 +6506,10 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/get-proto/-/get-proto-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -6095,8 +6520,10 @@ }, "node_modules/glob": { "version": "11.1.0", - "resolved": "https://mirrors.tencent.com/npm/glob/-/glob-11.1.0.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", @@ -6117,9 +6544,10 @@ }, "node_modules/google-auth-library": { "version": "10.5.0", - "resolved": "https://mirrors.tencent.com/npm/google-auth-library/-/google-auth-library-10.5.0.tgz", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -6135,17 +6563,20 @@ }, "node_modules/google-logging-utils": { "version": "1.1.3", - "resolved": "https://mirrors.tencent.com/npm/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://mirrors.tencent.com/npm/gopd/-/gopd-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -6155,13 +6586,14 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://mirrors.tencent.com/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/grammy": { "version": "1.39.3", - "resolved": "https://mirrors.tencent.com/npm/grammy/-/grammy-1.39.3.tgz", + "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.39.3.tgz", "integrity": "sha512-7arRRoOtOh9UwMwANZ475kJrWV6P3/EGNooeHlY0/SwZv4t3ZZ3Uiz9cAXK8Zg9xSdgmm8T21kx6n7SZaWvOcw==", "license": "MIT", "peer": true, @@ -6177,8 +6609,10 @@ }, "node_modules/grammy/node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://mirrors.tencent.com/npm/node-fetch/-/node-fetch-2.7.0.tgz", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6196,9 +6630,10 @@ }, "node_modules/gtoken": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/gtoken/-/gtoken-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", "license": "MIT", + "peer": true, "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" @@ -6209,16 +6644,20 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/has-flag/-/has-flag-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/has-symbols/-/has-symbols-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -6228,9 +6667,10 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", + "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -6243,16 +6683,18 @@ }, "node_modules/has-unicode": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/hashery": { "version": "1.4.0", - "resolved": "https://mirrors.tencent.com/npm/hashery/-/hashery-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz", "integrity": "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==", "license": "MIT", + "peer": true, "dependencies": { "hookified": "^1.14.0" }, @@ -6262,9 +6704,10 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://mirrors.tencent.com/npm/hasown/-/hasown-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -6274,16 +6717,17 @@ }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://mirrors.tencent.com/npm/highlight.js/-/highlight.js-10.7.3.tgz", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": "*" } }, "node_modules/hono": { "version": "4.11.4", - "resolved": "https://mirrors.tencent.com/npm/hono/-/hono-4.11.4.tgz", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", "license": "MIT", "peer": true, @@ -6293,19 +6737,21 @@ }, "node_modules/hookified": { "version": "1.15.0", - "resolved": "https://mirrors.tencent.com/npm/hookified/-/hookified-1.15.0.tgz", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.0.tgz", "integrity": "sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/html-escaper": { "version": "3.0.3", - "resolved": "https://mirrors.tencent.com/npm/html-escaper/-/html-escaper-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/htmlparser2": { "version": "10.1.0", - "resolved": "https://mirrors.tencent.com/npm/htmlparser2/-/htmlparser2-10.1.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -6314,6 +6760,8 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", + "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -6323,9 +6771,10 @@ }, "node_modules/htmlparser2/node_modules/entities": { "version": "7.0.1", - "resolved": "https://mirrors.tencent.com/npm/entities/-/entities-7.0.1.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.12" }, @@ -6335,8 +6784,10 @@ }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/http-errors/-/http-errors-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -6354,8 +6805,10 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.6", - "resolved": "https://mirrors.tencent.com/npm/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -6366,9 +6819,10 @@ }, "node_modules/iconv-lite": { "version": "0.7.2", - "resolved": "https://mirrors.tencent.com/npm/iconv-lite/-/iconv-lite-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -6382,7 +6836,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://mirrors.tencent.com/npm/ieee754/-/ieee754-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { @@ -6398,51 +6852,59 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/ignore": { "version": "7.0.5", - "resolved": "https://mirrors.tencent.com/npm/ignore/-/ignore-7.0.5.tgz", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 4" } }, "node_modules/immediate": { "version": "3.0.6", - "resolved": "https://mirrors.tencent.com/npm/immediate/-/immediate-3.0.6.tgz", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://mirrors.tencent.com/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC", + "peer": true }, "node_modules/ini": { "version": "1.3.8", - "resolved": "https://mirrors.tencent.com/npm/ini/-/ini-1.3.8.tgz", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://mirrors.tencent.com/npm/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } }, "node_modules/ipull": { "version": "3.9.3", - "resolved": "https://mirrors.tencent.com/npm/ipull/-/ipull-3.9.3.tgz", + "resolved": "https://registry.npmjs.org/ipull/-/ipull-3.9.3.tgz", "integrity": "sha512-ZMkxaopfwKHwmEuGDYx7giNBdLxbHbRCWcQVA1D2eqE4crUguupfxej6s7UqbidYEwT69dkyumYkY8DPHIxF9g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@tinyhttp/content-disposition": "^2.2.0", "async-retry": "^1.3.3", @@ -6480,27 +6942,30 @@ }, "node_modules/ipull/node_modules/commander": { "version": "10.0.1", - "resolved": "https://mirrors.tencent.com/npm/commander/-/commander-10.0.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14" } }, "node_modules/ipull/node_modules/lifecycle-utils": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/ipull/node_modules/parse-ms": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/parse-ms/-/parse-ms-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -6510,10 +6975,11 @@ }, "node_modules/ipull/node_modules/pretty-ms": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/pretty-ms/-/pretty-ms-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "parse-ms": "^3.0.0" }, @@ -6526,16 +6992,18 @@ }, "node_modules/is-electron": { "version": "2.2.2", - "resolved": "https://mirrors.tencent.com/npm/is-electron/-/is-electron-2.2.2.tgz", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-fullwidth-code-point": { "version": "5.1.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "get-east-asian-width": "^1.3.1" }, @@ -6548,9 +7016,11 @@ }, "node_modules/is-interactive": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-interactive/-/is-interactive-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -6560,14 +7030,17 @@ }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-promise/-/is-promise-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/is-stream/-/is-stream-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -6577,10 +7050,11 @@ }, "node_modules/is-unicode-supported": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -6590,20 +7064,24 @@ }, "node_modules/isarray": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "peer": true }, "node_modules/jackspeak": { "version": "4.1.1", - "resolved": "https://mirrors.tencent.com/npm/jackspeak/-/jackspeak-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -6616,26 +7094,30 @@ }, "node_modules/jiti": { "version": "2.6.1", - "resolved": "https://mirrors.tencent.com/npm/jiti/-/jiti-2.6.1.tgz", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } }, "node_modules/json-bigint": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/json-bigint/-/json-bigint-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", + "peer": true, "dependencies": { "bignumber.js": "^9.0.0" } }, "node_modules/json-schema-to-ts": { "version": "3.1.1", - "resolved": "https://mirrors.tencent.com/npm/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" @@ -6646,13 +7128,17 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "peer": true }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://mirrors.tencent.com/npm/json5/-/json5-2.2.3.tgz", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -6662,10 +7148,11 @@ }, "node_modules/jsonfile": { "version": "6.2.0", - "resolved": "https://mirrors.tencent.com/npm/jsonfile/-/jsonfile-6.2.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -6675,9 +7162,10 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.3", - "resolved": "https://mirrors.tencent.com/npm/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", + "peer": true, "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", @@ -6697,9 +7185,10 @@ }, "node_modules/jszip": { "version": "3.10.1", - "resolved": "https://mirrors.tencent.com/npm/jszip/-/jszip-3.10.1.tgz", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "license": "(MIT OR GPL-3.0-or-later)", + "peer": true, "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -6709,9 +7198,10 @@ }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/jwa/-/jwa-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", + "peer": true, "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -6720,9 +7210,10 @@ }, "node_modules/jws": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/jws/-/jws-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", + "peer": true, "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6730,8 +7221,9 @@ }, "node_modules/keyv": { "version": "5.6.0", - "resolved": "https://mirrors.tencent.com/npm/keyv/-/keyv-5.6.0.tgz", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "license": "MIT", "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" @@ -6742,6 +7234,7 @@ "version": "2.0.1", "resolved": "git+ssh://git@github.com/whiskeysockets/libsignal-node.git#1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67", "license": "GPL-3.0", + "peer": true, "dependencies": { "curve25519-js": "^0.0.4", "protobufjs": "6.8.8" @@ -6749,22 +7242,25 @@ }, "node_modules/libsignal/node_modules/@types/node": { "version": "10.17.60", - "resolved": "https://mirrors.tencent.com/npm/@types/node/-/node-10.17.60.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/libsignal/node_modules/long": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/long/-/long-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/libsignal/node_modules/protobufjs": { "version": "6.8.8", - "resolved": "https://mirrors.tencent.com/npm/protobufjs/-/protobufjs-6.8.8.tgz", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", "hasInstallScript": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -6787,24 +7283,28 @@ }, "node_modules/lie": { "version": "3.3.0", - "resolved": "https://mirrors.tencent.com/npm/lie/-/lie-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "peer": true, "dependencies": { "immediate": "~3.0.5" } }, "node_modules/lifecycle-utils": { "version": "3.0.1", - "resolved": "https://mirrors.tencent.com/npm/lifecycle-utils/-/lifecycle-utils-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-3.0.1.tgz", "integrity": "sha512-Qt/Jl5dsNIsyCAZsHB6x3mbwHFn0HJbdmvF49sVX/bHgX2cW7+G+U+I67Zw+TPM1Sr21Gb2nfJMd2g6iUcI1EQ==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/linkedom": { "version": "0.18.12", - "resolved": "https://mirrors.tencent.com/npm/linkedom/-/linkedom-0.18.12.tgz", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", "license": "ISC", + "peer": true, "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", @@ -6826,68 +7326,78 @@ }, "node_modules/linkify-it": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/linkify-it/-/linkify-it-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "license": "MIT", + "peer": true, "dependencies": { "uc.micro": "^2.0.0" } }, "node_modules/lodash.debounce": { "version": "4.0.8", - "resolved": "https://mirrors.tencent.com/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/lodash.includes": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/lodash.includes/-/lodash.includes-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isboolean": { "version": "3.0.3", - "resolved": "https://mirrors.tencent.com/npm/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isinteger": { "version": "4.0.4", - "resolved": "https://mirrors.tencent.com/npm/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isnumber": { "version": "3.0.3", - "resolved": "https://mirrors.tencent.com/npm/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://mirrors.tencent.com/npm/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://mirrors.tencent.com/npm/lodash.once/-/lodash.once-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/log-symbols": { "version": "7.0.1", - "resolved": "https://mirrors.tencent.com/npm/log-symbols/-/log-symbols-7.0.1.tgz", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" @@ -6901,16 +7411,18 @@ }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://mirrors.tencent.com/npm/long/-/long-5.3.2.tgz", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/lowdb": { "version": "7.0.1", - "resolved": "https://mirrors.tencent.com/npm/lowdb/-/lowdb-7.0.1.tgz", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "steno": "^4.0.2" }, @@ -6923,18 +7435,20 @@ }, "node_modules/lru-cache": { "version": "11.2.5", - "resolved": "https://mirrors.tencent.com/npm/lru-cache/-/lru-cache-11.2.5.tgz", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": "20 || >=22" } }, "node_modules/markdown-it": { "version": "14.1.0", - "resolved": "https://mirrors.tencent.com/npm/markdown-it/-/markdown-it-14.1.0.tgz", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -6949,8 +7463,10 @@ }, "node_modules/marked": { "version": "15.0.12", - "resolved": "https://mirrors.tencent.com/npm/marked/-/marked-15.0.12.tgz", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -6960,41 +7476,49 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/mdurl": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT", + "peer": true }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/media-typer/-/media-typer-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/memory-stream": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/memory-stream/-/memory-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "readable-stream": "^3.4.0" } }, "node_modules/memory-stream/node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://mirrors.tencent.com/npm/readable-stream/-/readable-stream-3.6.2.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7006,9 +7530,10 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -7018,17 +7543,20 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://mirrors.tencent.com/npm/mime-db/-/mime-db-1.54.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://mirrors.tencent.com/npm/mime-types/-/mime-types-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -7042,10 +7570,11 @@ }, "node_modules/mimic-function": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/mimic-function/-/mimic-function-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -7055,8 +7584,10 @@ }, "node_modules/minimatch": { "version": "10.1.1", - "resolved": "https://mirrors.tencent.com/npm/minimatch/-/minimatch-10.1.1.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -7069,26 +7600,31 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://mirrors.tencent.com/npm/minimist/-/minimist-1.2.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.2", - "resolved": "https://mirrors.tencent.com/npm/minipass/-/minipass-7.1.2.tgz", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { "version": "3.1.0", - "resolved": "https://mirrors.tencent.com/npm/minizlib/-/minizlib-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", + "peer": true, "dependencies": { "minipass": "^7.1.2" }, @@ -7098,15 +7634,18 @@ }, "node_modules/mitt": { "version": "3.0.1", - "resolved": "https://mirrors.tencent.com/npm/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT", + "peer": true }, "node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://mirrors.tencent.com/npm/mkdirp/-/mkdirp-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "license": "MIT", "optional": true, + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -7114,24 +7653,16 @@ "node": ">=10" } }, - "node_modules/moltbot": { - "version": "0.1.0", - "resolved": "https://mirrors.tencent.com/npm/moltbot/-/moltbot-0.1.0.tgz", - "integrity": "sha512-lJaI85TWhrDYmsaMXElp4EuvJpsdyaVpeoOnmOLXwOUPUUlwkJv7aAz+AlxFmwqtYP5wT95ZUCu16sfrM/VtiA==", - "peer": true, - "engines": { - "node": ">=24", - "pnpm": ">=10" - } - }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://mirrors.tencent.com/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true }, "node_modules/music-metadata": { "version": "11.11.1", - "resolved": "https://mirrors.tencent.com/npm/music-metadata/-/music-metadata-11.11.1.tgz", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.11.1.tgz", "integrity": "sha512-8FT+lSLznASDhn5KNJtQE6ZH95VqhxtKWNPrvdfhlqgbdZZEEAXehx+xpUvas4VuEZAu49BhQgLa3NlmPeRaww==", "funding": [ { @@ -7144,6 +7675,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", @@ -7162,9 +7694,10 @@ }, "node_modules/mz": { "version": "2.7.0", - "resolved": "https://mirrors.tencent.com/npm/mz/-/mz-2.7.0.tgz", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "license": "MIT", + "peer": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -7173,7 +7706,7 @@ }, "node_modules/nanoid": { "version": "5.1.6", - "resolved": "https://mirrors.tencent.com/npm/nanoid/-/nanoid-5.1.6.tgz", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", "funding": [ { @@ -7183,6 +7716,7 @@ ], "license": "MIT", "optional": true, + "peer": true, "bin": { "nanoid": "bin/nanoid.js" }, @@ -7192,32 +7726,36 @@ }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/negotiator/-/negotiator-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/node-addon-api": { "version": "8.5.0", - "resolved": "https://mirrors.tencent.com/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^18 || ^20 || >= 21" } }, "node_modules/node-api-headers": { "version": "1.8.0", - "resolved": "https://mirrors.tencent.com/npm/node-api-headers/-/node-api-headers-1.8.0.tgz", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/node-domexception/-/node-domexception-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "deprecated": "Use your platform's native DOMException instead", "funding": [ @@ -7230,15 +7768,18 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", + "peer": true, "engines": { "node": ">=10.5.0" } }, "node_modules/node-edge-tts": { "version": "1.2.9", - "resolved": "https://mirrors.tencent.com/npm/node-edge-tts/-/node-edge-tts-1.2.9.tgz", + "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.9.tgz", "integrity": "sha512-fvfW1dUgJdZAdTniC6MzLTMwnNUFKGKaUdRJ1OsveOYlfnPUETBU973CG89565txvbBowCQ4Czdeu3qSX8bNOg==", "license": "MIT", + "peer": true, "dependencies": { "https-proxy-agent": "^7.0.1", "ws": "^8.13.0", @@ -7250,17 +7791,20 @@ }, "node_modules/node-edge-tts/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/node-edge-tts/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7273,8 +7817,10 @@ }, "node_modules/node-edge-tts/node_modules/cliui": { "version": "8.0.1", - "resolved": "https://mirrors.tencent.com/npm/cliui/-/cliui-8.0.1.tgz", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -7286,24 +7832,27 @@ }, "node_modules/node-edge-tts/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/node-edge-tts/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/node-edge-tts/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7315,9 +7864,10 @@ }, "node_modules/node-edge-tts/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7327,9 +7877,10 @@ }, "node_modules/node-edge-tts/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7344,9 +7895,10 @@ }, "node_modules/node-edge-tts/node_modules/yargs": { "version": "17.7.2", - "resolved": "https://mirrors.tencent.com/npm/yargs/-/yargs-17.7.2.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", + "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -7362,17 +7914,20 @@ }, "node_modules/node-edge-tts/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://mirrors.tencent.com/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } }, "node_modules/node-fetch": { "version": "3.3.2", - "resolved": "https://mirrors.tencent.com/npm/node-fetch/-/node-fetch-3.3.2.tgz", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "peer": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -7388,11 +7943,12 @@ }, "node_modules/node-llama-cpp": { "version": "3.15.0", - "resolved": "https://mirrors.tencent.com/npm/node-llama-cpp/-/node-llama-cpp-3.15.0.tgz", + "resolved": "https://registry.npmjs.org/node-llama-cpp/-/node-llama-cpp-3.15.0.tgz", "integrity": "sha512-xQKl+MvKiA5QNi/CTwqLKMos7hefhRVyzJuNIAEwl7zvOoF+gNMOXEsR4Ojwl7qvgpcjsVeGKWSK3Rb6zoUP1w==", "hasInstallScript": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@huggingface/jinja": "^0.5.3", "async-retry": "^1.3.3", @@ -7461,20 +8017,22 @@ }, "node_modules/node-llama-cpp/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/node-llama-cpp/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7487,10 +8045,11 @@ }, "node_modules/node-llama-cpp/node_modules/cliui": { "version": "8.0.1", - "resolved": "https://mirrors.tencent.com/npm/cliui/-/cliui-8.0.1.tgz", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -7502,10 +8061,11 @@ }, "node_modules/node-llama-cpp/node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7515,37 +8075,41 @@ }, "node_modules/node-llama-cpp/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/node-llama-cpp/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/node-llama-cpp/node_modules/isexe": { "version": "3.1.1", - "resolved": "https://mirrors.tencent.com/npm/isexe/-/isexe-3.1.1.tgz", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": ">=16" } }, "node_modules/node-llama-cpp/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7557,10 +8121,11 @@ }, "node_modules/node-llama-cpp/node_modules/string-width/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7570,10 +8135,11 @@ }, "node_modules/node-llama-cpp/node_modules/which": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/which/-/which-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "isexe": "^3.1.1" }, @@ -7586,10 +8152,11 @@ }, "node_modules/node-llama-cpp/node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7604,10 +8171,11 @@ }, "node_modules/node-llama-cpp/node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7617,10 +8185,11 @@ }, "node_modules/node-llama-cpp/node_modules/yargs": { "version": "17.7.2", - "resolved": "https://mirrors.tencent.com/npm/yargs/-/yargs-17.7.2.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -7636,21 +8205,23 @@ }, "node_modules/node-llama-cpp/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://mirrors.tencent.com/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": ">=12" } }, "node_modules/npmlog": { "version": "6.0.2", - "resolved": "https://mirrors.tencent.com/npm/npmlog/-/npmlog-6.0.2.tgz", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -7663,9 +8234,10 @@ }, "node_modules/nth-check": { "version": "2.1.1", - "resolved": "https://mirrors.tencent.com/npm/nth-check/-/nth-check-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -7675,16 +8247,20 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://mirrors.tencent.com/npm/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://mirrors.tencent.com/npm/object-inspect/-/object-inspect-1.13.4.tgz", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -7694,10 +8270,11 @@ }, "node_modules/octokit": { "version": "5.0.5", - "resolved": "https://mirrors.tencent.com/npm/octokit/-/octokit-5.0.5.tgz", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-5.0.5.tgz", "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@octokit/app": "^16.1.2", "@octokit/core": "^7.0.6", @@ -7717,18 +8294,20 @@ }, "node_modules/on-exit-leak-free": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", "license": "MIT", + "peer": true, "engines": { "node": ">=14.0.0" } }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://mirrors.tencent.com/npm/on-finished/-/on-finished-2.4.1.tgz", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", + "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -7738,18 +8317,21 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://mirrors.tencent.com/npm/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "peer": true, "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/onetime/-/onetime-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "mimic-function": "^5.0.0" }, @@ -7762,9 +8344,10 @@ }, "node_modules/openai": { "version": "6.10.0", - "resolved": "https://mirrors.tencent.com/npm/openai/-/openai-6.10.0.tgz", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.10.0.tgz", "integrity": "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==", "license": "Apache-2.0", + "peer": true, "bin": { "openai": "bin/cli" }, @@ -7783,7 +8366,7 @@ }, "node_modules/openclaw": { "version": "2026.1.29", - "resolved": "https://mirrors.tencent.com/npm/openclaw/-/openclaw-2026.1.29.tgz", + "resolved": "https://registry.npmjs.org/openclaw/-/openclaw-2026.1.29.tgz", "integrity": "sha512-CVUOAH83BCrlnD56I1JahGVpg0mjp4fjJPdJg9q6Pijn/YdOopsfdInDqSeIEZwGtu/+0w69/uJccY23C9I+vg==", "hasInstallScript": true, "license": "MIT", @@ -7856,9 +8439,11 @@ }, "node_modules/ora": { "version": "8.2.0", - "resolved": "https://mirrors.tencent.com/npm/ora/-/ora-8.2.0.tgz", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", "optional": true, + "peer": true, "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", @@ -7879,17 +8464,19 @@ }, "node_modules/ora/node_modules/emoji-regex": { "version": "10.6.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-10.6.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/ora/node_modules/log-symbols": { "version": "6.0.0", - "resolved": "https://mirrors.tencent.com/npm/log-symbols/-/log-symbols-6.0.0.tgz", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -7903,10 +8490,11 @@ }, "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { "version": "1.3.0", - "resolved": "https://mirrors.tencent.com/npm/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=12" }, @@ -7916,10 +8504,11 @@ }, "node_modules/ora/node_modules/string-width": { "version": "7.2.0", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-7.2.0.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -7934,27 +8523,30 @@ }, "node_modules/osc-progress": { "version": "0.3.0", - "resolved": "https://mirrors.tencent.com/npm/osc-progress/-/osc-progress-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/osc-progress/-/osc-progress-0.3.0.tgz", "integrity": "sha512-4/8JfsetakdeEa4vAYV45FW20aY+B/+K8NEXp5Eiar3wR8726whgHrbSg5Ar/ZY1FLJ/AGtUqV7W2IVF+Gvp9A==", "license": "MIT", + "peer": true, "engines": { "node": ">=20" } }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/p-finally/-/p-finally-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "license": "MIT", + "peer": true, "engines": { "node": ">=4" } }, "node_modules/p-queue": { "version": "6.6.2", - "resolved": "https://mirrors.tencent.com/npm/p-queue/-/p-queue-6.6.2.tgz", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", "license": "MIT", + "peer": true, "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" @@ -7968,14 +8560,17 @@ }, "node_modules/p-queue/node_modules/eventemitter3": { "version": "4.0.7", - "resolved": "https://mirrors.tencent.com/npm/eventemitter3/-/eventemitter3-4.0.7.tgz", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/p-retry": { "version": "4.6.2", - "resolved": "https://mirrors.tencent.com/npm/p-retry/-/p-retry-4.6.2.tgz", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "peer": true, "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" @@ -7986,9 +8581,10 @@ }, "node_modules/p-timeout": { "version": "3.2.0", - "resolved": "https://mirrors.tencent.com/npm/p-timeout/-/p-timeout-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "license": "MIT", + "peer": true, "dependencies": { "p-finally": "^1.0.0" }, @@ -7998,21 +8594,25 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" + "license": "BlueOak-1.0.0", + "peer": true }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://mirrors.tencent.com/npm/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)", + "peer": true }, "node_modules/parse-ms": { "version": "4.0.0", - "resolved": "https://mirrors.tencent.com/npm/parse-ms/-/parse-ms-4.0.0.tgz", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -8022,51 +8622,61 @@ }, "node_modules/parse5": { "version": "5.1.1", - "resolved": "https://mirrors.tencent.com/npm/parse5/-/parse5-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "license": "MIT", + "peer": true, "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/parse5/-/parse5-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://mirrors.tencent.com/npm/parseurl/-/parseurl-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/partial-json": { "version": "0.1.7", - "resolved": "https://mirrors.tencent.com/npm/partial-json/-/partial-json-0.1.7.tgz", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://mirrors.tencent.com/npm/path-key/-/path-key-3.1.1.tgz", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/path-scurry/-/path-scurry-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -8080,9 +8690,10 @@ }, "node_modules/path-to-regexp": { "version": "8.3.0", - "resolved": "https://mirrors.tencent.com/npm/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -8090,9 +8701,10 @@ }, "node_modules/pdfjs-dist": { "version": "5.4.530", - "resolved": "https://mirrors.tencent.com/npm/pdfjs-dist/-/pdfjs-dist-5.4.530.tgz", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.530.tgz", "integrity": "sha512-r1hWsSIGGmyYUAHR26zSXkxYWLXLMd6AwqcaFYG9YUZ0GBf5GvcjJSeo512tabM4GYFhxhl5pMCmPr7Q72Rq2Q==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=20.16.0 || >=22.3.0" }, @@ -8102,15 +8714,17 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/picocolors/-/picocolors-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/pino": { "version": "9.14.0", - "resolved": "https://mirrors.tencent.com/npm/pino/-/pino-9.14.0.tgz", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", + "peer": true, "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", @@ -8130,24 +8744,27 @@ }, "node_modules/pino-abstract-transport": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", "license": "MIT", + "peer": true, "dependencies": { "split2": "^4.0.0" } }, "node_modules/pino-std-serializers": { "version": "7.1.0", - "resolved": "https://mirrors.tencent.com/npm/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/playwright-core": { "version": "1.58.0", - "resolved": "https://mirrors.tencent.com/npm/playwright-core/-/playwright-core-1.58.0.tgz", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.0.tgz", "integrity": "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==", "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -8157,10 +8774,11 @@ }, "node_modules/pretty-bytes": { "version": "6.1.1", - "resolved": "https://mirrors.tencent.com/npm/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "^14.13.1 || >=16.0.0" }, @@ -8170,10 +8788,11 @@ }, "node_modules/pretty-ms": { "version": "9.3.0", - "resolved": "https://mirrors.tencent.com/npm/pretty-ms/-/pretty-ms-9.3.0.tgz", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "parse-ms": "^4.0.0" }, @@ -8186,10 +8805,11 @@ }, "node_modules/prism-media": { "version": "1.3.5", - "resolved": "https://mirrors.tencent.com/npm/prism-media/-/prism-media-1.3.5.tgz", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", "license": "Apache-2.0", "optional": true, + "peer": true, "peerDependencies": { "@discordjs/opus": ">=0.8.0 <1.0.0", "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", @@ -8213,12 +8833,14 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT", + "peer": true }, "node_modules/process-warning": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/process-warning/-/process-warning-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", "funding": [ { @@ -8230,13 +8852,15 @@ "url": "https://opencollective.com/fastify" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/proper-lockfile": { "version": "4.1.2", - "resolved": "https://mirrors.tencent.com/npm/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", @@ -8245,25 +8869,28 @@ }, "node_modules/proper-lockfile/node_modules/retry": { "version": "0.12.0", - "resolved": "https://mirrors.tencent.com/npm/retry/-/retry-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } }, "node_modules/proper-lockfile/node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://mirrors.tencent.com/npm/signal-exit/-/signal-exit-3.0.7.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/protobufjs": { "version": "7.5.4", - "resolved": "https://mirrors.tencent.com/npm/protobufjs/-/protobufjs-7.5.4.tgz", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", "hasInstallScript": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -8284,9 +8911,10 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://mirrors.tencent.com/npm/proxy-addr/-/proxy-addr-2.0.7.tgz", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", + "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -8297,23 +8925,27 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT", + "peer": true }, "node_modules/punycode.js": { "version": "2.3.1", - "resolved": "https://mirrors.tencent.com/npm/punycode.js/-/punycode.js-2.3.1.tgz", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } }, "node_modules/qified": { "version": "0.6.0", - "resolved": "https://mirrors.tencent.com/npm/qified/-/qified-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", "license": "MIT", + "peer": true, "dependencies": { "hookified": "^1.14.0" }, @@ -8323,16 +8955,19 @@ }, "node_modules/qrcode-terminal": { "version": "0.12.0", - "resolved": "https://mirrors.tencent.com/npm/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "peer": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } }, "node_modules/qs": { "version": "6.14.1", - "resolved": "https://mirrors.tencent.com/npm/qs/-/qs-6.14.1.tgz", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -8345,23 +8980,27 @@ }, "node_modules/quick-format-unescaped": { "version": "4.0.4", - "resolved": "https://mirrors.tencent.com/npm/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://mirrors.tencent.com/npm/range-parser/-/range-parser-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://mirrors.tencent.com/npm/raw-body/-/raw-body-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", + "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -8374,10 +9013,11 @@ }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://mirrors.tencent.com/npm/rc/-/rc-1.2.8.tgz", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "optional": true, + "peer": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -8390,8 +9030,10 @@ }, "node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://mirrors.tencent.com/npm/readable-stream/-/readable-stream-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8404,14 +9046,17 @@ }, "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://mirrors.tencent.com/npm/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT", + "peer": true }, "node_modules/readdirp": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/readdirp/-/readdirp-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 20.19.0" }, @@ -8422,37 +9067,41 @@ }, "node_modules/real-require": { "version": "0.2.0", - "resolved": "https://mirrors.tencent.com/npm/real-require/-/real-require-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 12.13.0" } }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://mirrors.tencent.com/npm/require-directory/-/require-directory-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://mirrors.tencent.com/npm/require-from-string/-/require-from-string-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/restore-cursor": { "version": "5.1.0", - "resolved": "https://mirrors.tencent.com/npm/restore-cursor/-/restore-cursor-5.1.0.tgz", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" @@ -8466,17 +9115,20 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://mirrors.tencent.com/npm/retry/-/retry-0.13.1.tgz", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } }, "node_modules/rimraf": { "version": "5.0.10", - "resolved": "https://mirrors.tencent.com/npm/rimraf/-/rimraf-5.0.10.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "peer": true, "dependencies": { "glob": "^10.3.7" }, @@ -8489,8 +9141,10 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "10.5.0", - "resolved": "https://mirrors.tencent.com/npm/glob/-/glob-10.5.0.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -8508,9 +9162,10 @@ }, "node_modules/rimraf/node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://mirrors.tencent.com/npm/jackspeak/-/jackspeak-3.4.3.tgz", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -8523,14 +9178,17 @@ }, "node_modules/rimraf/node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://mirrors.tencent.com/npm/lru-cache/-/lru-cache-10.4.3.tgz", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/rimraf/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://mirrors.tencent.com/npm/minimatch/-/minimatch-9.0.5.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8543,8 +9201,10 @@ }, "node_modules/rimraf/node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://mirrors.tencent.com/npm/path-scurry/-/path-scurry-1.11.1.tgz", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -8558,9 +9218,10 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://mirrors.tencent.com/npm/router/-/router-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -8574,7 +9235,7 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://mirrors.tencent.com/npm/safe-buffer/-/safe-buffer-5.2.1.tgz", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { @@ -8589,27 +9250,33 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT", + "peer": true }, "node_modules/safe-stable-stringify": { "version": "2.5.0", - "resolved": "https://mirrors.tencent.com/npm/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" } }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "peer": true }, "node_modules/semver": { "version": "7.7.3", - "resolved": "https://mirrors.tencent.com/npm/semver/-/semver-7.7.3.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8619,8 +9286,10 @@ }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://mirrors.tencent.com/npm/send/-/send-1.2.1.tgz", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -8644,9 +9313,10 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://mirrors.tencent.com/npm/serve-static/-/serve-static-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -8663,25 +9333,29 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://mirrors.tencent.com/npm/setimmediate/-/setimmediate-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://mirrors.tencent.com/npm/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "peer": true }, "node_modules/sharp": { "version": "0.34.5", - "resolved": "https://mirrors.tencent.com/npm/sharp/-/sharp-0.34.5.tgz", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", @@ -8726,9 +9400,10 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/shebang-command/-/shebang-command-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -8738,17 +9413,20 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/shebang-regex/-/shebang-regex-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://mirrors.tencent.com/npm/side-channel/-/side-channel-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -8765,8 +9443,10 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/side-channel-list/-/side-channel-list-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -8780,9 +9460,10 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/side-channel-map/-/side-channel-map-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -8798,9 +9479,10 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -8817,9 +9499,10 @@ }, "node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://mirrors.tencent.com/npm/signal-exit/-/signal-exit-4.1.0.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", + "peer": true, "engines": { "node": ">=14" }, @@ -8829,10 +9512,11 @@ }, "node_modules/simple-git": { "version": "3.30.0", - "resolved": "https://mirrors.tencent.com/npm/simple-git/-/simple-git-3.30.0.tgz", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -8845,22 +9529,26 @@ }, "node_modules/sisteransi": { "version": "1.0.5", - "resolved": "https://mirrors.tencent.com/npm/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT", + "peer": true }, "node_modules/sleep-promise": { "version": "9.1.0", - "resolved": "https://mirrors.tencent.com/npm/sleep-promise/-/sleep-promise-9.1.0.tgz", + "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/slice-ansi": { "version": "7.1.2", - "resolved": "https://mirrors.tencent.com/npm/slice-ansi/-/slice-ansi-7.1.2.tgz", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" @@ -8874,26 +9562,30 @@ }, "node_modules/sonic-boom": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/sonic-boom/-/sonic-boom-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "license": "MIT", + "peer": true, "dependencies": { "atomic-sleep": "^1.0.0" } }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://mirrors.tencent.com/npm/source-map/-/source-map-0.6.1.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", - "resolved": "https://mirrors.tencent.com/npm/source-map-support/-/source-map-support-0.5.21.tgz", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -8901,18 +9593,20 @@ }, "node_modules/split2": { "version": "4.2.0", - "resolved": "https://mirrors.tencent.com/npm/split2/-/split2-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", + "peer": true, "engines": { "node": ">= 10.x" } }, "node_modules/sqlite-vec": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec/-/sqlite-vec-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec/-/sqlite-vec-0.1.7-alpha.2.tgz", "integrity": "sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ==", "license": "MIT OR Apache", + "peer": true, "optionalDependencies": { "sqlite-vec-darwin-arm64": "0.1.7-alpha.2", "sqlite-vec-darwin-x64": "0.1.7-alpha.2", @@ -8923,7 +9617,7 @@ }, "node_modules/sqlite-vec-darwin-arm64": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.7-alpha.2.tgz", "integrity": "sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw==", "cpu": [ "arm64" @@ -8932,11 +9626,12 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/sqlite-vec-darwin-x64": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-jeZEELsQjjRsVojsvU5iKxOvkaVuE+JYC8Y4Ma8U45aAERrDYmqZoHvgSG7cg1PXL3bMlumFTAmHynf1y4pOzA==", "cpu": [ "x64" @@ -8945,11 +9640,12 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/sqlite-vec-linux-arm64": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.7-alpha.2.tgz", "integrity": "sha512-6Spj4Nfi7tG13jsUG+W7jnT0bCTWbyPImu2M8nWp20fNrd1SZ4g3CSlDAK8GBdavX7wRlbBHCZ+BDa++rbDewA==", "cpu": [ "arm64" @@ -8958,11 +9654,12 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/sqlite-vec-linux-x64": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-IcgrbHaDccTVhXDf8Orwdc2+hgDLAFORl6OBUhcvlmwswwBP1hqBTSEhovClG4NItwTOBNgpwOoQ7Qp3VDPWLg==", "cpu": [ "x64" @@ -8971,11 +9668,12 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/sqlite-vec-windows-x64": { "version": "0.1.7-alpha.2", - "resolved": "https://mirrors.tencent.com/npm/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.7-alpha.2.tgz", + "resolved": "https://registry.npmjs.org/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ==", "cpu": [ "x64" @@ -8984,29 +9682,33 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://mirrors.tencent.com/npm/statuses/-/statuses-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/std-env": { "version": "3.10.0", - "resolved": "https://mirrors.tencent.com/npm/std-env/-/std-env-3.10.0.tgz", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/stdin-discarder": { "version": "0.2.2", - "resolved": "https://mirrors.tencent.com/npm/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -9016,10 +9718,11 @@ }, "node_modules/stdout-update": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/stdout-update/-/stdout-update-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/stdout-update/-/stdout-update-4.0.1.tgz", "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-escapes": "^6.2.0", "ansi-styles": "^6.2.1", @@ -9032,17 +9735,19 @@ }, "node_modules/stdout-update/node_modules/emoji-regex": { "version": "10.6.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-10.6.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/stdout-update/node_modules/string-width": { "version": "7.2.0", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-7.2.0.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -9057,10 +9762,11 @@ }, "node_modules/steno": { "version": "4.0.2", - "resolved": "https://mirrors.tencent.com/npm/steno/-/steno-4.0.2.tgz", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -9070,22 +9776,27 @@ }, "node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://mirrors.tencent.com/npm/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://mirrors.tencent.com/npm/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT", + "peer": true }, "node_modules/string-width": { "version": "5.1.2", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-5.1.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", + "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -9101,9 +9812,10 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9115,32 +9827,37 @@ }, "node_modules/string-width-cjs/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9150,9 +9867,10 @@ }, "node_modules/strip-ansi": { "version": "7.1.2", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-7.1.2.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -9166,9 +9884,10 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9178,38 +9897,44 @@ }, "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/strnum": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/strnum/-/strnum-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } - ] + ], + "license": "MIT", + "peer": true }, "node_modules/strtok3": { "version": "10.3.4", - "resolved": "https://mirrors.tencent.com/npm/strtok3/-/strtok3-10.3.4.tgz", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "license": "MIT", + "peer": true, "dependencies": { "@tokenizer/token": "^0.3.0" }, @@ -9223,9 +9948,10 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://mirrors.tencent.com/npm/supports-color/-/supports-color-7.2.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -9235,9 +9961,11 @@ }, "node_modules/tar": { "version": "7.5.4", - "resolved": "https://mirrors.tencent.com/npm/tar/-/tar-7.5.4.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.4.tgz", "integrity": "sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -9251,16 +9979,20 @@ }, "node_modules/thenify": { "version": "3.3.1", - "resolved": "https://mirrors.tencent.com/npm/thenify/-/thenify-3.3.1.tgz", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "peer": true, "dependencies": { "any-promise": "^1.0.0" } }, "node_modules/thenify-all": { "version": "1.6.0", - "resolved": "https://mirrors.tencent.com/npm/thenify-all/-/thenify-all-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "peer": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -9270,36 +10002,41 @@ }, "node_modules/thread-stream": { "version": "3.1.0", - "resolved": "https://mirrors.tencent.com/npm/thread-stream/-/thread-stream-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "license": "MIT", + "peer": true, "dependencies": { "real-require": "^0.2.0" } }, "node_modules/toad-cache": { "version": "3.7.0", - "resolved": "https://mirrors.tencent.com/npm/toad-cache/-/toad-cache-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=12" } }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://mirrors.tencent.com/npm/toidentifier/-/toidentifier-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "peer": true, "engines": { "node": ">=0.6" } }, "node_modules/token-types": { "version": "6.1.2", - "resolved": "https://mirrors.tencent.com/npm/token-types/-/token-types-6.1.2.tgz", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "license": "MIT", + "peer": true, "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", @@ -9315,26 +10052,31 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://mirrors.tencent.com/npm/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "peer": true }, "node_modules/ts-algebra": { "version": "2.0.0", - "resolved": "https://mirrors.tencent.com/npm/ts-algebra/-/ts-algebra-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://mirrors.tencent.com/npm/tslib/-/tslib-2.8.1.tgz", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tslog": { "version": "4.10.2", - "resolved": "https://mirrors.tencent.com/npm/tslog/-/tslog-4.10.2.tgz", + "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.10.2.tgz", "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=16" }, @@ -9344,16 +10086,20 @@ }, "node_modules/tsscmp": { "version": "1.0.6", - "resolved": "https://mirrors.tencent.com/npm/tsscmp/-/tsscmp-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "peer": true, "engines": { "node": ">=0.6.x" } }, "node_modules/type-is": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/type-is/-/type-is-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -9365,11 +10111,10 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://mirrors.tencent.com/npm/typescript/-/typescript-5.9.3.tgz", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9380,20 +10125,24 @@ }, "node_modules/uc.micro": { "version": "2.1.0", - "resolved": "https://mirrors.tencent.com/npm/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT", + "peer": true }, "node_modules/uhyphen": { "version": "0.2.0", - "resolved": "https://mirrors.tencent.com/npm/uhyphen/-/uhyphen-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/uint8array-extras": { "version": "1.5.0", - "resolved": "https://mirrors.tencent.com/npm/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -9403,98 +10152,116 @@ }, "node_modules/undici": { "version": "7.19.2", - "resolved": "https://mirrors.tencent.com/npm/undici/-/undici-7.19.2.tgz", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.19.2.tgz", "integrity": "sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg==", "license": "MIT", + "peer": true, "engines": { "node": ">=20.18.1" } }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://mirrors.tencent.com/npm/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" }, "node_modules/universal-github-app-jwt": { "version": "2.2.2", - "resolved": "https://mirrors.tencent.com/npm/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/universal-user-agent": { "version": "7.0.3", - "resolved": "https://mirrors.tencent.com/npm/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://mirrors.tencent.com/npm/universalify/-/universalify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://mirrors.tencent.com/npm/unpipe/-/unpipe-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/url-join": { "version": "4.0.1", - "resolved": "https://mirrors.tencent.com/npm/url-join/-/url-join-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT", + "peer": true }, "node_modules/validate-npm-package-name": { "version": "6.0.2", - "resolved": "https://mirrors.tencent.com/npm/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", "license": "ISC", "optional": true, + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://mirrors.tencent.com/npm/vary/-/vary-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/web-streams-polyfill": { "version": "3.3.3", - "resolved": "https://mirrors.tencent.com/npm/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 8" } }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://mirrors.tencent.com/npm/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause", + "peer": true }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/whatwg-url/-/whatwg-url-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -9502,8 +10269,10 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://mirrors.tencent.com/npm/which/-/which-2.0.2.tgz", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -9516,47 +10285,52 @@ }, "node_modules/wide-align": { "version": "1.1.5", - "resolved": "https://mirrors.tencent.com/npm/wide-align/-/wide-align-1.1.5.tgz", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "license": "ISC", "optional": true, + "peer": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "node_modules/wide-align/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/wide-align/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=8" } }, "node_modules/wide-align/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9568,10 +10342,11 @@ }, "node_modules/wide-align/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9581,15 +10356,17 @@ }, "node_modules/win-guid": { "version": "0.2.1", - "resolved": "https://mirrors.tencent.com/npm/win-guid/-/win-guid-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/win-guid/-/win-guid-0.2.1.tgz", "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -9605,9 +10382,10 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://mirrors.tencent.com/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9622,17 +10400,20 @@ }, "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://mirrors.tencent.com/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9645,24 +10426,27 @@ }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9674,9 +10458,10 @@ }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9686,15 +10471,17 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://mirrors.tencent.com/npm/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/ws": { "version": "8.19.0", - "resolved": "https://mirrors.tencent.com/npm/ws/-/ws-8.19.0.tgz", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "inBundle": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -9713,24 +10500,30 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://mirrors.tencent.com/npm/y18n/-/y18n-5.0.8.tgz", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "peer": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "5.0.0", - "resolved": "https://mirrors.tencent.com/npm/yallist/-/yallist-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } }, "node_modules/yaml": { "version": "2.8.2", - "resolved": "https://mirrors.tencent.com/npm/yaml/-/yaml-2.8.2.tgz", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -9743,9 +10536,10 @@ }, "node_modules/yargs": { "version": "16.2.0", - "resolved": "https://mirrors.tencent.com/npm/yargs/-/yargs-16.2.0.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "license": "MIT", + "peer": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -9761,41 +10555,47 @@ }, "node_modules/yargs-parser": { "version": "20.2.9", - "resolved": "https://mirrors.tencent.com/npm/yargs-parser/-/yargs-parser-20.2.9.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "license": "ISC", + "peer": true, "engines": { "node": ">=10" } }, "node_modules/yargs/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://mirrors.tencent.com/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://mirrors.tencent.com/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://mirrors.tencent.com/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://mirrors.tencent.com/npm/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9807,9 +10607,10 @@ }, "node_modules/yargs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://mirrors.tencent.com/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -9819,9 +10620,10 @@ }, "node_modules/yoctocolors": { "version": "2.1.2", - "resolved": "https://mirrors.tencent.com/npm/yoctocolors/-/yoctocolors-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -9831,7 +10633,7 @@ }, "node_modules/zod": { "version": "4.3.6", - "resolved": "https://mirrors.tencent.com/npm/zod/-/zod-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "peer": true, @@ -9841,9 +10643,10 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.1", - "resolved": "https://mirrors.tencent.com/npm/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", + "peer": true, "peerDependencies": { "zod": "^3.25 || ^4" } diff --git a/package.json b/package.json index ba893f0..a0034d5 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,14 @@ { - "name": "qqbot", - "version": "1.2.3", + "name": "@openclaw/qqbot", + "version": "2026.1.31", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", - "clawdbot": { - "extensions": ["./index.ts"] - }, - "moltbot": { - "extensions": ["./index.ts"] - }, + "description": "OpenClaw QQ Bot channel plugin with streaming message support", "openclaw": { - "extensions": ["./index.ts"] + "extensions": [ + "./index.ts" + ] }, "scripts": { "build": "tsc || true", @@ -27,11 +24,9 @@ "devDependencies": { "@types/node": "^20.0.0", "@types/ws": "^8.5.0", - "typescript": "^5.0.0" + "typescript": "^5.9.3" }, "peerDependencies": { - "clawdbot": "*", - "moltbot": "*", "openclaw": "*" } } diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 452f7ad..c890771 100755 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -1,106 +1,62 @@ #!/bin/bash # QQBot 插件升级脚本 # 用于清理旧版本插件并重新安装 -# 兼容 clawdbot 和 openclaw 两种安装 set -e +OPENCLAW_DIR="$HOME/.openclaw" +CONFIG_FILE="$OPENCLAW_DIR/openclaw.json" +EXTENSION_DIR="$OPENCLAW_DIR/extensions/qqbot" + echo "=== QQBot 插件升级脚本 ===" -# 检测使用的是 clawdbot 还是 openclaw -detect_installation() { - if [ -d "$HOME/.clawdbot" ]; then - echo "clawdbot" - elif [ -d "$HOME/.openclaw" ]; then - echo "openclaw" - else - echo "" - fi -} - -# 清理指定目录的函数 -cleanup_installation() { - local APP_NAME="$1" - local APP_DIR="$HOME/.$APP_NAME" - local CONFIG_FILE="$APP_DIR/$APP_NAME.json" - local EXTENSION_DIR="$APP_DIR/extensions/qqbot" - - echo "" - echo ">>> 处理 $APP_NAME 安装..." - - # 1. 删除旧的扩展目录 - if [ -d "$EXTENSION_DIR" ]; then - echo "删除旧版本插件: $EXTENSION_DIR" - rm -rf "$EXTENSION_DIR" - else - echo "未找到旧版本插件目录,跳过删除" - fi - - # 2. 清理配置文件中的 qqbot 相关字段 - if [ -f "$CONFIG_FILE" ]; then - echo "清理配置文件中的 qqbot 字段..." - - # 使用 node 处理 JSON(比 jq 更可靠处理复杂结构) - node -e " - const fs = require('fs'); - const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8')); - - // 删除 channels.qqbot - if (config.channels && config.channels.qqbot) { - delete config.channels.qqbot; - console.log(' - 已删除 channels.qqbot'); - } - - // 删除 plugins.entries.qqbot - if (config.plugins && config.plugins.entries && config.plugins.entries.qqbot) { - delete config.plugins.entries.qqbot; - console.log(' - 已删除 plugins.entries.qqbot'); - } - - // 删除 plugins.installs.qqbot - if (config.plugins && config.plugins.installs && config.plugins.installs.qqbot) { - delete config.plugins.installs.qqbot; - console.log(' - 已删除 plugins.installs.qqbot'); - } - - fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2)); - console.log('配置文件已更新'); - " - else - echo "未找到配置文件: $CONFIG_FILE" - fi -} - -# 检测并处理所有可能的安装 -FOUND_INSTALLATION="" - -# 检查 clawdbot -if [ -d "$HOME/.clawdbot" ]; then - cleanup_installation "clawdbot" - FOUND_INSTALLATION="clawdbot" +# 1. 删除旧的扩展目录 +if [ -d "$EXTENSION_DIR" ]; then + echo "删除旧版本插件: $EXTENSION_DIR" + rm -rf "$EXTENSION_DIR" +else + echo "未找到旧版本插件目录,跳过删除" fi -# 检查 openclaw -if [ -d "$HOME/.openclaw" ]; then - cleanup_installation "openclaw" - FOUND_INSTALLATION="openclaw" -fi +# 2. 清理配置文件中的 qqbot 相关字段 +if [ -f "$CONFIG_FILE" ]; then + echo "清理配置文件中的 qqbot 字段..." -# 如果都没找到 -if [ -z "$FOUND_INSTALLATION" ]; then - echo "未找到 clawdbot 或 openclaw 安装目录" - echo "请确认已安装 clawdbot 或 openclaw" - exit 1 -fi + # 使用 node 处理 JSON(比 jq 更可靠处理复杂结构) + node -e " + const fs = require('fs'); + const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8')); -# 使用检测到的安装类型作为命令 -CMD="$FOUND_INSTALLATION" + // 删除 channels.qqbot + if (config.channels && config.channels.qqbot) { + delete config.channels.qqbot; + console.log(' - 已删除 channels.qqbot'); + } + + // 删除 plugins.entries.qqbot + if (config.plugins && config.plugins.entries && config.plugins.entries.qqbot) { + delete config.plugins.entries.qqbot; + console.log(' - 已删除 plugins.entries.qqbot'); + } + + // 删除 plugins.installs.qqbot + if (config.plugins && config.plugins.installs && config.plugins.installs.qqbot) { + delete config.plugins.installs.qqbot; + console.log(' - 已删除 plugins.installs.qqbot'); + } + + fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2)); + console.log('配置文件已更新'); + " +else + echo "未找到配置文件: $CONFIG_FILE" +fi echo "" echo "=== 清理完成 ===" echo "" echo "接下来请执行以下命令重新安装插件:" echo " cd /path/to/qqbot" -echo " $CMD plugins install ." -echo " $CMD channels add --channel qqbot --token \"AppID:AppSecret\"" -echo " $CMD gateway restart" +echo " openclaw plugins install ." +echo " openclaw channels add --channel qqbot --token \"AppID:AppSecret\"" +echo " openclaw gateway restart" \ No newline at end of file diff --git a/src/api.ts b/src/api.ts index eaace99..106d5e9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,10 +1,30 @@ /** - * QQ Bot API 鉴权和请求封装 + * QQ Bot API 鉴权和请求封装(支持流式消息) */ +import { StreamState, type StreamConfig } from "./types.js"; + const API_BASE = "https://api.sgroup.qq.com"; const TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken"; +// 运行时配置 +let currentMarkdownSupport = false; + +/** + * 初始化 API 配置 + * @param options.markdownSupport - 是否支持 markdown 消息 + */ +export function initApiConfig(options: { markdownSupport?: boolean }): void { + currentMarkdownSupport = options.markdownSupport === true; // 默认为 false +} + +/** + * 获取当前是否支持 markdown + */ +export function isMarkdownSupport(): boolean { + return currentMarkdownSupport; +} + let cachedToken: { token: string; expiresAt: number } | null = null; /** @@ -16,21 +36,44 @@ export async function getAccessToken(appId: string, clientSecret: string): Promi return cachedToken.token; } + const requestBody = { appId, clientSecret }; + const requestHeaders = { "Content-Type": "application/json" }; + + // 打印请求信息(隐藏敏感信息) + console.log(`[qqbot-api] >>> POST ${TOKEN_URL}`); + console.log(`[qqbot-api] >>> Headers:`, JSON.stringify(requestHeaders, null, 2)); + console.log(`[qqbot-api] >>> Body:`, JSON.stringify({ appId, clientSecret: "***" }, null, 2)); + let response: Response; try { response = await fetch(TOKEN_URL, { method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ appId, clientSecret }), + headers: requestHeaders, + body: JSON.stringify(requestBody), }); } catch (err) { + console.error(`[qqbot-api] <<< Network error:`, err); throw new Error(`Network error getting access_token: ${err instanceof Error ? err.message : String(err)}`); } + // 打印响应头 + const responseHeaders: Record = {}; + response.headers.forEach((value, key) => { + responseHeaders[key] = value; + }); + console.log(`[qqbot-api] <<< Status: ${response.status} ${response.statusText}`); + console.log(`[qqbot-api] <<< Headers:`, JSON.stringify(responseHeaders, null, 2)); + let data: { access_token?: string; expires_in?: number }; + let rawBody: string; try { - data = (await response.json()) as { access_token?: string; expires_in?: number }; + rawBody = await response.text(); + // 隐藏 token 值 + const logBody = rawBody.replace(/"access_token"\s*:\s*"[^"]+"/g, '"access_token": "***"'); + console.log(`[qqbot-api] <<< Body:`, logBody); + data = JSON.parse(rawBody) as { access_token?: string; expires_in?: number }; } catch (err) { + console.error(`[qqbot-api] <<< Parse error:`, err); throw new Error(`Failed to parse access_token response: ${err instanceof Error ? err.message : String(err)}`); } @@ -93,29 +136,50 @@ export async function apiRequest( body?: unknown ): Promise { const url = `${API_BASE}${path}`; + const headers: Record = { + Authorization: `QQBot ${accessToken}`, + "Content-Type": "application/json", + }; const options: RequestInit = { method, - headers: { - Authorization: `QQBot ${accessToken}`, - "Content-Type": "application/json", - }, + headers, }; if (body) { options.body = JSON.stringify(body); } + // 打印请求信息 + console.log(`[qqbot-api] >>> ${method} ${url}`); + console.log(`[qqbot-api] >>> Headers:`, JSON.stringify(headers, null, 2)); + if (body) { + console.log(`[qqbot-api] >>> Body:`, JSON.stringify(body, null, 2)); + } + let res: Response; try { res = await fetch(url, options); } catch (err) { + console.error(`[qqbot-api] <<< Network error:`, err); throw new Error(`Network error [${path}]: ${err instanceof Error ? err.message : String(err)}`); } + // 打印响应头 + const responseHeaders: Record = {}; + res.headers.forEach((value, key) => { + responseHeaders[key] = value; + }); + console.log(`[qqbot-api] <<< Status: ${res.status} ${res.statusText}`); + console.log(`[qqbot-api] <<< Headers:`, JSON.stringify(responseHeaders, null, 2)); + let data: T; + let rawBody: string; try { - data = (await res.json()) as T; + rawBody = await res.text(); + console.log(`[qqbot-api] <<< Body:`, rawBody); + data = JSON.parse(rawBody) as T; } catch (err) { + console.error(`[qqbot-api] <<< Parse error:`, err); throw new Error(`Failed to parse response [${path}]: ${err instanceof Error ? err.message : String(err)}`); } @@ -135,26 +199,98 @@ export async function getGatewayUrl(accessToken: string): Promise { return data.url; } +// ============ 流式消息发送接口 ============ + /** - * 发送 C2C 单聊消息 + * 流式消息响应 + */ +export interface StreamMessageResponse { + id: string; + timestamp: number | string; + /** 流式消息ID,用于后续分片 */ + stream_id?: string; +} + +/** + * 构建流式消息体 + * 根据 markdownSupport 配置决定消息格式: + * - markdown 模式: { markdown: { content }, msg_type: 2 } + * - 纯文本模式: { content, msg_type: 0 } + */ +function buildStreamBody( + content: string, + msgId: string | undefined, + msgSeq: number, + stream?: StreamConfig +): Record { + const body: Record = currentMarkdownSupport + ? { + markdown: { content }, + msg_type: 2, + msg_seq: msgSeq, + } + : { + content, + msg_type: 0, + msg_seq: msgSeq, + }; + + if (msgId) { + body.msg_id = msgId; + } + + if (stream) { + body.stream = { + state: stream.state, + index: stream.index, + ...(stream.id ? { id: stream.id } : {}), + }; + } + + return body; +} + +/** + * 发送 C2C 单聊消息(支持流式) */ export async function sendC2CMessage( accessToken: string, openid: string, content: string, - msgId?: string -): Promise<{ id: string; timestamp: number }> { + msgId?: string, + stream?: StreamConfig +): Promise { const msgSeq = msgId ? getNextMsgSeq(msgId) : 1; - return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, { - content, - msg_type: 0, - msg_seq: msgSeq, - ...(msgId ? { msg_id: msgId } : {}), - }); + const body = buildStreamBody(content, msgId, msgSeq, stream); + + return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, body); } /** - * 发送频道消息 + * 发送 C2C 输入状态提示(告知用户机器人正在输入) + */ +export async function sendC2CInputNotify( + accessToken: string, + openid: string, + msgId?: string, + inputSecond: number = 60 +): Promise { + const msgSeq = msgId ? getNextMsgSeq(msgId) : 1; + const body = { + msg_type: 6, + input_notify: { + input_type: 1, + input_second: inputSecond, + }, + msg_seq: msgSeq, + ...(msgId ? { msg_id: msgId } : {}), + }; + + await apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, body); +} + +/** + * 发送频道消息(不支持流式) */ export async function sendChannelMessage( accessToken: string, @@ -169,21 +305,19 @@ export async function sendChannelMessage( } /** - * 发送群聊消息 + * 发送群聊消息(支持流式) */ export async function sendGroupMessage( accessToken: string, groupOpenid: string, content: string, - msgId?: string -): Promise<{ id: string; timestamp: string }> { + msgId?: string, + stream?: StreamConfig +): Promise { const msgSeq = msgId ? getNextMsgSeq(msgId) : 1; - return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, { - content, - msg_type: 0, - msg_seq: msgSeq, - ...(msgId ? { msg_id: msgId } : {}), - }); + const body = buildStreamBody(content, msgId, msgSeq, stream); + + return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, body); } /** @@ -238,11 +372,6 @@ export interface UploadMediaResponse { /** * 上传富媒体文件到 C2C 单聊 - * @param accessToken 访问令牌 - * @param openid 用户 openid - * @param fileType 文件类型 - * @param url 媒体资源 URL - * @param srvSendMsg 是否直接发送(推荐 false,获取 file_info 后再发送) */ export async function uploadC2CMedia( accessToken: string, @@ -260,11 +389,6 @@ export async function uploadC2CMedia( /** * 上传富媒体文件到群聊 - * @param accessToken 访问令牌 - * @param groupOpenid 群 openid - * @param fileType 文件类型 - * @param url 媒体资源 URL - * @param srvSendMsg 是否直接发送(推荐 false,获取 file_info 后再发送) */ export async function uploadGroupMedia( accessToken: string, @@ -282,11 +406,6 @@ export async function uploadGroupMedia( /** * 发送 C2C 单聊富媒体消息 - * @param accessToken 访问令牌 - * @param openid 用户 openid - * @param fileInfo 从 uploadC2CMedia 获取的 file_info - * @param msgId 被动回复时需要的消息 ID - * @param content 可选的文字内容 */ export async function sendC2CMediaMessage( accessToken: string, @@ -307,11 +426,6 @@ export async function sendC2CMediaMessage( /** * 发送群聊富媒体消息 - * @param accessToken 访问令牌 - * @param groupOpenid 群 openid - * @param fileInfo 从 uploadGroupMedia 获取的 file_info - * @param msgId 被动回复时需要的消息 ID - * @param content 可选的文字内容 */ export async function sendGroupMediaMessage( accessToken: string, @@ -332,11 +446,6 @@ export async function sendGroupMediaMessage( /** * 发送带图片的 C2C 单聊消息(封装上传+发送) - * @param accessToken 访问令牌 - * @param openid 用户 openid - * @param imageUrl 图片 URL - * @param msgId 被动回复时需要的消息 ID - * @param content 可选的文字内容 */ export async function sendC2CImageMessage( accessToken: string, @@ -353,11 +462,6 @@ export async function sendC2CImageMessage( /** * 发送带图片的群聊消息(封装上传+发送) - * @param accessToken 访问令牌 - * @param groupOpenid 群 openid - * @param imageUrl 图片 URL - * @param msgId 被动回复时需要的消息 ID - * @param content 可选的文字内容 */ export async function sendGroupImageMessage( accessToken: string, diff --git a/src/channel.ts b/src/channel.ts index ec179d7..2d2a79b 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -1,27 +1,73 @@ -import type { ChannelPlugin } from "clawdbot/plugin-sdk"; +import { + type ChannelPlugin, + type OpenClawConfig, + applyAccountNameToChannelSection, + deleteAccountFromConfigSection, + setAccountEnabledInConfigSection, +} from "openclaw/plugin-sdk"; + import type { ResolvedQQBotAccount } from "./types.js"; -import { listQQBotAccountIds, resolveQQBotAccount, applyQQBotAccountConfig } from "./config.js"; -import { sendText } from "./outbound.js"; +import { DEFAULT_ACCOUNT_ID, listQQBotAccountIds, resolveQQBotAccount, applyQQBotAccountConfig, resolveDefaultQQBotAccountId } from "./config.js"; +import { sendText, sendMedia, createStreamSender, sendTextStream, StreamSender } from "./outbound.js"; import { startGateway } from "./gateway.js"; import { qqbotOnboardingAdapter } from "./onboarding.js"; +import { getQQBotRuntime } from "./runtime.js"; -const DEFAULT_ACCOUNT_ID = "default"; +/** + * 简单的文本分块函数 + * QQ Bot 使用流式消息时,不需要预先分块,而是在发送时逐步累积 + * 但框架可能调用此函数来预分块长文本 + */ +function chunkText(text: string, limit: number): string[] { + if (text.length <= limit) return [text]; + + const chunks: string[] = []; + let remaining = text; + + while (remaining.length > 0) { + if (remaining.length <= limit) { + chunks.push(remaining); + break; + } + + // 尝试在换行处分割 + let splitAt = remaining.lastIndexOf("\n", limit); + if (splitAt <= 0 || splitAt < limit * 0.5) { + // 没找到合适的换行,尝试在空格处分割 + splitAt = remaining.lastIndexOf(" ", limit); + } + if (splitAt <= 0 || splitAt < limit * 0.5) { + // 还是没找到,强制在 limit 处分割 + splitAt = limit; + } + + chunks.push(remaining.slice(0, splitAt)); + remaining = remaining.slice(splitAt).trimStart(); + } + + return chunks; +} export const qqbotPlugin: ChannelPlugin = { id: "qqbot", meta: { id: "qqbot", - label: "QQ Bot", - selectionLabel: "QQ Bot", + label: "QQ Bot (Stream)", + selectionLabel: "QQ Bot (Stream)", docsPath: "/docs/channels/qqbot", - blurb: "Connect to QQ via official QQ Bot API", + blurb: "Connect to QQ via official QQ Bot API with streaming message support", order: 50, }, capabilities: { chatTypes: ["direct", "group"], - media: false, + media: true, reactions: false, threads: false, + /** + * blockStreaming: true 表示该 Channel 支持块流式 + * 框架会收集流式响应,然后通过 deliver 回调发送 + */ + blockStreaming: true, }, reload: { configPrefixes: ["channels.qqbot"] }, // CLI onboarding wizard @@ -29,7 +75,24 @@ export const qqbotPlugin: ChannelPlugin = { config: { listAccountIds: (cfg) => listQQBotAccountIds(cfg), resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId), - defaultAccountId: () => DEFAULT_ACCOUNT_ID, + defaultAccountId: (cfg) => resolveDefaultQQBotAccountId(cfg), + // 新增:设置账户启用状态 + setAccountEnabled: ({ cfg, accountId, enabled }) => + setAccountEnabledInConfigSection({ + cfg, + sectionKey: "qqbot", + accountId, + enabled, + allowTopLevel: true, + }), + // 新增:删除账户 + deleteAccount: ({ cfg, accountId }) => + deleteAccountFromConfigSection({ + cfg, + sectionKey: "qqbot", + accountId, + clearBaseFields: ["appId", "clientSecret", "clientSecretFile", "name"], + }), isConfigured: (account) => Boolean(account?.appId && account?.clientSecret), describeAccount: (account) => ({ accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID, @@ -40,6 +103,16 @@ export const qqbotPlugin: ChannelPlugin = { }), }, setup: { + // 新增:规范化账户 ID + resolveAccountId: ({ accountId }) => accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID, + // 新增:应用账户名称 + applyAccountName: ({ cfg, accountId, name }) => + applyAccountNameToChannelSection({ + cfg, + channelKey: "qqbot", + accountId, + name, + }), validateInput: ({ input }) => { if (!input.token && !input.tokenFile && !input.useEnv) { return "QQBot requires --token (format: appId:clientSecret) or --use-env"; @@ -67,8 +140,22 @@ export const qqbotPlugin: ChannelPlugin = { }); }, }, + // 新增:消息目标解析 + messaging: { + normalizeTarget: (target) => { + // 支持格式: qqbot:openid, qqbot:group:xxx, openid, group:xxx + const normalized = target.replace(/^qqbot:/i, ""); + return { ok: true, to: normalized }; + }, + targetResolver: { + looksLikeId: (id) => /^[A-F0-9]{32}$/i.test(id) || id.startsWith("group:") || id.startsWith("channel:"), + hint: " or group:", + }, + }, outbound: { deliveryMode: "direct", + chunker: chunkText, + chunkerMode: "markdown", textChunkLimit: 2000, sendText: async ({ to, text, accountId, replyToId, cfg }) => { const account = resolveQQBotAccount(cfg, accountId); @@ -79,12 +166,21 @@ export const qqbotPlugin: ChannelPlugin = { error: result.error ? new Error(result.error) : undefined, }; }, + sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => { + const account = resolveQQBotAccount(cfg, accountId); + const result = await sendMedia({ to, text, mediaUrl, accountId, replyToId, account }); + return { + channel: "qqbot", + messageId: result.messageId, + error: result.error ? new Error(result.error) : undefined, + }; + }, }, gateway: { startAccount: async (ctx) => { const { account, abortSignal, log, cfg } = ctx; - log?.info(`[qqbot:${account.accountId}] Starting gateway`); + log?.info(`[qqbot:${account.accountId}] Starting gateway (stream-enabled)`); await startGateway({ account, @@ -109,6 +205,46 @@ export const qqbotPlugin: ChannelPlugin = { }, }); }, + // 新增:登出账户(清除配置中的凭证) + logoutAccount: async ({ accountId, cfg }) => { + const nextCfg = { ...cfg } as OpenClawConfig; + const nextQQBot = cfg.channels?.qqbot ? { ...cfg.channels.qqbot } : undefined; + let cleared = false; + let changed = false; + + if (nextQQBot) { + const qqbot = nextQQBot as Record; + if (accountId === DEFAULT_ACCOUNT_ID && qqbot.clientSecret) { + delete qqbot.clientSecret; + cleared = true; + changed = true; + } + const accounts = qqbot.accounts as Record> | undefined; + if (accounts && accountId in accounts) { + const entry = accounts[accountId]; + if (entry && "clientSecret" in entry) { + delete entry.clientSecret; + cleared = true; + changed = true; + } + if (Object.keys(entry).length === 0) { + delete accounts[accountId]; + changed = true; + } + } + } + + if (changed && nextQQBot) { + nextCfg.channels = { ...nextCfg.channels, qqbot: nextQQBot }; + await getQQBotRuntime().config.writeConfigFile(nextCfg); + } + + const resolved = resolveQQBotAccount(changed ? nextCfg : cfg, accountId); + const loggedOut = resolved.secretSource === "none"; + const envToken = Boolean(process.env.QQBOT_CLIENT_SECRET); + + return { cleared, envToken, loggedOut }; + }, }, status: { defaultRuntime: { @@ -117,7 +253,18 @@ export const qqbotPlugin: ChannelPlugin = { connected: false, lastConnectedAt: null, lastError: null, + lastInboundAt: null, + lastOutboundAt: null, }, + // 新增:构建通道摘要 + buildChannelSummary: ({ snapshot }) => ({ + configured: snapshot.configured ?? false, + tokenSource: snapshot.tokenSource ?? "none", + running: snapshot.running ?? false, + connected: snapshot.connected ?? false, + lastConnectedAt: snapshot.lastConnectedAt ?? null, + lastError: snapshot.lastError ?? null, + }), buildAccountSnapshot: ({ account, runtime }) => ({ accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID, name: account?.name, @@ -128,6 +275,41 @@ export const qqbotPlugin: ChannelPlugin = { connected: runtime?.connected ?? false, lastConnectedAt: runtime?.lastConnectedAt ?? null, lastError: runtime?.lastError ?? null, + lastInboundAt: runtime?.lastInboundAt ?? null, + lastOutboundAt: runtime?.lastOutboundAt ?? null, }), }, }; + +/** + * 导出流式消息工具函数,供外部使用 + * + * 使用示例: + * ```typescript + * import { createStreamSender } from "qqbot"; + * + * // 创建流式发送器 + * const sender = createStreamSender(account, "group:xxx", replyMsgId); + * + * // 发送第一个分片 (state=1, index=0, id="") + * await sender.send("Hello, ", false); + * + * // 发送中间分片 (state=1, index=1, id=从上次响应获取) + * await sender.send("Hello, this is ", false); + * + * // 发送最后分片并结束 (state=10, index=2) + * await sender.end("Hello, this is a streaming message!"); + * ``` + * + * 或使用 AsyncGenerator: + * ```typescript + * async function* generateText() { + * yield "Hello, "; + * yield "this is "; + * yield "a streaming message!"; + * } + * + * await sendTextStream(ctx, generateText()); + * ``` + */ +export { createStreamSender, sendTextStream, StreamSender }; diff --git a/src/config.ts b/src/config.ts index dadcdf2..f898941 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,14 +1,7 @@ import type { ResolvedQQBotAccount, QQBotAccountConfig } from "./types.js"; +import type { OpenClawConfig } from "openclaw/plugin-sdk"; -const DEFAULT_ACCOUNT_ID = "default"; - -interface MoltbotConfig { - channels?: { - qqbot?: QQBotChannelConfig; - [key: string]: unknown; - }; - [key: string]: unknown; -} +export const DEFAULT_ACCOUNT_ID = "default"; interface QQBotChannelConfig extends QQBotAccountConfig { accounts?: Record; @@ -17,9 +10,9 @@ interface QQBotChannelConfig extends QQBotAccountConfig { /** * 列出所有 QQBot 账户 ID */ -export function listQQBotAccountIds(cfg: MoltbotConfig): string[] { +export function listQQBotAccountIds(cfg: OpenClawConfig): string[] { const ids = new Set(); - const qqbot = cfg.channels?.qqbot; + const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined; if (qqbot?.appId) { ids.add(DEFAULT_ACCOUNT_ID); @@ -36,15 +29,34 @@ export function listQQBotAccountIds(cfg: MoltbotConfig): string[] { return Array.from(ids); } +/** + * 获取默认账户 ID + */ +export function resolveDefaultQQBotAccountId(cfg: OpenClawConfig): string { + const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined; + // 如果有默认账户配置,返回 default + if (qqbot?.appId) { + return DEFAULT_ACCOUNT_ID; + } + // 否则返回第一个配置的账户 + if (qqbot?.accounts) { + const ids = Object.keys(qqbot.accounts); + if (ids.length > 0) { + return ids[0]; + } + } + return DEFAULT_ACCOUNT_ID; +} + /** * 解析 QQBot 账户配置 */ export function resolveQQBotAccount( - cfg: MoltbotConfig, + cfg: OpenClawConfig, accountId?: string | null ): ResolvedQQBotAccount { const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID; - const qqbot = cfg.channels?.qqbot; + const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined; // 基础配置 let accountConfig: QQBotAccountConfig = {}; @@ -64,6 +76,7 @@ export function resolveQQBotAccount( allowFrom: qqbot?.allowFrom, systemPrompt: qqbot?.systemPrompt, imageServerBaseUrl: qqbot?.imageServerBaseUrl, + markdownSupport: qqbot?.markdownSupport, }; appId = qqbot?.appId ?? ""; } else { @@ -99,6 +112,7 @@ export function resolveQQBotAccount( secretSource, systemPrompt: accountConfig.systemPrompt, imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL, + markdownSupport: accountConfig.markdownSupport, config: accountConfig, }; } @@ -107,10 +121,10 @@ export function resolveQQBotAccount( * 应用账户配置 */ export function applyQQBotAccountConfig( - cfg: MoltbotConfig, + cfg: OpenClawConfig, accountId: string, input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string; imageServerBaseUrl?: string } -): MoltbotConfig { +): OpenClawConfig { const next = { ...cfg }; if (accountId === DEFAULT_ACCOUNT_ID) { diff --git a/src/gateway.ts b/src/gateway.ts index 45cb46c..95f1df1 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -1,9 +1,11 @@ import WebSocket from "ws"; import path from "node:path"; import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js"; -import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage } from "./api.js"; +import { StreamState } from "./types.js"; +import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, sendC2CInputNotify } from "./api.js"; import { getQQBotRuntime } from "./runtime.js"; import { startImageServer, saveImage, saveImageFromPath, isImageServerRunning, downloadFile, type ImageServerConfig } from "./image-server.js"; +import { createStreamSender } from "./outbound.js"; // QQ Bot intents - 按权限级别分组 const INTENTS = { @@ -50,6 +52,11 @@ const IMAGE_SERVER_PORT = parseInt(process.env.QQBOT_IMAGE_SERVER_PORT || "18765 // 使用绝对路径,确保文件保存和读取使用同一目录 const IMAGE_SERVER_DIR = process.env.QQBOT_IMAGE_SERVER_DIR || path.join(process.env.HOME || "/home/ubuntu", "clawd", "qqbot-images"); +// 流式消息配置 +const STREAM_CHUNK_INTERVAL = 500; // 流式消息分片间隔(毫秒) +const STREAM_MIN_CHUNK_SIZE = 10; // 最小分片大小(字符) +const STREAM_KEEPALIVE_INTERVAL = 8000; // 流式心跳间隔(毫秒),需要在 10 秒内发送 + export interface GatewayContext { account: ResolvedQQBotAccount; abortSignal: AbortSignal; @@ -90,6 +97,7 @@ async function ensureImageServer(log?: GatewayContext["log"], publicBaseUrl?: st /** * 启动 Gateway WebSocket 连接(带自动重连) + * 支持流式消息发送 */ export async function startGateway(ctx: GatewayContext): Promise { const { account, abortSignal, cfg, onReady, onError, log } = ctx; @@ -98,6 +106,12 @@ export async function startGateway(ctx: GatewayContext): Promise { throw new Error("QQBot not configured (missing appId or clientSecret)"); } + // 初始化 API 配置(markdown 支持) + initApiConfig({ + markdownSupport: account.markdownSupport, + }); + log?.info(`[qqbot:${account.accountId}] API config: markdownSupport=${account.markdownSupport !== false}`); + // 如果配置了公网 URL,启动图床服务器 let imageServerBaseUrl: string | null = null; if (account.imageServerBaseUrl) { @@ -218,6 +232,17 @@ export async function startGateway(ctx: GatewayContext): Promise { log?.info(`[qqbot:${account.accountId}] Attachments: ${event.attachments.length}`); } + // 对于 C2C 消息,先发送输入状态提示用户机器人正在输入 + if (event.type === "c2c") { + try { + const token = await getAccessToken(account.appId, account.clientSecret); + await sendC2CInputNotify(token, event.senderId, event.messageId, 60); + log?.info(`[qqbot:${account.accountId}] Sent input notify to ${event.senderId}`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send input notify: ${err}`); + } + } + pluginRuntime.channel.activity.record({ channel: "qqbot", accountId: account.accountId, @@ -242,7 +267,7 @@ export async function startGateway(ctx: GatewayContext): Promise { const envelopeOptions = pluginRuntime.channel.reply.resolveEnvelopeFormatOptions(cfg); // 组装消息体,添加系统提示词 - let builtinPrompt = "由于平台限制,你的回复中不可以包含任何URL。"; + let builtinPrompt = ""; // 只有配置了图床公网地址,才告诉 AI 可以发送图片 if (imageServerBaseUrl) { @@ -309,7 +334,7 @@ export async function startGateway(ctx: GatewayContext): Promise { const fromAddress = event.type === "guild" ? `qqbot:channel:${event.channelId}` : event.type === "group" ? `qqbot:group:${event.groupOpenid}` - : `qqbot:${event.senderId}`; + : `qqbot:c2c:${event.senderId}`; const toAddress = fromAddress; const ctxPayload = pluginRuntime.channel.reply.finalizeInboundContext({ @@ -386,22 +411,178 @@ export async function startGateway(ctx: GatewayContext): Promise { }, responseTimeout); }); + // ============ 流式消息发送器 ============ + // 确定发送目标 + const targetTo = event.type === "c2c" ? event.senderId + : event.type === "group" ? `group:${event.groupOpenid}` + : `channel:${event.channelId}`; + + // 判断是否支持流式(仅 c2c 支持,群聊不支持流式) + const supportsStream = event.type === "c2c"; + + // 创建流式发送器 + const streamSender = supportsStream ? createStreamSender(account, targetTo, event.messageId) : null; + let streamBuffer = ""; // 累积的全部文本(用于记录完整内容) + let lastSentLength = 0; // 上次发送时的文本长度(用于计算增量) + let lastStreamSendTime = 0; // 上次流式发送时间 + let streamStarted = false; // 是否已开始流式发送 + let streamEnded = false; // 流式是否已结束 + let sendingLock = false; // 发送锁,防止并发发送 + let pendingFullText = ""; // 待发送的完整文本(在锁定期间积累) + let keepaliveTimer: ReturnType | null = null; // 心跳定时器 + + // 清理心跳定时器 + const clearKeepalive = () => { + if (keepaliveTimer) { + clearTimeout(keepaliveTimer); + keepaliveTimer = null; + } + }; + + // 重置心跳定时器(每次发送后调用) + const resetKeepalive = () => { + clearKeepalive(); + if (streamSender && streamStarted && !streamEnded) { + keepaliveTimer = setTimeout(async () => { + // 10 秒内没有新消息,发送空分片保持连接 + if (!streamEnded && !sendingLock) { + log?.info(`[qqbot:${account.accountId}] Sending keepalive empty chunk`); + sendingLock = true; + try { + // 发送空内容 + await streamSender!.send("", false); + lastStreamSendTime = Date.now(); + resetKeepalive(); // 继续下一个心跳 + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Keepalive failed: ${err}`); + } finally { + sendingLock = false; + } + } + }, STREAM_KEEPALIVE_INTERVAL); + } + }; + + // 流式发送函数 - 用于 onPartialReply 实时发送(增量模式) + // markdown 分片需要以 \n 结尾 + const sendStreamChunk = async (text: string, isEnd: boolean): Promise => { + if (!streamSender || streamEnded) return false; + + // markdown 分片需要以 \n 结尾(除非是空内容或结束标记) + let contentToSend = text; + if (isEnd && contentToSend && !contentToSend.endsWith("\n") && !isEnd) { + contentToSend = contentToSend + "\n"; + } + + const result = await streamSender.send(contentToSend, isEnd); + if (result.error) { + log?.error(`[qqbot:${account.accountId}] Stream send error: ${result.error}`); + return false; + } else { + log?.debug?.(`[qqbot:${account.accountId}] Stream chunk sent, index: ${streamSender.getContext().index - 1}, isEnd: ${isEnd}, text: "${text.slice(0, 50)}..."`); + } + + if (isEnd) { + streamEnded = true; + clearKeepalive(); + } else { + // 发送成功后重置心跳 + resetKeepalive(); + } + return true; + }; + + // 执行一次流式发送(带锁保护) + const doStreamSend = async (fullText: string, forceEnd: boolean = false): Promise => { + // 如果正在发送,记录待发送的完整文本,稍后处理 + if (sendingLock) { + pendingFullText = fullText; + return; + } + + sendingLock = true; + try { + // 发送当前增量 + if (fullText.length > lastSentLength) { + const increment = fullText.slice(lastSentLength); + const success = await sendStreamChunk(increment, forceEnd); + if (success) { + lastSentLength = fullText.length; + lastStreamSendTime = Date.now(); + streamStarted = true; + log?.info(`[qqbot:${account.accountId}] Stream partial #${streamSender!.getContext().index}, increment: ${increment.length} chars, total: ${fullText.length} chars`); + } + } else if (forceEnd && !streamEnded) { + // 没有新内容但需要结束 + await sendStreamChunk("", true); + } + } finally { + sendingLock = false; + } + + // 处理在锁定期间积累的内容 + if (pendingFullText && pendingFullText.length > lastSentLength && !streamEnded) { + const pending = pendingFullText; + pendingFullText = ""; + // 递归发送积累的内容(不强制结束) + await doStreamSend(pending, false); + } + }; + + // onPartialReply 回调 - 实时接收 AI 生成的文本(payload.text 是累积的全文) + const handlePartialReply = async (payload: { text?: string }) => { + if (!streamSender || streamEnded) { + log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply skipped: streamSender=${!!streamSender}, streamEnded=${streamEnded}`); + return; + } + + const fullText = payload.text ?? ""; + if (!fullText) { + log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: empty text`); + return; + } + + // 始终更新累积缓冲区(即使不发送,也要记录最新内容) + streamBuffer = fullText; + hasResponse = true; + + log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: fullText.length=${fullText.length}, lastSentLength=${lastSentLength}`); + + // 如果没有新内容,跳过 + if (fullText.length <= lastSentLength) return; + + const now = Date.now(); + // 控制发送频率:首次发送或间隔超过阈值 + if (!streamStarted || now - lastStreamSendTime >= STREAM_CHUNK_INTERVAL) { + log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending stream chunk, length=${fullText.length}`); + await doStreamSend(fullText, false); + } else { + // 不到发送时间,但记录待发送内容,确保最终会被发送 + pendingFullText = fullText; + } + }; + const dispatchPromise = pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ ctx: ctxPayload, cfg, dispatcherOptions: { responsePrefix: messagesConfig.responsePrefix, - deliver: async (payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string }) => { + deliver: async (payload: { text?: string; mediaUrls?: string[]; mediaUrl?: string }, info: { kind: string }) => { hasResponse = true; if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } - log?.info(`[qqbot:${account.accountId}] deliver called, payload keys: ${Object.keys(payload).join(", ")}`); + log?.info(`[qqbot:${account.accountId}] deliver called, kind: ${info.kind}, payload keys: ${Object.keys(payload).join(", ")}`); let replyText = payload.text ?? ""; + // 更新 streamBuffer,确保最终内容不会丢失 + if (replyText.length > streamBuffer.length) { + streamBuffer = replyText; + } + // 收集所有图片路径 const imageUrls: string[] = []; @@ -430,70 +611,52 @@ export async function startGateway(ctx: GatewayContext): Promise { } } - // 如果没有文本也没有图片,跳过 - if (!replyText.trim() && imageUrls.length === 0) { - log?.info(`[qqbot:${account.accountId}] Empty reply, skipping`); - return; - } - - // 0. 提取 MEDIA: 前缀的本地文件路径(从文本中) + // 提取文本中的各种图片格式 + // 0. 提取 MEDIA: 前缀的本地文件路径 const mediaPathRegex = /MEDIA:([^\s\n]+)/gi; const mediaMatches = [...replyText.matchAll(mediaPathRegex)]; - for (const match of mediaMatches) { const localPath = match[1]; if (localPath && imageServerBaseUrl) { - // 将本地文件复制到图床 try { const savedUrl = saveImageFromPath(localPath); if (savedUrl) { imageUrls.push(savedUrl); log?.info(`[qqbot:${account.accountId}] Saved local image to server: ${localPath}`); - } else { - log?.error(`[qqbot:${account.accountId}] Failed to save local image (not found or not image): ${localPath}`); } } catch (err) { log?.error(`[qqbot:${account.accountId}] Failed to save local image: ${err}`); } } - // 从文本中移除 MEDIA: 行 replyText = replyText.replace(match[0], "").trim(); } - // 0.5. 提取本地绝对文件路径(/path/to/image.png 或 /path/to/image_123_png 格式) - // 支持标准扩展名和下划线替换后的扩展名 + // 0.5. 提取本地绝对文件路径 const localPathRegex = /(\/[^\s\n]+?(?:\.(?:png|jpg|jpeg|gif|webp)|_(?:png|jpg|jpeg|gif|webp)(?:\s|$)))/gi; const localPathMatches = [...replyText.matchAll(localPathRegex)]; - for (const match of localPathMatches) { let localPath = match[1].trim(); if (localPath && imageServerBaseUrl) { - // 如果是下划线格式的扩展名,转换回点格式 localPath = localPath.replace(/_(?=(?:png|jpg|jpeg|gif|webp)$)/, "."); try { const savedUrl = saveImageFromPath(localPath); if (savedUrl) { imageUrls.push(savedUrl); log?.info(`[qqbot:${account.accountId}] Saved local path image to server: ${localPath}`); - } else { - log?.error(`[qqbot:${account.accountId}] Local path not found or not image: ${localPath}`); } } catch (err) { log?.error(`[qqbot:${account.accountId}] Failed to save local path image: ${err}`); } } - // 从文本中移除本地路径 replyText = replyText.replace(match[0], "").trim(); } - // 1. 提取 base64 图片(data:image/xxx;base64,...) + // 1. 提取 base64 图片 const base64ImageRegex = /!\[([^\]]*)\]\((data:image\/[^;]+;base64,[A-Za-z0-9+/=]+)\)|(? { log?.error(`[qqbot:${account.accountId}] Failed to save base64 image: ${err}`); } } - // 从文本中移除 base64 replyText = replyText.replace(match[0], "").trim(); } - // 2. 提取 URL 图片(Markdown 格式或纯 URL) + // 2. 提取 URL 图片 const imageUrlRegex = /!\[([^\]]*)\]\((https?:\/\/[^\s)]+\.(?:png|jpg|jpeg|gif|webp)(?:\?[^\s)]*)?)\)|(? 0; - let hasReplacement = false; - if (!hasImages) { + if (!hasImages && textWithoutImages) { const originalText = textWithoutImages; textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2"); - hasReplacement = textWithoutImages !== originalText; - if (hasReplacement && textWithoutImages.trim()) { + if (textWithoutImages !== originalText && textWithoutImages.trim()) { textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)"; } } try { - // 先发送图片(如果有) + // 发送图片(如果有) for (const imageUrl of imageUrls) { try { await sendWithTokenRetry(async (token) => { @@ -546,29 +704,24 @@ export async function startGateway(ctx: GatewayContext): Promise { } else if (event.type === "group" && event.groupOpenid) { await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); } - // 频道消息暂不支持富媒体,跳过图片 }); log?.info(`[qqbot:${account.accountId}] Sent image: ${imageUrl.slice(0, 50)}...`); } catch (imgErr) { log?.error(`[qqbot:${account.accountId}] Failed to send image: ${imgErr}`); - // 图片发送失败时,显示错误信息而不是 URL - const errMsg = String(imgErr).slice(0, 200); - textWithoutImages = `[图片发送失败: ${errMsg}]\n${textWithoutImages}`; } } - // 再发送文本(如果有) - if (textWithoutImages.trim()) { + // 只有频道和群聊消息(不支持流式)在 deliver 中发送文本 + // c2c 的文本通过 onPartialReply 流式发送 + if (!supportsStream && textWithoutImages.trim()) { await sendWithTokenRetry(async (token) => { - if (event.type === "c2c") { - await sendC2CMessage(token, event.senderId, textWithoutImages, event.messageId); - } else if (event.type === "group" && event.groupOpenid) { + if (event.type === "group" && event.groupOpenid) { await sendGroupMessage(token, event.groupOpenid, textWithoutImages, event.messageId); } else if (event.channelId) { await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); } }); - log?.info(`[qqbot:${account.accountId}] Sent text reply`); + log?.info(`[qqbot:${account.accountId}] Sent text reply (${event.type}, non-stream)`); } pluginRuntime.channel.activity.record({ @@ -587,6 +740,28 @@ export async function startGateway(ctx: GatewayContext): Promise { clearTimeout(timeoutId); timeoutId = null; } + + // 清理心跳定时器 + clearKeepalive(); + + // 如果在流式模式中出错,发送结束标记(增量模式) + if (streamSender && !streamEnded && streamBuffer) { + try { + // 等待发送锁释放 + while (sendingLock) { + await new Promise(resolve => setTimeout(resolve, 50)); + } + // 发送剩余增量 + 错误标记 + const remainingIncrement = streamBuffer.slice(lastSentLength); + const errorIncrement = remainingIncrement + "\n\n[生成中断]"; + await streamSender.end(errorIncrement); + streamEnded = true; + log?.info(`[qqbot:${account.accountId}] Stream ended due to error`); + } catch (endErr) { + log?.error(`[qqbot:${account.accountId}] Failed to end stream: ${endErr}`); + } + } + // 发送错误提示给用户,显示完整错误信息 const errMsg = String(err); if (errMsg.includes("401") || errMsg.includes("key") || errMsg.includes("auth")) { @@ -597,13 +772,47 @@ export async function startGateway(ctx: GatewayContext): Promise { } }, }, - replyOptions: {}, + replyOptions: { + // 使用 onPartialReply 实现真正的流式消息 + // 这个回调在 AI 生成过程中被实时调用 + onPartialReply: supportsStream ? handlePartialReply : undefined, + // 禁用 block streaming,因为我们用 onPartialReply 实现更实时的流式 + disableBlockStreaming: supportsStream, + }, }); // 等待分发完成或超时 try { await Promise.race([dispatchPromise, timeoutPromise]); + + // 清理心跳定时器 + clearKeepalive(); + + // 分发完成后,如果使用了流式且有内容,发送结束标记 + if (streamSender && !streamEnded) { + // 等待发送锁释放 + while (sendingLock) { + await new Promise(resolve => setTimeout(resolve, 50)); + } + + // 确保所有待发送内容都发送出去 + // 优先使用 pendingFullText,因为它可能包含最新的完整文本 + const finalFullText = pendingFullText && pendingFullText.length > streamBuffer.length + ? pendingFullText + : streamBuffer; + + // 计算剩余未发送的增量内容 + const remainingIncrement = finalFullText.slice(lastSentLength); + if (remainingIncrement || streamStarted) { + // 有剩余内容或者已开始流式,都需要发送结束标记 + await streamSender.end(remainingIncrement); + streamEnded = true; + log?.info(`[qqbot:${account.accountId}] Stream completed, final increment: ${remainingIncrement.length} chars, total: ${finalFullText.length} chars, chunks: ${streamSender.getContext().index}`); + } + } } catch (err) { + // 清理心跳定时器 + clearKeepalive(); if (timeoutId) { clearTimeout(timeoutId); } diff --git a/src/onboarding.ts b/src/onboarding.ts index 6310ba1..364efd3 100644 --- a/src/onboarding.ts +++ b/src/onboarding.ts @@ -1,7 +1,7 @@ /** * QQBot CLI Onboarding Adapter * - * 提供 moltbot onboard 命令的交互式配置支持 + * 提供 openclaw onboard 命令的交互式配置支持 */ import type { ChannelOnboardingAdapter, @@ -9,20 +9,11 @@ import type { ChannelOnboardingStatusContext, ChannelOnboardingConfigureContext, ChannelOnboardingResult, -} from "clawdbot/plugin-sdk"; -import { listQQBotAccountIds, resolveQQBotAccount } from "./config.js"; - -const DEFAULT_ACCOUNT_ID = "default"; - -// 内部类型(避免循环依赖) -interface MoltbotConfig { - channels?: { - qqbot?: QQBotChannelConfig; - [key: string]: unknown; - }; - [key: string]: unknown; -} + OpenClawConfig, +} from "openclaw/plugin-sdk"; +import { DEFAULT_ACCOUNT_ID, listQQBotAccountIds, resolveQQBotAccount } from "./config.js"; +// 内部类型(用于类型安全) interface QQBotChannelConfig { enabled?: boolean; appId?: string; @@ -43,7 +34,7 @@ interface QQBotChannelConfig { /** * 解析默认账户 ID */ -function resolveDefaultQQBotAccountId(cfg: MoltbotConfig): string { +function resolveDefaultQQBotAccountId(cfg: OpenClawConfig): string { const ids = listQQBotAccountIds(cfg); return ids[0] ?? DEFAULT_ACCOUNT_ID; } @@ -56,31 +47,30 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { getStatus: async (ctx: ChannelOnboardingStatusContext): Promise => { const { cfg } = ctx; - const configured = listQQBotAccountIds(cfg as MoltbotConfig).some((accountId) => { - const account = resolveQQBotAccount(cfg as MoltbotConfig, accountId); + const configured = listQQBotAccountIds(cfg).some((accountId) => { + const account = resolveQQBotAccount(cfg, accountId); return Boolean(account.appId && account.clientSecret); }); return { channel: "qqbot" as any, configured, - statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`], - selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊", + statusLines: [`QQ Bot (Stream): ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`], + selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊(流式消息)", quickstartScore: configured ? 1 : 20, }; }, configure: async (ctx: ChannelOnboardingConfigureContext): Promise => { const { cfg, prompter, accountOverrides, shouldPromptAccountIds } = ctx; - const moltbotCfg = cfg as MoltbotConfig; const qqbotOverride = (accountOverrides as Record).qqbot?.trim(); - const defaultAccountId = resolveDefaultQQBotAccountId(moltbotCfg); + const defaultAccountId = resolveDefaultQQBotAccountId(cfg); let accountId = qqbotOverride ?? defaultAccountId; // 是否需要提示选择账户 if (shouldPromptAccountIds && !qqbotOverride) { - const existingIds = listQQBotAccountIds(moltbotCfg); + const existingIds = listQQBotAccountIds(cfg); if (existingIds.length > 1) { accountId = await prompter.select({ message: "选择 QQBot 账户", @@ -93,7 +83,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { } } - let next = moltbotCfg; + let next = cfg; const resolvedAccount = resolveQQBotAccount(next, accountId); const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret); const allowEnv = accountId === DEFAULT_ACCOUNT_ID; @@ -115,8 +105,10 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { "4) 你也可以设置环境变量 QQBOT_APP_ID 和 QQBOT_CLIENT_SECRET", "", "文档: https://bot.q.qq.com/wiki/", + "", + "此版本支持流式消息发送!", ].join("\n"), - "QQ Bot 配置", + "QQ Bot (Stream) 配置", ); } @@ -241,8 +233,8 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { disable: (cfg) => ({ ...cfg, channels: { - ...(cfg as MoltbotConfig).channels, - qqbot: { ...(cfg as MoltbotConfig).channels?.qqbot, enabled: false }, + ...(cfg as OpenClawConfig).channels, + qqbot: { ...(cfg as OpenClawConfig).channels?.qqbot, enabled: false }, }, }) as any, }; diff --git a/src/outbound.ts b/src/outbound.ts index a8f9809..1d9510b 100644 --- a/src/outbound.ts +++ b/src/outbound.ts @@ -1,4 +1,9 @@ -import type { ResolvedQQBotAccount } from "./types.js"; +/** + * QQ Bot 消息发送模块(支持流式消息) + */ + +import type { ResolvedQQBotAccount, StreamContext } from "./types.js"; +import { StreamState } from "./types.js"; import { getAccessToken, sendC2CMessage, @@ -6,6 +11,9 @@ import { sendGroupMessage, sendProactiveC2CMessage, sendProactiveGroupMessage, + sendC2CImageMessage, + sendGroupImageMessage, + type StreamMessageResponse, } from "./api.js"; export interface OutboundContext { @@ -16,11 +24,164 @@ export interface OutboundContext { account: ResolvedQQBotAccount; } +export interface MediaOutboundContext extends OutboundContext { + mediaUrl: string; +} + export interface OutboundResult { channel: string; messageId?: string; timestamp?: string | number; error?: string; + /** 流式消息ID,用于后续分片 */ + streamId?: string; +} + +/** + * 流式消息发送器 + * 用于管理一个完整的流式消息会话 + */ +export class StreamSender { + private context: StreamContext; + private accessToken: string | null = null; + private targetType: "c2c" | "group" | "channel"; + private targetId: string; + private msgId?: string; + private account: ResolvedQQBotAccount; + + constructor( + account: ResolvedQQBotAccount, + to: string, + replyToId?: string | null + ) { + this.account = account; + this.msgId = replyToId ?? undefined; + this.context = { + index: 0, + streamId: "", + ended: false, + }; + + // 解析目标地址 + const target = parseTarget(to); + this.targetType = target.type; + this.targetId = target.id; + } + + /** + * 发送流式消息分片 + * @param text 分片内容 + * @param isEnd 是否是最后一个分片 + * @returns 发送结果 + */ + async send(text: string, isEnd = false): Promise { + if (this.context.ended) { + return { channel: "qqbot", error: "Stream already ended" }; + } + + if (!this.account.appId || !this.account.clientSecret) { + return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; + } + + try { + // 获取或复用 accessToken + if (!this.accessToken) { + this.accessToken = await getAccessToken(this.account.appId, this.account.clientSecret); + } + + const streamConfig = { + state: isEnd ? StreamState.END : StreamState.STREAMING, + index: this.context.index, + id: this.context.streamId, + }; + + let result: StreamMessageResponse; + + if (this.targetType === "c2c") { + result = await sendC2CMessage( + this.accessToken, + this.targetId, + text, + this.msgId, + streamConfig + ); + } else if (this.targetType === "group") { + // 群聊不支持流式,直接发送普通消息 + const groupResult = await sendGroupMessage( + this.accessToken, + this.targetId, + text, + this.msgId + // 不传 streamConfig + ); + return { + channel: "qqbot", + messageId: groupResult.id, + timestamp: groupResult.timestamp + }; + } else { + // 频道不支持流式,直接发送普通消息 + const channelResult = await sendChannelMessage( + this.accessToken, + this.targetId, + text, + this.msgId + ); + return { + channel: "qqbot", + messageId: channelResult.id, + timestamp: channelResult.timestamp + }; + } + + // 更新流式上下文 + // 第一次发送后,服务端会返回 stream_id(或在 id 字段中),后续需要带上 + if (this.context.index === 0 && result.stream_id) { + this.context.streamId = result.stream_id; + } else if (this.context.index === 0 && result.id && !this.context.streamId) { + // 某些情况下 stream_id 可能在 id 字段返回 + this.context.streamId = result.id; + } + + this.context.index++; + + if (isEnd) { + this.context.ended = true; + } + + return { + channel: "qqbot", + messageId: result.id, + timestamp: result.timestamp, + streamId: this.context.streamId, + }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { channel: "qqbot", error: message }; + } + } + + /** + * 结束流式消息 + * @param text 最后一个分片的内容(可选) + */ + async end(text?: string): Promise { + return this.send(text ?? "", true); + } + + /** + * 获取当前流式上下文状态 + */ + getContext(): Readonly { + return { ...this.context }; + } + + /** + * 是否已结束 + */ + isEnded(): boolean { + return this.context.ended; + } } /** @@ -32,26 +193,32 @@ export interface OutboundResult { * - 纯数字 -> 频道 */ function parseTarget(to: string): { type: "c2c" | "group" | "channel"; id: string } { - if (to.startsWith("group:")) { - return { type: "group", id: to.slice(6) }; + // 去掉 qqbot: 前缀 + let id = to.replace(/^qqbot:/i, ""); + + if (id.startsWith("c2c:")) { + return { type: "c2c", id: id.slice(4) }; } - if (to.startsWith("channel:")) { - return { type: "channel", id: to.slice(8) }; + if (id.startsWith("group:")) { + return { type: "group", id: id.slice(6) }; } - // openid 通常是 32 位十六进制 - if (/^[A-F0-9]{32}$/i.test(to)) { - return { type: "c2c", id: to }; + if (id.startsWith("channel:")) { + return { type: "channel", id: id.slice(8) }; } - // 默认当作频道 ID - return { type: "channel", id: to }; + // 默认当作 c2c(私聊) + return { type: "c2c", id }; } /** - * 发送文本消息(被动回复,需要 replyToId) + * 发送文本消息 + * - 有 replyToId: 被动回复,无配额限制 + * - 无 replyToId: 主动发送,有配额限制(每月4条/用户/群) */ export async function sendText(ctx: OutboundContext): Promise { const { to, text, replyToId, account } = ctx; + console.log("[qqbot] sendText ctx:", JSON.stringify({ to, text: text?.slice(0, 50), replyToId, accountId: account.accountId }, null, 2)); + if (!account.appId || !account.clientSecret) { return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; } @@ -59,15 +226,32 @@ export async function sendText(ctx: OutboundContext): Promise { try { const accessToken = await getAccessToken(account.appId, account.clientSecret); const target = parseTarget(to); + console.log("[qqbot] sendText target:", JSON.stringify(target)); + // 如果没有 replyToId,使用主动发送接口 + if (!replyToId) { + if (target.type === "c2c") { + const result = await sendProactiveC2CMessage(accessToken, target.id, text); + return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; + } else if (target.type === "group") { + const result = await sendProactiveGroupMessage(accessToken, target.id, text); + return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; + } else { + // 频道暂不支持主动消息 + const result = await sendChannelMessage(accessToken, target.id, text); + return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; + } + } + + // 有 replyToId,使用被动回复接口 if (target.type === "c2c") { - const result = await sendC2CMessage(accessToken, target.id, text, replyToId ?? undefined); + const result = await sendC2CMessage(accessToken, target.id, text, replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } else if (target.type === "group") { - const result = await sendGroupMessage(accessToken, target.id, text, replyToId ?? undefined); + const result = await sendGroupMessage(accessToken, target.id, text, replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } else { - const result = await sendChannelMessage(accessToken, target.id, text, replyToId ?? undefined); + const result = await sendChannelMessage(accessToken, target.id, text, replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } } catch (err) { @@ -76,6 +260,69 @@ export async function sendText(ctx: OutboundContext): Promise { } } +/** + * 流式发送文本消息 + * + * @param ctx 发送上下文 + * @param textGenerator 异步文本生成器,每次 yield 一个分片 + * @returns 最终发送结果 + * + * @example + * ```typescript + * async function* generateText() { + * yield "Hello, "; + * yield "this is "; + * yield "a streaming "; + * yield "message!"; + * } + * + * const result = await sendTextStream(ctx, generateText()); + * ``` + */ +export async function sendTextStream( + ctx: OutboundContext, + textGenerator: AsyncIterable +): Promise { + const { to, replyToId, account } = ctx; + + const sender = new StreamSender(account, to, replyToId); + let lastResult: OutboundResult = { channel: "qqbot" }; + let buffer = ""; + + try { + for await (const chunk of textGenerator) { + buffer += chunk; + + // 发送当前分片 + lastResult = await sender.send(buffer, false); + + if (lastResult.error) { + return lastResult; + } + } + + // 发送结束标记 + lastResult = await sender.end(buffer); + + return lastResult; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { channel: "qqbot", error: message }; + } +} + +/** + * 创建流式消息发送器 + * 提供更细粒度的控制 + */ +export function createStreamSender( + account: ResolvedQQBotAccount, + to: string, + replyToId?: string | null +): StreamSender { + return new StreamSender(account, to, replyToId); +} + /** * 主动发送消息(不需要 replyToId,有配额限制:每月 4 条/用户/群) * @@ -112,3 +359,81 @@ export async function sendProactiveMessage( return { channel: "qqbot", error: message }; } } + +/** + * 发送富媒体消息(图片) + * + * @param ctx - 发送上下文,包含 mediaUrl + * @returns 发送结果 + * + * @example + * ```typescript + * const result = await sendMedia({ + * to: "group:xxx", + * text: "这是图片说明", + * mediaUrl: "https://example.com/image.png", + * account, + * replyToId: msgId, + * }); + * ``` + */ +export async function sendMedia(ctx: MediaOutboundContext): Promise { + const { to, text, mediaUrl, replyToId, account } = ctx; + + if (!account.appId || !account.clientSecret) { + return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; + } + + if (!mediaUrl) { + return { channel: "qqbot", error: "mediaUrl is required for sendMedia" }; + } + + try { + const accessToken = await getAccessToken(account.appId, account.clientSecret); + const target = parseTarget(to); + + // 先发送图片 + let imageResult: { id: string; timestamp: number | string }; + if (target.type === "c2c") { + imageResult = await sendC2CImageMessage( + accessToken, + target.id, + mediaUrl, + replyToId ?? undefined, + undefined // content 参数,图片消息不支持同时带文本 + ); + } else if (target.type === "group") { + imageResult = await sendGroupImageMessage( + accessToken, + target.id, + mediaUrl, + replyToId ?? undefined, + undefined + ); + } else { + // 频道暂不支持富媒体消息,只发送文本 + URL + const textWithUrl = text ? `${text}\n${mediaUrl}` : mediaUrl; + const result = await sendChannelMessage(accessToken, target.id, textWithUrl, replyToId ?? undefined); + return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; + } + + // 如果有文本说明,再发送一条文本消息 + if (text?.trim()) { + try { + if (target.type === "c2c") { + await sendC2CMessage(accessToken, target.id, text, replyToId ?? undefined); + } else if (target.type === "group") { + await sendGroupMessage(accessToken, target.id, text, replyToId ?? undefined); + } + } catch (textErr) { + // 文本发送失败不影响整体结果,图片已发送成功 + console.error(`[qqbot] Failed to send text after image: ${textErr}`); + } + } + + return { channel: "qqbot", messageId: imageResult.id, timestamp: imageResult.timestamp }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { channel: "qqbot", error: message }; + } +} diff --git a/src/runtime.ts b/src/runtime.ts index 414e19c..ce364ca 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -1,4 +1,4 @@ -import type { PluginRuntime } from "clawdbot/plugin-sdk"; +import type { PluginRuntime } from "openclaw/plugin-sdk"; let runtime: PluginRuntime | null = null; diff --git a/src/types.ts b/src/types.ts index e185263..b86eae9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,8 @@ export interface ResolvedQQBotAccount { systemPrompt?: string; /** 图床服务器公网地址 */ imageServerBaseUrl?: string; + /** 是否支持 markdown 消息 */ + markdownSupport?: boolean; config: QQBotAccountConfig; } @@ -39,6 +41,8 @@ export interface QQBotAccountConfig { systemPrompt?: string; /** 图床服务器公网地址,用于发送图片,例如 http://your-ip:18765 */ imageServerBaseUrl?: string; + /** 是否支持 markdown 消息,默认 true */ + markdownSupport?: boolean; } /** @@ -117,3 +121,37 @@ export interface WSPayload { s?: number; t?: string; } + +/** + * 流式消息状态 + */ +export enum StreamState { + /** 流式消息开始/进行中 */ + STREAMING = 1, + /** 流式消息结束 */ + END = 10, +} + +/** + * 流式消息配置 + */ +export interface StreamConfig { + /** 流式状态: 1=开始/进行中, 10=结束 */ + state: StreamState; + /** 分片索引,从0开始 */ + index: number; + /** 流式消息ID,第一次发送为空,后续需要带上服务端返回的ID */ + id: string; +} + +/** + * 流式消息发送上下文 + */ +export interface StreamContext { + /** 当前分片索引 */ + index: number; + /** 流式消息ID(首次发送后由服务端返回) */ + streamId: string; + /** 是否已结束 */ + ended: boolean; +} diff --git a/upgrade-and-run.sh b/upgrade-and-run.sh new file mode 100755 index 0000000..b47c279 --- /dev/null +++ b/upgrade-and-run.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# QQBot 一键更新并启动脚本 + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +echo "=========================================" +echo " QQBot 一键更新启动脚本" +echo "=========================================" + +# 1. 移除老版本 +echo "" +echo "[1/4] 移除老版本..." +if [ -f "./scripts/upgrade.sh" ]; then + bash ./scripts/upgrade.sh +else + echo "警告: upgrade.sh 不存在,跳过移除步骤" +fi + +# 2. 安装当前版本 +echo "" +echo "[2/4] 安装当前版本..." +openclaw plugins install . + +# 3. 配置机器人通道 +echo "" +echo "[3/4] 配置机器人通道..." +# 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖 +QQBOT_TOKEN="${QQBOT_TOKEN:-xxx:xxx}" +openclaw channels add --channel qqbot --token "$QQBOT_TOKEN" +# 启用 markdown 支持 +openclaw config set channels.qqbot.markdownSupport true + +# 4. 启动 openclaw +echo "" +echo "[4/4] 启动 openclaw..." +echo "=========================================" +openclaw gateway --verbose From a3e87f2f373a8be3d815ff9238c92787d93a088b Mon Sep 17 00:00:00 2001 From: rianli Date: Sun, 1 Feb 2026 17:10:55 +0800 Subject: [PATCH 02/14] =?UTF-8?q?feat(qqbot):=20=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E6=8F=90=E9=86=92=E6=8A=80=E8=83=BD=E4=B8=8E=E4=B8=BB=E5=8A=A8?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **新增提醒技能** - 新增 skills/qqbot-cron/SKILL.md 定时提醒技能定义 - 支持一次性提醒(--at)和周期性提醒(--cron) - 支持设置、查询、取消提醒操作 **主动消息系统** - 新增 src/proactive.ts 主动消息发送模块 - 新增 src/known-users.ts 已知用户管理 - 新增 src/session-store.ts 会话存储 - 支持主动向用户/群组发送消息 **工具脚本** - 新增 scripts/proactive-api-server.ts 主动消息API服务 --- console.md | 1041 +++++++++++++++++++++++++++++++ openclaw.plugin.json | 8 + scripts/proactive-api-server.ts | 346 ++++++++++ scripts/send-proactive.ts | 273 ++++++++ skills/qqbot-cron/SKILL.md | 476 ++++++++++++++ src/api.ts | 244 +++++++- src/config.ts | 2 + src/gateway.ts | 450 +++++++++++-- src/known-users.ts | 358 +++++++++++ src/openclaw-plugin-sdk.d.ts | 483 ++++++++++++++ src/outbound.ts | 184 +++++- src/proactive.ts | 528 ++++++++++++++++ src/session-store.ts | 292 +++++++++ src/types.ts | 4 + upgrade-and-run.sh | 2 +- 15 files changed, 4639 insertions(+), 52 deletions(-) create mode 100644 console.md create mode 100644 scripts/proactive-api-server.ts create mode 100644 scripts/send-proactive.ts create mode 100644 skills/qqbot-cron/SKILL.md create mode 100644 src/known-users.ts create mode 100644 src/openclaw-plugin-sdk.d.ts create mode 100644 src/proactive.ts create mode 100644 src/session-store.ts diff --git a/console.md b/console.md new file mode 100644 index 0000000..fa74c12 --- /dev/null +++ b/console.md @@ -0,0 +1,1041 @@ +08:27:48 [qqbot] [qqbot:default] Heartbeat sent +08:27:48 [qqbot] [qqbot:default] Received op=11 t=undefined +08:27:48 [qqbot] [qqbot:default] Heartbeat ACK +08:27:50 [ws] ⇄ res ✓ node.list 1ms id=0404fd79…e7be +08:27:55 [ws] ⇄ res ✓ node.list 1ms id=84c095a4…be49 +08:28:00 [ws] ⇄ res ✓ node.list 2ms id=33639de8…7fc3 +08:28:02 [ws] → event health seq=9 clients=5 presenceVersion=6 healthVersion=8 +08:28:02 [ws] → event tick seq=10 clients=5 dropIfSlow=true +08:28:03 [session-store] Saved session for default: sessionId=dbae1ca2-e53c-48f9-8721-fb93e5356c77, lastSeq=2 +08:28:03 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE +08:28:03 [known-users] Loaded 1 users from file +08:28:03 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 8 +08:28:03 [qqbot] [qqbot:default] Message enqueued, queue size: 1 +08:28:03 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 5分钟后提醒我喝水 +08:28:03 [qqbot] [qqbot:default] Stream enabled: true +08:28:03 [qqbot] [qqbot:default] Stream support: true (type=c2c, enabled=true) +08:28:03 [skills] plugin skill path not found (qqbot): /Users/lishoushuai/.openclaw/extensions/qqbot/qqbot-cron +08:28:03 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 +08:28:03 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=2 queueSize=0 +08:28:03 [diagnostic] lane enqueue: lane=main queueSize=1 +08:28:03 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 +08:28:03 [agent/embedded] embedded run start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot +08:28:03 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 +08:28:03 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 +08:28:03 [agent/embedded] embedded run prompt start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 +08:28:03 [agent/embedded] embedded run agent start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b +08:28:03 [ws] → event agent seq=11 clients=5 run=49d64fc4…e41b agent=main session=main stream=lifecycle aseq=1 phase=start +08:28:05 [ws] ⇄ res ✓ node.list 3ms id=15a727fe…d5a4 +08:28:08 [known-users] Saved 1 users to file +08:28:10 [ws] → event agent seq=12 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=2 text=我已经 +08:28:10 [ws] → event chat seq=13 clients=5 dropIfSlow=true +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=3, lastSentLength=0, streamBuffer.length=3, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=3 +08:28:10 [qqbot] [qqbot:default] Stream started, max duration: 180s +08:28:10 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:10 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:10 [qqbot-api] >>> Body: { +"markdown": { +"content": "我已经" +}, +"msg_type": 2, +"msg_seq": 69934422, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 0 +} +} +08:28:10 [ws] → event agent seq=14 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=3 text=我已经为您 +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=5, lastSentLength=0, streamBuffer.length=5, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=5 +08:28:10 [ws] → event agent seq=15 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=4 text=我已经为您设置了一个5分钟后 +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=14, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 +08:28:10 [ws] → event agent seq=16 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=5 text=我已经为您设置了一个5分钟后提醒喝水的定时 +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=21, lastSentLength=0, streamBuffer.length=21, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=21 +08:28:10 [ws] → event agent seq=17 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=6 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次 +08:28:10 [ws] → event chat seq=18 clients=5 dropIfSlow=true +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=28, lastSentLength=0, streamBuffer.length=28, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=28 +08:28:10 [ws] → event agent seq=19 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=7 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒 +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=36, lastSentLength=0, streamBuffer.length=36, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=36 +08:28:10 [ws] → event agent seq=20 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=8 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建: +08:28:10 [ws] → event chat seq=21 clients=5 dropIfSlow=true +08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=43, lastSentLength=0, streamBuffer.length=43, isNewSegment=false +08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=43 +08:28:11 [ws] ⇄ res ✓ node.list 27ms id=be4d996c…f5de + +08:28:11 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_45275505bcb24671aac9d040 +08:28:12 [qqbot-api] <<< Status: 200 OK +08:28:12 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:12 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "1e9be398831193b8e9a5da84f0296922" +} +08:28:12 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:12+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:12 [qqbot] [qqbot:default] Stream chunk sent, index: 0, isEnd: false, text: "我已经..." +08:28:12 [qqbot] [qqbot:default] Stream partial #1, increment: 3 chars, total: 3 chars +08:28:12 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:12 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:12 [qqbot-api] >>> Body: { +"markdown": { +"content": "为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建:" +}, +"msg_type": 2, +"msg_seq": 69934423, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 1, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:13 [qqbot-api] <<< Status: 200 OK +08:28:13 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:13 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "bd194a3c84af19b053c8c1c044c8fad9" +} +08:28:13 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:13+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:13 [qqbot] [qqbot:default] Stream chunk sent, index: 1, isEnd: false, text: "为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建:..." +08:28:13 [qqbot] [qqbot:default] Stream partial #2, increment: 40 chars, total: 43 chars +08:28:15 [ws] ⇄ res ✓ node.list 2ms id=02ca1f98…f997 +08:28:16 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 6s, since chunk: 3s) +08:28:16 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:16 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:16 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934424, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 2, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:16 [qqbot-api] <<< Status: 200 OK +08:28:16 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:16 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "2eeba590381c912b5f1eb324ec471901" +} +08:28:16 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:16+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:20 [ws] ⇄ res ✓ node.list 3ms id=9d64851e…eec3 +08:28:21 [ws] ← open remoteAddr=127.0.0.1 conn=41fc0659…f78a +08:28:21 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +08:28:21 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +08:28:21 [ws] → event health seq=22 clients=6 presenceVersion=6 healthVersion=9 +08:28:21 [ws] ⇄ res ✓ cron.list 0ms id=ee26e051…4691 +08:28:21 [ws] → close code=1005 reason= durationMs=45 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=ee26e051-977a-449c-b01d-38efaa2f4691 +08:28:21 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_45275505bcb24671aac9d040 +08:28:25 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=process toolCallId=call_360f71d0e0ed4cd0ab3001cf +08:28:25 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=process toolCallId=call_360f71d0e0ed4cd0ab3001cf +08:28:25 [ws] ⇄ res ✓ node.list 1ms conn=f395f45d…3099 id=c1d093d8…8763 +08:28:26 [qqbot] [qqbot:default] Sending keepalive #2 (elapsed: 16s, since chunk: 14s) +08:28:26 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:26 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:26 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934425, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 3, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:28 [qqbot-api] <<< Status: 200 OK +08:28:28 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:28 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "b687de25f82d1b9147dbd36e87178b92" +} +08:28:28 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:28+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:29 [qqbot] [qqbot:default] Heartbeat sent +08:28:29 [qqbot] [qqbot:default] Received op=11 t=undefined +08:28:29 [qqbot] [qqbot:default] Heartbeat ACK +08:28:30 [ws] → event agent seq=23 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=14 text=奇怪 +08:28:30 [ws] → event chat seq=24 clients=5 dropIfSlow=true +08:28:30 [qqbot] [qqbot:default] New segment detected! lastSentLength=43, newTextLength=2, lastSentText="我已经为您设置了一个5分钟后提醒喝水的定...", newText="奇怪..." +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=2, lastSentLength=0, streamBuffer.length=47, isNewSegment=true +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=2 +08:28:30 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:30 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:30 [qqbot-api] >>> Body: { +"markdown": { +"content": "奇怪" +}, +"msg_type": 2, +"msg_seq": 69934426, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 4, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:30 [ws] → event agent seq=25 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=15 text=奇怪,我刚才明明 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=8, lastSentLength=0, streamBuffer.length=53, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=8 +08:28:30 [ws] → event agent seq=26 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=16 text=奇怪,我刚才明明设置了 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=11, lastSentLength=0, streamBuffer.length=56, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=11 +08:28:30 [ws] → event agent seq=27 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=17 text=奇怪,我刚才明明设置了提醒 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=13, lastSentLength=0, streamBuffer.length=58, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=13 +08:28:30 [ws] → event agent seq=28 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=18 text=奇怪,我刚才明明设置了提醒,但现在查看却没有 +08:28:30 [ws] → event chat seq=29 clients=5 dropIfSlow=true +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=22, lastSentLength=0, streamBuffer.length=67, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=22 +08:28:30 [ws] → event agent seq=30 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=19 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=32, lastSentLength=0, streamBuffer.length=77, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=32 +08:28:30 [ws] → event agent seq=31 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=20 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=36, lastSentLength=0, streamBuffer.length=81, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=36 +08:28:30 [qqbot-api] <<< Status: 200 OK +08:28:30 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:30 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "db76f75156e1e7e8aec8ba21899869c9" +} +08:28:30 [ws] → event agent seq=32 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=21 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5 +08:28:30 [ws] → event chat seq=33 clients=5 dropIfSlow=true +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=43, lastSentLength=0, streamBuffer.length=88, isNewSegment=false +08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=43 +08:28:30 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:30+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:30 [qqbot] [qqbot:default] Stream chunk sent, index: 4, isEnd: false, text: "奇怪..." +08:28:30 [qqbot] [qqbot:default] Stream partial #5, increment: 2 chars, total: 2 chars +08:28:30 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:30 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:30 [qqbot-api] >>> Body: { +"markdown": { +"content": ",我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5" +}, +"msg_type": 2, +"msg_seq": 69934427, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 5, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:30 [ws] → event agent seq=34 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=22 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5分钟后提醒喝水的任务 +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=53, lastSentLength=2, streamBuffer.length=98, isNewSegment=false +08:28:30 [ws] → event agent seq=35 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=23 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5分钟后提醒喝水的任务: +08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=54, lastSentLength=2, streamBuffer.length=99, isNewSegment=false +08:28:30 [ws] ⇄ res ✓ node.list 3ms id=beba66e6…6e52 +08:28:31 [qqbot-api] <<< Status: 200 OK +08:28:31 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:31 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "70ff645913e01f94f4014c6679384b48" +} +08:28:31 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:31+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:31 [qqbot] [qqbot:default] Stream chunk sent, index: 5, isEnd: false, text: ",我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5..." +08:28:31 [qqbot] [qqbot:default] Stream partial #6, increment: 41 chars, total: 43 chars +08:28:31 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:31 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:31 [qqbot-api] >>> Body: { +"markdown": { +"content": "分钟后提醒喝水的任务:" +}, +"msg_type": 2, +"msg_seq": 69934428, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 6, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:31 [qqbot-api] <<< Status: 200 OK +08:28:31 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:31 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "061e4f7d858be66d22dd886ce97225a0" +} +08:28:31 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:31+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:31 [qqbot] [qqbot:default] Stream chunk sent, index: 6, isEnd: false, text: "分钟后提醒喝水的任务:..." +08:28:31 [qqbot] [qqbot:default] Stream partial #7, increment: 11 chars, total: 54 chars +08:28:32 [ws] → event tick seq=36 clients=5 dropIfSlow=true +08:28:32 [ws] ⇄ res ✓ node.list 30ms conn=3f770fc7…c168 id=4645acb6…1a05 +08:28:32 [ws] ⇄ res ✓ node.list 7ms conn=d582275d…8cf7 id=f4425358…807e +08:28:32 [ws] ⇄ res ✓ node.list 9ms conn=30e84c3b…37f9 id=e90ce807…5806 +08:28:32 [ws] ⇄ res ✓ node.list 14ms conn=d84f2780…f80d id=9f6c21c9…5ec8 +08:28:32 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_912594aaec8b4dcfaae751f7 +08:28:34 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 24s, since chunk: 3s) +08:28:34 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:34 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:34 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934429, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 7, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:35 [qqbot-api] <<< Status: 200 OK +08:28:35 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:35 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "a28217b289890ad50f06343398486b54" +} +08:28:35 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:35+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:35 [ws] ⇄ res ✓ node.list 3ms conn=f395f45d…3099 id=0ff7cfa6…e3e9 +08:28:40 tools: exec failed stack: +Error: error: unknown option '--reply-to' + +Command exited with code 1 +at file:///Users/lishoushuai/.nvm/versions/node/v22.14.0/lib/node_modules/openclaw/dist/agents/bash-tools.exec.js:1146:32 +08:28:40 [tools] exec failed: error: unknown option '--reply-to' + +Command exited with code 1 +08:28:40 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_912594aaec8b4dcfaae751f7 +08:28:40 [ws] ⇄ res ✓ node.list 1ms id=10e301db…d453 +08:28:43 [ws] → event agent seq=37 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=27 text=我 +08:28:43 [ws] → event chat seq=38 clients=5 dropIfSlow=true +08:28:43 [qqbot] [qqbot:default] New segment detected! lastSentLength=54, newTextLength=1, lastSentText="奇怪,我刚才明明设置了提醒,但现在查看却...", newText="我..." +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=102, isNewSegment=true +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 +08:28:43 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:43 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:43 [qqbot-api] >>> Body: { +"markdown": { +"content": "我" +}, +"msg_type": 2, +"msg_seq": 69934430, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 8, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:43 [ws] → event agent seq=39 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=28 text=我明白了问题 +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=6, lastSentLength=0, streamBuffer.length=107, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=6 +08:28:43 [ws] → event agent seq=40 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=29 text=我明白了问题所在 +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=8, lastSentLength=0, streamBuffer.length=109, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=8 +08:28:43 [ws] → event agent seq=41 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=30 text=我明白了问题所在, +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=9, lastSentLength=0, streamBuffer.length=110, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=9 +08:28:43 [ws] → event agent seq=42 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=31 text=我明白了问题所在,`--reply-to +08:28:43 [ws] → event chat seq=43 clients=5 dropIfSlow=true +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=20, lastSentLength=0, streamBuffer.length=121, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=20 +08:28:43 [ws] → event agent seq=44 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=32 text=我明白了问题所在,`--reply-to` +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=21, lastSentLength=0, streamBuffer.length=122, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=21 +08:28:43 [ws] → event agent seq=45 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=33 text=我明白了问题所在,`--reply-to` 参数似乎在当前 +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=29, lastSentLength=0, streamBuffer.length=130, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=29 +08:28:43 [ws] → event agent seq=46 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=34 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron +08:28:43 [ws] → event chat seq=47 clients=5 dropIfSlow=true +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=37, lastSentLength=0, streamBuffer.length=138, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=37 +08:28:43 [ws] → event agent seq=48 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=35 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中 +08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=41, lastSentLength=0, streamBuffer.length=142, isNewSegment=false +08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=41 +08:28:44 [ws] → event agent seq=49 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=36 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据 +08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=49, lastSentLength=0, streamBuffer.length=150, isNewSegment=false +08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=49 +08:28:44 [ws] → event agent seq=50 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=37 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新 +08:28:44 [ws] → event chat seq=51 clients=5 dropIfSlow=true +08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=58, lastSentLength=0, streamBuffer.length=159, isNewSegment=false +08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=58 +08:28:44 [ws] → event agent seq=52 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=38 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉 +08:28:44 [ws] → event chat seq=53 clients=5 dropIfSlow=true +08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=65, lastSentLength=0, streamBuffer.length=166, isNewSegment=false +08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=65 +08:28:44 [ws] → event agent seq=54 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=39 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数 +08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=71, lastSentLength=0, streamBuffer.length=172, isNewSegment=false +08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=71 +08:28:44 [ws] → event agent seq=55 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=40 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数: +08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=72, lastSentLength=0, streamBuffer.length=173, isNewSegment=false +08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=72 +08:28:45 [qqbot-api] <<< Status: 200 OK +08:28:45 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:45 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "dce05ada4170c53c5ea7f6207c77b5ab" +} +08:28:45 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:45+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:45 [qqbot] [qqbot:default] Stream chunk sent, index: 8, isEnd: false, text: "我..." +08:28:45 [qqbot] [qqbot:default] Stream partial #9, increment: 1 chars, total: 1 chars +08:28:45 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:45 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:45 [qqbot-api] >>> Body: { +"markdown": { +"content": "明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数:" +}, +"msg_type": 2, +"msg_seq": 69934431, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 9, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:45 [qqbot-api] <<< Status: 200 OK +08:28:45 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:45 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "52bbde0882bfc077bf1b3784e059d358" +} +08:28:45 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:45+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:45 [qqbot] [qqbot:default] Stream chunk sent, index: 9, isEnd: false, text: "明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统..." +08:28:45 [qqbot] [qqbot:default] Stream partial #10, increment: 71 chars, total: 72 chars +08:28:45 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_5eb242f4bd724c8c8903f449 +08:28:45 [ws] ⇄ res ✓ node.list 1ms id=92a48702…c670 +08:28:48 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 38s, since chunk: 3s) +08:28:48 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:28:48 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:28:48 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934432, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 10, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:28:50 [qqbot-api] <<< Status: 200 OK +08:28:50 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:28:50 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "ecc67124362fb054de3c318a9ec372c4" +} +08:28:50 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:50+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:28:50 [ws] ⇄ res ✓ node.list 2ms id=35f24303…102e +08:28:54 [ws] ← open remoteAddr=127.0.0.1 conn=7c10c747…e275 +08:28:54 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +08:28:54 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +08:28:54 [ws] → event health seq=56 clients=6 presenceVersion=6 healthVersion=10 +08:28:54 [ws] → event cron seq=57 clients=6 dropIfSlow=true +08:28:54 [ws] ⇄ res ✓ cron.add 7ms id=89227308…8748 +08:28:54 [ws] → close code=1005 reason= durationMs=65 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=89227308-d24e-46e1-9384-e87ed93b8748 +08:28:54 [ws] ← open remoteAddr=127.0.0.1 conn=4d66603c…ea45 +08:28:54 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +08:28:54 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +08:28:54 [ws] → event health seq=58 clients=6 presenceVersion=6 healthVersion=11 +08:28:54 [ws] ⇄ res ✓ cron.status 1ms id=3c52f4c3…aaef +08:28:54 [ws] → close code=1005 reason= durationMs=15 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=3c52f4c3-a561-4ac8-9bd2-01c0875aaaef +08:28:54 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_5eb242f4bd724c8c8903f449 +08:28:55 [ws] ⇄ res ✓ node.list 0ms conn=f395f45d…3099 id=cf12e732…a5c2 +08:29:00 [qqbot] [qqbot:default] Sending keepalive #2 (elapsed: 49s, since chunk: 14s) +08:29:00 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:00 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:00 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934433, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 11, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:00 [ws] ⇄ res ✓ node.list 2ms id=4e006266…9e62 +08:29:01 [qqbot-api] <<< Status: 200 OK +08:29:01 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:01 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "4290ff8b0e8b880ee5610b0f7d80852f" +} +08:29:01 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:01+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:02 [ws] → event health seq=59 clients=5 presenceVersion=6 healthVersion=12 +08:29:02 [ws] → event tick seq=60 clients=5 dropIfSlow=true +08:29:04 [ws] → event agent seq=61 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=44 text=太 +08:29:04 [ws] → event chat seq=62 clients=5 dropIfSlow=true +08:29:04 [qqbot] [qqbot:default] New segment detected! lastSentLength=72, newTextLength=1, lastSentText="我明白了问题所在,`--reply-to...", newText="太..." +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=176, isNewSegment=true +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 +08:29:04 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:04 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:04 [qqbot-api] >>> Body: { +"markdown": { +"content": "太" +}, +"msg_type": 2, +"msg_seq": 69934434, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 12, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:04 [ws] → event agent seq=63 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=45 text=太好了!这次 +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=6, lastSentLength=0, streamBuffer.length=181, isNewSegment=false +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=6 +08:29:04 [ws] → event agent seq=64 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=46 text=太好了!这次提醒设置 +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=10, lastSentLength=0, streamBuffer.length=185, isNewSegment=false +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=10 +08:29:04 [ws] → event agent seq=65 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=47 text=太好了!这次提醒设置成功了。 +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=189, isNewSegment=false +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 +08:29:04 [ws] → event agent seq=66 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=48 text=太好了!这次提醒设置成功了。让我确认一下当前 +08:29:04 [ws] → event chat seq=67 clients=5 dropIfSlow=true +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=22, lastSentLength=0, streamBuffer.length=197, isNewSegment=false +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=22 +08:29:04 [ws] → event agent seq=68 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=49 text=太好了!这次提醒设置成功了。让我确认一下当前的提醒列表: +08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=28, lastSentLength=0, streamBuffer.length=203, isNewSegment=false +08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=28 +08:29:04 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_4ac86cf98611444ca4feb0ad +08:29:04 [qqbot-api] <<< Status: 200 OK +08:29:04 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:04 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "6a598176480a4164edbca5d53ce5ccc1" +} +08:29:04 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:04+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:04 [qqbot] [qqbot:default] Stream chunk sent, index: 12, isEnd: false, text: "太..." +08:29:04 [qqbot] [qqbot:default] Stream partial #13, increment: 1 chars, total: 1 chars +08:29:04 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:04 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:04 [qqbot-api] >>> Body: { +"markdown": { +"content": "好了!这次提醒设置成功了。让我确认一下当前的提醒列表:" +}, +"msg_type": 2, +"msg_seq": 69934435, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 13, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:05 [qqbot-api] <<< Status: 200 OK +08:29:05 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:05 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "ee274468916b0f43bac839509aa0fa31" +} +08:29:05 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:05+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:05 [qqbot] [qqbot:default] Stream chunk sent, index: 13, isEnd: false, text: "好了!这次提醒设置成功了。让我确认一下当前的提醒列表:..." +08:29:05 [qqbot] [qqbot:default] Stream partial #14, increment: 27 chars, total: 28 chars +08:29:05 [ws] ⇄ res ✓ node.list 1ms id=6e471ada…faeb +08:29:08 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 58s, since chunk: 3s) +08:29:08 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:08 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:08 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934436, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 14, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:09 [qqbot-api] <<< Status: 200 OK +08:29:09 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:09 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "c87fa23d25bf5155e7b8db5a7576e846" +} +08:29:09 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:09+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:10 [ws] ← open remoteAddr=127.0.0.1 conn=6f9da40f…d57c +08:29:10 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +08:29:10 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +08:29:10 [ws] → event health seq=69 clients=6 presenceVersion=6 healthVersion=13 +08:29:10 [ws] ⇄ res ✓ cron.list 1ms id=44a1542c…3d8a +08:29:10 [ws] → close code=1005 reason= durationMs=33 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=44a1542c-ecca-4eb2-8af2-bf6ea2823d8a +08:29:10 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_4ac86cf98611444ca4feb0ad +08:29:10 [qqbot] [qqbot:default] Heartbeat sent +08:29:10 [ws] ⇄ res ✓ node.list 3ms conn=f395f45d…3099 id=ce7b0659…d104 +08:29:11 [qqbot] [qqbot:default] Received op=11 t=undefined +08:29:11 [qqbot] [qqbot:default] Heartbeat ACK +08:29:15 [ws] → event agent seq=70 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=54 text=✅ +08:29:15 [ws] → event chat seq=71 clients=5 dropIfSlow=true +08:29:15 [qqbot] [qqbot:default] New segment detected! lastSentLength=28, newTextLength=1, lastSentText="太好了!这次提醒设置成功了。让我确认一下...", newText="✅..." +08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=206, isNewSegment=true +08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 +08:29:15 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:15 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:15 [qqbot-api] >>> Body: { +"markdown": { +"content": "✅" +}, +"msg_type": 2, +"msg_seq": 69934437, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 15, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:15 [ws] → event agent seq=72 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=55 text=✅ 提 +08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=3, lastSentLength=0, streamBuffer.length=208, isNewSegment=false +08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=3 +08:29:15 [ws] → event agent seq=73 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=56 text=✅ 提醒已设置成功 +08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=9, lastSentLength=0, streamBuffer.length=214, isNewSegment=false +08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=9 +08:29:15 [ws] → event agent seq=74 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=57 text=✅ 提醒已设置成功! 📝 +08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=219, isNewSegment=false +08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 +08:29:15 [ws] ⇄ res ✓ node.list 1ms id=ebe1e48d…3352 +08:29:16 [ws] → event agent seq=75 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=58 text=✅ 提醒已设置成功! 📝 内容: +08:29:16 [ws] → event chat seq=76 clients=5 dropIfSlow=true +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=18, lastSentLength=0, streamBuffer.length=223, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=18 +08:29:16 [ws] → event agent seq=77 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=59 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=25, lastSentLength=0, streamBuffer.length=230, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=25 +08:29:16 [ws] → event agent seq=78 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=60 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=27, lastSentLength=0, streamBuffer.length=232, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=27 +08:29:16 [ws] → event agent seq=79 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=61 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 +08:29:16 [ws] → event chat seq=80 clients=5 dropIfSlow=true +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=35, lastSentLength=0, streamBuffer.length=240, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=35 +08:29:16 [ws] → event agent seq=81 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=62 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:3 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=40, lastSentLength=0, streamBuffer.length=245, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=40 +08:29:16 [ws] → event agent seq=82 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=63 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会 +08:29:16 [ws] → event chat seq=83 clients=5 dropIfSlow=true +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=48, lastSentLength=0, streamBuffer.length=253, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=48 +08:29:16 [ws] → event agent seq=84 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=64 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=54, lastSentLength=0, streamBuffer.length=259, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=54 +08:29:16 [ws] → event agent seq=85 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=65 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经 +08:29:16 [ws] → event chat seq=86 clients=5 dropIfSlow=true +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=64, lastSentLength=0, streamBuffer.length=269, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=64 +08:29:16 [ws] → event agent seq=87 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=66 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=66, lastSentLength=0, streamBuffer.length=271, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=66 +08:29:16 [ws] → event agent seq=88 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=67 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=73, lastSentLength=0, streamBuffer.length=278, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=73 +08:29:16 [ws] → event agent seq=89 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=68 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=75, lastSentLength=0, streamBuffer.length=280, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=75 +08:29:16 [ws] → event agent seq=90 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=69 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会 +08:29:16 [ws] → event chat seq=91 clients=5 dropIfSlow=true +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=80, lastSentLength=0, streamBuffer.length=285, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=80 +08:29:16 [ws] → event agent seq=92 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=70 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=82, lastSentLength=0, streamBuffer.length=287, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=82 +08:29:16 [ws] → event agent seq=93 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=71 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=87, lastSentLength=0, streamBuffer.length=292, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=87 +08:29:16 [ws] → event agent seq=94 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=72 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=91, lastSentLength=0, streamBuffer.length=296, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=91 +08:29:16 [ws] → event agent seq=95 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=73 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持 +08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=97, lastSentLength=0, streamBuffer.length=302, isNewSegment=false +08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=97 +08:29:16 [qqbot-api] <<< Status: 200 OK +08:29:16 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:17 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "42b9030307d7e3b838a856700fbe2422" +} +08:29:16 [ws] → event agent seq=96 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=74 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入, +08:29:17 [ws] → event chat seq=97 clients=5 dropIfSlow=true +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=105, lastSentLength=0, streamBuffer.length=310, isNewSegment=false +08:29:17 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=105 +08:29:17 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:17+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:17 [qqbot] [qqbot:default] Stream chunk sent, index: 15, isEnd: false, text: "✅..." +08:29:17 [qqbot] [qqbot:default] Stream partial #16, increment: 1 chars, total: 1 chars +08:29:17 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:17 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:17 [qqbot-api] >>> Body: { +"markdown": { +"content": " 提醒已设置成功!\n\n📝 内容:喝水\n⏰ 时间:5分钟后 (大约在 16:33)\n到时候我会准时提醒您~\n\n您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条\"💧 该喝水啦!记得保持充足的水分摄入," +}, +"msg_type": 2, +"msg_seq": 69934438, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 16, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:17 [ws] → event agent seq=98 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=75 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将帮助 +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=114, lastSentLength=1, streamBuffer.length=319, isNewSegment=false +08:29:17 [ws] → event agent seq=99 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=76 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将帮助哦~" +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=117, lastSentLength=1, streamBuffer.length=322, isNewSegment=false +08:29:17 [ws] → event agent seq=100 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=77 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在 +08:29:17 [ws] → event chat seq=101 clients=5 dropIfSlow=true +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=125, lastSentLength=1, streamBuffer.length=330, isNewSegment=false +08:29:17 [ws] → event agent seq=102 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=78 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在执行后自动删除 +08:29:17 [ws] → event chat seq=103 clients=5 dropIfSlow=true +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=132, lastSentLength=1, streamBuffer.length=337, isNewSegment=false +08:29:17 [ws] → event agent seq=104 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=79 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在执行后自动删除。 +08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=133, lastSentLength=1, streamBuffer.length=338, isNewSegment=false +08:29:17 [agent/embedded] embedded run agent end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b +08:29:17 [ws] → event agent seq=105 clients=5 run=49d64fc4…e41b agent=main session=main stream=lifecycle aseq=80 phase=end +08:29:17 [ws] → event chat seq=106 clients=5 +08:29:17 [agent/embedded] embedded run prompt end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=74069 +08:29:17 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 +08:29:17 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 +08:29:17 [ws] ⇄ res ✓ chat.history 17ms conn=3f770fc7…c168 id=15fe49d7…4c9c +08:29:17 [ws] ⇄ res ✓ chat.history 17ms conn=d582275d…8cf7 id=83955d28…1198 +08:29:17 [ws] ⇄ res ✓ chat.history 6ms conn=d84f2780…f80d id=8c36b63a…8b6e +08:29:17 [ws] ⇄ res ✓ chat.history 10ms conn=30e84c3b…37f9 id=b09e1eb5…e1fa +08:29:17 [ws] ⇄ res ✓ chat.history 6ms conn=f395f45d…3099 id=c08d10c0…6fbd +08:29:17 [qqbot-api] <<< Status: 200 OK +08:29:17 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:17 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "230c60a2e8e313d4315ba94e35baba51" +} +08:29:17 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:17+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:17 [qqbot] [qqbot:default] Stream chunk sent, index: 16, isEnd: false, text: " 提醒已设置成功! + +📝 内容:喝水 +⏰ 时间:5分钟后 (大约在 16:33) +到时候我会准时提..." +08:29:17 [qqbot] [qqbot:default] Stream partial #17, increment: 104 chars, total: 105 chars +08:29:17 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:17 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:17 [qqbot-api] >>> Body: { +"markdown": { +"content": "对身体健康很有帮助哦~\"的消息。提醒将在执行后自动删除。" +}, +"msg_type": 2, +"msg_seq": 69934439, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 1, +"index": 17, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:17 [agent/embedded] embedded run done: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=74520 aborted=false +08:29:17 [diagnostic] lane task done: lane=main durationMs=74539 active=0 queued=0 +08:29:17 [diagnostic] lane task done: lane=session:agent:main:main durationMs=74543 active=0 queued=0 +08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice +08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice +08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice +08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice +08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice +08:29:18 [ws] → event heartbeat seq=107 clients=5 dropIfSlow=true +08:29:19 [qqbot-api] <<< Status: 200 OK +08:29:19 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:18 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "e5678ab04a2ed8977c7a736f721aa76f" +} +08:29:19 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:18+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:19 [qqbot] [qqbot:default] Stream chunk sent, index: 17, isEnd: false, text: "对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。..." +08:29:19 [qqbot] [qqbot:default] Stream partial #18, increment: 28 chars, total: 133 chars +08:29:19 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages +08:29:19 [qqbot-api] >>> Headers: { +"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", +"Content-Type": "application/json" +} +08:29:19 [qqbot-api] >>> Body: { +"markdown": { +"content": "" +}, +"msg_type": 2, +"msg_seq": 69934440, +"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", +"stream": { +"state": 10, +"index": 18, +"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" +} +} +08:29:20 [qqbot-api] <<< Status: 200 OK +08:29:20 [qqbot-api] <<< Headers: { +"access-control-allow-credentials": "true", +"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", +"access-control-allow-methods": "GET, POST, OPTIONS", +"connection": "keep-alive", +"content-encoding": "gzip", +"content-type": "application/json", +"date": "Sun, 01 Feb 2026 08:29:20 GMT", +"server": "TAPISIX/2.2.2", +"transfer-encoding": "chunked", +"vary": "Accept-Encoding", +"x-content-type-options": "nosniff", +"x-tps-trace-id": "d9cde505d1f2e65ccb4c9ef5b7a42435" +} +08:29:20 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:20+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} +08:29:20 [qqbot] [qqbot:default] Stream completed, final increment: 0 chars, total streamBuffer: 338 chars, chunks: 19 +08:29:20 [ws] ⇄ res ✓ node.list 2ms id=785fc4ca…d50e +08:29:25 [ws] ⇄ res ✓ node.list 0ms id=04fce377…7c39 +08:29:30 [ws] ⇄ res ✓ node.list 1ms id=508c034d…ef00 +08:29:32 [ws] → event tick seq=108 clients=5 dropIfSlow=true +08:29:32 [ws] ⇄ res ✓ node.list 37ms conn=30e84c3b…37f9 id=e58ccf94…226a +08:29:32 [ws] ⇄ res ✓ node.list 107ms conn=d582275d…8cf7 id=3877bd6b…89c6 +08:29:32 [ws] ⇄ res ✓ node.list 112ms conn=d84f2780…f80d id=1a2c8310…4582 +08:29:32 [ws] ⇄ res ✓ node.list 122ms conn=3f770fc7…c168 id=418dd535…513b +08:29:35 [ws] ⇄ res ✓ node.list 1ms conn=f395f45d…3099 id=a69018c7…2ad6 +^C08:29:37 [gateway] signal SIGINT received +08:29:37 [gateway] received SIGINT; shutting down +08:29:37 [qqbot] [qqbot-api] Background token refresh stopped +08:29:37 [gmail-watcher] gmail watcher stopped +08:29:37 [ws] → event shutdown seq=109 clients=5 +08:29:37 [gateway] signal SIGINT received +08:29:37 [gateway] received SIGINT during shutdown; ignoring +08:29:37 [qqbot] [qqbot:default] Message processor stopped +08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=f395f45d-ef22-42bc-b309-01d0a8b13099 +08:29:37 [ws] → event presence seq=110 clients=0 dropIfSlow=true presenceVersion=7 healthVersion=13 +08:29:37 [ws] → close code=1012 reason=service restart durationMs=148474 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=a69018c7-cf65-4275-b430-bf3ea5fd2ad6 +08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=30e84c3b-ec6d-4d80-aaf8-b42c47ad37f9 +08:29:37 [ws] → event presence seq=111 clients=0 dropIfSlow=true presenceVersion=8 healthVersion=13 +08:29:37 [ws] → close code=1012 reason=service restart durationMs=149077 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=e58ccf94-9248-46f3-932b-e8e89624226a conn=30e84c3b…37f9 +08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=d84f2780-43ad-4cf7-800a-8dc2a97bf80d +08:29:37 [ws] → event presence seq=112 clients=0 dropIfSlow=true presenceVersion=9 healthVersion=13 +08:29:37 [ws] → close code=1012 reason=service restart durationMs=150032 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=1a2c8310-e1d2-49a4-b9c7-d8e134854582 conn=d84f2780…f80d +08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=d582275d-ed0e-408f-a43e-1e0777478cf7 +08:29:37 [ws] → event presence seq=113 clients=0 dropIfSlow=true presenceVersion=10 healthVersion=13 +08:29:37 [ws] → close code=1012 reason=service restart durationMs=150052 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=3877bd6b-1cb1-4654-a699-71875f9189c6 conn=d582275d…8cf7 +08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=3f770fc7-7722-4458-8fdb-86284e5cc168 +08:29:37 [ws] → event presence seq=114 clients=0 dropIfSlow=true presenceVersion=11 healthVersion=13 +08:29:37 [ws] → close code=1012 reason=service restart durationMs=150093 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=418dd535-ddc7-4f9a-adc8-b39cfa25513b conn=3f770fc7…c168 diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 32017b4..1184778 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -1,6 +1,14 @@ { "id": "qqbot", + "name": "QQ Bot Channel", + "description": "QQ Bot channel plugin with streaming message support, cron jobs, and proactive messaging", "channels": ["qqbot"], + "skills": ["qqbot-cron"], + "capabilities": { + "proactiveMessaging": true, + "cronJobs": true, + "streamingMessages": true + }, "configSchema": { "type": "object", "additionalProperties": false, diff --git a/scripts/proactive-api-server.ts b/scripts/proactive-api-server.ts new file mode 100644 index 0000000..db56b2a --- /dev/null +++ b/scripts/proactive-api-server.ts @@ -0,0 +1,346 @@ +/** + * QQBot 主动消息 HTTP API 服务 + * + * 提供 RESTful API 用于: + * 1. 发送主动消息 + * 2. 查询已知用户 + * 3. 广播消息 + * + * 启动方式: + * npx ts-node scripts/proactive-api-server.ts --port 3721 + * + * API 端点: + * POST /send - 发送主动消息 + * GET /users - 列出已知用户 + * GET /users/stats - 获取用户统计 + * POST /broadcast - 广播消息 + */ + +import * as http from "node:http"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import * as url from "node:url"; +import { + sendProactiveMessageDirect, + listKnownUsers, + getKnownUsersStats, + getKnownUser, + broadcastMessage, +} from "../src/proactive.js"; +import type { ResolvedQQBotAccount } from "../src/types.js"; + +// 默认端口 +const DEFAULT_PORT = 3721; + +// 从配置文件加载账户信息 +function loadAccount(accountId = "default"): ResolvedQQBotAccount | null { + const configPath = path.join(process.env.HOME || "/home/ubuntu", "clawd", "config.json"); + + try { + // 优先从环境变量获取 + const envAppId = process.env.QQBOT_APP_ID; + const envClientSecret = process.env.QQBOT_CLIENT_SECRET; + + if (!fs.existsSync(configPath)) { + if (envAppId && envClientSecret) { + return { + accountId, + appId: envAppId, + clientSecret: envClientSecret, + enabled: true, + secretSource: "env", + }; + } + return null; + } + + const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); + const qqbot = config.channels?.qqbot; + + if (!qqbot) { + if (envAppId && envClientSecret) { + return { + accountId, + appId: envAppId, + clientSecret: envClientSecret, + enabled: true, + secretSource: "env", + }; + } + return null; + } + + // 解析账户配置 + if (accountId === "default") { + return { + accountId: "default", + appId: qqbot.appId || envAppId, + clientSecret: qqbot.clientSecret || envClientSecret, + enabled: qqbot.enabled ?? true, + secretSource: qqbot.clientSecret ? "config" : "env", + }; + } + + const accountConfig = qqbot.accounts?.[accountId]; + if (accountConfig) { + return { + accountId, + appId: accountConfig.appId || qqbot.appId || envAppId, + clientSecret: accountConfig.clientSecret || qqbot.clientSecret || envClientSecret, + enabled: accountConfig.enabled ?? true, + secretSource: accountConfig.clientSecret ? "config" : "env", + }; + } + + return null; + } catch { + return null; + } +} + +// 加载配置(用于 broadcastMessage) +function loadConfig(): Record { + const configPath = path.join(process.env.HOME || "/home/ubuntu", "clawd", "config.json"); + try { + if (fs.existsSync(configPath)) { + return JSON.parse(fs.readFileSync(configPath, "utf-8")); + } + } catch {} + return {}; +} + +// 解析请求体 +async function parseBody(req: http.IncomingMessage): Promise> { + return new Promise((resolve) => { + let body = ""; + req.on("data", (chunk) => { + body += chunk; + }); + req.on("end", () => { + try { + resolve(body ? JSON.parse(body) : {}); + } catch { + resolve({}); + } + }); + }); +} + +// 发送 JSON 响应 +function sendJson(res: http.ServerResponse, statusCode: number, data: unknown) { + res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" }); + res.end(JSON.stringify(data, null, 2)); +} + +// 处理请求 +async function handleRequest(req: http.IncomingMessage, res: http.ServerResponse) { + const parsedUrl = url.parse(req.url || "", true); + const pathname = parsedUrl.pathname || "/"; + const method = req.method || "GET"; + const query = parsedUrl.query; + + // CORS 支持 + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); + + if (method === "OPTIONS") { + res.writeHead(204); + res.end(); + return; + } + + console.log(`[${new Date().toISOString()}] ${method} ${pathname}`); + + try { + // POST /send - 发送主动消息 + if (pathname === "/send" && method === "POST") { + const body = await parseBody(req); + const { to, text, type = "c2c", accountId = "default" } = body as { + to?: string; + text?: string; + type?: "c2c" | "group"; + accountId?: string; + }; + + if (!to || !text) { + sendJson(res, 400, { error: "Missing required fields: to, text" }); + return; + } + + const account = loadAccount(accountId); + if (!account) { + sendJson(res, 500, { error: "Failed to load account configuration" }); + return; + } + + const result = await sendProactiveMessageDirect(account, to, text, type); + sendJson(res, result.success ? 200 : 500, result); + return; + } + + // GET /users - 列出已知用户 + if (pathname === "/users" && method === "GET") { + const type = query.type as "c2c" | "group" | "channel" | undefined; + const accountId = query.accountId as string | undefined; + const limit = query.limit ? parseInt(query.limit as string, 10) : undefined; + + const users = listKnownUsers({ type, accountId, limit }); + sendJson(res, 200, { total: users.length, users }); + return; + } + + // GET /users/stats - 获取用户统计 + if (pathname === "/users/stats" && method === "GET") { + const accountId = query.accountId as string | undefined; + const stats = getKnownUsersStats(accountId); + sendJson(res, 200, stats); + return; + } + + // GET /users/:openid - 获取单个用户 + if (pathname.startsWith("/users/") && method === "GET" && pathname !== "/users/stats") { + const openid = pathname.slice("/users/".length); + const type = (query.type as string) || "c2c"; + const accountId = (query.accountId as string) || "default"; + + const user = getKnownUser(type, openid, accountId); + if (user) { + sendJson(res, 200, user); + } else { + sendJson(res, 404, { error: "User not found" }); + } + return; + } + + // POST /broadcast - 广播消息 + if (pathname === "/broadcast" && method === "POST") { + const body = await parseBody(req); + const { text, type = "c2c", accountId, limit } = body as { + text?: string; + type?: "c2c" | "group"; + accountId?: string; + limit?: number; + }; + + if (!text) { + sendJson(res, 400, { error: "Missing required field: text" }); + return; + } + + const cfg = loadConfig(); + const result = await broadcastMessage(text, cfg as any, { type, accountId, limit }); + sendJson(res, 200, result); + return; + } + + // GET / - API 文档 + if (pathname === "/" && method === "GET") { + sendJson(res, 200, { + name: "QQBot Proactive Message API", + version: "1.0.0", + endpoints: { + "POST /send": { + description: "发送主动消息", + body: { + to: "目标 openid (必需)", + text: "消息内容 (必需)", + type: "消息类型: c2c | group (默认 c2c)", + accountId: "账户 ID (默认 default)", + }, + }, + "GET /users": { + description: "列出已知用户", + query: { + type: "过滤类型: c2c | group | channel", + accountId: "过滤账户 ID", + limit: "限制返回数量", + }, + }, + "GET /users/stats": { + description: "获取用户统计", + query: { + accountId: "过滤账户 ID", + }, + }, + "GET /users/:openid": { + description: "获取单个用户信息", + query: { + type: "用户类型 (默认 c2c)", + accountId: "账户 ID (默认 default)", + }, + }, + "POST /broadcast": { + description: "广播消息给所有已知用户", + body: { + text: "消息内容 (必需)", + type: "消息类型: c2c | group (默认 c2c)", + accountId: "账户 ID", + limit: "限制发送数量", + }, + }, + }, + notes: [ + "只有曾经与机器人交互过的用户才能收到主动消息", + ], + }); + return; + } + + // 404 + sendJson(res, 404, { error: "Not found" }); + } catch (err) { + console.error(`Error handling request: ${err}`); + sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) }); + } +} + +// 解析命令行参数获取端口 +function getPort(): number { + const args = process.argv.slice(2); + for (let i = 0; i < args.length; i++) { + if (args[i] === "--port" && args[i + 1]) { + return parseInt(args[i + 1], 10) || DEFAULT_PORT; + } + } + return parseInt(process.env.PROACTIVE_API_PORT || "", 10) || DEFAULT_PORT; +} + +// 启动服务器 +function main() { + const port = getPort(); + + const server = http.createServer(handleRequest); + + server.listen(port, () => { + console.log(` +╔═══════════════════════════════════════════════════════════════╗ +║ QQBot Proactive Message API Server ║ +╠═══════════════════════════════════════════════════════════════╣ +║ Server running at: http://localhost:${port.toString().padEnd(25)}║ +║ ║ +║ Endpoints: ║ +║ GET / - API documentation ║ +║ POST /send - Send proactive message ║ +║ GET /users - List known users ║ +║ GET /users/stats - Get user statistics ║ +║ POST /broadcast - Broadcast message ║ +║ ║ +║ Example: ║ +║ curl -X POST http://localhost:${port}/send \\ ║ +║ -H "Content-Type: application/json" \\ ║ +║ -d '{"to":"openid","text":"Hello!"}' ║ +╚═══════════════════════════════════════════════════════════════╝ +`); + }); + + // 优雅关闭 + process.on("SIGINT", () => { + console.log("\nShutting down..."); + server.close(() => { + process.exit(0); + }); + }); +} + +main(); diff --git a/scripts/send-proactive.ts b/scripts/send-proactive.ts new file mode 100644 index 0000000..0ebd5f0 --- /dev/null +++ b/scripts/send-proactive.ts @@ -0,0 +1,273 @@ +#!/usr/bin/env npx ts-node +/** + * QQBot 主动消息 CLI 工具 + * + * 使用示例: + * # 发送私聊消息 + * npx ts-node scripts/send-proactive.ts --to "用户openid" --text "你好!" + * + * # 发送群聊消息 + * npx ts-node scripts/send-proactive.ts --to "群组openid" --type group --text "群公告" + * + * # 列出已知用户 + * npx ts-node scripts/send-proactive.ts --list + * + * # 列出群聊用户 + * npx ts-node scripts/send-proactive.ts --list --type group + * + * # 广播消息 + * npx ts-node scripts/send-proactive.ts --broadcast --text "系统公告" --type c2c --limit 10 + */ + +import { + sendProactiveMessageDirect, + listKnownUsers, + getKnownUsersStats, + broadcastMessage, +} from "../src/proactive.js"; +import type { ResolvedQQBotAccount } from "../src/types.js"; +import * as fs from "node:fs"; +import * as path from "node:path"; + +// 解析命令行参数 +function parseArgs(): Record { + const args: Record = {}; + const argv = process.argv.slice(2); + + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg.startsWith("--")) { + const key = arg.slice(2); + const nextArg = argv[i + 1]; + if (nextArg && !nextArg.startsWith("--")) { + args[key] = nextArg; + i++; + } else { + args[key] = true; + } + } + } + + return args; +} + +// 从配置文件加载账户信息 +function loadAccount(accountId = "default"): ResolvedQQBotAccount | null { + const configPath = path.join(process.env.HOME || "/home/ubuntu", "clawd", "config.json"); + + try { + if (!fs.existsSync(configPath)) { + // 尝试从环境变量获取 + const appId = process.env.QQBOT_APP_ID; + const clientSecret = process.env.QQBOT_CLIENT_SECRET; + + if (appId && clientSecret) { + return { + accountId, + appId, + clientSecret, + enabled: true, + secretSource: "env", + }; + } + + console.error("配置文件不存在且环境变量未设置"); + return null; + } + + const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); + const qqbot = config.channels?.qqbot; + + if (!qqbot) { + console.error("配置中没有 qqbot 配置"); + return null; + } + + // 解析账户配置 + if (accountId === "default") { + return { + accountId: "default", + appId: qqbot.appId || process.env.QQBOT_APP_ID, + clientSecret: qqbot.clientSecret || process.env.QQBOT_CLIENT_SECRET, + enabled: qqbot.enabled ?? true, + secretSource: qqbot.clientSecret ? "config" : "env", + }; + } + + const accountConfig = qqbot.accounts?.[accountId]; + if (accountConfig) { + return { + accountId, + appId: accountConfig.appId || qqbot.appId || process.env.QQBOT_APP_ID, + clientSecret: accountConfig.clientSecret || qqbot.clientSecret || process.env.QQBOT_CLIENT_SECRET, + enabled: accountConfig.enabled ?? true, + secretSource: accountConfig.clientSecret ? "config" : "env", + }; + } + + console.error(`账户 ${accountId} 不存在`); + return null; + } catch (err) { + console.error(`加载配置失败: ${err}`); + return null; + } +} + +async function main() { + const args = parseArgs(); + + // 显示帮助 + if (args.help || args.h) { + console.log(` +QQBot 主动消息 CLI 工具 + +用法: + npx ts-node scripts/send-proactive.ts [选项] + +选项: + --to 目标用户或群组的 openid + --text 要发送的消息内容 + --type 消息类型: c2c (私聊) 或 group (群聊),默认 c2c + --account 账户 ID,默认 default + + --list 列出已知用户 + --stats 显示用户统计 + --broadcast 广播消息给所有已知用户 + --limit 限制数量 + + --help, -h 显示帮助 + +示例: + # 发送私聊消息 + npx ts-node scripts/send-proactive.ts --to "0Eda5EA7-xxx" --text "你好!" + + # 发送群聊消息 + npx ts-node scripts/send-proactive.ts --to "A1B2C3D4" --type group --text "群公告" + + # 列出最近 10 个私聊用户 + npx ts-node scripts/send-proactive.ts --list --type c2c --limit 10 + + # 广播消息 + npx ts-node scripts/send-proactive.ts --broadcast --text "系统公告" --limit 5 +`); + return; + } + + const accountId = (args.account as string) || "default"; + const type = (args.type as "c2c" | "group") || "c2c"; + const limit = args.limit ? parseInt(args.limit as string, 10) : undefined; + + // 列出已知用户 + if (args.list) { + const users = listKnownUsers({ + type: args.type as "c2c" | "group" | "channel" | undefined, + accountId: args.account as string | undefined, + limit, + }); + + if (users.length === 0) { + console.log("没有已知用户"); + return; + } + + console.log(`\n已知用户列表 (共 ${users.length} 个):\n`); + console.log("类型\t\tOpenID\t\t\t\t\t\t昵称\t\t最后交互时间"); + console.log("─".repeat(100)); + + for (const user of users) { + const lastTime = new Date(user.lastInteractionAt).toLocaleString(); + console.log(`${user.type}\t\t${user.openid.slice(0, 20)}...\t${user.nickname || "-"}\t\t${lastTime}`); + } + return; + } + + // 显示统计 + if (args.stats) { + const stats = getKnownUsersStats(args.account as string | undefined); + console.log(`\n用户统计:`); + console.log(` 总计: ${stats.total}`); + console.log(` 私聊: ${stats.c2c}`); + console.log(` 群聊: ${stats.group}`); + console.log(` 频道: ${stats.channel}`); + return; + } + + // 广播消息 + if (args.broadcast) { + if (!args.text) { + console.error("请指定消息内容 (--text)"); + process.exit(1); + } + + // 加载配置用于广播 + const configPath = path.join(process.env.HOME || "/home/ubuntu", "clawd", "config.json"); + let cfg: Record = {}; + try { + if (fs.existsSync(configPath)) { + cfg = JSON.parse(fs.readFileSync(configPath, "utf-8")); + } + } catch {} + + console.log(`\n开始广播消息...\n`); + const result = await broadcastMessage(args.text as string, cfg as any, { + type, + accountId, + limit, + }); + + console.log(`\n广播完成:`); + console.log(` 发送总数: ${result.total}`); + console.log(` 成功: ${result.success}`); + console.log(` 失败: ${result.failed}`); + + if (result.failed > 0) { + console.log(`\n失败详情:`); + for (const r of result.results) { + if (!r.result.success) { + console.log(` ${r.to}: ${r.result.error}`); + } + } + } + return; + } + + // 发送单条消息 + if (args.to && args.text) { + const account = loadAccount(accountId); + if (!account) { + console.error("无法加载账户配置"); + process.exit(1); + } + + console.log(`\n发送消息...`); + console.log(` 目标: ${args.to}`); + console.log(` 类型: ${type}`); + console.log(` 内容: ${args.text}`); + + const result = await sendProactiveMessageDirect( + account, + args.to as string, + args.text as string, + type + ); + + if (result.success) { + console.log(`\n✅ 发送成功!`); + console.log(` 消息ID: ${result.messageId}`); + console.log(` 时间戳: ${result.timestamp}`); + } else { + console.log(`\n❌ 发送失败: ${result.error}`); + process.exit(1); + } + return; + } + + // 没有有效参数 + console.error("请指定操作。使用 --help 查看帮助。"); + process.exit(1); +} + +main().catch((err) => { + console.error(`执行失败: ${err}`); + process.exit(1); +}); diff --git a/skills/qqbot-cron/SKILL.md b/skills/qqbot-cron/SKILL.md new file mode 100644 index 0000000..5de8bc0 --- /dev/null +++ b/skills/qqbot-cron/SKILL.md @@ -0,0 +1,476 @@ +--- +name: qqbot-cron +description: QQ Bot 智能提醒技能。支持一次性提醒、周期性任务、自动降级确保送达。可设置、查询、取消提醒。 +metadata: {"clawdbot":{"emoji":"⏰","requires":{"channels":["qqbot"]}}} +--- + +# QQ Bot 智能提醒 + +让 AI 帮用户设置、管理定时提醒,支持私聊和群聊。 + +--- + +## 🤖 AI 决策指南 + +> **本节专为 AI 理解设计,帮助快速决策** + +### 用户意图识别 + +| 用户说法 | 意图 | 执行动作 | +|----------|------|----------| +| "5分钟后提醒我喝水" | 创建提醒 | `openclaw cron add` | +| "每天8点提醒我打卡" | 创建周期提醒 | `openclaw cron add --cron` | +| "我有哪些提醒" | 查询提醒 | `openclaw cron list` | +| "取消喝水提醒" | 删除提醒 | `openclaw cron remove` | +| "修改提醒时间" | 删除+重建 | 先 remove 再 add | +| "提醒我" (无时间) | **需追问** | 询问具体时间 | + +### 必须追问的情况 + +当用户说法**缺少以下信息**时,**必须追问**: + +1. **没有时间**:"提醒我喝水" → 询问"请问什么时候提醒你?" +2. **时间模糊**:"晚点提醒我" → 询问"具体几点呢?" +3. **周期不明**:"定期提醒我" → 询问"多久一次?每天?每周?" + +### 无需追问可直接执行 + +| 用户说法 | 理解为 | +|----------|--------| +| "5分钟后" | `--at 5m` | +| "半小时后" | `--at 30m` | +| "1小时后" | `--at 1h` | +| "明天早上8点" | `--at 2026-02-02T08:00:00+08:00` | +| "每天早上8点" | `--cron "0 8 * * *"` | +| "工作日9点" | `--cron "0 9 * * 1-5"` | + +--- + +## 📋 命令速查 + +### 创建提醒(完整模板) + +```bash +openclaw cron add \ + --name "{任务名}" \ + --at "{时间}" \ + --session isolated \ + --system-event '{"type":"reminder","user_openid":"{openid}","user_name":"{用户名称}","original_message_id":"{message_id}","reminder_content":"{提醒内容}","created_at":"{当前时间ISO格式}"}' \ + --message "{消息内容}" \ + --deliver \ + --channel qqbot \ + --to "{openid}" \ + --delete-after-run +``` + +> 💡 **`--system-event` 说明**:用于存储用户上下文信息,提醒触发时 AI 可以获取这些信息来提供更个性化的提醒。 + +> ⚠️ **注意**:`cron add` 命令不支持 `--reply-to` 参数。提醒消息将作为主动消息直接发送给用户。 + +### 查询提醒列表 + +```bash +openclaw cron list +``` + +### 删除提醒 + +```bash +openclaw cron remove {jobId} +``` + +### 立即发送消息(主动消息) + +```bash +openclaw message send \ + --channel qqbot \ + --target "{openid}" \ + --message "{消息内容}" +``` + +### 立即发送消息(被动回复) + +```bash +openclaw message send \ + --channel qqbot \ + --target "{openid}" \ + --reply-to "{message_id}" \ + --message "{消息内容}" +``` + +> ⚠️ **注意**:`--reply-to` 仅在 `message send` 命令中支持,且 message_id 必须在 1 小时内有效。定时提醒不支持被动回复。 + +--- + +## 💬 用户交互模板 + +> **创建提醒后,必须给用户反馈确认** + +### 创建成功反馈 + +**一次性提醒**: +``` +✅ 提醒已设置! + +📝 内容:{提醒内容} +⏰ 时间:{具体时间} + +到时候我会准时提醒你~ +``` + +**周期提醒**: +``` +✅ 周期提醒已设置! + +📝 内容:{提醒内容} +🔄 周期:{周期描述,如"每天早上8:00"} + +提醒会持续生效,说"取消xx提醒"可停止~ +``` + +### 查询提醒反馈 + +``` +📋 你当前的提醒: + +1. ⏰ 喝水提醒 - 5分钟后 (一次性) +2. 🔄 打卡提醒 - 每天08:00 (周期) +3. 🔄 日报提醒 - 工作日18:00 (周期) + +说"取消xx提醒"可删除~ +``` + +### 无提醒时反馈 + +``` +📋 你当前没有设置任何提醒 + +说"5分钟后提醒我xxx"可创建提醒~ +``` + +### 删除成功反馈 + +``` +✅ 已取消"{提醒名称}"提醒 +``` + +--- + +## ⏱️ 时间格式 + +### 相对时间(--at) + +> ⚠️ **不要加 + 号!** 用 `5m` 而不是 `+5m` + +| 用户说法 | 参数值 | +|----------|--------| +| 5分钟后 | `5m` | +| 半小时后 | `30m` | +| 1小时后 | `1h` | +| 2小时后 | `2h` | +| 明天这时候 | `24h` | + +### 绝对时间(--at) + +| 用户说法 | 参数值 | +|----------|--------| +| 今天下午3点 | `2026-02-01T15:00:00+08:00` | +| 明天早上8点 | `2026-02-02T08:00:00+08:00` | +| 2月14日中午 | `2026-02-14T12:00:00+08:00` | + +### Cron 表达式(--cron) + +| 用户说法 | Cron 表达式 | 必须加 `--tz "Asia/Shanghai"` | +|----------|-------------|------------------------------| +| 每天早上8点 | `0 8 * * *` | ✅ | +| 每天晚上10点 | `0 22 * * *` | ✅ | +| 每个工作日早上9点 | `0 9 * * 1-5` | ✅ | +| 每周一早上9点 | `0 9 * * 1` | ✅ | +| 每周末上午10点 | `0 10 * * 0,6` | ✅ | +| 每小时整点 | `0 * * * *` | ✅ | + +--- + +## 📌 参数说明 + +### 必填参数 + +| 参数 | 说明 | 示例 | +|------|------|------| +| `--name` | 任务名,含用户标识 | `"喝水提醒-小明"` | +| `--at` / `--cron` | 触发时间(二选一) | `5m` / `0 8 * * *` | +| `--session isolated` | 隔离会话 | 固定值 | +| `--message` | 消息内容,**不能为空** | `"💧 该喝水啦!"` | +| `--deliver` | 启用投递 | 固定值 | +| `--channel qqbot` | QQ 渠道 | 固定值 | +| `--to` | 接收者 openid | 从系统消息获取 | + +### 推荐参数(用于存储用户上下文) + +| 参数 | 说明 | 何时使用 | +|------|------|----------| +| `--system-event` | 用户上下文 JSON | **建议所有任务都使用** | +| `--delete-after-run` | 执行后删除 | **一次性任务必须** | +| `--tz "Asia/Shanghai"` | 时区 | **周期任务必须** | + +### --system-event 字段说明 + +`--system-event` 用于存储提醒的上下文信息,格式为 JSON: + +```json +{ + "type": "reminder", + "user_openid": "用户的openid", + "user_name": "用户名称(如有)", + "original_message_id": "创建提醒时的message_id", + "reminder_content": "提醒内容摘要", + "created_at": "创建时间ISO格式" +} +``` + +| 字段 | 说明 | 来源 | +|------|------|------| +| `type` | 事件类型,固定为 `"reminder"` | 固定值 | +| `user_openid` | 用户 openid | 从系统消息获取 | +| `user_name` | 用户名称 | 从系统消息获取(如有) | +| `original_message_id` | 创建时的消息 ID | 从系统消息获取 | +| `reminder_content` | 提醒内容摘要 | AI 根据用户请求生成 | +| `created_at` | 提醒创建时间 | 当前时间 ISO 格式 | + +> 💡 **为什么需要 `--system-event`?** +> - 提醒触发时,AI 可以获取完整的用户上下文 +> - 便于追踪提醒来源和调试 +> - 为未来可能的功能扩展预留信息 + +--- + +## 🎯 使用场景示例 + +### 场景1:一次性提醒 + +**用户**: 5分钟后提醒我喝水 + +**AI 执行**: +```bash +openclaw cron add \ + --name "喝水提醒-用户" \ + --at "5m" \ + --session isolated \ + --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"喝水","created_at":"2026-02-01T16:50:00+08:00"}' \ + --message "💧 该喝水啦!这是你5分钟前设置的提醒~" \ + --deliver \ + --channel qqbot \ + --to "{openid}" \ + --delete-after-run +``` + +**AI 回复**: +``` +✅ 提醒已设置! + +📝 内容:喝水 +⏰ 时间:5分钟后 + +到时候我会准时提醒你~ +``` + +--- + +### 场景2:每日周期提醒 + +**用户**: 每天早上8点提醒我打卡 + +**AI 执行**: +```bash +openclaw cron add \ + --name "打卡提醒-用户" \ + --cron "0 8 * * *" \ + --tz "Asia/Shanghai" \ + --session isolated \ + --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"打卡","created_at":"2026-02-01T16:50:00+08:00"}' \ + --message "🌅 早上好!记得打卡签到~" \ + --deliver \ + --channel qqbot \ + --to "{openid}" +``` + +**AI 回复**: +``` +✅ 周期提醒已设置! + +📝 内容:打卡 +🔄 周期:每天早上 8:00 + +提醒会持续生效,说"取消打卡提醒"可停止~ +``` + +> 💡 周期任务**不加** `--delete-after-run` + +--- + +### 场景3:工作日提醒 + +**用户**: 工作日下午6点提醒我写日报 + +**AI 执行**: +```bash +openclaw cron add \ + --name "日报提醒-用户" \ + --cron "0 18 * * 1-5" \ + --tz "Asia/Shanghai" \ + --session isolated \ + --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"写日报","created_at":"2026-02-01T16:50:00+08:00"}' \ + --message "📝 今天的日报别忘了提交!" \ + --deliver \ + --channel qqbot \ + --to "{openid}" +``` + +--- + +### 场景4:群组提醒 + +**用户**(群聊): 每天早上9点提醒大家站会 + +**AI 执行**: +```bash +openclaw cron add \ + --name "站会提醒-群" \ + --cron "0 9 * * 1-5" \ + --tz "Asia/Shanghai" \ + --session isolated \ + --system-event '{"type":"reminder","user_openid":"group:{group_openid}","original_message_id":"{message_id}","reminder_content":"站会","created_at":"2026-02-01T16:50:00+08:00"}' \ + --message "📢 各位同事,9点站会时间到!请准时参加~" \ + --deliver \ + --channel qqbot \ + --to "group:{group_openid}" +``` + +> 💡 群组使用 `group:{group_openid}` 格式 + +--- + +### 场景5:查询提醒 + +**用户**: 我有哪些提醒? + +**AI 执行**: +```bash +openclaw cron list +``` + +**AI 回复**(根据返回结果组织): +``` +📋 你当前的提醒: + +1. ⏰ 喝水提醒 - 3分钟后 (一次性) +2. 🔄 打卡提醒 - 每天08:00 (周期) + +说"取消xx提醒"可删除~ +``` + +--- + +### 场景6:取消提醒 + +**用户**: 取消打卡提醒 + +**AI 执行**: +1. 先执行 `openclaw cron list` 找到对应任务 ID +2. 执行 `openclaw cron remove {jobId}` + +**AI 回复**: +``` +✅ 已取消"打卡提醒" +``` + +--- + +## ⚙️ 消息发送说明 + +### 定时提醒(cron add) + +定时提醒**只能发送主动消息**,因为: +- 提醒执行时,原始 message_id 通常已超过 1 小时有效期 +- `openclaw cron add` 命令不支持 `--reply-to` 参数 + +``` +┌─────────────────────┐ +│ 定时任务触发 │ +└──────────┬──────────┘ + ↓ +┌─────────────────────┐ +│ AI 通过 system-event │ +│ 获取用户上下文信息 │ +└──────────┬──────────┘ + ↓ +┌─────────────────────┐ +│ 发送主动消息到用户 │ +│ --channel qqbot │ +│ --to {openid} │ +└──────────┬──────────┘ + ↓ + ✅ 用户收到提醒 +``` + +### 即时回复(message send) + +即时消息发送支持被动回复(如果 message_id 有效): + +``` + ┌─────────────────────┐ + │ 发送即时消息 │ + └──────────┬──────────┘ + ↓ + ┌──────────────────────────────┐ + │ 有 --reply-to 且 message_id │ + │ 在 1 小时内有效? │ + └──────────────────────────────┘ + ↓ ↓ + 是 否 + ↓ ↓ + ┌───────────────┐ ┌─────────────────┐ + │ 被动消息回复 │ │ 发送主动消息 │ + │ (引用原消息) │ │ (直接发送) │ + └───────────────┘ └─────────────────┘ +``` + +--- + +## ⚠️ 重要限制 + +| 限制 | 说明 | +|------|------| +| **message_id 有效期** | 1 小时内有效,超时自动降级 | +| **回复次数限制** | 同一 message_id 最多回复 4 次 | +| **主动消息限制** | 只能发给与机器人交互过的用户 | +| **消息内容** | `--message` 不能为空 | + +--- + +## 📝 消息模板 + +| 场景 | 模板 | Emoji | +|------|------|-------| +| 喝水 | 该喝水啦! | 💧 🚰 | +| 打卡 | 早上好!记得打卡~ | 🌅 ✅ | +| 会议 | xx会议马上开始! | 📅 👥 | +| 休息 | 该休息一下了~ | 😴 💤 | +| 日报 | 今日日报别忘了~ | 📝 ✍️ | +| 运动 | 该运动了! | 🏃 💪 | +| 吃药 | 记得按时吃药~ | 💊 🏥 | +| 生日 | 今天是xx的生日! | 🎂 🎉 | + +--- + +## 🔧 用户标识 + +| 类型 | 格式 | 来源 | +|------|------|------| +| 用户 openid | `B3EA9A1d-2D3c-5CBD-...` | 系统消息自动提供 | +| 群组 openid | `group:FeC1ADaf-...` | 系统消息自动提供 | +| message_id | `ROBOT1.0_xxx` | 系统消息自动提供 | + +> 💡 这些信息在系统消息中格式如: +> - `当前用户 openid: B3EA9A1d-...` +> - `当前消息 message_id: ROBOT1.0_...` \ No newline at end of file diff --git a/src/api.ts b/src/api.ts index 106d5e9..c517782 100644 --- a/src/api.ts +++ b/src/api.ts @@ -26,9 +26,14 @@ export function isMarkdownSupport(): boolean { } let cachedToken: { token: string; expiresAt: number } | null = null; +// Singleflight: 防止并发获取 Token 的 Promise 缓存 +let tokenFetchPromise: Promise | null = null; /** - * 获取 AccessToken(带缓存) + * 获取 AccessToken(带缓存 + singleflight 并发安全) + * + * 使用 singleflight 模式:当多个请求同时发现 Token 过期时, + * 只有第一个请求会真正去获取新 Token,其他请求复用同一个 Promise。 */ export async function getAccessToken(appId: string, clientSecret: string): Promise { // 检查缓存,提前 5 分钟刷新 @@ -36,6 +41,30 @@ export async function getAccessToken(appId: string, clientSecret: string): Promi return cachedToken.token; } + // Singleflight: 如果已有进行中的 Token 获取请求,复用它 + if (tokenFetchPromise) { + console.log(`[qqbot-api] Token fetch in progress, waiting for existing request...`); + return tokenFetchPromise; + } + + // 创建新的 Token 获取 Promise(singleflight 入口) + tokenFetchPromise = (async () => { + try { + return await doFetchToken(appId, clientSecret); + } finally { + // 无论成功失败,都清除 Promise 缓存 + tokenFetchPromise = null; + } + })(); + + return tokenFetchPromise; +} + +/** + * 实际执行 Token 获取的内部函数 + */ +async function doFetchToken(appId: string, clientSecret: string): Promise { + const requestBody = { appId, clientSecret }; const requestHeaders = { "Content-Type": "application/json" }; @@ -86,6 +115,7 @@ export async function getAccessToken(appId: string, clientSecret: string): Promi expiresAt: Date.now() + (data.expires_in ?? 7200) * 1000, }; + console.log(`[qqbot-api] Token cached, expires at: ${new Date(cachedToken.expiresAt).toISOString()}`); return cachedToken.token; } @@ -94,6 +124,22 @@ export async function getAccessToken(appId: string, clientSecret: string): Promi */ export function clearTokenCache(): void { cachedToken = null; + // 注意:不清除 tokenFetchPromise,让进行中的请求完成 + // 下次调用 getAccessToken 时会自动获取新 Token +} + +/** + * 获取 Token 缓存状态(用于监控) + */ +export function getTokenStatus(): { status: "valid" | "expired" | "refreshing" | "none"; expiresAt: number | null } { + if (tokenFetchPromise) { + return { status: "refreshing", expiresAt: cachedToken?.expiresAt ?? null }; + } + if (!cachedToken) { + return { status: "none", expiresAt: null }; + } + const isValid = Date.now() < cachedToken.expiresAt - 5 * 60 * 1000; + return { status: isValid ? "valid" : "expired", expiresAt: cachedToken.expiresAt }; } /** @@ -320,32 +366,65 @@ export async function sendGroupMessage( return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, body); } +/** + * 构建主动消息请求体 + * 根据 markdownSupport 配置决定消息格式: + * - markdown 模式: { markdown: { content }, msg_type: 2 } + * - 纯文本模式: { content, msg_type: 0 } + * + * 注意:主动消息不支持流式发送 + */ +function buildProactiveMessageBody(content: string): Record { + // 主动消息内容校验(参考 Telegram 机制) + if (!content || content.trim().length === 0) { + throw new Error("主动消息内容不能为空 (markdown.content is empty)"); + } + + if (currentMarkdownSupport) { + return { + markdown: { content }, + msg_type: 2, + }; + } else { + return { + content, + msg_type: 0, + }; + } +} + /** * 主动发送 C2C 单聊消息(不需要 msg_id,每月限 4 条/用户) + * + * 注意: + * 1. 内容不能为空(对应 markdown.content 字段) + * 2. 不支持流式发送 */ export async function sendProactiveC2CMessage( accessToken: string, openid: string, content: string ): Promise<{ id: string; timestamp: number }> { - return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, { - content, - msg_type: 0, - }); + const body = buildProactiveMessageBody(content); + console.log(`[qqbot-api] sendProactiveC2CMessage: openid=${openid}, msg_type=${body.msg_type}, content_len=${content.length}`); + return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, body); } /** * 主动发送群聊消息(不需要 msg_id,每月限 4 条/群) + * + * 注意: + * 1. 内容不能为空(对应 markdown.content 字段) + * 2. 不支持流式发送 */ export async function sendProactiveGroupMessage( accessToken: string, groupOpenid: string, content: string ): Promise<{ id: string; timestamp: string }> { - return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, { - content, - msg_type: 0, - }); + const body = buildProactiveMessageBody(content); + console.log(`[qqbot-api] sendProactiveGroupMessage: group=${groupOpenid}, msg_type=${body.msg_type}, content_len=${content.length}`); + return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, body); } // ============ 富媒体消息支持 ============ @@ -475,3 +554,150 @@ export async function sendGroupImageMessage( // 再发送富媒体消息 return sendGroupMediaMessage(accessToken, groupOpenid, uploadResult.file_info, msgId, content); } + +// ============ 后台 Token 刷新 (P1-1) ============ + +/** + * 后台 Token 刷新配置 + */ +interface BackgroundTokenRefreshOptions { + /** 提前刷新时间(毫秒,默认 5 分钟) */ + refreshAheadMs?: number; + /** 随机偏移范围(毫秒,默认 0-30 秒) */ + randomOffsetMs?: number; + /** 最小刷新间隔(毫秒,默认 1 分钟) */ + minRefreshIntervalMs?: number; + /** 失败后重试间隔(毫秒,默认 5 秒) */ + retryDelayMs?: number; + /** 日志函数 */ + log?: { + info: (msg: string) => void; + error: (msg: string) => void; + debug?: (msg: string) => void; + }; +} + +// 后台刷新状态 +let backgroundRefreshRunning = false; +let backgroundRefreshAbortController: AbortController | null = null; + +/** + * 启动后台 Token 刷新 + * 在后台定时刷新 Token,避免请求时才发现过期 + * + * @param appId 应用 ID + * @param clientSecret 应用密钥 + * @param options 配置选项 + */ +export function startBackgroundTokenRefresh( + appId: string, + clientSecret: string, + options?: BackgroundTokenRefreshOptions +): void { + if (backgroundRefreshRunning) { + console.log("[qqbot-api] Background token refresh already running"); + return; + } + + const { + refreshAheadMs = 5 * 60 * 1000, // 提前 5 分钟刷新 + randomOffsetMs = 30 * 1000, // 0-30 秒随机偏移 + minRefreshIntervalMs = 60 * 1000, // 最少 1 分钟后刷新 + retryDelayMs = 5 * 1000, // 失败后 5 秒重试 + log, + } = options ?? {}; + + backgroundRefreshRunning = true; + backgroundRefreshAbortController = new AbortController(); + const signal = backgroundRefreshAbortController.signal; + + const refreshLoop = async () => { + log?.info?.("[qqbot-api] Background token refresh started"); + + while (!signal.aborted) { + try { + // 先确保有一个有效 Token + await getAccessToken(appId, clientSecret); + + // 计算下次刷新时间 + if (cachedToken) { + const expiresIn = cachedToken.expiresAt - Date.now(); + // 提前刷新时间 + 随机偏移(避免集群同时刷新) + const randomOffset = Math.random() * randomOffsetMs; + const refreshIn = Math.max( + expiresIn - refreshAheadMs - randomOffset, + minRefreshIntervalMs + ); + + log?.debug?.( + `[qqbot-api] Token valid, next refresh in ${Math.round(refreshIn / 1000)}s` + ); + + // 等待到刷新时间 + await sleep(refreshIn, signal); + } else { + // 没有缓存的 Token,等待一段时间后重试 + log?.debug?.("[qqbot-api] No cached token, retrying soon"); + await sleep(minRefreshIntervalMs, signal); + } + } catch (err) { + if (signal.aborted) break; + + // 刷新失败,等待后重试 + log?.error?.(`[qqbot-api] Background token refresh failed: ${err}`); + await sleep(retryDelayMs, signal); + } + } + + backgroundRefreshRunning = false; + log?.info?.("[qqbot-api] Background token refresh stopped"); + }; + + // 异步启动,不阻塞调用者 + refreshLoop().catch((err) => { + backgroundRefreshRunning = false; + log?.error?.(`[qqbot-api] Background token refresh crashed: ${err}`); + }); +} + +/** + * 停止后台 Token 刷新 + */ +export function stopBackgroundTokenRefresh(): void { + if (backgroundRefreshAbortController) { + backgroundRefreshAbortController.abort(); + backgroundRefreshAbortController = null; + } + backgroundRefreshRunning = false; +} + +/** + * 检查后台 Token 刷新是否正在运行 + */ +export function isBackgroundTokenRefreshRunning(): boolean { + return backgroundRefreshRunning; +} + +/** + * 可中断的 sleep 函数 + */ +async function sleep(ms: number, signal?: AbortSignal): Promise { + return new Promise((resolve, reject) => { + const timer = setTimeout(resolve, ms); + + if (signal) { + if (signal.aborted) { + clearTimeout(timer); + reject(new Error("Aborted")); + return; + } + + const onAbort = () => { + clearTimeout(timer); + reject(new Error("Aborted")); + }; + + signal.addEventListener("abort", onAbort, { once: true }); + } + }); +} diff --git a/src/config.ts b/src/config.ts index f898941..0bc1722 100644 --- a/src/config.ts +++ b/src/config.ts @@ -77,6 +77,7 @@ export function resolveQQBotAccount( systemPrompt: qqbot?.systemPrompt, imageServerBaseUrl: qqbot?.imageServerBaseUrl, markdownSupport: qqbot?.markdownSupport, + streamEnabled: qqbot?.streamEnabled, }; appId = qqbot?.appId ?? ""; } else { @@ -113,6 +114,7 @@ export function resolveQQBotAccount( systemPrompt: accountConfig.systemPrompt, imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL, markdownSupport: accountConfig.markdownSupport, + streamEnabled: accountConfig.streamEnabled, config: accountConfig, }; } diff --git a/src/gateway.ts b/src/gateway.ts index 95f1df1..68fe910 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -2,7 +2,9 @@ import WebSocket from "ws"; import path from "node:path"; import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js"; import { StreamState } from "./types.js"; -import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, sendC2CInputNotify } from "./api.js"; +import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh } from "./api.js"; +import { loadSession, saveSession, clearSession, type SessionState } from "./session-store.js"; +import { recordKnownUser, flushKnownUsers } from "./known-users.js"; import { getQQBotRuntime } from "./runtime.js"; import { startImageServer, saveImage, saveImageFromPath, isImageServerRunning, downloadFile, type ImageServerConfig } from "./image-server.js"; import { createStreamSender } from "./outbound.js"; @@ -55,7 +57,79 @@ const IMAGE_SERVER_DIR = process.env.QQBOT_IMAGE_SERVER_DIR || path.join(process // 流式消息配置 const STREAM_CHUNK_INTERVAL = 500; // 流式消息分片间隔(毫秒) const STREAM_MIN_CHUNK_SIZE = 10; // 最小分片大小(字符) -const STREAM_KEEPALIVE_INTERVAL = 8000; // 流式心跳间隔(毫秒),需要在 10 秒内发送 +const STREAM_KEEPALIVE_FIRST_DELAY = 3000; // 首次状态保持延迟(毫秒),openclaw 3s 内未回复时发送 +const STREAM_KEEPALIVE_GAP = 10000; // 状态保持消息之间的间隔(毫秒) +const STREAM_KEEPALIVE_MAX_PER_CHUNK = 2; // 每 2 个消息分片之间最多发送的状态保持消息数量 +const STREAM_MAX_DURATION = 3 * 60 * 1000; // 流式消息最大持续时间(毫秒),超过 3 分钟自动结束 + +// 消息队列配置(异步处理,防止阻塞心跳) +const MESSAGE_QUEUE_SIZE = 1000; // 最大队列长度 +const MESSAGE_QUEUE_WARN_THRESHOLD = 800; // 队列告警阈值 + +// ============ 消息回复限流器 ============ +// 同一 message_id 1小时内最多回复 4 次,超过1小时需降级为主动消息 +const MESSAGE_REPLY_LIMIT = 4; +const MESSAGE_REPLY_TTL = 60 * 60 * 1000; // 1小时 + +interface MessageReplyRecord { + count: number; + firstReplyAt: number; +} + +const messageReplyTracker = new Map(); + +/** + * 检查是否可以回复该消息(限流检查) + * @param messageId 消息ID + * @returns { allowed: boolean, remaining: number } allowed=是否允许回复,remaining=剩余次数 + */ +function checkMessageReplyLimit(messageId: string): { allowed: boolean; remaining: number } { + const now = Date.now(); + const record = messageReplyTracker.get(messageId); + + // 清理过期记录(定期清理,避免内存泄漏) + if (messageReplyTracker.size > 10000) { + for (const [id, rec] of messageReplyTracker) { + if (now - rec.firstReplyAt > MESSAGE_REPLY_TTL) { + messageReplyTracker.delete(id); + } + } + } + + if (!record) { + return { allowed: true, remaining: MESSAGE_REPLY_LIMIT }; + } + + // 检查是否过期 + if (now - record.firstReplyAt > MESSAGE_REPLY_TTL) { + messageReplyTracker.delete(messageId); + return { allowed: true, remaining: MESSAGE_REPLY_LIMIT }; + } + + // 检查是否超过限制 + const remaining = MESSAGE_REPLY_LIMIT - record.count; + return { allowed: remaining > 0, remaining: Math.max(0, remaining) }; +} + +/** + * 记录一次消息回复 + * @param messageId 消息ID + */ +function recordMessageReply(messageId: string): void { + const now = Date.now(); + const record = messageReplyTracker.get(messageId); + + if (!record) { + messageReplyTracker.set(messageId, { count: 1, firstReplyAt: now }); + } else { + // 检查是否过期,过期则重新计数 + if (now - record.firstReplyAt > MESSAGE_REPLY_TTL) { + messageReplyTracker.set(messageId, { count: 1, firstReplyAt: now }); + } else { + record.count++; + } + } +} export interface GatewayContext { account: ResolvedQQBotAccount; @@ -70,6 +144,22 @@ export interface GatewayContext { }; } +/** + * 消息队列项类型(用于异步处理消息,防止阻塞心跳) + */ +interface QueuedMessage { + type: "c2c" | "guild" | "dm" | "group"; + senderId: string; + senderName?: string; + content: string; + messageId: string; + timestamp: string; + channelId?: string; + guildId?: string; + groupOpenid?: string; + attachments?: Array<{ content_type: string; url: string; filename?: string }>; +} + /** * 启动图床服务器 */ @@ -137,6 +227,74 @@ export async function startGateway(ctx: GatewayContext): Promise { let intentLevelIndex = 0; // 当前尝试的权限级别索引 let lastSuccessfulIntentLevel = -1; // 上次成功的权限级别 + // ============ P1-2: 尝试从持久化存储恢复 Session ============ + const savedSession = loadSession(account.accountId); + if (savedSession) { + sessionId = savedSession.sessionId; + lastSeq = savedSession.lastSeq; + intentLevelIndex = savedSession.intentLevelIndex; + lastSuccessfulIntentLevel = savedSession.intentLevelIndex; + log?.info(`[qqbot:${account.accountId}] Restored session from storage: sessionId=${sessionId}, lastSeq=${lastSeq}, intentLevel=${intentLevelIndex}`); + } + + // ============ 消息队列(异步处理,防止阻塞心跳) ============ + const messageQueue: QueuedMessage[] = []; + let messageProcessorRunning = false; + let messagesProcessed = 0; // 统计已处理消息数 + + /** + * 将消息加入队列(非阻塞) + */ + const enqueueMessage = (msg: QueuedMessage): void => { + if (messageQueue.length >= MESSAGE_QUEUE_SIZE) { + // 队列满了,丢弃最旧的消息 + const dropped = messageQueue.shift(); + log?.error(`[qqbot:${account.accountId}] Message queue full, dropping oldest message from ${dropped?.senderId}`); + } + if (messageQueue.length >= MESSAGE_QUEUE_WARN_THRESHOLD) { + log?.info(`[qqbot:${account.accountId}] Message queue size: ${messageQueue.length}/${MESSAGE_QUEUE_SIZE}`); + } + messageQueue.push(msg); + log?.debug?.(`[qqbot:${account.accountId}] Message enqueued, queue size: ${messageQueue.length}`); + }; + + /** + * 启动消息处理循环(独立于 WS 消息循环) + */ + const startMessageProcessor = (handleMessageFn: (msg: QueuedMessage) => Promise): void => { + if (messageProcessorRunning) return; + messageProcessorRunning = true; + + const processLoop = async () => { + while (!isAborted) { + if (messageQueue.length === 0) { + // 队列为空,等待一小段时间 + await new Promise(resolve => setTimeout(resolve, 50)); + continue; + } + + const msg = messageQueue.shift()!; + try { + await handleMessageFn(msg); + messagesProcessed++; + } catch (err) { + // 捕获处理异常,防止影响队列循环 + log?.error(`[qqbot:${account.accountId}] Message processor error: ${err}`); + } + } + messageProcessorRunning = false; + log?.info(`[qqbot:${account.accountId}] Message processor stopped`); + }; + + // 异步启动,不阻塞调用者 + processLoop().catch(err => { + log?.error(`[qqbot:${account.accountId}] Message processor crashed: ${err}`); + messageProcessorRunning = false; + }); + + log?.info(`[qqbot:${account.accountId}] Message processor started`); + }; + abortSignal.addEventListener("abort", () => { isAborted = true; if (reconnectTimer) { @@ -144,6 +302,10 @@ export async function startGateway(ctx: GatewayContext): Promise { reconnectTimer = null; } cleanup(); + // P1-1: 停止后台 Token 刷新 + stopBackgroundTokenRefresh(); + // P1-3: 保存已知用户数据 + flushKnownUsers(); }); const cleanup = () => { @@ -232,16 +394,9 @@ export async function startGateway(ctx: GatewayContext): Promise { log?.info(`[qqbot:${account.accountId}] Attachments: ${event.attachments.length}`); } - // 对于 C2C 消息,先发送输入状态提示用户机器人正在输入 - if (event.type === "c2c") { - try { - const token = await getAccessToken(account.appId, account.clientSecret); - await sendC2CInputNotify(token, event.senderId, event.messageId, 60); - log?.info(`[qqbot:${account.accountId}] Sent input notify to ${event.senderId}`); - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to send input notify: ${err}`); - } - } + // 流式消息开关(默认启用,仅 c2c 支持) + const streamEnabled = account.streamEnabled !== false; + log?.debug?.(`[qqbot:${account.accountId}] Stream enabled: ${streamEnabled}`); pluginRuntime.channel.activity.record({ channel: "qqbot", @@ -269,6 +424,51 @@ export async function startGateway(ctx: GatewayContext): Promise { // 组装消息体,添加系统提示词 let builtinPrompt = ""; + // ============ 用户标识信息(用于定时提醒和主动消息) ============ + const isGroupChat = event.type === "group"; + const targetAddress = isGroupChat ? `group:${event.groupOpenid}` : event.senderId; + + builtinPrompt += ` +【当前用户信息】 +- 用户 openid: ${event.senderId} +- 用户昵称: ${event.senderName || "未知"} +- 消息类型: ${isGroupChat ? "群聊" : "私聊"} +- 当前消息 message_id: ${event.messageId}${isGroupChat ? ` +- 群组 group_openid: ${event.groupOpenid}` : ""} + +【定时提醒能力】 +你可以帮助用户设置定时提醒。使用 openclaw cron 命令: + +示例:5分钟后提醒用户喝水 +\`\`\`bash +openclaw cron add \\ + --name "提醒喝水-${event.senderName || "用户"}" \\ + --at "5m" \\ + --session isolated \\ + --message "💧 该喝水啦!" \\ + --deliver \\ + --channel qqbot \\ + --to "${targetAddress}" \\ + --reply-to "${event.messageId}" \\ + --delete-after-run +\`\`\` + +关键参数说明: +- \`--to\`: 目标地址(当前用户: ${targetAddress}) +- \`--reply-to\`: 回复消息ID(当前消息: ${event.messageId},使提醒能引用原消息) +- \`--at\`: 一次性定时任务的触发时间 + - 相对时间格式:数字+单位,如 \`5m\`(5分钟)、\`1h\`(1小时)、\`2d\`(2天)【注意:不要加 + 号】 + - 绝对时间格式:ISO 8601 带时区,如 \`2026-02-01T14:00:00+08:00\` +- \`--cron\`: 周期性任务(如 \`0 8 * * *\` 每天早上8点) +- \`--tz "Asia/Shanghai"\`: 周期任务务必设置时区 +- \`--delete-after-run\`: 一次性任务必须添加此参数 +- \`--message\`: 消息内容(必填,不能为空!对应 QQ API 的 markdown.content 字段) + +⚠️ 重要注意事项: +1. --at 参数格式:相对时间用 \`5m\`、\`1h\` 等(不要加 + 号!);绝对时间用完整 ISO 格式 +2. 定时提醒消息不支持流式发送,命令中不要添加 --stream 参数 +3. --message 参数必须有实际内容,不能为空字符串`; + // 只有配置了图床公网地址,才告诉 AI 可以发送图片 if (imageServerBaseUrl) { builtinPrompt += ` @@ -400,7 +600,7 @@ export async function startGateway(ctx: GatewayContext): Promise { // 追踪是否有响应 let hasResponse = false; - const responseTimeout = 30000; // 30秒超时 + const responseTimeout = 60000; // 60秒超时(1分钟) let timeoutId: ReturnType | null = null; const timeoutPromise = new Promise((_, reject) => { @@ -417,19 +617,25 @@ export async function startGateway(ctx: GatewayContext): Promise { : event.type === "group" ? `group:${event.groupOpenid}` : `channel:${event.channelId}`; - // 判断是否支持流式(仅 c2c 支持,群聊不支持流式) - const supportsStream = event.type === "c2c"; + // 判断是否支持流式(仅 c2c 支持,群聊不支持流式,且需要开关启用) + const supportsStream = event.type === "c2c" && streamEnabled; + log?.info(`[qqbot:${account.accountId}] Stream support: ${supportsStream} (type=${event.type}, enabled=${streamEnabled})`); // 创建流式发送器 const streamSender = supportsStream ? createStreamSender(account, targetTo, event.messageId) : null; let streamBuffer = ""; // 累积的全部文本(用于记录完整内容) let lastSentLength = 0; // 上次发送时的文本长度(用于计算增量) + let lastSentText = ""; // 上次发送时的完整文本(用于检测新段落) + let currentSegmentStart = 0; // 当前段落在 streamBuffer 中的起始位置 let lastStreamSendTime = 0; // 上次流式发送时间 let streamStarted = false; // 是否已开始流式发送 let streamEnded = false; // 流式是否已结束 + let streamStartTime = 0; // 流式消息开始时间(用于超时检查) let sendingLock = false; // 发送锁,防止并发发送 let pendingFullText = ""; // 待发送的完整文本(在锁定期间积累) let keepaliveTimer: ReturnType | null = null; // 心跳定时器 + let keepaliveCountSinceLastChunk = 0; // 自上次分片以来发送的状态保持消息数量 + let lastChunkSendTime = 0; // 上次分片发送时间(用于判断是否需要发送状态保持) // 清理心跳定时器 const clearKeepalive = () => { @@ -440,26 +646,78 @@ export async function startGateway(ctx: GatewayContext): Promise { }; // 重置心跳定时器(每次发送后调用) - const resetKeepalive = () => { + // isContentChunk: 是否为内容分片(非状态保持消息) + const resetKeepalive = (isContentChunk: boolean = false) => { clearKeepalive(); + + // 如果是内容分片,重置状态保持计数器和时间 + if (isContentChunk) { + keepaliveCountSinceLastChunk = 0; + lastChunkSendTime = Date.now(); + } + if (streamSender && streamStarted && !streamEnded) { + // 计算下次状态保持消息的延迟时间 + // - 首次:3s(STREAM_KEEPALIVE_FIRST_DELAY) + // - 后续:10s(STREAM_KEEPALIVE_GAP) + const delay = keepaliveCountSinceLastChunk === 0 + ? STREAM_KEEPALIVE_FIRST_DELAY + : STREAM_KEEPALIVE_GAP; + keepaliveTimer = setTimeout(async () => { - // 10 秒内没有新消息,发送空分片保持连接 + // 检查流式消息是否超时(超过 3 分钟自动结束) + const elapsed = Date.now() - streamStartTime; + if (elapsed >= STREAM_MAX_DURATION) { + log?.info(`[qqbot:${account.accountId}] Stream timeout after ${Math.round(elapsed / 1000)}s, auto ending stream`); + if (!streamEnded && !sendingLock) { + sendingLock = true; + try { + // 发送结束标记 + await streamSender!.send("", true); + streamEnded = true; + clearKeepalive(); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Stream auto-end failed: ${err}`); + } finally { + sendingLock = false; + } + } + return; // 超时后不再继续心跳 + } + + // 检查是否已达到每2个分片之间的最大状态保持消息数量 + if (keepaliveCountSinceLastChunk >= STREAM_KEEPALIVE_MAX_PER_CHUNK) { + log?.debug?.(`[qqbot:${account.accountId}] Max keepalive reached (${keepaliveCountSinceLastChunk}/${STREAM_KEEPALIVE_MAX_PER_CHUNK}), waiting for next content chunk`); + // 不再发送状态保持,但继续监控超时 + resetKeepalive(false); + return; + } + + // 检查距上次分片是否超过 3s + const timeSinceLastChunk = Date.now() - lastChunkSendTime; + if (timeSinceLastChunk < STREAM_KEEPALIVE_FIRST_DELAY) { + // 还未到发送状态保持的时机,继续等待 + resetKeepalive(false); + return; + } + + // 发送状态保持消息 if (!streamEnded && !sendingLock) { - log?.info(`[qqbot:${account.accountId}] Sending keepalive empty chunk`); + log?.info(`[qqbot:${account.accountId}] Sending keepalive #${keepaliveCountSinceLastChunk + 1} (elapsed: ${Math.round(elapsed / 1000)}s, since chunk: ${Math.round(timeSinceLastChunk / 1000)}s)`); sendingLock = true; try { // 发送空内容 await streamSender!.send("", false); lastStreamSendTime = Date.now(); - resetKeepalive(); // 继续下一个心跳 + keepaliveCountSinceLastChunk++; + resetKeepalive(false); // 继续下一个状态保持(非内容分片) } catch (err) { log?.error(`[qqbot:${account.accountId}] Keepalive failed: ${err}`); } finally { sendingLock = false; } } - }, STREAM_KEEPALIVE_INTERVAL); + }, delay); } }; @@ -486,8 +744,9 @@ export async function startGateway(ctx: GatewayContext): Promise { streamEnded = true; clearKeepalive(); } else { - // 发送成功后重置心跳 - resetKeepalive(); + // 发送成功后重置心跳,如果是有内容的分片则重置计数器 + const isContentChunk = text.length > 0; + resetKeepalive(isContentChunk); } return true; }; @@ -505,11 +764,17 @@ export async function startGateway(ctx: GatewayContext): Promise { // 发送当前增量 if (fullText.length > lastSentLength) { const increment = fullText.slice(lastSentLength); + // 首次发送前,先设置流式状态和开始时间 + if (!streamStarted) { + streamStarted = true; + streamStartTime = Date.now(); + log?.info(`[qqbot:${account.accountId}] Stream started, max duration: ${STREAM_MAX_DURATION / 1000}s`); + } const success = await sendStreamChunk(increment, forceEnd); if (success) { lastSentLength = fullText.length; + lastSentText = fullText; // 记录完整发送文本,用于检测新段落 lastStreamSendTime = Date.now(); - streamStarted = true; log?.info(`[qqbot:${account.accountId}] Stream partial #${streamSender!.getContext().index}, increment: ${increment.length} chars, total: ${fullText.length} chars`); } } else if (forceEnd && !streamEnded) { @@ -530,6 +795,8 @@ export async function startGateway(ctx: GatewayContext): Promise { }; // onPartialReply 回调 - 实时接收 AI 生成的文本(payload.text 是累积的全文) + // 注意:agent 在一次对话中可能产生多个回复段落(如思考、工具调用后继续回复) + // 每个新段落的 text 会从头开始累积,需要检测并处理 const handlePartialReply = async (payload: { text?: string }) => { if (!streamSender || streamEnded) { log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply skipped: streamSender=${!!streamSender}, streamEnded=${streamEnded}`); @@ -542,11 +809,39 @@ export async function startGateway(ctx: GatewayContext): Promise { return; } - // 始终更新累积缓冲区(即使不发送,也要记录最新内容) - streamBuffer = fullText; hasResponse = true; - log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: fullText.length=${fullText.length}, lastSentLength=${lastSentLength}`); + // 检测是否是新段落: + // 1. lastSentText 不为空(说明已经发送过内容) + // 2. 当前文本不是以 lastSentText 开头(说明不是同一段落的增量) + // 3. 当前文本长度小于 lastSentLength(说明文本被重置了) + const isNewSegment = lastSentText.length > 0 && + (fullText.length < lastSentLength || !fullText.startsWith(lastSentText.slice(0, Math.min(10, lastSentText.length)))); + + if (isNewSegment) { + // 新段落开始,将之前的内容追加到 streamBuffer,并重置发送位置 + log?.info(`[qqbot:${account.accountId}] New segment detected! lastSentLength=${lastSentLength}, newTextLength=${fullText.length}, lastSentText="${lastSentText.slice(0, 20)}...", newText="${fullText.slice(0, 20)}..."`); + + // 记录当前段落在 streamBuffer 中的起始位置 + currentSegmentStart = streamBuffer.length; + + // 追加换行分隔符(如果前面有内容且不以换行结尾) + if (streamBuffer.length > 0 && !streamBuffer.endsWith("\n")) { + streamBuffer += "\n\n"; + currentSegmentStart = streamBuffer.length; + } + + // 重置发送位置,从新段落开始发送 + lastSentLength = 0; + lastSentText = ""; + } + + // 更新当前段落内容到 streamBuffer + // streamBuffer = 之前的段落内容 + 当前段落的完整内容 + const beforeCurrentSegment = streamBuffer.slice(0, currentSegmentStart); + streamBuffer = beforeCurrentSegment + fullText; + + log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: fullText.length=${fullText.length}, lastSentLength=${lastSentLength}, streamBuffer.length=${streamBuffer.length}, isNewSegment=${isNewSegment}`); // 如果没有新内容,跳过 if (fullText.length <= lastSentLength) return; @@ -578,9 +873,15 @@ export async function startGateway(ctx: GatewayContext): Promise { let replyText = payload.text ?? ""; - // 更新 streamBuffer,确保最终内容不会丢失 - if (replyText.length > streamBuffer.length) { - streamBuffer = replyText; + // 更新当前段落内容到 streamBuffer + // deliver 中的 replyText 是当前段落的完整文本 + if (replyText.length > 0) { + const beforeCurrentSegment = streamBuffer.slice(0, currentSegmentStart); + const newStreamBuffer = beforeCurrentSegment + replyText; + if (newStreamBuffer.length > streamBuffer.length) { + streamBuffer = newStreamBuffer; + log?.debug?.(`[qqbot:${account.accountId}] deliver: updated streamBuffer, replyText=${replyText.length}, total=${streamBuffer.length}`); + } } // 收集所有图片路径 @@ -796,18 +1097,18 @@ export async function startGateway(ctx: GatewayContext): Promise { } // 确保所有待发送内容都发送出去 - // 优先使用 pendingFullText,因为它可能包含最新的完整文本 - const finalFullText = pendingFullText && pendingFullText.length > streamBuffer.length + // 当前段落的最新完整文本 + const currentSegmentText = pendingFullText && pendingFullText.length > (streamBuffer.length - currentSegmentStart) ? pendingFullText - : streamBuffer; + : streamBuffer.slice(currentSegmentStart); - // 计算剩余未发送的增量内容 - const remainingIncrement = finalFullText.slice(lastSentLength); + // 计算当前段落剩余未发送的增量内容 + const remainingIncrement = currentSegmentText.slice(lastSentLength); if (remainingIncrement || streamStarted) { // 有剩余内容或者已开始流式,都需要发送结束标记 await streamSender.end(remainingIncrement); streamEnded = true; - log?.info(`[qqbot:${account.accountId}] Stream completed, final increment: ${remainingIncrement.length} chars, total: ${finalFullText.length} chars, chunks: ${streamSender.getContext().index}`); + log?.info(`[qqbot:${account.accountId}] Stream completed, final increment: ${remainingIncrement.length} chars, total streamBuffer: ${streamBuffer.length} chars, chunks: ${streamSender.getContext().index}`); } } } catch (err) { @@ -832,6 +1133,12 @@ export async function startGateway(ctx: GatewayContext): Promise { isConnecting = false; // 连接完成,释放锁 reconnectAttempts = 0; // 连接成功,重置重试计数 lastConnectTime = Date.now(); // 记录连接时间 + // 启动消息处理器(异步处理,防止阻塞心跳) + startMessageProcessor(handleMessage); + // P1-1: 启动后台 Token 刷新 + startBackgroundTokenRefresh(account.appId, account.clientSecret, { + log: log as { info: (msg: string) => void; error: (msg: string) => void; debug?: (msg: string) => void }, + }); }); ws.on("message", async (data) => { @@ -840,7 +1147,20 @@ export async function startGateway(ctx: GatewayContext): Promise { const payload = JSON.parse(rawData) as WSPayload; const { op, d, s, t } = payload; - if (s) lastSeq = s; + if (s) { + lastSeq = s; + // P1-2: 更新持久化存储中的 lastSeq(节流保存) + if (sessionId) { + saveSession({ + sessionId, + lastSeq, + lastConnectedAt: lastConnectTime, + intentLevelIndex: lastSuccessfulIntentLevel >= 0 ? lastSuccessfulIntentLevel : intentLevelIndex, + accountId: account.accountId, + savedAt: Date.now(), + }); + } + } log?.debug?.(`[qqbot:${account.accountId}] Received op=${op} t=${t}`); @@ -894,12 +1214,39 @@ export async function startGateway(ctx: GatewayContext): Promise { lastSuccessfulIntentLevel = intentLevelIndex; const successLevel = INTENT_LEVELS[intentLevelIndex]; log?.info(`[qqbot:${account.accountId}] Ready with ${successLevel.description}, session: ${sessionId}`); + // P1-2: 保存新的 Session 状态 + saveSession({ + sessionId, + lastSeq, + lastConnectedAt: Date.now(), + intentLevelIndex, + accountId: account.accountId, + savedAt: Date.now(), + }); onReady?.(d); } else if (t === "RESUMED") { log?.info(`[qqbot:${account.accountId}] Session resumed`); + // P1-2: 更新 Session 连接时间 + if (sessionId) { + saveSession({ + sessionId, + lastSeq, + lastConnectedAt: Date.now(), + intentLevelIndex: lastSuccessfulIntentLevel >= 0 ? lastSuccessfulIntentLevel : intentLevelIndex, + accountId: account.accountId, + savedAt: Date.now(), + }); + } } else if (t === "C2C_MESSAGE_CREATE") { const event = d as C2CMessageEvent; - await handleMessage({ + // P1-3: 记录已知用户 + recordKnownUser({ + openid: event.author.user_openid, + type: "c2c", + accountId: account.accountId, + }); + // 使用消息队列异步处理,防止阻塞心跳 + enqueueMessage({ type: "c2c", senderId: event.author.user_openid, content: event.content, @@ -909,7 +1256,14 @@ export async function startGateway(ctx: GatewayContext): Promise { }); } else if (t === "AT_MESSAGE_CREATE") { const event = d as GuildMessageEvent; - await handleMessage({ + // P1-3: 记录已知用户(频道用户) + recordKnownUser({ + openid: event.author.id, + type: "c2c", // 频道用户按 c2c 类型存储 + nickname: event.author.username, + accountId: account.accountId, + }); + enqueueMessage({ type: "guild", senderId: event.author.id, senderName: event.author.username, @@ -922,7 +1276,14 @@ export async function startGateway(ctx: GatewayContext): Promise { }); } else if (t === "DIRECT_MESSAGE_CREATE") { const event = d as GuildMessageEvent; - await handleMessage({ + // P1-3: 记录已知用户(频道私信用户) + recordKnownUser({ + openid: event.author.id, + type: "c2c", + nickname: event.author.username, + accountId: account.accountId, + }); + enqueueMessage({ type: "dm", senderId: event.author.id, senderName: event.author.username, @@ -934,7 +1295,14 @@ export async function startGateway(ctx: GatewayContext): Promise { }); } else if (t === "GROUP_AT_MESSAGE_CREATE") { const event = d as GroupMessageEvent; - await handleMessage({ + // P1-3: 记录已知用户(群组用户) + recordKnownUser({ + openid: event.author.member_openid, + type: "group", + groupOpenid: event.group_openid, + accountId: account.accountId, + }); + enqueueMessage({ type: "group", senderId: event.author.member_openid, content: event.content, @@ -964,6 +1332,8 @@ export async function startGateway(ctx: GatewayContext): Promise { if (!canResume) { sessionId = null; lastSeq = null; + // P1-2: 清除持久化的 Session + clearSession(account.accountId); // 尝试降级到下一个权限级别 if (intentLevelIndex < INTENT_LEVELS.length - 1) { diff --git a/src/known-users.ts b/src/known-users.ts new file mode 100644 index 0000000..19a5650 --- /dev/null +++ b/src/known-users.ts @@ -0,0 +1,358 @@ +/** + * 已知用户存储 + * 记录与机器人交互过的所有用户 + * 支持主动消息和批量通知功能 + */ + +import fs from "node:fs"; +import path from "node:path"; + +// 已知用户信息接口 +export interface KnownUser { + /** 用户 openid(唯一标识) */ + openid: string; + /** 消息类型:私聊用户 / 群组 */ + type: "c2c" | "group"; + /** 用户昵称(如有) */ + nickname?: string; + /** 群组 openid(如果是群消息) */ + groupOpenid?: string; + /** 关联的机器人账户 ID */ + accountId: string; + /** 首次交互时间戳 */ + firstSeenAt: number; + /** 最后交互时间戳 */ + lastSeenAt: number; + /** 交互次数 */ + interactionCount: number; +} + +// 存储文件路径 +const KNOWN_USERS_DIR = path.join( + process.env.HOME || "/tmp", + "clawd", + "qqbot-data" +); + +const KNOWN_USERS_FILE = path.join(KNOWN_USERS_DIR, "known-users.json"); + +// 内存缓存 +let usersCache: Map | null = null; + +// 写入节流配置 +const SAVE_THROTTLE_MS = 5000; // 5秒写入一次 +let saveTimer: ReturnType | null = null; +let isDirty = false; + +/** + * 确保目录存在 + */ +function ensureDir(): void { + if (!fs.existsSync(KNOWN_USERS_DIR)) { + fs.mkdirSync(KNOWN_USERS_DIR, { recursive: true }); + } +} + +/** + * 从文件加载用户数据到缓存 + */ +function loadUsersFromFile(): Map { + if (usersCache !== null) { + return usersCache; + } + + usersCache = new Map(); + + try { + if (fs.existsSync(KNOWN_USERS_FILE)) { + const data = fs.readFileSync(KNOWN_USERS_FILE, "utf-8"); + const users = JSON.parse(data) as KnownUser[]; + + for (const user of users) { + // 使用复合键:accountId + type + openid(群组还要加 groupOpenid) + const key = makeUserKey(user); + usersCache.set(key, user); + } + + console.log(`[known-users] Loaded ${usersCache.size} users from file`); + } + } catch (err) { + console.error(`[known-users] Failed to load users: ${err}`); + usersCache = new Map(); + } + + return usersCache; +} + +/** + * 保存用户数据到文件(节流版本) + */ +function saveUsersToFile(): void { + if (!isDirty) return; + + if (saveTimer) { + return; // 已有定时器在等待 + } + + saveTimer = setTimeout(() => { + saveTimer = null; + doSaveUsersToFile(); + }, SAVE_THROTTLE_MS); +} + +/** + * 实际执行保存 + */ +function doSaveUsersToFile(): void { + if (!usersCache || !isDirty) return; + + try { + ensureDir(); + const users = Array.from(usersCache.values()); + fs.writeFileSync(KNOWN_USERS_FILE, JSON.stringify(users, null, 2), "utf-8"); + isDirty = false; + console.log(`[known-users] Saved ${users.length} users to file`); + } catch (err) { + console.error(`[known-users] Failed to save users: ${err}`); + } +} + +/** + * 强制立即保存(用于进程退出前) + */ +export function flushKnownUsers(): void { + if (saveTimer) { + clearTimeout(saveTimer); + saveTimer = null; + } + doSaveUsersToFile(); +} + +/** + * 生成用户唯一键 + */ +function makeUserKey(user: Partial): string { + const base = `${user.accountId}:${user.type}:${user.openid}`; + if (user.type === "group" && user.groupOpenid) { + return `${base}:${user.groupOpenid}`; + } + return base; +} + +/** + * 记录已知用户(收到消息时调用) + * @param user 用户信息(部分字段) + */ +export function recordKnownUser(user: { + openid: string; + type: "c2c" | "group"; + nickname?: string; + groupOpenid?: string; + accountId: string; +}): void { + const cache = loadUsersFromFile(); + const key = makeUserKey(user); + const now = Date.now(); + + const existing = cache.get(key); + + if (existing) { + // 更新已存在的用户 + existing.lastSeenAt = now; + existing.interactionCount++; + if (user.nickname && user.nickname !== existing.nickname) { + existing.nickname = user.nickname; + } + console.log(`[known-users] Updated user ${user.openid}, interactions: ${existing.interactionCount}`); + } else { + // 新用户 + const newUser: KnownUser = { + openid: user.openid, + type: user.type, + nickname: user.nickname, + groupOpenid: user.groupOpenid, + accountId: user.accountId, + firstSeenAt: now, + lastSeenAt: now, + interactionCount: 1, + }; + cache.set(key, newUser); + console.log(`[known-users] New user recorded: ${user.openid} (${user.type})`); + } + + isDirty = true; + saveUsersToFile(); +} + +/** + * 获取单个用户信息 + * @param accountId 机器人账户 ID + * @param openid 用户 openid + * @param type 消息类型 + * @param groupOpenid 群组 openid(可选) + */ +export function getKnownUser( + accountId: string, + openid: string, + type: "c2c" | "group" = "c2c", + groupOpenid?: string +): KnownUser | undefined { + const cache = loadUsersFromFile(); + const key = makeUserKey({ accountId, openid, type, groupOpenid }); + return cache.get(key); +} + +/** + * 列出所有已知用户 + * @param options 筛选选项 + */ +export function listKnownUsers(options?: { + /** 筛选特定机器人账户的用户 */ + accountId?: string; + /** 筛选消息类型 */ + type?: "c2c" | "group"; + /** 最近活跃时间(毫秒,如 86400000 表示最近 24 小时) */ + activeWithin?: number; + /** 返回数量限制 */ + limit?: number; + /** 排序方式 */ + sortBy?: "lastSeenAt" | "firstSeenAt" | "interactionCount"; + /** 排序方向 */ + sortOrder?: "asc" | "desc"; +}): KnownUser[] { + const cache = loadUsersFromFile(); + let users = Array.from(cache.values()); + + // 筛选 + if (options?.accountId) { + users = users.filter(u => u.accountId === options.accountId); + } + if (options?.type) { + users = users.filter(u => u.type === options.type); + } + if (options?.activeWithin) { + const cutoff = Date.now() - options.activeWithin; + users = users.filter(u => u.lastSeenAt >= cutoff); + } + + // 排序 + const sortBy = options?.sortBy ?? "lastSeenAt"; + const sortOrder = options?.sortOrder ?? "desc"; + users.sort((a, b) => { + const aVal = a[sortBy] ?? 0; + const bVal = b[sortBy] ?? 0; + return sortOrder === "asc" ? aVal - bVal : bVal - aVal; + }); + + // 限制数量 + if (options?.limit && options.limit > 0) { + users = users.slice(0, options.limit); + } + + return users; +} + +/** + * 获取用户统计信息 + * @param accountId 机器人账户 ID(可选,不传则返回所有账户的统计) + */ +export function getKnownUsersStats(accountId?: string): { + totalUsers: number; + c2cUsers: number; + groupUsers: number; + activeIn24h: number; + activeIn7d: number; +} { + let users = listKnownUsers({ accountId }); + + const now = Date.now(); + const day = 24 * 60 * 60 * 1000; + + return { + totalUsers: users.length, + c2cUsers: users.filter(u => u.type === "c2c").length, + groupUsers: users.filter(u => u.type === "group").length, + activeIn24h: users.filter(u => now - u.lastSeenAt < day).length, + activeIn7d: users.filter(u => now - u.lastSeenAt < 7 * day).length, + }; +} + +/** + * 删除用户记录 + * @param accountId 机器人账户 ID + * @param openid 用户 openid + * @param type 消息类型 + * @param groupOpenid 群组 openid(可选) + */ +export function removeKnownUser( + accountId: string, + openid: string, + type: "c2c" | "group" = "c2c", + groupOpenid?: string +): boolean { + const cache = loadUsersFromFile(); + const key = makeUserKey({ accountId, openid, type, groupOpenid }); + + if (cache.has(key)) { + cache.delete(key); + isDirty = true; + saveUsersToFile(); + console.log(`[known-users] Removed user ${openid}`); + return true; + } + + return false; +} + +/** + * 清除所有用户记录 + * @param accountId 机器人账户 ID(可选,不传则清除所有) + */ +export function clearKnownUsers(accountId?: string): number { + const cache = loadUsersFromFile(); + let count = 0; + + if (accountId) { + // 只清除指定账户的用户 + for (const [key, user] of cache.entries()) { + if (user.accountId === accountId) { + cache.delete(key); + count++; + } + } + } else { + // 清除所有 + count = cache.size; + cache.clear(); + } + + if (count > 0) { + isDirty = true; + doSaveUsersToFile(); // 立即保存 + console.log(`[known-users] Cleared ${count} users`); + } + + return count; +} + +/** + * 获取用户的所有群组(某用户在哪些群里交互过) + * @param accountId 机器人账户 ID + * @param openid 用户 openid + */ +export function getUserGroups(accountId: string, openid: string): string[] { + const users = listKnownUsers({ accountId, type: "group" }); + return users + .filter(u => u.openid === openid && u.groupOpenid) + .map(u => u.groupOpenid!); +} + +/** + * 获取群组的所有成员 + * @param accountId 机器人账户 ID + * @param groupOpenid 群组 openid + */ +export function getGroupMembers(accountId: string, groupOpenid: string): KnownUser[] { + return listKnownUsers({ accountId, type: "group" }) + .filter(u => u.groupOpenid === groupOpenid); +} diff --git a/src/openclaw-plugin-sdk.d.ts b/src/openclaw-plugin-sdk.d.ts new file mode 100644 index 0000000..ef1f1d6 --- /dev/null +++ b/src/openclaw-plugin-sdk.d.ts @@ -0,0 +1,483 @@ +/** + * OpenClaw Plugin SDK 类型声明 + * + * 此文件为 openclaw/plugin-sdk 模块提供 TypeScript 类型声明 + * 仅包含本项目实际使用的类型和函数 + */ + +declare module "openclaw/plugin-sdk" { + // ============ 配置类型 ============ + + /** + * OpenClaw 主配置对象 + */ + export interface OpenClawConfig { + /** 频道配置 */ + channels?: { + qqbot?: unknown; + telegram?: unknown; + discord?: unknown; + slack?: unknown; + whatsapp?: unknown; + [key: string]: unknown; + }; + /** 其他配置字段 */ + [key: string]: unknown; + } + + // ============ 插件运行时 ============ + + /** + * Channel Activity 接口 + */ + export interface ChannelActivity { + record?: (...args: unknown[]) => void; + recordActivity?: (key: string, data?: unknown) => void; + [key: string]: unknown; + } + + /** + * Channel Routing 接口 + */ + export interface ChannelRouting { + resolveAgentRoute?: (...args: unknown[]) => unknown; + resolveSenderAndSession?: (options: unknown) => unknown; + [key: string]: unknown; + } + + /** + * Channel Reply 接口 + */ + export interface ChannelReply { + handleIncomingMessage?: (options: unknown) => Promise; + formatInboundEnvelope?: (...args: unknown[]) => unknown; + finalizeInboundContext?: (...args: unknown[]) => unknown; + resolveEnvelopeFormatOptions?: (...args: unknown[]) => unknown; + handleAutoReply?: (...args: unknown[]) => Promise; + [key: string]: unknown; + } + + /** + * Channel 接口(用于 PluginRuntime) + * 注意:这是一个宽松的类型定义,实际 SDK 中的类型更复杂 + */ + export interface ChannelInterface { + recordInboundSession?: (options: unknown) => void; + handleIncomingMessage?: (options: unknown) => Promise; + activity?: ChannelActivity; + routing?: ChannelRouting; + reply?: ChannelReply; + [key: string]: unknown; + } + + /** + * 插件运行时接口 + * 注意:channel 属性设为 any 是因为 SDK 内部类型非常复杂, + * 且会随 SDK 版本变化。实际使用时 SDK 会提供正确的运行时类型。 + */ + export interface PluginRuntime { + /** 获取当前配置 */ + getConfig(): OpenClawConfig; + /** 更新配置 */ + setConfig(config: OpenClawConfig): void; + /** 获取数据目录路径 */ + getDataDir(): string; + /** Channel 接口 - 使用 any 类型以兼容 SDK 内部复杂类型 */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + channel?: any; + /** 日志函数 */ + log: { + info: (message: string, ...args: unknown[]) => void; + warn: (message: string, ...args: unknown[]) => void; + error: (message: string, ...args: unknown[]) => void; + debug: (message: string, ...args: unknown[]) => void; + }; + /** 其他运行时方法 */ + [key: string]: unknown; + } + + // ============ 插件 API ============ + + /** + * OpenClaw 插件 API + */ + export interface OpenClawPluginApi { + /** 运行时实例 */ + runtime: PluginRuntime; + /** 注册频道 */ + registerChannel(options: { plugin: ChannelPlugin }): void; + /** 其他 API 方法 */ + [key: string]: unknown; + } + + // ============ 插件配置 Schema ============ + + /** + * 空的插件配置 Schema + */ + export function emptyPluginConfigSchema(): unknown; + + // ============ 频道插件 ============ + + /** + * 频道插件 Meta 信息 + */ + export interface ChannelPluginMeta { + id: string; + label: string; + selectionLabel?: string; + docsPath?: string; + blurb?: string; + order?: number; + [key: string]: unknown; + } + + /** + * 频道插件能力配置 + */ + export interface ChannelPluginCapabilities { + chatTypes?: ("direct" | "group" | "channel")[]; + media?: boolean; + reactions?: boolean; + threads?: boolean; + blockStreaming?: boolean; + [key: string]: unknown; + } + + /** + * 账户描述 + */ + export interface AccountDescription { + accountId: string; + name?: string; + enabled: boolean; + configured: boolean; + tokenSource?: string; + [key: string]: unknown; + } + + /** + * 频道插件配置接口(泛型) + */ + export interface ChannelPluginConfig { + listAccountIds: (cfg: OpenClawConfig) => string[]; + resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) => TAccount; + defaultAccountId: (cfg: OpenClawConfig) => string; + setAccountEnabled?: (ctx: { cfg: OpenClawConfig; accountId: string; enabled: boolean }) => OpenClawConfig; + deleteAccount?: (ctx: { cfg: OpenClawConfig; accountId: string }) => OpenClawConfig; + isConfigured?: (account: TAccount | undefined) => boolean; + describeAccount?: (account: TAccount | undefined) => AccountDescription; + [key: string]: unknown; + } + + /** + * Setup 输入参数(扩展类型以支持 QQBot 特定字段) + */ + export interface SetupInput { + token?: string; + tokenFile?: string; + useEnv?: boolean; + name?: string; + imageServerBaseUrl?: string; + [key: string]: unknown; + } + + /** + * 频道插件 Setup 接口 + */ + export interface ChannelPluginSetup { + resolveAccountId?: (ctx: { accountId?: string }) => string; + applyAccountName?: (ctx: { cfg: OpenClawConfig; accountId: string; name: string }) => OpenClawConfig; + validateInput?: (ctx: { input: SetupInput }) => string | null; + applyConfig?: (ctx: { cfg: OpenClawConfig; accountId: string; input: SetupInput }) => OpenClawConfig; + applyAccountConfig?: (ctx: { cfg: OpenClawConfig; accountId: string; input: SetupInput }) => OpenClawConfig; + [key: string]: unknown; + } + + /** + * 消息目标解析结果 + */ + export interface NormalizeTargetResult { + ok: boolean; + to?: string; + error?: string; + } + + /** + * 目标解析器 + */ + export interface TargetResolver { + looksLikeId?: (id: string) => boolean; + hint?: string; + } + + /** + * 频道插件 Messaging 接口 + */ + export interface ChannelPluginMessaging { + normalizeTarget?: (target: string) => NormalizeTargetResult; + targetResolver?: TargetResolver; + [key: string]: unknown; + } + + /** + * 发送文本结果 + */ + export interface SendTextResult { + channel: string; + messageId?: string; + error?: Error; + } + + /** + * 发送文本上下文 + */ + export interface SendTextContext { + to: string; + text: string; + accountId?: string; + replyToId?: string; + cfg: OpenClawConfig; + } + + /** + * 发送媒体上下文 + */ + export interface SendMediaContext { + to: string; + text?: string; + mediaUrl?: string; + accountId?: string; + replyToId?: string; + cfg: OpenClawConfig; + } + + /** + * 频道插件 Outbound 接口 + */ + export interface ChannelPluginOutbound { + deliveryMode?: "direct" | "queued"; + chunker?: (text: string, limit: number) => string[]; + chunkerMode?: "markdown" | "plain"; + textChunkLimit?: number; + sendText?: (ctx: SendTextContext) => Promise; + sendMedia?: (ctx: SendMediaContext) => Promise; + [key: string]: unknown; + } + + /** + * 账户状态 + */ + export interface AccountStatus { + running?: boolean; + connected?: boolean; + lastConnectedAt?: number; + lastError?: string; + [key: string]: unknown; + } + + /** + * Gateway 启动上下文 + */ + export interface GatewayStartContext { + account: TAccount; + accountId: string; + abortSignal: AbortSignal; + cfg: OpenClawConfig; + log?: { + info: (msg: string) => void; + warn: (msg: string) => void; + error: (msg: string) => void; + debug: (msg: string) => void; + }; + getStatus: () => AccountStatus; + setStatus: (status: AccountStatus) => void; + [key: string]: unknown; + } + + /** + * Gateway 登出上下文 + */ + export interface GatewayLogoutContext { + accountId: string; + cfg: OpenClawConfig; + [key: string]: unknown; + } + + /** + * Gateway 登出结果 + */ + export interface GatewayLogoutResult { + ok: boolean; + cleared: boolean; + updatedConfig?: OpenClawConfig; + error?: string; + } + + /** + * 频道插件 Gateway 接口 + */ + export interface ChannelPluginGateway { + startAccount?: (ctx: GatewayStartContext) => Promise; + logoutAccount?: (ctx: GatewayLogoutContext) => Promise; + [key: string]: unknown; + } + + /** + * 频道插件接口(泛型) + */ + export interface ChannelPlugin { + /** 插件 ID */ + id: string; + /** 插件 Meta 信息 */ + meta?: ChannelPluginMeta; + /** 插件版本 */ + version?: string; + /** 插件能力 */ + capabilities?: ChannelPluginCapabilities; + /** 重载配置 */ + reload?: { configPrefixes?: string[] }; + /** Onboarding 适配器 */ + onboarding?: ChannelOnboardingAdapter; + /** 配置方法 */ + config?: ChannelPluginConfig; + /** Setup 方法 */ + setup?: ChannelPluginSetup; + /** Messaging 配置 */ + messaging?: ChannelPluginMessaging; + /** Outbound 配置 */ + outbound?: ChannelPluginOutbound; + /** Gateway 配置 */ + gateway?: ChannelPluginGateway; + /** 启动函数 */ + start?: (runtime: PluginRuntime) => void | Promise; + /** 停止函数 */ + stop?: () => void | Promise; + /** deliver 函数 - 发送消息 */ + deliver?: (ctx: unknown) => Promise; + /** 其他插件属性 */ + [key: string]: unknown; + } + + // ============ Onboarding 类型 ============ + + /** + * Onboarding 状态结果 + */ + export interface ChannelOnboardingStatus { + channel?: string; + configured: boolean; + statusLines?: string[]; + selectionHint?: string; + quickstartScore?: number; + [key: string]: unknown; + } + + /** + * Onboarding 状态字符串枚举(部分 API 使用) + */ + export type ChannelOnboardingStatusString = + | "not-configured" + | "configured" + | "connected" + | "error"; + + /** + * Onboarding 状态上下文 + */ + export interface ChannelOnboardingStatusContext { + /** 当前配置 */ + config: OpenClawConfig; + /** 账户 ID */ + accountId?: string; + /** Prompter */ + prompter?: unknown; + /** 其他上下文 */ + [key: string]: unknown; + } + + /** + * Onboarding 配置上下文 + */ + export interface ChannelOnboardingConfigureContext { + /** 当前配置 */ + config: OpenClawConfig; + /** 账户 ID */ + accountId?: string; + /** 输入参数 */ + input?: Record; + /** Prompter */ + prompter?: unknown; + /** 其他上下文 */ + [key: string]: unknown; + } + + /** + * Onboarding 结果 + */ + export interface ChannelOnboardingResult { + /** 是否成功 */ + success: boolean; + /** 更新后的配置 */ + config?: OpenClawConfig; + /** 错误信息 */ + error?: string; + /** 消息 */ + message?: string; + /** 其他结果字段 */ + [key: string]: unknown; + } + + /** + * Onboarding 适配器接口 + */ + export interface ChannelOnboardingAdapter { + /** 获取状态 */ + getStatus?: (ctx: ChannelOnboardingStatusContext) => ChannelOnboardingStatus | Promise; + /** 配置函数 */ + configure?: (ctx: ChannelOnboardingConfigureContext) => ChannelOnboardingResult | Promise; + /** 其他适配器方法 */ + [key: string]: unknown; + } + + // ============ 配置辅助函数 ============ + + /** + * 将账户名称应用到频道配置段 + */ + export function applyAccountNameToChannelSection(ctx: { + cfg: OpenClawConfig; + channelKey: string; + accountId: string; + name: string; + }): OpenClawConfig; + + /** + * 从配置段删除账户 + */ + export function deleteAccountFromConfigSection(ctx: { + cfg: OpenClawConfig; + sectionKey: string; + accountId: string; + clearBaseFields?: string[]; + }): OpenClawConfig; + + /** + * 设置账户启用状态 + */ + export function setAccountEnabledInConfigSection(ctx: { + cfg: OpenClawConfig; + sectionKey: string; + accountId: string; + enabled: boolean; + allowTopLevel?: boolean; + }): OpenClawConfig; + + // ============ 其他导出 ============ + + /** 默认账户 ID 常量 */ + export const DEFAULT_ACCOUNT_ID: string; + + /** 规范化账户 ID */ + export function normalizeAccountId(accountId: string | undefined | null): string; +} diff --git a/src/outbound.ts b/src/outbound.ts index 1d9510b..2a00889 100644 --- a/src/outbound.ts +++ b/src/outbound.ts @@ -16,6 +16,133 @@ import { type StreamMessageResponse, } from "./api.js"; +// ============ 消息回复限流器 ============ +// 同一 message_id 1小时内最多回复 4 次,超过 1 小时无法被动回复(需改为主动消息) +const MESSAGE_REPLY_LIMIT = 4; +const MESSAGE_REPLY_TTL = 60 * 60 * 1000; // 1小时 + +interface MessageReplyRecord { + count: number; + firstReplyAt: number; +} + +const messageReplyTracker = new Map(); + +/** 限流检查结果 */ +export interface ReplyLimitResult { + /** 是否允许被动回复 */ + allowed: boolean; + /** 剩余被动回复次数 */ + remaining: number; + /** 是否需要降级为主动消息(超期或超过次数) */ + shouldFallbackToProactive: boolean; + /** 降级原因 */ + fallbackReason?: "expired" | "limit_exceeded"; + /** 提示消息 */ + message?: string; +} + +/** + * 检查是否可以回复该消息(限流检查) + * @param messageId 消息ID + * @returns ReplyLimitResult 限流检查结果 + */ +export function checkMessageReplyLimit(messageId: string): ReplyLimitResult { + const now = Date.now(); + const record = messageReplyTracker.get(messageId); + + // 清理过期记录(定期清理,避免内存泄漏) + if (messageReplyTracker.size > 10000) { + for (const [id, rec] of messageReplyTracker) { + if (now - rec.firstReplyAt > MESSAGE_REPLY_TTL) { + messageReplyTracker.delete(id); + } + } + } + + // 新消息,首次回复 + if (!record) { + return { + allowed: true, + remaining: MESSAGE_REPLY_LIMIT, + shouldFallbackToProactive: false, + }; + } + + // 检查是否超过1小时(message_id 过期) + if (now - record.firstReplyAt > MESSAGE_REPLY_TTL) { + // 超过1小时,被动回复不可用,需要降级为主动消息 + return { + allowed: false, + remaining: 0, + shouldFallbackToProactive: true, + fallbackReason: "expired", + message: `消息已超过1小时有效期,将使用主动消息发送`, + }; + } + + // 检查是否超过回复次数限制 + const remaining = MESSAGE_REPLY_LIMIT - record.count; + if (remaining <= 0) { + return { + allowed: false, + remaining: 0, + shouldFallbackToProactive: true, + fallbackReason: "limit_exceeded", + message: `该消息已达到1小时内最大回复次数(${MESSAGE_REPLY_LIMIT}次),将使用主动消息发送`, + }; + } + + return { + allowed: true, + remaining, + shouldFallbackToProactive: false, + }; +} + +/** + * 记录一次消息回复 + * @param messageId 消息ID + */ +export function recordMessageReply(messageId: string): void { + const now = Date.now(); + const record = messageReplyTracker.get(messageId); + + if (!record) { + messageReplyTracker.set(messageId, { count: 1, firstReplyAt: now }); + } else { + // 检查是否过期,过期则重新计数 + if (now - record.firstReplyAt > MESSAGE_REPLY_TTL) { + messageReplyTracker.set(messageId, { count: 1, firstReplyAt: now }); + } else { + record.count++; + } + } + console.log(`[qqbot] recordMessageReply: ${messageId}, count=${messageReplyTracker.get(messageId)?.count}`); +} + +/** + * 获取消息回复统计信息 + */ +export function getMessageReplyStats(): { trackedMessages: number; totalReplies: number } { + let totalReplies = 0; + for (const record of messageReplyTracker.values()) { + totalReplies += record.count; + } + return { trackedMessages: messageReplyTracker.size, totalReplies }; +} + +/** + * 获取消息回复限制配置(供外部查询) + */ +export function getMessageReplyConfig(): { limit: number; ttlMs: number; ttlHours: number } { + return { + limit: MESSAGE_REPLY_LIMIT, + ttlMs: MESSAGE_REPLY_TTL, + ttlHours: MESSAGE_REPLY_TTL / (60 * 60 * 1000), + }; +} + export interface OutboundContext { to: string; text: string; @@ -211,14 +338,61 @@ function parseTarget(to: string): { type: "c2c" | "group" | "channel"; id: strin /** * 发送文本消息 - * - 有 replyToId: 被动回复,无配额限制 + * - 有 replyToId: 被动回复,1小时内最多回复4次 * - 无 replyToId: 主动发送,有配额限制(每月4条/用户/群) + * + * 注意: + * 1. 主动消息(无 replyToId)必须有消息内容,不支持流式发送 + * 2. 当被动回复不可用(超期或超过次数)时,自动降级为主动消息 */ export async function sendText(ctx: OutboundContext): Promise { - const { to, text, replyToId, account } = ctx; + const { to, text, account } = ctx; + let { replyToId } = ctx; + let fallbackToProactive = false; console.log("[qqbot] sendText ctx:", JSON.stringify({ to, text: text?.slice(0, 50), replyToId, accountId: account.accountId }, null, 2)); + // ============ 消息回复限流检查 ============ + // 如果有 replyToId,检查是否可以被动回复 + if (replyToId) { + const limitCheck = checkMessageReplyLimit(replyToId); + + if (!limitCheck.allowed) { + // 检查是否需要降级为主动消息 + if (limitCheck.shouldFallbackToProactive) { + console.warn(`[qqbot] sendText: 被动回复不可用,降级为主动消息 - ${limitCheck.message}`); + fallbackToProactive = true; + replyToId = null; // 清除 replyToId,改为主动消息 + } else { + // 不应该发生,但作为保底 + console.error(`[qqbot] sendText: 消息回复被限流但未设置降级 - ${limitCheck.message}`); + return { + channel: "qqbot", + error: limitCheck.message + }; + } + } else { + console.log(`[qqbot] sendText: 消息 ${replyToId} 剩余被动回复次数: ${limitCheck.remaining}/${MESSAGE_REPLY_LIMIT}`); + } + } + + // ============ 主动消息校验(参考 Telegram 机制) ============ + // 如果是主动消息(无 replyToId 或降级后),必须有消息内容 + if (!replyToId) { + if (!text || text.trim().length === 0) { + console.error("[qqbot] sendText error: 主动消息的内容不能为空 (text is empty)"); + return { + channel: "qqbot", + error: "主动消息必须有内容 (--message 参数不能为空)" + }; + } + if (fallbackToProactive) { + console.log(`[qqbot] sendText: [降级] 发送主动消息到 ${to}, 内容长度: ${text.length}`); + } else { + console.log(`[qqbot] sendText: 发送主动消息到 ${to}, 内容长度: ${text.length}`); + } + } + if (!account.appId || !account.clientSecret) { return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; } @@ -246,12 +420,18 @@ export async function sendText(ctx: OutboundContext): Promise { // 有 replyToId,使用被动回复接口 if (target.type === "c2c") { const result = await sendC2CMessage(accessToken, target.id, text, replyToId); + // 记录回复次数 + recordMessageReply(replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } else if (target.type === "group") { const result = await sendGroupMessage(accessToken, target.id, text, replyToId); + // 记录回复次数 + recordMessageReply(replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } else { const result = await sendChannelMessage(accessToken, target.id, text, replyToId); + // 记录回复次数 + recordMessageReply(replyToId); return { channel: "qqbot", messageId: result.id, timestamp: result.timestamp }; } } catch (err) { diff --git a/src/proactive.ts b/src/proactive.ts new file mode 100644 index 0000000..2393bf3 --- /dev/null +++ b/src/proactive.ts @@ -0,0 +1,528 @@ +/** + * QQ Bot 主动发送消息模块 + * + * 该模块提供以下能力: + * 1. 记录已知用户(曾与机器人交互过的用户) + * 2. 主动发送消息给用户或群组 + * 3. 查询已知用户列表 + */ + +import * as fs from "node:fs"; +import * as path from "node:path"; +import type { ResolvedQQBotAccount } from "./types.js"; + +// ============ 类型定义(本地) ============ + +/** + * 已知用户信息 + */ +export interface KnownUser { + type: "c2c" | "group" | "channel"; + openid: string; + accountId: string; + nickname?: string; + firstInteractionAt: number; + lastInteractionAt: number; +} + +/** + * 主动发送消息选项 + */ +export interface ProactiveSendOptions { + to: string; + text: string; + type?: "c2c" | "group" | "channel"; + imageUrl?: string; + accountId?: string; +} + +/** + * 主动发送消息结果 + */ +export interface ProactiveSendResult { + success: boolean; + messageId?: string; + timestamp?: number | string; + error?: string; +} + +/** + * 列出已知用户选项 + */ +export interface ListKnownUsersOptions { + type?: "c2c" | "group" | "channel"; + accountId?: string; + sortByLastInteraction?: boolean; + limit?: number; +} +import { + getAccessToken, + sendProactiveC2CMessage, + sendProactiveGroupMessage, + sendChannelMessage, + sendC2CImageMessage, + sendGroupImageMessage, +} from "./api.js"; +import { resolveQQBotAccount } from "./config.js"; +import type { OpenClawConfig } from "openclaw/plugin-sdk"; + +// ============ 用户存储管理 ============ + +/** + * 已知用户存储 + * 使用简单的 JSON 文件存储,保存在 clawd 目录下 + */ +const STORAGE_DIR = path.join(process.env.HOME || "/home/ubuntu", "clawd", "qqbot-data"); +const KNOWN_USERS_FILE = path.join(STORAGE_DIR, "known-users.json"); + +// 内存缓存 +let knownUsersCache: Map | null = null; +let cacheLastModified = 0; + +/** + * 确保存储目录存在 + */ +function ensureStorageDir(): void { + if (!fs.existsSync(STORAGE_DIR)) { + fs.mkdirSync(STORAGE_DIR, { recursive: true }); + } +} + +/** + * 生成用户唯一键 + */ +function getUserKey(type: string, openid: string, accountId: string): string { + return `${accountId}:${type}:${openid}`; +} + +/** + * 从文件加载已知用户 + */ +function loadKnownUsers(): Map { + if (knownUsersCache !== null) { + // 检查文件是否被修改 + try { + const stat = fs.statSync(KNOWN_USERS_FILE); + if (stat.mtimeMs <= cacheLastModified) { + return knownUsersCache; + } + } catch { + // 文件不存在,使用缓存 + return knownUsersCache; + } + } + + const users = new Map(); + + try { + if (fs.existsSync(KNOWN_USERS_FILE)) { + const data = fs.readFileSync(KNOWN_USERS_FILE, "utf-8"); + const parsed = JSON.parse(data) as KnownUser[]; + for (const user of parsed) { + const key = getUserKey(user.type, user.openid, user.accountId); + users.set(key, user); + } + cacheLastModified = fs.statSync(KNOWN_USERS_FILE).mtimeMs; + } + } catch (err) { + console.error(`[qqbot:proactive] Failed to load known users: ${err}`); + } + + knownUsersCache = users; + return users; +} + +/** + * 保存已知用户到文件 + */ +function saveKnownUsers(users: Map): void { + try { + ensureStorageDir(); + const data = Array.from(users.values()); + fs.writeFileSync(KNOWN_USERS_FILE, JSON.stringify(data, null, 2), "utf-8"); + cacheLastModified = Date.now(); + knownUsersCache = users; + } catch (err) { + console.error(`[qqbot:proactive] Failed to save known users: ${err}`); + } +} + +/** + * 记录一个已知用户(当收到用户消息时调用) + * + * @param user - 用户信息 + */ +export function recordKnownUser(user: Omit): void { + const users = loadKnownUsers(); + const key = getUserKey(user.type, user.openid, user.accountId); + + const existing = users.get(key); + const now = user.lastInteractionAt || Date.now(); + + users.set(key, { + ...user, + lastInteractionAt: now, + firstInteractionAt: existing?.firstInteractionAt ?? now, + // 更新昵称(如果有新的) + nickname: user.nickname || existing?.nickname, + }); + + saveKnownUsers(users); + console.log(`[qqbot:proactive] Recorded user: ${key}`); +} + +/** + * 获取一个已知用户 + * + * @param type - 用户类型 + * @param openid - 用户 openid + * @param accountId - 账户 ID + */ +export function getKnownUser(type: string, openid: string, accountId: string): KnownUser | undefined { + const users = loadKnownUsers(); + const key = getUserKey(type, openid, accountId); + return users.get(key); +} + +/** + * 列出已知用户 + * + * @param options - 过滤选项 + */ +export function listKnownUsers(options?: ListKnownUsersOptions): KnownUser[] { + const users = loadKnownUsers(); + let result = Array.from(users.values()); + + // 过滤类型 + if (options?.type) { + result = result.filter(u => u.type === options.type); + } + + // 过滤账户 + if (options?.accountId) { + result = result.filter(u => u.accountId === options.accountId); + } + + // 排序 + if (options?.sortByLastInteraction !== false) { + result.sort((a, b) => b.lastInteractionAt - a.lastInteractionAt); + } + + // 限制数量 + if (options?.limit && options.limit > 0) { + result = result.slice(0, options.limit); + } + + return result; +} + +/** + * 删除一个已知用户 + * + * @param type - 用户类型 + * @param openid - 用户 openid + * @param accountId - 账户 ID + */ +export function removeKnownUser(type: string, openid: string, accountId: string): boolean { + const users = loadKnownUsers(); + const key = getUserKey(type, openid, accountId); + const deleted = users.delete(key); + if (deleted) { + saveKnownUsers(users); + } + return deleted; +} + +/** + * 清除所有已知用户 + * + * @param accountId - 可选,只清除指定账户的用户 + */ +export function clearKnownUsers(accountId?: string): number { + const users = loadKnownUsers(); + let count = 0; + + if (accountId) { + for (const [key, user] of users) { + if (user.accountId === accountId) { + users.delete(key); + count++; + } + } + } else { + count = users.size; + users.clear(); + } + + if (count > 0) { + saveKnownUsers(users); + } + return count; +} + +// ============ 主动发送消息 ============ + +/** + * 主动发送消息(带配置解析) + * 注意:与 outbound.ts 中的 sendProactiveMessage 不同,这个函数接受 OpenClawConfig 并自动解析账户 + * + * @param options - 发送选项 + * @param cfg - OpenClaw 配置 + * @returns 发送结果 + * + * @example + * ```typescript + * // 发送私聊消息 + * const result = await sendProactive({ + * to: "E7A8F3B2C1D4E5F6A7B8C9D0E1F2A3B4", // 用户 openid + * text: "你好!这是一条主动消息", + * type: "c2c", + * }, cfg); + * + * // 发送群聊消息 + * const result = await sendProactive({ + * to: "A1B2C3D4E5F6A7B8", // 群组 openid + * text: "群公告:今天有活动", + * type: "group", + * }, cfg); + * + * // 发送带图片的消息 + * const result = await sendProactive({ + * to: "E7A8F3B2C1D4E5F6A7B8C9D0E1F2A3B4", + * text: "看看这张图片", + * imageUrl: "https://example.com/image.png", + * type: "c2c", + * }, cfg); + * ``` + */ +export async function sendProactive( + options: ProactiveSendOptions, + cfg: OpenClawConfig +): Promise { + const { to, text, type = "c2c", imageUrl, accountId = "default" } = options; + + // 解析账户配置 + const account = resolveQQBotAccount(cfg, accountId); + + if (!account.appId || !account.clientSecret) { + return { + success: false, + error: "QQBot not configured (missing appId or clientSecret)", + }; + } + + try { + const accessToken = await getAccessToken(account.appId, account.clientSecret); + + // 如果有图片,先发送图片 + if (imageUrl) { + try { + if (type === "c2c") { + await sendC2CImageMessage(accessToken, to, imageUrl, undefined, undefined); + } else if (type === "group") { + await sendGroupImageMessage(accessToken, to, imageUrl, undefined, undefined); + } + console.log(`[qqbot:proactive] Sent image to ${type}:${to}`); + } catch (err) { + console.error(`[qqbot:proactive] Failed to send image: ${err}`); + // 图片发送失败不影响文本发送 + } + } + + // 发送文本消息 + let result: { id: string; timestamp: number | string }; + + if (type === "c2c") { + result = await sendProactiveC2CMessage(accessToken, to, text); + } else if (type === "group") { + result = await sendProactiveGroupMessage(accessToken, to, text); + } else if (type === "channel") { + // 频道消息需要 channel_id,这里暂时不支持主动发送 + return { + success: false, + error: "Channel proactive messages are not supported. Please use group or c2c.", + }; + } else { + return { + success: false, + error: `Unknown message type: ${type}`, + }; + } + + console.log(`[qqbot:proactive] Sent message to ${type}:${to}, id: ${result.id}`); + + return { + success: true, + messageId: result.id, + timestamp: result.timestamp, + }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`[qqbot:proactive] Failed to send message: ${message}`); + + return { + success: false, + error: message, + }; + } +} + +/** + * 批量发送主动消息 + * + * @param recipients - 接收者列表(openid 数组) + * @param text - 消息内容 + * @param type - 消息类型 + * @param cfg - OpenClaw 配置 + * @param accountId - 账户 ID + * @returns 发送结果列表 + */ +export async function sendBulkProactiveMessage( + recipients: string[], + text: string, + type: "c2c" | "group", + cfg: OpenClawConfig, + accountId = "default" +): Promise> { + const results: Array<{ to: string; result: ProactiveSendResult }> = []; + + for (const to of recipients) { + const result = await sendProactive({ to, text, type, accountId }, cfg); + results.push({ to, result }); + + // 添加延迟,避免频率限制 + await new Promise(resolve => setTimeout(resolve, 500)); + } + + return results; +} + +/** + * 发送消息给所有已知用户 + * + * @param text - 消息内容 + * @param cfg - OpenClaw 配置 + * @param options - 过滤选项 + * @returns 发送结果统计 + */ +export async function broadcastMessage( + text: string, + cfg: OpenClawConfig, + options?: { + type?: "c2c" | "group"; + accountId?: string; + limit?: number; + } +): Promise<{ + total: number; + success: number; + failed: number; + results: Array<{ to: string; result: ProactiveSendResult }>; +}> { + const users = listKnownUsers({ + type: options?.type, + accountId: options?.accountId, + limit: options?.limit, + sortByLastInteraction: true, + }); + + // 过滤掉频道用户(不支持主动发送) + const validUsers = users.filter(u => u.type === "c2c" || u.type === "group"); + + const results: Array<{ to: string; result: ProactiveSendResult }> = []; + let success = 0; + let failed = 0; + + for (const user of validUsers) { + const result = await sendProactive({ + to: user.openid, + text, + type: user.type as "c2c" | "group", + accountId: user.accountId, + }, cfg); + + results.push({ to: user.openid, result }); + + if (result.success) { + success++; + } else { + failed++; + } + + // 添加延迟,避免频率限制 + await new Promise(resolve => setTimeout(resolve, 500)); + } + + return { + total: validUsers.length, + success, + failed, + results, + }; +} + +// ============ 辅助函数 ============ + +/** + * 根据账户配置直接发送主动消息(不需要 cfg) + * + * @param account - 已解析的账户配置 + * @param to - 目标 openid + * @param text - 消息内容 + * @param type - 消息类型 + */ +export async function sendProactiveMessageDirect( + account: ResolvedQQBotAccount, + to: string, + text: string, + type: "c2c" | "group" = "c2c" +): Promise { + if (!account.appId || !account.clientSecret) { + return { + success: false, + error: "QQBot not configured (missing appId or clientSecret)", + }; + } + + try { + const accessToken = await getAccessToken(account.appId, account.clientSecret); + + let result: { id: string; timestamp: number | string }; + + if (type === "c2c") { + result = await sendProactiveC2CMessage(accessToken, to, text); + } else { + result = await sendProactiveGroupMessage(accessToken, to, text); + } + + return { + success: true, + messageId: result.id, + timestamp: result.timestamp, + }; + } catch (err) { + return { + success: false, + error: err instanceof Error ? err.message : String(err), + }; + } +} + +/** + * 获取已知用户统计 + */ +export function getKnownUsersStats(accountId?: string): { + total: number; + c2c: number; + group: number; + channel: number; +} { + const users = listKnownUsers({ accountId }); + + return { + total: users.length, + c2c: users.filter(u => u.type === "c2c").length, + group: users.filter(u => u.type === "group").length, + channel: users.filter(u => u.type === "channel").length, + }; +} diff --git a/src/session-store.ts b/src/session-store.ts new file mode 100644 index 0000000..20491d0 --- /dev/null +++ b/src/session-store.ts @@ -0,0 +1,292 @@ +/** + * Session 持久化存储 + * 将 WebSocket 连接状态(sessionId、lastSeq)持久化到文件 + * 支持进程重启后通过 Resume 机制快速恢复连接 + */ + +import fs from "node:fs"; +import path from "node:path"; + +// Session 状态接口 +export interface SessionState { + /** WebSocket Session ID */ + sessionId: string | null; + /** 最后收到的消息序号 */ + lastSeq: number | null; + /** 上次连接成功的时间戳 */ + lastConnectedAt: number; + /** 上次成功的权限级别索引 */ + intentLevelIndex: number; + /** 关联的机器人账户 ID */ + accountId: string; + /** 保存时间 */ + savedAt: number; +} + +// Session 文件目录 +const SESSION_DIR = path.join( + process.env.HOME || "/tmp", + "clawd", + "qqbot-data" +); + +// Session 过期时间(5分钟)- Resume 要求在断开后一定时间内恢复 +const SESSION_EXPIRE_TIME = 5 * 60 * 1000; + +// 写入节流时间(避免频繁写入) +const SAVE_THROTTLE_MS = 1000; + +// 每个账户的节流状态 +const throttleState = new Map | null; +}>(); + +/** + * 确保目录存在 + */ +function ensureDir(): void { + if (!fs.existsSync(SESSION_DIR)) { + fs.mkdirSync(SESSION_DIR, { recursive: true }); + } +} + +/** + * 获取 Session 文件路径 + */ +function getSessionPath(accountId: string): string { + // 清理 accountId 中的特殊字符 + const safeId = accountId.replace(/[^a-zA-Z0-9_-]/g, "_"); + return path.join(SESSION_DIR, `session-${safeId}.json`); +} + +/** + * 加载 Session 状态 + * @param accountId 账户 ID + * @returns Session 状态,如果不存在或已过期返回 null + */ +export function loadSession(accountId: string): SessionState | null { + const filePath = getSessionPath(accountId); + + try { + if (!fs.existsSync(filePath)) { + return null; + } + + const data = fs.readFileSync(filePath, "utf-8"); + const state = JSON.parse(data) as SessionState; + + // 检查是否过期 + const now = Date.now(); + if (now - state.savedAt > SESSION_EXPIRE_TIME) { + console.log(`[session-store] Session expired for ${accountId}, age: ${Math.round((now - state.savedAt) / 1000)}s`); + // 删除过期文件 + try { + fs.unlinkSync(filePath); + } catch { + // 忽略删除错误 + } + return null; + } + + // 验证必要字段 + if (!state.sessionId || state.lastSeq === null || state.lastSeq === undefined) { + console.log(`[session-store] Invalid session data for ${accountId}`); + return null; + } + + console.log(`[session-store] Loaded session for ${accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}, age=${Math.round((now - state.savedAt) / 1000)}s`); + return state; + } catch (err) { + console.error(`[session-store] Failed to load session for ${accountId}: ${err}`); + return null; + } +} + +/** + * 保存 Session 状态(带节流,避免频繁写入) + * @param state Session 状态 + */ +export function saveSession(state: SessionState): void { + const { accountId } = state; + + // 获取或初始化节流状态 + let throttle = throttleState.get(accountId); + if (!throttle) { + throttle = { + pendingState: null, + lastSaveTime: 0, + throttleTimer: null, + }; + throttleState.set(accountId, throttle); + } + + const now = Date.now(); + const timeSinceLastSave = now - throttle.lastSaveTime; + + // 如果距离上次保存时间足够长,立即保存 + if (timeSinceLastSave >= SAVE_THROTTLE_MS) { + doSaveSession(state); + throttle.lastSaveTime = now; + throttle.pendingState = null; + + // 清除待定的节流定时器 + if (throttle.throttleTimer) { + clearTimeout(throttle.throttleTimer); + throttle.throttleTimer = null; + } + } else { + // 记录待保存的状态 + throttle.pendingState = state; + + // 如果没有设置定时器,设置一个 + if (!throttle.throttleTimer) { + const delay = SAVE_THROTTLE_MS - timeSinceLastSave; + throttle.throttleTimer = setTimeout(() => { + const t = throttleState.get(accountId); + if (t && t.pendingState) { + doSaveSession(t.pendingState); + t.lastSaveTime = Date.now(); + t.pendingState = null; + } + if (t) { + t.throttleTimer = null; + } + }, delay); + } + } +} + +/** + * 实际执行保存操作 + */ +function doSaveSession(state: SessionState): void { + const filePath = getSessionPath(state.accountId); + + try { + ensureDir(); + + // 更新保存时间 + const stateToSave: SessionState = { + ...state, + savedAt: Date.now(), + }; + + fs.writeFileSync(filePath, JSON.stringify(stateToSave, null, 2), "utf-8"); + console.log(`[session-store] Saved session for ${state.accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}`); + } catch (err) { + console.error(`[session-store] Failed to save session for ${state.accountId}: ${err}`); + } +} + +/** + * 清除 Session 状态 + * @param accountId 账户 ID + */ +export function clearSession(accountId: string): void { + const filePath = getSessionPath(accountId); + + // 清除节流状态 + const throttle = throttleState.get(accountId); + if (throttle) { + if (throttle.throttleTimer) { + clearTimeout(throttle.throttleTimer); + } + throttleState.delete(accountId); + } + + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + console.log(`[session-store] Cleared session for ${accountId}`); + } + } catch (err) { + console.error(`[session-store] Failed to clear session for ${accountId}: ${err}`); + } +} + +/** + * 更新 lastSeq(轻量级更新) + * @param accountId 账户 ID + * @param lastSeq 最新的消息序号 + */ +export function updateLastSeq(accountId: string, lastSeq: number): void { + const existing = loadSession(accountId); + if (existing && existing.sessionId) { + saveSession({ + ...existing, + lastSeq, + }); + } +} + +/** + * 获取所有保存的 Session 状态 + */ +export function getAllSessions(): SessionState[] { + const sessions: SessionState[] = []; + + try { + ensureDir(); + const files = fs.readdirSync(SESSION_DIR); + + for (const file of files) { + if (file.startsWith("session-") && file.endsWith(".json")) { + const filePath = path.join(SESSION_DIR, file); + try { + const data = fs.readFileSync(filePath, "utf-8"); + const state = JSON.parse(data) as SessionState; + sessions.push(state); + } catch { + // 忽略解析错误 + } + } + } + } catch { + // 目录不存在等错误 + } + + return sessions; +} + +/** + * 清理过期的 Session 文件 + */ +export function cleanupExpiredSessions(): number { + let cleaned = 0; + + try { + ensureDir(); + const files = fs.readdirSync(SESSION_DIR); + const now = Date.now(); + + for (const file of files) { + if (file.startsWith("session-") && file.endsWith(".json")) { + const filePath = path.join(SESSION_DIR, file); + try { + const data = fs.readFileSync(filePath, "utf-8"); + const state = JSON.parse(data) as SessionState; + + if (now - state.savedAt > SESSION_EXPIRE_TIME) { + fs.unlinkSync(filePath); + cleaned++; + console.log(`[session-store] Cleaned expired session: ${file}`); + } + } catch { + // 忽略解析错误,但也删除损坏的文件 + try { + fs.unlinkSync(filePath); + cleaned++; + } catch { + // 忽略 + } + } + } + } + } catch { + // 目录不存在等错误 + } + + return cleaned; +} diff --git a/src/types.ts b/src/types.ts index b86eae9..0da4944 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,8 @@ export interface ResolvedQQBotAccount { imageServerBaseUrl?: string; /** 是否支持 markdown 消息 */ markdownSupport?: boolean; + /** 是否启用流式消息(仅 c2c 私聊支持),默认 true */ + streamEnabled?: boolean; config: QQBotAccountConfig; } @@ -43,6 +45,8 @@ export interface QQBotAccountConfig { imageServerBaseUrl?: string; /** 是否支持 markdown 消息,默认 true */ markdownSupport?: boolean; + /** 是否启用流式消息,默认 true(仅 c2c 私聊支持) */ + streamEnabled?: boolean; } /** diff --git a/upgrade-and-run.sh b/upgrade-and-run.sh index b47c279..fe70bd0 100755 --- a/upgrade-and-run.sh +++ b/upgrade-and-run.sh @@ -29,7 +29,7 @@ openclaw plugins install . echo "" echo "[3/4] 配置机器人通道..." # 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖 -QQBOT_TOKEN="${QQBOT_TOKEN:-xxx:xxx}" +QQBOT_TOKEN="${QQBOT_TOKEN:-102831906:tOtPvS0Y7gGqR2eGtXBqVBrYFxfO8sdO}" openclaw channels add --channel qqbot --token "$QQBOT_TOKEN" # 启用 markdown 支持 openclaw config set channels.qqbot.markdownSupport true From 62d52c836122356886d86fcbb14d2a65dc5e1acd Mon Sep 17 00:00:00 2001 From: rianli Date: Mon, 2 Feb 2026 00:51:21 +0800 Subject: [PATCH 03/14] =?UTF-8?q?feat(qqbot):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=8F=90=E9=86=92=E6=8A=80=E8=83=BD=E4=B8=8E=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E6=96=AD=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **提醒技能优化** - 优化 SKILL.md,新增 --system-prompt 参数说明 - 修复 AI 角色混淆问题(避免说"谢谢提醒") - 完善提醒触发时的 AI 角色指引 **流式消息智能断句** - 新增语义边界检测(句号/感叹号/问号等) - 首个分片在自然断句位置发送,避免奇怪的换行 - 支持 emoji 结尾识别 **其他优化** - 移除 metadata 中的 channels 限制 - 精简 console.md 内容 - 优化 onboarding 引导流程 --- console.md | 1442 ++++++++++-------------------------- openclaw.plugin.json | 2 +- skills/qqbot-cron/SKILL.md | 133 +++- src/api.ts | 11 +- src/channel.ts | 16 +- src/config.ts | 8 +- src/gateway.ts | 151 +++- src/onboarding.ts | 60 +- 8 files changed, 720 insertions(+), 1103 deletions(-) diff --git a/console.md b/console.md index fa74c12..26df470 100644 --- a/console.md +++ b/console.md @@ -1,1041 +1,401 @@ -08:27:48 [qqbot] [qqbot:default] Heartbeat sent -08:27:48 [qqbot] [qqbot:default] Received op=11 t=undefined -08:27:48 [qqbot] [qqbot:default] Heartbeat ACK -08:27:50 [ws] ⇄ res ✓ node.list 1ms id=0404fd79…e7be -08:27:55 [ws] ⇄ res ✓ node.list 1ms id=84c095a4…be49 -08:28:00 [ws] ⇄ res ✓ node.list 2ms id=33639de8…7fc3 -08:28:02 [ws] → event health seq=9 clients=5 presenceVersion=6 healthVersion=8 -08:28:02 [ws] → event tick seq=10 clients=5 dropIfSlow=true -08:28:03 [session-store] Saved session for default: sessionId=dbae1ca2-e53c-48f9-8721-fb93e5356c77, lastSeq=2 -08:28:03 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE -08:28:03 [known-users] Loaded 1 users from file -08:28:03 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 8 -08:28:03 [qqbot] [qqbot:default] Message enqueued, queue size: 1 -08:28:03 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 5分钟后提醒我喝水 -08:28:03 [qqbot] [qqbot:default] Stream enabled: true -08:28:03 [qqbot] [qqbot:default] Stream support: true (type=c2c, enabled=true) -08:28:03 [skills] plugin skill path not found (qqbot): /Users/lishoushuai/.openclaw/extensions/qqbot/qqbot-cron -08:28:03 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 -08:28:03 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=2 queueSize=0 -08:28:03 [diagnostic] lane enqueue: lane=main queueSize=1 -08:28:03 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 -08:28:03 [agent/embedded] embedded run start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot -08:28:03 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 -08:28:03 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 -08:28:03 [agent/embedded] embedded run prompt start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 -08:28:03 [agent/embedded] embedded run agent start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b -08:28:03 [ws] → event agent seq=11 clients=5 run=49d64fc4…e41b agent=main session=main stream=lifecycle aseq=1 phase=start -08:28:05 [ws] ⇄ res ✓ node.list 3ms id=15a727fe…d5a4 -08:28:08 [known-users] Saved 1 users to file -08:28:10 [ws] → event agent seq=12 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=2 text=我已经 -08:28:10 [ws] → event chat seq=13 clients=5 dropIfSlow=true -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=3, lastSentLength=0, streamBuffer.length=3, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=3 -08:28:10 [qqbot] [qqbot:default] Stream started, max duration: 180s -08:28:10 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:10 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:10 [qqbot-api] >>> Body: { -"markdown": { -"content": "我已经" -}, -"msg_type": 2, -"msg_seq": 69934422, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 0 -} -} -08:28:10 [ws] → event agent seq=14 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=3 text=我已经为您 -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=5, lastSentLength=0, streamBuffer.length=5, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=5 -08:28:10 [ws] → event agent seq=15 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=4 text=我已经为您设置了一个5分钟后 -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=14, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 -08:28:10 [ws] → event agent seq=16 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=5 text=我已经为您设置了一个5分钟后提醒喝水的定时 -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=21, lastSentLength=0, streamBuffer.length=21, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=21 -08:28:10 [ws] → event agent seq=17 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=6 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次 -08:28:10 [ws] → event chat seq=18 clients=5 dropIfSlow=true -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=28, lastSentLength=0, streamBuffer.length=28, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=28 -08:28:10 [ws] → event agent seq=19 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=7 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒 -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=36, lastSentLength=0, streamBuffer.length=36, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=36 -08:28:10 [ws] → event agent seq=20 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=8 text=我已经为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建: -08:28:10 [ws] → event chat seq=21 clients=5 dropIfSlow=true -08:28:10 [qqbot] [qqbot:default] handlePartialReply: fullText.length=43, lastSentLength=0, streamBuffer.length=43, isNewSegment=false -08:28:10 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=43 -08:28:11 [ws] ⇄ res ✓ node.list 27ms id=be4d996c…f5de - -08:28:11 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_45275505bcb24671aac9d040 -08:28:12 [qqbot-api] <<< Status: 200 OK -08:28:12 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:12 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "1e9be398831193b8e9a5da84f0296922" -} -08:28:12 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:12+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:12 [qqbot] [qqbot:default] Stream chunk sent, index: 0, isEnd: false, text: "我已经..." -08:28:12 [qqbot] [qqbot:default] Stream partial #1, increment: 3 chars, total: 3 chars -08:28:12 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:12 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:12 [qqbot-api] >>> Body: { -"markdown": { -"content": "为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建:" -}, -"msg_type": 2, -"msg_seq": 69934423, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 1, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:13 [qqbot-api] <<< Status: 200 OK -08:28:13 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:13 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "bd194a3c84af19b053c8c1c044c8fad9" -} -08:28:13 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:13+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:13 [qqbot] [qqbot:default] Stream chunk sent, index: 1, isEnd: false, text: "为您设置了一个5分钟后提醒喝水的定时任务。让我再次确认一下这个提醒已经成功创建:..." -08:28:13 [qqbot] [qqbot:default] Stream partial #2, increment: 40 chars, total: 43 chars -08:28:15 [ws] ⇄ res ✓ node.list 2ms id=02ca1f98…f997 -08:28:16 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 6s, since chunk: 3s) -08:28:16 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:16 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:16 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934424, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 2, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:16 [qqbot-api] <<< Status: 200 OK -08:28:16 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:16 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "2eeba590381c912b5f1eb324ec471901" -} -08:28:16 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:16+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:20 [ws] ⇄ res ✓ node.list 3ms id=9d64851e…eec3 -08:28:21 [ws] ← open remoteAddr=127.0.0.1 conn=41fc0659…f78a -08:28:21 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -08:28:21 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -08:28:21 [ws] → event health seq=22 clients=6 presenceVersion=6 healthVersion=9 -08:28:21 [ws] ⇄ res ✓ cron.list 0ms id=ee26e051…4691 -08:28:21 [ws] → close code=1005 reason= durationMs=45 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=ee26e051-977a-449c-b01d-38efaa2f4691 -08:28:21 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_45275505bcb24671aac9d040 -08:28:25 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=process toolCallId=call_360f71d0e0ed4cd0ab3001cf -08:28:25 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=process toolCallId=call_360f71d0e0ed4cd0ab3001cf -08:28:25 [ws] ⇄ res ✓ node.list 1ms conn=f395f45d…3099 id=c1d093d8…8763 -08:28:26 [qqbot] [qqbot:default] Sending keepalive #2 (elapsed: 16s, since chunk: 14s) -08:28:26 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:26 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:26 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934425, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 3, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:28 [qqbot-api] <<< Status: 200 OK -08:28:28 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:28 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "b687de25f82d1b9147dbd36e87178b92" -} -08:28:28 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:28+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:29 [qqbot] [qqbot:default] Heartbeat sent -08:28:29 [qqbot] [qqbot:default] Received op=11 t=undefined -08:28:29 [qqbot] [qqbot:default] Heartbeat ACK -08:28:30 [ws] → event agent seq=23 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=14 text=奇怪 -08:28:30 [ws] → event chat seq=24 clients=5 dropIfSlow=true -08:28:30 [qqbot] [qqbot:default] New segment detected! lastSentLength=43, newTextLength=2, lastSentText="我已经为您设置了一个5分钟后提醒喝水的定...", newText="奇怪..." -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=2, lastSentLength=0, streamBuffer.length=47, isNewSegment=true -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=2 -08:28:30 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:30 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:30 [qqbot-api] >>> Body: { -"markdown": { -"content": "奇怪" -}, -"msg_type": 2, -"msg_seq": 69934426, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 4, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:30 [ws] → event agent seq=25 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=15 text=奇怪,我刚才明明 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=8, lastSentLength=0, streamBuffer.length=53, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=8 -08:28:30 [ws] → event agent seq=26 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=16 text=奇怪,我刚才明明设置了 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=11, lastSentLength=0, streamBuffer.length=56, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=11 -08:28:30 [ws] → event agent seq=27 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=17 text=奇怪,我刚才明明设置了提醒 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=13, lastSentLength=0, streamBuffer.length=58, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=13 -08:28:30 [ws] → event agent seq=28 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=18 text=奇怪,我刚才明明设置了提醒,但现在查看却没有 -08:28:30 [ws] → event chat seq=29 clients=5 dropIfSlow=true -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=22, lastSentLength=0, streamBuffer.length=67, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=22 -08:28:30 [ws] → event agent seq=30 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=19 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=32, lastSentLength=0, streamBuffer.length=77, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=32 -08:28:30 [ws] → event agent seq=31 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=20 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=36, lastSentLength=0, streamBuffer.length=81, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=36 -08:28:30 [qqbot-api] <<< Status: 200 OK -08:28:30 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:30 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "db76f75156e1e7e8aec8ba21899869c9" -} -08:28:30 [ws] → event agent seq=32 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=21 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5 -08:28:30 [ws] → event chat seq=33 clients=5 dropIfSlow=true -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=43, lastSentLength=0, streamBuffer.length=88, isNewSegment=false -08:28:30 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=43 -08:28:30 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:30+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:30 [qqbot] [qqbot:default] Stream chunk sent, index: 4, isEnd: false, text: "奇怪..." -08:28:30 [qqbot] [qqbot:default] Stream partial #5, increment: 2 chars, total: 2 chars -08:28:30 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:30 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:30 [qqbot-api] >>> Body: { -"markdown": { -"content": ",我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5" -}, -"msg_type": 2, -"msg_seq": 69934427, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 5, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:30 [ws] → event agent seq=34 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=22 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5分钟后提醒喝水的任务 -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=53, lastSentLength=2, streamBuffer.length=98, isNewSegment=false -08:28:30 [ws] → event agent seq=35 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=23 text=奇怪,我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5分钟后提醒喝水的任务: -08:28:30 [qqbot] [qqbot:default] handlePartialReply: fullText.length=54, lastSentLength=2, streamBuffer.length=99, isNewSegment=false -08:28:30 [ws] ⇄ res ✓ node.list 3ms id=beba66e6…6e52 -08:28:31 [qqbot-api] <<< Status: 200 OK -08:28:31 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:31 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "70ff645913e01f94f4014c6679384b48" -} -08:28:31 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:31+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:31 [qqbot] [qqbot:default] Stream chunk sent, index: 5, isEnd: false, text: ",我刚才明明设置了提醒,但现在查看却没有找到任何cron任务。让我再为您设置一次5..." -08:28:31 [qqbot] [qqbot:default] Stream partial #6, increment: 41 chars, total: 43 chars -08:28:31 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:31 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:31 [qqbot-api] >>> Body: { -"markdown": { -"content": "分钟后提醒喝水的任务:" -}, -"msg_type": 2, -"msg_seq": 69934428, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 6, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:31 [qqbot-api] <<< Status: 200 OK -08:28:31 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:31 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "061e4f7d858be66d22dd886ce97225a0" -} -08:28:31 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:31+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:31 [qqbot] [qqbot:default] Stream chunk sent, index: 6, isEnd: false, text: "分钟后提醒喝水的任务:..." -08:28:31 [qqbot] [qqbot:default] Stream partial #7, increment: 11 chars, total: 54 chars -08:28:32 [ws] → event tick seq=36 clients=5 dropIfSlow=true -08:28:32 [ws] ⇄ res ✓ node.list 30ms conn=3f770fc7…c168 id=4645acb6…1a05 -08:28:32 [ws] ⇄ res ✓ node.list 7ms conn=d582275d…8cf7 id=f4425358…807e -08:28:32 [ws] ⇄ res ✓ node.list 9ms conn=30e84c3b…37f9 id=e90ce807…5806 -08:28:32 [ws] ⇄ res ✓ node.list 14ms conn=d84f2780…f80d id=9f6c21c9…5ec8 -08:28:32 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_912594aaec8b4dcfaae751f7 -08:28:34 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 24s, since chunk: 3s) -08:28:34 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:34 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:34 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934429, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 7, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:35 [qqbot-api] <<< Status: 200 OK -08:28:35 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:35 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "a28217b289890ad50f06343398486b54" -} -08:28:35 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:35+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:35 [ws] ⇄ res ✓ node.list 3ms conn=f395f45d…3099 id=0ff7cfa6…e3e9 -08:28:40 tools: exec failed stack: -Error: error: unknown option '--reply-to' - -Command exited with code 1 -at file:///Users/lishoushuai/.nvm/versions/node/v22.14.0/lib/node_modules/openclaw/dist/agents/bash-tools.exec.js:1146:32 -08:28:40 [tools] exec failed: error: unknown option '--reply-to' - -Command exited with code 1 -08:28:40 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_912594aaec8b4dcfaae751f7 -08:28:40 [ws] ⇄ res ✓ node.list 1ms id=10e301db…d453 -08:28:43 [ws] → event agent seq=37 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=27 text=我 -08:28:43 [ws] → event chat seq=38 clients=5 dropIfSlow=true -08:28:43 [qqbot] [qqbot:default] New segment detected! lastSentLength=54, newTextLength=1, lastSentText="奇怪,我刚才明明设置了提醒,但现在查看却...", newText="我..." -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=102, isNewSegment=true -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 -08:28:43 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:43 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:43 [qqbot-api] >>> Body: { -"markdown": { -"content": "我" -}, -"msg_type": 2, -"msg_seq": 69934430, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 8, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:43 [ws] → event agent seq=39 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=28 text=我明白了问题 -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=6, lastSentLength=0, streamBuffer.length=107, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=6 -08:28:43 [ws] → event agent seq=40 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=29 text=我明白了问题所在 -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=8, lastSentLength=0, streamBuffer.length=109, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=8 -08:28:43 [ws] → event agent seq=41 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=30 text=我明白了问题所在, -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=9, lastSentLength=0, streamBuffer.length=110, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=9 -08:28:43 [ws] → event agent seq=42 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=31 text=我明白了问题所在,`--reply-to -08:28:43 [ws] → event chat seq=43 clients=5 dropIfSlow=true -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=20, lastSentLength=0, streamBuffer.length=121, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=20 -08:28:43 [ws] → event agent seq=44 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=32 text=我明白了问题所在,`--reply-to` -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=21, lastSentLength=0, streamBuffer.length=122, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=21 -08:28:43 [ws] → event agent seq=45 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=33 text=我明白了问题所在,`--reply-to` 参数似乎在当前 -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=29, lastSentLength=0, streamBuffer.length=130, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=29 -08:28:43 [ws] → event agent seq=46 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=34 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron -08:28:43 [ws] → event chat seq=47 clients=5 dropIfSlow=true -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=37, lastSentLength=0, streamBuffer.length=138, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=37 -08:28:43 [ws] → event agent seq=48 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=35 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中 -08:28:43 [qqbot] [qqbot:default] handlePartialReply: fullText.length=41, lastSentLength=0, streamBuffer.length=142, isNewSegment=false -08:28:43 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=41 -08:28:44 [ws] → event agent seq=49 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=36 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据 -08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=49, lastSentLength=0, streamBuffer.length=150, isNewSegment=false -08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=49 -08:28:44 [ws] → event agent seq=50 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=37 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新 -08:28:44 [ws] → event chat seq=51 clients=5 dropIfSlow=true -08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=58, lastSentLength=0, streamBuffer.length=159, isNewSegment=false -08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=58 -08:28:44 [ws] → event agent seq=52 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=38 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉 -08:28:44 [ws] → event chat seq=53 clients=5 dropIfSlow=true -08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=65, lastSentLength=0, streamBuffer.length=166, isNewSegment=false -08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=65 -08:28:44 [ws] → event agent seq=54 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=39 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数 -08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=71, lastSentLength=0, streamBuffer.length=172, isNewSegment=false -08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=71 -08:28:44 [ws] → event agent seq=55 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=40 text=我明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数: -08:28:44 [qqbot] [qqbot:default] handlePartialReply: fullText.length=72, lastSentLength=0, streamBuffer.length=173, isNewSegment=false -08:28:44 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=72 -08:28:45 [qqbot-api] <<< Status: 200 OK -08:28:45 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:45 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "dce05ada4170c53c5ea7f6207c77b5ab" -} -08:28:45 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:45+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:45 [qqbot] [qqbot:default] Stream chunk sent, index: 8, isEnd: false, text: "我..." -08:28:45 [qqbot] [qqbot:default] Stream partial #9, increment: 1 chars, total: 1 chars -08:28:45 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:45 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:45 [qqbot-api] >>> Body: { -"markdown": { -"content": "明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统提供的信息重新设置提醒,去掉不支持的参数:" -}, -"msg_type": 2, -"msg_seq": 69934431, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 9, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:45 [qqbot-api] <<< Status: 200 OK -08:28:45 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:45 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "52bbde0882bfc077bf1b3784e059d358" -} -08:28:45 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:45+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:45 [qqbot] [qqbot:default] Stream chunk sent, index: 9, isEnd: false, text: "明白了问题所在,`--reply-to` 参数似乎在当前版本的 cron 命令中不存在。让我根据系统..." -08:28:45 [qqbot] [qqbot:default] Stream partial #10, increment: 71 chars, total: 72 chars -08:28:45 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_5eb242f4bd724c8c8903f449 -08:28:45 [ws] ⇄ res ✓ node.list 1ms id=92a48702…c670 -08:28:48 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 38s, since chunk: 3s) -08:28:48 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:28:48 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:28:48 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934432, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 10, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:28:50 [qqbot-api] <<< Status: 200 OK -08:28:50 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:28:50 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "ecc67124362fb054de3c318a9ec372c4" -} -08:28:50 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:28:50+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:28:50 [ws] ⇄ res ✓ node.list 2ms id=35f24303…102e -08:28:54 [ws] ← open remoteAddr=127.0.0.1 conn=7c10c747…e275 -08:28:54 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -08:28:54 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -08:28:54 [ws] → event health seq=56 clients=6 presenceVersion=6 healthVersion=10 -08:28:54 [ws] → event cron seq=57 clients=6 dropIfSlow=true -08:28:54 [ws] ⇄ res ✓ cron.add 7ms id=89227308…8748 -08:28:54 [ws] → close code=1005 reason= durationMs=65 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=89227308-d24e-46e1-9384-e87ed93b8748 -08:28:54 [ws] ← open remoteAddr=127.0.0.1 conn=4d66603c…ea45 -08:28:54 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -08:28:54 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -08:28:54 [ws] → event health seq=58 clients=6 presenceVersion=6 healthVersion=11 -08:28:54 [ws] ⇄ res ✓ cron.status 1ms id=3c52f4c3…aaef -08:28:54 [ws] → close code=1005 reason= durationMs=15 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=3c52f4c3-a561-4ac8-9bd2-01c0875aaaef -08:28:54 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_5eb242f4bd724c8c8903f449 -08:28:55 [ws] ⇄ res ✓ node.list 0ms conn=f395f45d…3099 id=cf12e732…a5c2 -08:29:00 [qqbot] [qqbot:default] Sending keepalive #2 (elapsed: 49s, since chunk: 14s) -08:29:00 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:00 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:00 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934433, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 11, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:00 [ws] ⇄ res ✓ node.list 2ms id=4e006266…9e62 -08:29:01 [qqbot-api] <<< Status: 200 OK -08:29:01 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:01 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "4290ff8b0e8b880ee5610b0f7d80852f" -} -08:29:01 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:01+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:02 [ws] → event health seq=59 clients=5 presenceVersion=6 healthVersion=12 -08:29:02 [ws] → event tick seq=60 clients=5 dropIfSlow=true -08:29:04 [ws] → event agent seq=61 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=44 text=太 -08:29:04 [ws] → event chat seq=62 clients=5 dropIfSlow=true -08:29:04 [qqbot] [qqbot:default] New segment detected! lastSentLength=72, newTextLength=1, lastSentText="我明白了问题所在,`--reply-to...", newText="太..." -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=176, isNewSegment=true -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 -08:29:04 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:04 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:04 [qqbot-api] >>> Body: { -"markdown": { -"content": "太" -}, -"msg_type": 2, -"msg_seq": 69934434, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 12, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:04 [ws] → event agent seq=63 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=45 text=太好了!这次 -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=6, lastSentLength=0, streamBuffer.length=181, isNewSegment=false -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=6 -08:29:04 [ws] → event agent seq=64 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=46 text=太好了!这次提醒设置 -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=10, lastSentLength=0, streamBuffer.length=185, isNewSegment=false -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=10 -08:29:04 [ws] → event agent seq=65 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=47 text=太好了!这次提醒设置成功了。 -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=189, isNewSegment=false -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 -08:29:04 [ws] → event agent seq=66 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=48 text=太好了!这次提醒设置成功了。让我确认一下当前 -08:29:04 [ws] → event chat seq=67 clients=5 dropIfSlow=true -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=22, lastSentLength=0, streamBuffer.length=197, isNewSegment=false -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=22 -08:29:04 [ws] → event agent seq=68 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=49 text=太好了!这次提醒设置成功了。让我确认一下当前的提醒列表: -08:29:04 [qqbot] [qqbot:default] handlePartialReply: fullText.length=28, lastSentLength=0, streamBuffer.length=203, isNewSegment=false -08:29:04 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=28 -08:29:04 [agent/embedded] embedded run tool start: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_4ac86cf98611444ca4feb0ad -08:29:04 [qqbot-api] <<< Status: 200 OK -08:29:04 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:04 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "6a598176480a4164edbca5d53ce5ccc1" -} -08:29:04 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:04+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:04 [qqbot] [qqbot:default] Stream chunk sent, index: 12, isEnd: false, text: "太..." -08:29:04 [qqbot] [qqbot:default] Stream partial #13, increment: 1 chars, total: 1 chars -08:29:04 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:04 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:04 [qqbot-api] >>> Body: { -"markdown": { -"content": "好了!这次提醒设置成功了。让我确认一下当前的提醒列表:" -}, -"msg_type": 2, -"msg_seq": 69934435, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 13, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:05 [qqbot-api] <<< Status: 200 OK -08:29:05 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:05 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "ee274468916b0f43bac839509aa0fa31" -} -08:29:05 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:05+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:05 [qqbot] [qqbot:default] Stream chunk sent, index: 13, isEnd: false, text: "好了!这次提醒设置成功了。让我确认一下当前的提醒列表:..." -08:29:05 [qqbot] [qqbot:default] Stream partial #14, increment: 27 chars, total: 28 chars -08:29:05 [ws] ⇄ res ✓ node.list 1ms id=6e471ada…faeb -08:29:08 [qqbot] [qqbot:default] Sending keepalive #1 (elapsed: 58s, since chunk: 3s) -08:29:08 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:08 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:08 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934436, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 14, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:09 [qqbot-api] <<< Status: 200 OK -08:29:09 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:09 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "c87fa23d25bf5155e7b8db5a7576e846" -} -08:29:09 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:09+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:10 [ws] ← open remoteAddr=127.0.0.1 conn=6f9da40f…d57c -08:29:10 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -08:29:10 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -08:29:10 [ws] → event health seq=69 clients=6 presenceVersion=6 healthVersion=13 -08:29:10 [ws] ⇄ res ✓ cron.list 1ms id=44a1542c…3d8a -08:29:10 [ws] → close code=1005 reason= durationMs=33 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=44a1542c-ecca-4eb2-8af2-bf6ea2823d8a -08:29:10 [agent/embedded] embedded run tool end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b tool=exec toolCallId=call_4ac86cf98611444ca4feb0ad -08:29:10 [qqbot] [qqbot:default] Heartbeat sent -08:29:10 [ws] ⇄ res ✓ node.list 3ms conn=f395f45d…3099 id=ce7b0659…d104 -08:29:11 [qqbot] [qqbot:default] Received op=11 t=undefined -08:29:11 [qqbot] [qqbot:default] Heartbeat ACK -08:29:15 [ws] → event agent seq=70 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=54 text=✅ -08:29:15 [ws] → event chat seq=71 clients=5 dropIfSlow=true -08:29:15 [qqbot] [qqbot:default] New segment detected! lastSentLength=28, newTextLength=1, lastSentText="太好了!这次提醒设置成功了。让我确认一下...", newText="✅..." -08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=1, lastSentLength=0, streamBuffer.length=206, isNewSegment=true -08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=1 -08:29:15 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:15 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:15 [qqbot-api] >>> Body: { -"markdown": { -"content": "✅" -}, -"msg_type": 2, -"msg_seq": 69934437, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 15, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:15 [ws] → event agent seq=72 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=55 text=✅ 提 -08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=3, lastSentLength=0, streamBuffer.length=208, isNewSegment=false -08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=3 -08:29:15 [ws] → event agent seq=73 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=56 text=✅ 提醒已设置成功 -08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=9, lastSentLength=0, streamBuffer.length=214, isNewSegment=false -08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=9 -08:29:15 [ws] → event agent seq=74 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=57 text=✅ 提醒已设置成功! 📝 -08:29:15 [qqbot] [qqbot:default] handlePartialReply: fullText.length=14, lastSentLength=0, streamBuffer.length=219, isNewSegment=false -08:29:15 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=14 -08:29:15 [ws] ⇄ res ✓ node.list 1ms id=ebe1e48d…3352 -08:29:16 [ws] → event agent seq=75 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=58 text=✅ 提醒已设置成功! 📝 内容: -08:29:16 [ws] → event chat seq=76 clients=5 dropIfSlow=true -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=18, lastSentLength=0, streamBuffer.length=223, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=18 -08:29:16 [ws] → event agent seq=77 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=59 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=25, lastSentLength=0, streamBuffer.length=230, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=25 -08:29:16 [ws] → event agent seq=78 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=60 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=27, lastSentLength=0, streamBuffer.length=232, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=27 -08:29:16 [ws] → event agent seq=79 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=61 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 -08:29:16 [ws] → event chat seq=80 clients=5 dropIfSlow=true -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=35, lastSentLength=0, streamBuffer.length=240, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=35 -08:29:16 [ws] → event agent seq=81 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=62 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:3 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=40, lastSentLength=0, streamBuffer.length=245, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=40 -08:29:16 [ws] → event agent seq=82 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=63 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会 -08:29:16 [ws] → event chat seq=83 clients=5 dropIfSlow=true -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=48, lastSentLength=0, streamBuffer.length=253, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=48 -08:29:16 [ws] → event agent seq=84 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=64 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=54, lastSentLength=0, streamBuffer.length=259, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=54 -08:29:16 [ws] → event agent seq=85 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=65 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经 -08:29:16 [ws] → event chat seq=86 clients=5 dropIfSlow=true -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=64, lastSentLength=0, streamBuffer.length=269, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=64 -08:29:16 [ws] → event agent seq=87 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=66 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=66, lastSentLength=0, streamBuffer.length=271, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=66 -08:29:16 [ws] → event agent seq=88 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=67 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=73, lastSentLength=0, streamBuffer.length=278, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=73 -08:29:16 [ws] → event agent seq=89 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=68 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=75, lastSentLength=0, streamBuffer.length=280, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=75 -08:29:16 [ws] → event agent seq=90 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=69 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会 -08:29:16 [ws] → event chat seq=91 clients=5 dropIfSlow=true -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=80, lastSentLength=0, streamBuffer.length=285, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=80 -08:29:16 [ws] → event agent seq=92 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=70 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=82, lastSentLength=0, streamBuffer.length=287, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=82 -08:29:16 [ws] → event agent seq=93 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=71 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=87, lastSentLength=0, streamBuffer.length=292, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=87 -08:29:16 [ws] → event agent seq=94 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=72 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=91, lastSentLength=0, streamBuffer.length=296, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=91 -08:29:16 [ws] → event agent seq=95 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=73 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持 -08:29:16 [qqbot] [qqbot:default] handlePartialReply: fullText.length=97, lastSentLength=0, streamBuffer.length=302, isNewSegment=false -08:29:16 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=97 -08:29:16 [qqbot-api] <<< Status: 200 OK -08:29:16 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:17 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "42b9030307d7e3b838a856700fbe2422" -} -08:29:16 [ws] → event agent seq=96 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=74 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入, -08:29:17 [ws] → event chat seq=97 clients=5 dropIfSlow=true -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=105, lastSentLength=0, streamBuffer.length=310, isNewSegment=false -08:29:17 [qqbot] [qqbot:default] handlePartialReply: sending stream chunk, length=105 -08:29:17 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:17+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:17 [qqbot] [qqbot:default] Stream chunk sent, index: 15, isEnd: false, text: "✅..." -08:29:17 [qqbot] [qqbot:default] Stream partial #16, increment: 1 chars, total: 1 chars -08:29:17 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:17 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:17 [qqbot-api] >>> Body: { -"markdown": { -"content": " 提醒已设置成功!\n\n📝 内容:喝水\n⏰ 时间:5分钟后 (大约在 16:33)\n到时候我会准时提醒您~\n\n您的提醒任务已经创建并将在5分钟后执行,届时您会收到一条\"💧 该喝水啦!记得保持充足的水分摄入," -}, -"msg_type": 2, -"msg_seq": 69934438, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 16, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:17 [ws] → event agent seq=98 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=75 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将帮助 -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=114, lastSentLength=1, streamBuffer.length=319, isNewSegment=false -08:29:17 [ws] → event agent seq=99 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=76 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将帮助哦~" -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=117, lastSentLength=1, streamBuffer.length=322, isNewSegment=false -08:29:17 [ws] → event agent seq=100 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=77 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在 -08:29:17 [ws] → event chat seq=101 clients=5 dropIfSlow=true -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=125, lastSentLength=1, streamBuffer.length=330, isNewSegment=false -08:29:17 [ws] → event agent seq=102 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=78 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在执行后自动删除 -08:29:17 [ws] → event chat seq=103 clients=5 dropIfSlow=true -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=132, lastSentLength=1, streamBuffer.length=337, isNewSegment=false -08:29:17 [ws] → event agent seq=104 clients=5 run=49d64fc4…e41b agent=main session=main stream=assistant aseq=79 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:5分钟后 (大约在 16:33) 到时候我会准时提醒您~ 您的提醒任务已经创建并将有帮助哦~"的消息。提醒将在执行后自动删除。 -08:29:17 [qqbot] [qqbot:default] handlePartialReply: fullText.length=133, lastSentLength=1, streamBuffer.length=338, isNewSegment=false -08:29:17 [agent/embedded] embedded run agent end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b -08:29:17 [ws] → event agent seq=105 clients=5 run=49d64fc4…e41b agent=main session=main stream=lifecycle aseq=80 phase=end -08:29:17 [ws] → event chat seq=106 clients=5 -08:29:17 [agent/embedded] embedded run prompt end: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=74069 -08:29:17 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 -08:29:17 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 -08:29:17 [ws] ⇄ res ✓ chat.history 17ms conn=3f770fc7…c168 id=15fe49d7…4c9c -08:29:17 [ws] ⇄ res ✓ chat.history 17ms conn=d582275d…8cf7 id=83955d28…1198 -08:29:17 [ws] ⇄ res ✓ chat.history 6ms conn=d84f2780…f80d id=8c36b63a…8b6e -08:29:17 [ws] ⇄ res ✓ chat.history 10ms conn=30e84c3b…37f9 id=b09e1eb5…e1fa -08:29:17 [ws] ⇄ res ✓ chat.history 6ms conn=f395f45d…3099 id=c08d10c0…6fbd -08:29:17 [qqbot-api] <<< Status: 200 OK -08:29:17 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:17 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "230c60a2e8e313d4315ba94e35baba51" -} -08:29:17 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:17+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:17 [qqbot] [qqbot:default] Stream chunk sent, index: 16, isEnd: false, text: " 提醒已设置成功! - -📝 内容:喝水 -⏰ 时间:5分钟后 (大约在 16:33) -到时候我会准时提..." -08:29:17 [qqbot] [qqbot:default] Stream partial #17, increment: 104 chars, total: 105 chars -08:29:17 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:17 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:17 [qqbot-api] >>> Body: { -"markdown": { -"content": "对身体健康很有帮助哦~\"的消息。提醒将在执行后自动删除。" -}, -"msg_type": 2, -"msg_seq": 69934439, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 1, -"index": 17, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:17 [agent/embedded] embedded run done: runId=49d64fc4-e0f9-477f-99d8-f3efa3a0e41b sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=74520 aborted=false -08:29:17 [diagnostic] lane task done: lane=main durationMs=74539 active=0 queued=0 -08:29:17 [diagnostic] lane task done: lane=session:agent:main:main durationMs=74543 active=0 queued=0 -08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice -08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice -08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice -08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice -08:29:17 [qqbot] [qqbot:default] deliver called, kind: final, payload keys: text, mediaUrls, mediaUrl, isError, replyToId, replyToTag, replyToCurrent, audioAsVoice -08:29:18 [ws] → event heartbeat seq=107 clients=5 dropIfSlow=true -08:29:19 [qqbot-api] <<< Status: 200 OK -08:29:19 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:18 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "e5678ab04a2ed8977c7a736f721aa76f" -} -08:29:19 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:18+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:19 [qqbot] [qqbot:default] Stream chunk sent, index: 17, isEnd: false, text: "对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。..." -08:29:19 [qqbot] [qqbot:default] Stream partial #18, increment: 28 chars, total: 133 chars -08:29:19 [qqbot-api] >>> POST https://api.sgroup.qq.com/v2/users/207A5B8339D01F6582911C014668B77B/messages -08:29:19 [qqbot-api] >>> Headers: { -"Authorization": "QQBot Sg8XiGmMDO9yWtIzk-9uLqsq9KuFz7-lYWMzbeFOj-Vv9l5kdh0psYZdaD1wMIvdWKgHclaXoK2V", -"Content-Type": "application/json" -} -08:29:19 [qqbot-api] >>> Body: { -"markdown": { -"content": "" -}, -"msg_type": 2, -"msg_seq": 69934440, -"msg_id": "ROBOT1.0_kbcHtHqrSD7wOngdk8bLt2DJiE0iiDDqYy5ybyxRKcrO7JcBTYvD1s9zXt5auvjbGb1zS6Iz4jKZcvz9xFO5igjlj6H3wBCAbx3PilKrAso!", -"stream": { -"state": 10, -"index": 18, -"id": "ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!" -} -} -08:29:20 [qqbot-api] <<< Status: 200 OK -08:29:20 [qqbot-api] <<< Headers: { -"access-control-allow-credentials": "true", -"access-control-allow-headers": "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,authorization", -"access-control-allow-methods": "GET, POST, OPTIONS", -"connection": "keep-alive", -"content-encoding": "gzip", -"content-type": "application/json", -"date": "Sun, 01 Feb 2026 08:29:20 GMT", -"server": "TAPISIX/2.2.2", -"transfer-encoding": "chunked", -"vary": "Accept-Encoding", -"x-content-type-options": "nosniff", -"x-tps-trace-id": "d9cde505d1f2e65ccb4c9ef5b7a42435" -} -08:29:20 [qqbot-api] <<< Body: {"id":"ROBOT1.0_ZrebFMxtQ8iOJgOoGwIpn4b-s6SD6o.h9yF5myXaVq0sIoguSh8r12oxUi8LehQpIFWWYOTXJFg4qYG93f7Aug!!","timestamp":"2026-02-01T16:29:20+08:00","ext_info":{"ref_idx":"REFIDX_COG2AhCcnfzLBhjd7Oa+Dg=="}} -08:29:20 [qqbot] [qqbot:default] Stream completed, final increment: 0 chars, total streamBuffer: 338 chars, chunks: 19 -08:29:20 [ws] ⇄ res ✓ node.list 2ms id=785fc4ca…d50e -08:29:25 [ws] ⇄ res ✓ node.list 0ms id=04fce377…7c39 -08:29:30 [ws] ⇄ res ✓ node.list 1ms id=508c034d…ef00 -08:29:32 [ws] → event tick seq=108 clients=5 dropIfSlow=true -08:29:32 [ws] ⇄ res ✓ node.list 37ms conn=30e84c3b…37f9 id=e58ccf94…226a -08:29:32 [ws] ⇄ res ✓ node.list 107ms conn=d582275d…8cf7 id=3877bd6b…89c6 -08:29:32 [ws] ⇄ res ✓ node.list 112ms conn=d84f2780…f80d id=1a2c8310…4582 -08:29:32 [ws] ⇄ res ✓ node.list 122ms conn=3f770fc7…c168 id=418dd535…513b -08:29:35 [ws] ⇄ res ✓ node.list 1ms conn=f395f45d…3099 id=a69018c7…2ad6 -^C08:29:37 [gateway] signal SIGINT received -08:29:37 [gateway] received SIGINT; shutting down -08:29:37 [qqbot] [qqbot-api] Background token refresh stopped -08:29:37 [gmail-watcher] gmail watcher stopped -08:29:37 [ws] → event shutdown seq=109 clients=5 -08:29:37 [gateway] signal SIGINT received -08:29:37 [gateway] received SIGINT during shutdown; ignoring -08:29:37 [qqbot] [qqbot:default] Message processor stopped -08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=f395f45d-ef22-42bc-b309-01d0a8b13099 -08:29:37 [ws] → event presence seq=110 clients=0 dropIfSlow=true presenceVersion=7 healthVersion=13 -08:29:37 [ws] → close code=1012 reason=service restart durationMs=148474 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=a69018c7-cf65-4275-b430-bf3ea5fd2ad6 -08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=30e84c3b-ec6d-4d80-aaf8-b42c47ad37f9 -08:29:37 [ws] → event presence seq=111 clients=0 dropIfSlow=true presenceVersion=8 healthVersion=13 -08:29:37 [ws] → close code=1012 reason=service restart durationMs=149077 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=e58ccf94-9248-46f3-932b-e8e89624226a conn=30e84c3b…37f9 -08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=d84f2780-43ad-4cf7-800a-8dc2a97bf80d -08:29:37 [ws] → event presence seq=112 clients=0 dropIfSlow=true presenceVersion=9 healthVersion=13 -08:29:37 [ws] → close code=1012 reason=service restart durationMs=150032 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=1a2c8310-e1d2-49a4-b9c7-d8e134854582 conn=d84f2780…f80d -08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=d582275d-ed0e-408f-a43e-1e0777478cf7 -08:29:37 [ws] → event presence seq=113 clients=0 dropIfSlow=true presenceVersion=10 healthVersion=13 -08:29:37 [ws] → close code=1012 reason=service restart durationMs=150052 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=3877bd6b-1cb1-4654-a699-71875f9189c6 conn=d582275d…8cf7 -08:29:37 [ws] webchat disconnected code=1012 reason=service restart conn=3f770fc7-7722-4458-8fdb-86284e5cc168 -08:29:37 [ws] → event presence seq=114 clients=0 dropIfSlow=true presenceVersion=11 healthVersion=13 -08:29:37 [ws] → close code=1012 reason=service restart durationMs=150093 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=418dd535-ddc7-4f9a-adc8-b39cfa25513b conn=3f770fc7…c168 +16:43:01 [qqbot] [qqbot:default] Connecting to wss://api.sgroup.qq.com/websocket +16:43:01 [qqbot] [qqbot:default] WebSocket connected +16:43:01 [qqbot] [qqbot:default] Message processor started +16:43:01 [qqbot] [qqbot-api] Background token refresh started +16:43:01 [qqbot] [qqbot-api] Token valid, next refresh in 6891s +16:43:01 [qqbot] [qqbot:default] Received op=10 t=undefined +16:43:01 [qqbot] [qqbot:default] Hello received +16:43:01 [qqbot] [qqbot:default] Sending identify with intents: 1107300352 (群聊+私信+频道) +16:43:01 [qqbot] [qqbot:default] Received op=0 t=READY +16:43:01 [qqbot] [qqbot:default] Ready with 群聊+私信+频道, session: b879fa59-5251-4a98-b50a-b88e46d174ed +16:43:01 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=1 +16:43:01 [qqbot] [qqbot:default] Gateway ready +16:43:01 [ws] ← open remoteAddr=127.0.0.1 conn=225c0e81…5c15 +16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=bc20a8ce…0a0f +16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=225c0e81…5c15 +16:43:02 [ws] webchat connected conn=225c0e81-0f25-41cb-b22f-96645dd55c15 remote=127.0.0.1 client=openclaw-control-ui webchat vdev +16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=2 +16:43:02 [ws] → event health seq=2 clients=1 presenceVersion=2 healthVersion=3 +16:43:02 [ws] ⇄ res ✓ agent.identity.get 15ms id=a2c56be6…9e47 +16:43:02 [ws] ⇄ res ✓ agents.list 5ms id=3765ad30…e172 +16:43:02 [ws] ⇄ res ✓ sessions.list 2ms id=486adb2f…aa4e +16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=46458cc1…e736 +16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=5da3d69e…5ff8 +16:43:02 [ws] ⇄ res ✓ device.pair.list 67ms conn=225c0e81…5c15 id=1cc7c4d9…4df1 +16:43:02 [ws] ⇄ res ✓ node.list 71ms id=2114b4c2…b2bc +16:43:02 [ws] ⇄ res ✓ chat.history 116ms id=5d02d989…c6de +16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=bc20a8ce…0a0f +16:43:02 [ws] webchat connected conn=bc20a8ce-f484-4990-8b68-2d47e7110a0f remote=127.0.0.1 client=openclaw-control-ui webchat vdev +16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=3 +16:43:02 [ws] → event health seq=3 clients=2 presenceVersion=3 healthVersion=4 +16:43:02 [ws] ⇄ res ✓ agent.identity.get 1ms id=bfa4ae92…444a +16:43:02 [ws] ⇄ res ✓ agents.list 0ms id=fae89b26…1603 +16:43:02 [ws] ⇄ res ✓ sessions.list 2ms id=4209d85a…a3f1 +16:43:02 [ws] ⇄ res ✓ chat.history 20ms id=4d88edad…42b2 +16:43:02 [ws] ⇄ res ✓ device.pair.list 30ms id=b2c11fbd…54c0 +16:43:02 [ws] ⇄ res ✓ node.list 34ms id=876f77e9…2054 +16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=46458cc1…e736 +16:43:02 [ws] webchat connected conn=46458cc1-bece-4b13-9019-b38a4a35e736 remote=127.0.0.1 client=openclaw-control-ui webchat vdev +16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=4 +16:43:02 [ws] → event health seq=4 clients=3 presenceVersion=4 healthVersion=5 +16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=5da3d69e…5ff8 +16:43:02 [ws] webchat connected conn=5da3d69e-fe34-46d3-a334-0a64d1165ff8 remote=127.0.0.1 client=openclaw-control-ui webchat vdev +16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=5 +16:43:02 [ws] → event health seq=5 clients=4 presenceVersion=5 healthVersion=6 +16:43:02 [ws] ⇄ res ✓ agent.identity.get 5ms conn=46458cc1…e736 id=662c1327…1825 +16:43:02 [ws] ⇄ res ✓ agents.list 1ms id=fca54a59…274f +16:43:02 [ws] ⇄ res ✓ sessions.list 5ms id=46491b9c…ad0e +16:43:02 [ws] ⇄ res ✓ chat.history 114ms id=79b90e8b…8f1f +16:43:02 [ws] ⇄ res ✓ agent.identity.get 6ms conn=5da3d69e…5ff8 id=9d2035c5…2085 +16:43:02 [ws] ⇄ res ✓ agents.list 0ms id=0dcab6b0…4454 +16:43:02 [ws] ⇄ res ✓ sessions.list 1ms id=3d659adc…c458 +16:43:02 [ws] ⇄ res ✓ device.pair.list 57ms id=ed58f910…2276 +16:43:02 [ws] ⇄ res ✓ node.list 222ms conn=46458cc1…e736 id=052f8dd7…5eb8 +16:43:02 [ws] ⇄ res ✓ device.pair.list 216ms id=15036c28…aeb8 +16:43:02 [ws] ⇄ res ✓ node.list 81ms conn=5da3d69e…5ff8 id=0ae64f32…9c76 +16:43:15 [ws] ← open remoteAddr=127.0.0.1 conn=323cc0ed…16ff +16:43:15 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token +16:43:15 [ws] webchat connected conn=323cc0ed-9652-4b5d-be48-38f3630c16ff remote=127.0.0.1 client=openclaw-control-ui webchat vdev +16:43:15 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:43:15 [ws] → event health seq=6 clients=5 presenceVersion=6 healthVersion=7 +16:43:15 [ws] ⇄ res ✓ agent.identity.get 0ms id=fbe3c892…59db +16:43:15 [ws] ⇄ res ✓ agents.list 1ms id=8e48ad4e…a979 +16:43:15 [ws] ⇄ res ✓ sessions.list 4ms id=95813123…be20 +16:43:15 [ws] ⇄ res ✓ chat.history 37ms id=4f9bcddd…77cb +16:43:15 [ws] ⇄ res ✓ node.list 43ms id=5bc9dc66…afe0 +16:43:15 [ws] ⇄ res ✓ device.pair.list 45ms id=dc2c4ea8…4c94 +16:43:16 [ws] ⇄ res ✓ node.list 3ms conn=225c0e81…5c15 id=f27f7c56…64ae +16:43:16 [ws] ⇄ res ✓ node.list 6ms conn=46458cc1…e736 id=cd62e734…442b +16:43:16 [ws] ⇄ res ✓ node.list 19ms conn=5da3d69e…5ff8 id=b5bbde31…90e2 +16:43:16 [ws] ⇄ res ✓ node.list 29ms conn=bc20a8ce…0a0f id=a81eb5c7…420e +16:43:17 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=2 +16:43:17 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE +16:43:17 [known-users] Loaded 1 users from file +16:43:17 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 26 +16:43:17 [qqbot] [qqbot:default] Message enqueued, queue size: 1 +16:43:17 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 1分钟后提醒我喝水 +16:43:17 [qqbot] [qqbot:default] Stream enabled: false +16:43:17 [qqbot] [qqbot:default] Stream support: false (type=c2c, enabled=false) +16:43:17 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 +16:43:17 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=5 queueSize=0 +16:43:17 [diagnostic] lane enqueue: lane=main queueSize=1 +16:43:17 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 +16:43:17 [agent/embedded] embedded run start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot +16:43:17 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 +16:43:17 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 +16:43:17 [agent/embedded] embedded run prompt start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 +16:43:17 [agent/embedded] embedded run agent start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f +16:43:17 [ws] → event agent seq=7 clients=5 run=69e77a59…1c8f agent=main session=main stream=lifecycle aseq=1 phase=start +16:43:20 [ws] ⇄ res ✓ node.list 5ms conn=323cc0ed…16ff id=b56be68f…015b +16:43:22 [known-users] Saved 1 users to file +16:43:25 [ws] ⇄ res ✓ node.list 1ms id=834715ce…caa4 +16:43:30 [ws] ⇄ res ✓ node.list 2ms id=1948a1de…9895 +16:43:31 [ws] → event tick seq=8 clients=5 dropIfSlow=true +16:43:31 [ws] → event agent seq=9 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=2 text=我 +16:43:31 [ws] → event chat seq=10 clients=5 dropIfSlow=true +16:43:31 [ws] → event agent seq=11 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=3 text=我来为您设置一个 +16:43:31 [ws] → event agent seq=12 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=4 text=我来为您设置一个1分钟后提醒喝水 +16:43:31 [ws] → event agent seq=13 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=5 text=我来为您设置一个1分钟后提醒喝水的任务: +16:43:31 [ws] → event chat seq=14 clients=5 dropIfSlow=true +16:43:32 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_fcf2dfd8801340ea80cb052f +16:43:32 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice +16:43:32 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=20, total=20 +16:43:32 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) +16:43:35 [ws] ⇄ res ✓ node.list 164ms id=391c63f3…12d1 +16:43:40 [ws] ⇄ res ✓ node.list 3ms id=644e852e…b958 +16:43:42 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_fcf2dfd8801340ea80cb052f +16:43:43 [qqbot] [qqbot:default] Heartbeat sent +16:43:43 [qqbot] [qqbot:default] Received op=11 t=undefined +16:43:43 [qqbot] [qqbot:default] Heartbeat ACK +16:43:43 [ws] ← open remoteAddr=127.0.0.1 conn=f5252266…7c2a +16:43:43 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +16:43:43 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:43:43 [ws] → event health seq=15 clients=6 presenceVersion=6 healthVersion=8 +16:43:43 [ws] → event cron seq=16 clients=6 dropIfSlow=true +16:43:43 [ws] ⇄ res ✓ cron.add 4ms id=e8bdbb55…9286 +16:43:43 [ws] ← open remoteAddr=127.0.0.1 conn=677f535c…c4f2 +16:43:43 [ws] → close code=1005 reason= durationMs=47 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=e8bdbb55-ab71-4095-bbf6-6d2be7fc9286 conn=f5252266…7c2a +16:43:43 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token conn=677f535c…c4f2 +16:43:43 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:43:43 [ws] → event health seq=17 clients=6 presenceVersion=6 healthVersion=9 +16:43:43 [ws] ⇄ res ✓ cron.status 1ms id=ede03142…1a30 +16:43:43 [ws] → close code=1005 reason= durationMs=20 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=ede03142-7e12-4d87-b4e4-68ac3f9c1a30 +16:43:45 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=f44c9b12…680b +16:43:50 [ws] ⇄ res ✓ node.list 3ms id=c0f27c31…c51d +16:43:55 [ws] ⇄ res ✓ node.list 1ms id=01002807…e188 +16:43:57 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=process toolCallId=call_a5b895ffef304d7395d48838 +16:43:57 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=process toolCallId=call_a5b895ffef304d7395d48838 +16:43:59 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_18bf1944d51442cc9e9ddbee +16:44:00 [ws] ⇄ res ✓ node.list 4ms id=600b91b3…eab6 +16:44:01 [ws] → event health seq=18 clients=5 presenceVersion=6 healthVersion=10 +16:44:01 [ws] → event tick seq=19 clients=5 dropIfSlow=true +16:44:05 [ws] ⇄ res ✓ node.list 2ms id=80fbe5ce…a67a +16:44:08 [ws] ← open remoteAddr=127.0.0.1 conn=13e64828…0a4a +16:44:08 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +16:44:08 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:44:08 [ws] → event health seq=20 clients=6 presenceVersion=6 healthVersion=11 +16:44:08 [ws] ⇄ res ✓ cron.list 2ms id=c68b2bd5…49fe +16:44:08 [ws] → close code=1005 reason= durationMs=71 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=c68b2bd5-687b-4e9e-9b7d-38b3524f49fe +16:44:08 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_18bf1944d51442cc9e9ddbee +16:44:10 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=2c9fd79a…a47b +16:44:11 [ws] → event agent seq=21 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=15 text=✅ +16:44:11 [ws] → event chat seq=22 clients=5 dropIfSlow=true +16:44:11 [ws] → event agent seq=23 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=16 text=✅ 提醒已 +16:44:11 [ws] → event agent seq=24 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=17 text=✅ 提醒已设置成功! 📝 +16:44:11 [ws] → event agent seq=25 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=18 text=✅ 提醒已设置成功! 📝 内容: +16:44:11 [ws] → event agent seq=26 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=19 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 +16:44:11 [ws] → event agent seq=27 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=20 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 ( +16:44:11 [ws] → event agent seq=28 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=21 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 +16:44:11 [ws] → event chat seq=29 clients=5 dropIfSlow=true +16:44:11 [ws] → event agent seq=30 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=22 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00: +16:44:11 [ws] → event agent seq=31 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=23 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候 +16:44:11 [ws] → event agent seq=32 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=24 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒 +16:44:11 [ws] → event agent seq=33 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=25 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒 +16:44:11 [ws] → event agent seq=34 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=26 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并 +16:44:11 [ws] → event chat seq=35 clients=5 dropIfSlow=true +16:44:11 [ws] → event agent seq=36 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=27 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行 +16:44:11 [ws] → event agent seq=37 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=28 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会 +16:44:11 [ws] → event agent seq=38 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=29 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 +16:44:11 [ws] → event agent seq=39 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=30 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦 +16:44:11 [ws] → event chat seq=40 clients=5 dropIfSlow=true +16:44:11 [ws] → event agent seq=41 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=31 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的 +16:44:11 [ws] → event agent seq=42 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=32 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对 +16:44:11 [ws] → event agent seq=43 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=33 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦 +16:44:11 [ws] → event chat seq=44 clients=5 dropIfSlow=true +16:44:12 [ws] → event agent seq=45 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=34 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。 +16:44:12 [ws] → event agent seq=46 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=35 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后 +16:44:12 [ws] → event agent seq=47 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=36 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如 +16:44:12 [ws] → event agent seq=48 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=37 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的 +16:44:12 [ws] → event agent seq=49 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=38 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的, +16:44:12 [ws] → event agent seq=50 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=39 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置 +16:44:12 [ws] → event chat seq=51 clients=5 dropIfSlow=true +16:44:12 [ws] → event agent seq=52 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=40 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐 +16:44:12 [ws] → event agent seq=53 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=41 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=54 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=42 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event chat seq=55 clients=5 dropIfSlow=true +16:44:12 [ws] → event agent seq=56 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=43 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=57 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=44 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=58 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=45 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event chat seq=59 clients=5 dropIfSlow=true +16:44:12 [ws] → event agent seq=60 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=46 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=61 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=47 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=62 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=48 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event chat seq=63 clients=5 dropIfSlow=true +16:44:12 [ws] → event agent seq=64 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=49 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=65 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=50 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [ws] → event agent seq=66 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=51 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… +16:44:12 [agent/embedded] embedded run agent end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f +16:44:12 [ws] → event agent seq=67 clients=5 run=69e77a59…1c8f agent=main session=main stream=lifecycle aseq=52 phase=end +16:44:12 [ws] → event chat seq=68 clients=5 +16:44:12 [agent/embedded] embedded run prompt end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=55147 +16:44:12 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 +16:44:13 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 +16:44:13 [ws] ⇄ res ✓ chat.history 73ms conn=225c0e81…5c15 id=9bda295a…2635 +16:44:13 [ws] ⇄ res ✓ chat.history 25ms conn=bc20a8ce…0a0f id=d4f0e67a…785a +16:44:13 [ws] ⇄ res ✓ chat.history 170ms conn=46458cc1…e736 id=3ddab88b…0e80 +16:44:13 [ws] ⇄ res ✓ chat.history 35ms conn=323cc0ed…16ff id=2219df62…11d3 +16:44:13 [ws] ⇄ res ✓ chat.history 62ms conn=5da3d69e…5ff8 id=8d871c4c…c1f0 +16:44:13 [agent/embedded] embedded run done: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=55777 aborted=false +16:44:13 [diagnostic] lane task done: lane=main durationMs=55791 active=0 queued=0 +16:44:13 [diagnostic] lane task done: lane=session:agent:main:main durationMs=55794 active=0 queued=0 +16:44:13 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice +16:44:13 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=230, total=230 +16:44:13 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) +16:44:14 [ws] → event heartbeat seq=69 clients=5 dropIfSlow=true +16:44:14 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=3 +16:44:14 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE +16:44:14 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 27 +16:44:14 [qqbot] [qqbot:default] Message enqueued, queue size: 1 +16:44:14 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 1分钟后提醒我喝水 +16:44:14 [qqbot] [qqbot:default] Stream enabled: false +16:44:14 [qqbot] [qqbot:default] Stream support: false (type=c2c, enabled=false) +16:44:14 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 +16:44:14 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=2 queueSize=0 +16:44:14 [diagnostic] lane enqueue: lane=main queueSize=1 +16:44:14 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 +16:44:14 [agent/embedded] embedded run start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot +16:44:14 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 +16:44:14 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 +16:44:14 [agent/embedded] embedded run prompt start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 +16:44:14 [agent/embedded] embedded run agent start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 +16:44:14 [ws] → event agent seq=70 clients=5 run=9dca9346…c1b2 agent=main session=main stream=lifecycle aseq=1 phase=start +16:44:15 [ws] ⇄ res ✓ node.list 15ms conn=323cc0ed…16ff id=7459bca9…85d2 +16:44:16 [ws] ⇄ res ✓ node.list 1ms conn=225c0e81…5c15 id=cf44f720…01a2 +16:44:16 [ws] ⇄ res ✓ node.list 1ms conn=bc20a8ce…0a0f id=5d87baf2…7446 +16:44:16 [ws] ⇄ res ✓ node.list 12ms conn=46458cc1…e736 id=a8ac366b…689d +16:44:16 [ws] ⇄ res ✓ node.list 23ms conn=5da3d69e…5ff8 id=c615dcdc…e89a +16:44:16 [ws] → event agent seq=71 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=2 text=我 +16:44:16 [ws] → event chat seq=72 clients=5 dropIfSlow=true +16:44:16 [ws] → event agent seq=73 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=3 text=我来为您设置 +16:44:16 [ws] → event agent seq=74 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=4 text=我来为您设置一个1分钟后提醒 +16:44:16 [ws] → event agent seq=75 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=5 text=我来为您设置一个1分钟后提醒喝水的任务: +16:44:17 [agent/embedded] embedded run tool start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_8e66dce8daba4961add53c7d +16:44:17 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice +16:44:17 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=20, total=20 +16:44:17 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) +16:44:19 [known-users] Saved 1 users to file +16:44:20 [ws] ⇄ res ✓ node.list 8ms conn=323cc0ed…16ff id=90c37a1a…54aa +16:44:23 [ws] ← open remoteAddr=127.0.0.1 conn=9f789ce2…8ada +16:44:23 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +16:44:23 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:44:23 [ws] → event health seq=76 clients=6 presenceVersion=6 healthVersion=12 +16:44:23 [ws] → event cron seq=77 clients=6 dropIfSlow=true +16:44:23 [ws] ⇄ res ✓ cron.add 7ms id=2b23f7eb…0f7c +16:44:23 [ws] → close code=1005 reason= durationMs=84 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=2b23f7eb-b8ad-4d45-a8a1-16ba53190f7c +16:44:23 [ws] ← open remoteAddr=127.0.0.1 conn=7cdae80c…8021 +16:44:23 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +16:44:23 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:44:23 [ws] → event health seq=78 clients=6 presenceVersion=6 healthVersion=13 +16:44:23 [ws] ⇄ res ✓ cron.status 1ms id=307ff75d…dfc5 +16:44:23 [ws] → close code=1005 reason= durationMs=18 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=307ff75d-4398-4a98-b2e5-6472b84edfc5 +16:44:23 [agent/embedded] embedded run tool end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_8e66dce8daba4961add53c7d +16:44:24 [qqbot] [qqbot:default] Heartbeat sent +16:44:24 [qqbot] [qqbot:default] Received op=11 t=undefined +16:44:24 [qqbot] [qqbot:default] Heartbeat ACK +16:44:25 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=5ee8c04d…5076 +16:44:25 [agent/embedded] embedded run tool start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_1609d0a32d8d41dfa73a91e8 +16:44:30 [ws] ⇄ res ✓ node.list 2ms id=5f141923…4789 +16:44:31 [ws] → event tick seq=79 clients=5 dropIfSlow=true +16:44:33 [ws] ← open remoteAddr=127.0.0.1 conn=8771457d…0ee9 +16:44:33 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token +16:44:33 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 +16:44:33 [ws] → event health seq=80 clients=6 presenceVersion=6 healthVersion=14 +16:44:33 [ws] ⇄ res ✓ cron.list 0ms id=8ab8a305…5b60 +16:44:33 [ws] → close code=1005 reason= durationMs=256 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=8ab8a305-fd19-42eb-a333-2693de625b60 +16:44:34 [agent/embedded] embedded run tool end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_1609d0a32d8d41dfa73a91e8 +16:44:35 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=c33c4ad4…e4c5 +16:44:35 [ws] → event agent seq=81 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=12 text=✅ +16:44:35 [ws] → event chat seq=82 clients=5 dropIfSlow=true +16:44:35 [ws] → event agent seq=83 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=13 text=✅ 提醒已 +16:44:35 [ws] → event agent seq=84 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=14 text=✅ 提醒已设置成功! 📝 +16:44:35 [ws] → event agent seq=85 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=15 text=✅ 提醒已设置成功! 📝 内容: +16:44:36 [ws] → event agent seq=86 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=16 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 +16:44:36 [ws] → event agent seq=87 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=17 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 ( +16:44:36 [ws] → event agent seq=88 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=18 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 +16:44:36 [ws] → event chat seq=89 clients=5 dropIfSlow=true +16:44:36 [ws] → event agent seq=90 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=19 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00: +16:44:36 [ws] → event agent seq=91 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=20 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候 +16:44:36 [ws] → event agent seq=92 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=21 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您 +16:44:36 [ws] → event agent seq=93 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=22 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您 +16:44:36 [ws] → event agent seq=94 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=23 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在 +16:44:36 [ws] → event agent seq=95 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=24 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在 +16:44:36 [ws] → event chat seq=96 clients=5 dropIfSlow=true +16:44:36 [ws] → event agent seq=97 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=25 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继 +16:44:36 [ws] → event agent seq=98 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=26 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. +16:44:36 [ws] → event agent seq=99 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=27 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第 +16:44:36 [ws] → event chat seq=100 clients=5 dropIfSlow=true +16:44:36 [ws] → event agent seq=101 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=28 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约 +16:44:36 [ws] → event agent seq=102 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=29 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00 +16:44:36 [ws] → event agent seq=103 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=30 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44( +16:44:36 [ws] → event agent seq=104 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=31 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2 +16:44:36 [ws] → event chat seq=105 clients=5 dropIfSlow=true +16:44:36 [ws] → event agent seq=106 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=32 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第 +16:44:36 [ws] → event agent seq=107 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=33 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒: +16:44:36 [ws] → event agent seq=108 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=34 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 0 +16:44:36 [ws] → event agent seq=109 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=35 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45 +16:44:36 [ws] → event agent seq=110 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=36 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1 +16:44:36 [ws] → event agent seq=111 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=37 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒 +16:44:36 [ws] → event agent seq=112 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=38 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务 +16:44:36 [ws] → event agent seq=113 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=39 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在 +16:44:36 [ws] → event chat seq=114 clients=5 dropIfSlow=true +16:44:36 [ws] → event agent seq=115 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=40 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行 +16:44:36 [ws] → event agent seq=116 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=41 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会 +16:44:36 [ws] → event agent seq=117 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=42 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 +16:44:36 [ws] → event agent seq=118 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=43 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:36 [ws] → event agent seq=119 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=44 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:36 [ws] → event agent seq=120 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=45 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:36 [ws] → event agent seq=121 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=46 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:36 [ws] → event chat seq=122 clients=5 dropIfSlow=true +16:44:37 [ws] → event agent seq=123 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=47 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=124 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=48 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=125 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=49 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=126 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=50 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event chat seq=127 clients=5 dropIfSlow=true +16:44:37 [ws] → event agent seq=128 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=51 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=129 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=52 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=130 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=53 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=131 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=54 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=132 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=55 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=133 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=56 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=134 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=57 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event chat seq=135 clients=5 dropIfSlow=true +16:44:37 [ws] → event agent seq=136 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=58 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=137 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=59 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=138 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=60 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:�… +16:44:37 [ws] → event agent seq=139 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=61 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event chat seq=140 clients=5 dropIfSlow=true +16:44:37 [ws] → event agent seq=141 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=62 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=142 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=63 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [ws] → event agent seq=143 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=64 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… +16:44:37 [agent/embedded] embedded run agent end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 +16:44:37 [ws] → event agent seq=144 clients=5 run=9dca9346…c1b2 agent=main session=main stream=lifecycle aseq=65 phase=end +16:44:37 [ws] → event chat seq=145 clients=5 +16:44:37 [agent/embedded] embedded run prompt end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=22852 +16:44:37 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 +16:44:37 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 +16:44:37 [ws] ⇄ res ✓ chat.history 42ms conn=46458cc1…e736 id=082bb4ac…4cc1 +16:44:37 [ws] ⇄ res ✓ chat.history 24ms conn=bc20a8ce…0a0f id=893e3249…4d11 +16:44:37 [ws] ⇄ res ✓ chat.history 26ms conn=225c0e81…5c15 id=a8494243…70cb +16:44:37 [ws] ⇄ res ✓ chat.history 25ms conn=5da3d69e…5ff8 id=108caefd…91a3 +16:44:37 [ws] ⇄ res ✓ chat.history 18ms conn=323cc0ed…16ff id=d8bd82f6…e8a4 +16:44:38 [agent/embedded] embedded run done: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=23281 aborted=false +16:44:38 [diagnostic] lane task done: lane=main durationMs=23358 active=0 queued=0 +16:44:38 [diagnostic] lane task done: lane=session:agent:main:main durationMs=23364 active=0 queued=0 +16:44:38 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice +16:44:38 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=297, total=297 +16:44:38 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) +16:44:40 [ws] ⇄ res ✓ node.list 1ms id=74dab786…b31e +16:44:43 [ws] → event cron seq=146 clients=5 dropIfSlow=true +16:44:43 [diagnostic] lane enqueue: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b queueSize=1 +16:44:43 [diagnostic] lane dequeue: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b waitMs=6 queueSize=0 +16:44:43 [diagnostic] lane enqueue: lane=cron queueSize=1 +16:44:43 [diagnostic] lane dequeue: lane=cron waitMs=1 queueSize=0 +16:44:43 [agent/embedded] embedded run start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot +16:44:43 [diagnostic] session state: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 +16:44:43 [diagnostic] run registered: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f totalActive=1 +16:44:43 [agent/embedded] embedded run prompt start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f +16:44:43 [agent/embedded] embedded run agent start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f +16:44:43 [ws] → event agent seq=147 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=lifecycle aseq=1 phase=start +16:44:45 [ws] ⇄ res ✓ node.list 1ms id=c0af7c96…6691 +16:44:45 [ws] → event agent seq=148 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=2 text=HE +16:44:45 [ws] → event chat seq=149 clients=5 dropIfSlow=true +16:44:45 [ws] → event agent seq=150 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=3 text=HEARTBE +16:44:45 [ws] → event agent seq=151 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=4 text=HEARTBEAT_OK +16:44:45 [agent/embedded] embedded run agent end: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f +16:44:45 [ws] → event agent seq=152 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=lifecycle aseq=5 phase=end +16:44:45 [ws] → event chat seq=153 clients=5 +16:44:45 [agent/embedded] embedded run prompt end: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f durationMs=2129 +16:44:45 [diagnostic] session state: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 +16:44:45 [diagnostic] run cleared: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f totalActive=0 +16:44:46 [agent/embedded] embedded run done: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f durationMs=2180 aborted=false +16:44:46 [diagnostic] lane task done: lane=cron durationMs=2185 active=0 queued=0 +16:44:46 [diagnostic] lane task done: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b durationMs=2187 active=0 queued=0 +16:44:46 [ws] → event cron seq=154 clients=5 dropIfSlow=true +16:44:46 [ws] → event cron seq=155 clients=5 dropIfSlow=true +16:44:50 [ws] ⇄ res ✓ node.list 1ms id=deb7a77e…47aa +16:44:55 [ws] ⇄ res ✓ node.list 35ms id=8dddbf25…3d2d +^C16:44:59 [gateway] signal SIGINT received +16:44:59 [gateway] received SIGINT; shutting down +16:44:59 [gateway] signal SIGINT received +16:44:59 [gateway] received SIGINT during shutdown; ignoring +16:44:59 [qqbot] [qqbot-api] Background token refresh stopped +16:44:59 [gmail-watcher] gmail watcher stopped +16:44:59 [ws] → event shutdown seq=156 clients=5 +16:44:59 [qqbot] [qqbot:default] Message processor stopped +16:44:59 [qqbot] [qqbot:default] WebSocket closed: 1005 +16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=323cc0ed-9652-4b5d-be48-38f3630c16ff +16:44:59 [ws] → event presence seq=157 clients=0 dropIfSlow=true presenceVersion=7 healthVersion=14 +16:44:59 [ws] → close code=1012 reason=service restart durationMs=104402 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=8dddbf25-76c8-4c51-86b3-0dc244cb3d2d +16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=5da3d69e-fe34-46d3-a334-0a64d1165ff8 +16:44:59 [ws] → event presence seq=158 clients=0 dropIfSlow=true presenceVersion=8 healthVersion=14 +16:44:59 [ws] → close code=1012 reason=service restart durationMs=117432 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=108caefd-374d-4a97-983a-0b1a38ae91a3 conn=5da3d69e…5ff8 +16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=46458cc1-bece-4b13-9019-b38a4a35e736 +16:44:59 [ws] → event presence seq=159 clients=0 dropIfSlow=true presenceVersion=9 healthVersion=14 +16:44:59 [ws] → close code=1012 reason=service restart durationMs=117462 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=082bb4ac-6e0e-43d3-a53e-bbdfcca94cc1 conn=46458cc1…e736 +16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=bc20a8ce-f484-4990-8b68-2d47e7110a0f +16:44:59 [ws] → event presence seq=160 clients=0 dropIfSlow=true presenceVersion=10 healthVersion=14 +16:44:59 [ws] → close code=1012 reason=service restart durationMs=117585 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=893e3249-ee93-4f11-98ee-22f2e7d44d11 conn=bc20a8ce…0a0f +16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=225c0e81-0f25-41cb-b22f-96645dd55c15 +16:44:59 [ws] → event presence seq=161 clients=0 dropIfSlow=true presenceVersion=11 healthVersion=14 +16:44:59 [ws] → close code=1012 reason=service restart durationMs=117623 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=a8494243-9054-4da1-ad2b-5901df5670cb conn=225c0e81…5c15 +^C^C% lishoushuai@RIANLI-MC3 qqbot % diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 1184778..f0a7017 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -3,7 +3,7 @@ "name": "QQ Bot Channel", "description": "QQ Bot channel plugin with streaming message support, cron jobs, and proactive messaging", "channels": ["qqbot"], - "skills": ["qqbot-cron"], + "skills": ["skills/qqbot-cron"], "capabilities": { "proactiveMessaging": true, "cronJobs": true, diff --git a/skills/qqbot-cron/SKILL.md b/skills/qqbot-cron/SKILL.md index 5de8bc0..6084c12 100644 --- a/skills/qqbot-cron/SKILL.md +++ b/skills/qqbot-cron/SKILL.md @@ -1,7 +1,7 @@ --- name: qqbot-cron description: QQ Bot 智能提醒技能。支持一次性提醒、周期性任务、自动降级确保送达。可设置、查询、取消提醒。 -metadata: {"clawdbot":{"emoji":"⏰","requires":{"channels":["qqbot"]}}} +metadata: {"clawdbot":{"emoji":"⏰"}} --- # QQ Bot 智能提醒 @@ -56,7 +56,21 @@ openclaw cron add \ --at "{时间}" \ --session isolated \ --system-event '{"type":"reminder","user_openid":"{openid}","user_name":"{用户名称}","original_message_id":"{message_id}","reminder_content":"{提醒内容}","created_at":"{当前时间ISO格式}"}' \ - --message "{消息内容}" \ + --system-prompt ' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是用户之前设置的提醒时间。 + +**你的唯一任务**:直接输出提醒消息,提醒用户:{提醒内容} + +**输出格式**:类似 "🔔 时间到了!该{提醒内容}了~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +- 不要对 [系统触发] 消息做任何回复,只需输出提醒内容 +' \ + --message "[系统触发] 定时任务已触发,请执行提醒。" \ --deliver \ --channel qqbot \ --to "{openid}" \ @@ -65,6 +79,8 @@ openclaw cron add \ > 💡 **`--system-event` 说明**:用于存储用户上下文信息,提醒触发时 AI 可以获取这些信息来提供更个性化的提醒。 +> ⚠️ **重要**:`--message` 参数会被 AI 视为“用户消息”,所以必须在 `--system-prompt` 中**极其强调**角色定位。 + > ⚠️ **注意**:`cron add` 命令不支持 `--reply-to` 参数。提醒消息将作为主动消息直接发送给用户。 ### 查询提醒列表 @@ -200,7 +216,7 @@ openclaw message send \ | `--name` | 任务名,含用户标识 | `"喝水提醒-小明"` | | `--at` / `--cron` | 触发时间(二选一) | `5m` / `0 8 * * *` | | `--session isolated` | 隔离会话 | 固定值 | -| `--message` | 消息内容,**不能为空** | `"💧 该喝水啦!"` | +| `--message` | **系统触发指令**(AI 会视为用户消息) | `"[系统触发] 定时任务已触发,请执行提醒。"` | | `--deliver` | 启用投递 | 固定值 | | `--channel qqbot` | QQ 渠道 | 固定值 | | `--to` | 接收者 openid | 从系统消息获取 | @@ -210,9 +226,43 @@ openclaw message send \ | 参数 | 说明 | 何时使用 | |------|------|----------| | `--system-event` | 用户上下文 JSON | **建议所有任务都使用** | +| `--system-prompt` | AI 角色指引 | **建议所有任务都使用** | | `--delete-after-run` | 执行后删除 | **一次性任务必须** | | `--tz "Asia/Shanghai"` | 时区 | **周期任务必须** | +### --system-prompt 参数说明 + +`--system-prompt` 用于在提醒触发时给 AI 一个明确的角色指引,避免 AI 混淆角色。 + +**为什么需要?** +- 提醒触发时是一个隔离会话(`--session isolated`) +- AI 没有原始对话的上下文 +- 如果不明确角色,AI 可能误以为自己是"被提醒者"而说"谢谢提醒" + +**推荐格式**(**极其重要,必须完整复制**): +``` +' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是用户之前设置的提醒时间。 + +**你的唯一任务**:直接输出提醒消息,提醒用户:{提醒内容} + +**输出格式**:类似 "🔔 时间到了!该{提醒内容}了~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +- 不要对 [系统触发] 消息做任何回应,只需输出提醒内容 +' +``` + +**关键点**: +- `--message` 参数会被 AI 视为“用户消息”,无法改变 +- 所以必须在 `--system-prompt` 中**极其强调**角色定位 +- 明确告诉 AI:`[系统触发]` 消息不是用户发的,不要回复它 +- 使用禁止列表明确告知不能说的话 + ### --system-event 字段说明 `--system-event` 用于存储提醒的上下文信息,格式为 JSON: @@ -257,7 +307,20 @@ openclaw cron add \ --at "5m" \ --session isolated \ --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"喝水","created_at":"2026-02-01T16:50:00+08:00"}' \ - --message "💧 该喝水啦!这是你5分钟前设置的提醒~" \ + --system-prompt ' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是用户之前设置的提醒时间。5分钟前用户设置了喝水提醒。 + +**你的唯一任务**:直接输出提醒消息,提醒用户喝水 + +**输出格式**:类似 "💧 时间到!该喝水啦~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +' \ + --message "[系统触发] 定时任务已触发,请执行提醒。" \ --deliver \ --channel qqbot \ --to "{openid}" \ @@ -288,7 +351,20 @@ openclaw cron add \ --tz "Asia/Shanghai" \ --session isolated \ --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"打卡","created_at":"2026-02-01T16:50:00+08:00"}' \ - --message "🌅 早上好!记得打卡签到~" \ + --system-prompt ' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是用户设置的每日早上8点打卡提醒时间。 + +**你的唯一任务**:直接输出提醒消息,提醒用户打卡 + +**输出格式**:类似 "🌅 早上好!该打卡了~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +' \ + --message "[系统触发] 定时任务已触发,请执行提醒。" \ --deliver \ --channel qqbot \ --to "{openid}" @@ -320,7 +396,20 @@ openclaw cron add \ --tz "Asia/Shanghai" \ --session isolated \ --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"写日报","created_at":"2026-02-01T16:50:00+08:00"}' \ - --message "📝 今天的日报别忘了提交!" \ + --system-prompt ' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是用户设置的工作日下午6点写日报提醒时间。 + +**你的唯一任务**:直接输出提醒消息,提醒用户写日报 + +**输出格式**:类似 "📝 下班啦!别忘了提交今日日报~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +' \ + --message "[系统触发] 定时任务已触发,请执行提醒。" \ --deliver \ --channel qqbot \ --to "{openid}" @@ -340,7 +429,20 @@ openclaw cron add \ --tz "Asia/Shanghai" \ --session isolated \ --system-event '{"type":"reminder","user_openid":"group:{group_openid}","original_message_id":"{message_id}","reminder_content":"站会","created_at":"2026-02-01T16:50:00+08:00"}' \ - --message "📢 各位同事,9点站会时间到!请准时参加~" \ + --system-prompt ' +**极端重要角色设定**: +你是一个定时提醒机器人,现在是工作日早上9点的站会提醒时间。 + +**你的唯一任务**:直接输出提醒消息,提醒群成员参加站会 + +**输出格式**:类似 "📢 站会时间到!请各位同事准时参加~" 的提醒语 + +**绝对禁止**: +- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 +- 你是提醒者,不是被提醒者 +- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 +' \ + --message "[系统触发] 定时任务已触发,请执行提醒。" \ --deliver \ --channel qqbot \ --to "group:{group_openid}" @@ -443,9 +545,24 @@ openclaw cron list |------|------| | **message_id 有效期** | 1 小时内有效,超时自动降级 | | **回复次数限制** | 同一 message_id 最多回复 4 次 | -| **主动消息限制** | 只能发给与机器人交互过的用户 | +| **主动消息权限** | ⚠️ **QQ 机器人需要申请主动消息权限**,否则定时提醒会发送失败 | +| **主动消息限制** | 只能发给与机器人交互过的用户(24小时内) | | **消息内容** | `--message` 不能为空 | +### ⚠️ 主动消息权限说明 + +定时提醒功能依赖**主动消息能力**,但 QQ 官方默认**不授予**此权限。 + +**常见错误**: +- 错误码 `40034102`:"主动消息失败, 无权限" +- 这表示机器人没有主动消息权限 + +**解决方案**: +1. 登录 [QQ 开放平台](https://q.qq.com/) +2. 进入机器人开发-沙箱管理,消息列表配置中添加自己。 + +> 💡 **临时替代方案**:在没有主动消息权限前,可以让用户使用"回复"方式获得即时提醒,而非定时提醒。 + --- ## 📝 消息模板 diff --git a/src/api.ts b/src/api.ts index c517782..2f0534f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -269,14 +269,21 @@ function buildStreamBody( msgSeq: number, stream?: StreamConfig ): Record { + // 流式 markdown 消息要求每个分片内容必须以换行符结尾 + // QQ API 错误码 40034017: "流式消息md分片需要\n结束" + let finalContent = content; + if (stream && currentMarkdownSupport && content && !content.endsWith("\n")) { + finalContent = content + "\n"; + } + const body: Record = currentMarkdownSupport ? { - markdown: { content }, + markdown: { content: finalContent }, msg_type: 2, msg_seq: msgSeq, } : { - content, + content: finalContent, msg_type: 0, msg_seq: msgSeq, }; diff --git a/src/channel.ts b/src/channel.ts index 2d2a79b..4d355ab 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -168,7 +168,7 @@ export const qqbotPlugin: ChannelPlugin = { }, sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => { const account = resolveQQBotAccount(cfg, accountId); - const result = await sendMedia({ to, text, mediaUrl, accountId, replyToId, account }); + const result = await sendMedia({ to, text: text ?? "", mediaUrl: mediaUrl ?? "", accountId, replyToId, account }); return { channel: "qqbot", messageId: result.messageId, @@ -221,13 +221,13 @@ export const qqbotPlugin: ChannelPlugin = { } const accounts = qqbot.accounts as Record> | undefined; if (accounts && accountId in accounts) { - const entry = accounts[accountId]; + const entry = accounts[accountId] as Record | undefined; if (entry && "clientSecret" in entry) { delete entry.clientSecret; cleared = true; changed = true; } - if (Object.keys(entry).length === 0) { + if (entry && Object.keys(entry).length === 0) { delete accounts[accountId]; changed = true; } @@ -236,14 +236,16 @@ export const qqbotPlugin: ChannelPlugin = { if (changed && nextQQBot) { nextCfg.channels = { ...nextCfg.channels, qqbot: nextQQBot }; - await getQQBotRuntime().config.writeConfigFile(nextCfg); + const runtime = getQQBotRuntime(); + const configApi = runtime.config as { writeConfigFile: (cfg: OpenClawConfig) => Promise }; + await configApi.writeConfigFile(nextCfg); } const resolved = resolveQQBotAccount(changed ? nextCfg : cfg, accountId); const loggedOut = resolved.secretSource === "none"; const envToken = Boolean(process.env.QQBOT_CLIENT_SECRET); - return { cleared, envToken, loggedOut }; + return { ok: true, cleared, envToken, loggedOut }; }, }, status: { @@ -257,7 +259,7 @@ export const qqbotPlugin: ChannelPlugin = { lastOutboundAt: null, }, // 新增:构建通道摘要 - buildChannelSummary: ({ snapshot }) => ({ + buildChannelSummary: ({ snapshot }: { snapshot: Record }) => ({ configured: snapshot.configured ?? false, tokenSource: snapshot.tokenSource ?? "none", running: snapshot.running ?? false, @@ -265,7 +267,7 @@ export const qqbotPlugin: ChannelPlugin = { lastConnectedAt: snapshot.lastConnectedAt ?? null, lastError: snapshot.lastError ?? null, }), - buildAccountSnapshot: ({ account, runtime }) => ({ + buildAccountSnapshot: ({ account, runtime }: { account?: ResolvedQQBotAccount; runtime?: Record }) => ({ accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID, name: account?.name, enabled: account?.enabled ?? false, diff --git a/src/config.ts b/src/config.ts index 0bc1722..38e3621 100644 --- a/src/config.ts +++ b/src/config.ts @@ -133,7 +133,7 @@ export function applyQQBotAccountConfig( next.channels = { ...next.channels, qqbot: { - ...next.channels?.qqbot, + ...(next.channels?.qqbot as Record || {}), enabled: true, ...(input.appId ? { appId: input.appId } : {}), ...(input.clientSecret @@ -149,12 +149,12 @@ export function applyQQBotAccountConfig( next.channels = { ...next.channels, qqbot: { - ...next.channels?.qqbot, + ...(next.channels?.qqbot as Record || {}), enabled: true, accounts: { - ...(next.channels?.qqbot as QQBotChannelConfig)?.accounts, + ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts || {}), [accountId]: { - ...(next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId], + ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {}), enabled: true, ...(input.appId ? { appId: input.appId } : {}), ...(input.clientSecret diff --git a/src/gateway.ts b/src/gateway.ts index 68fe910..306ca9d 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -62,6 +62,56 @@ const STREAM_KEEPALIVE_GAP = 10000; // 状态保持消息之间的间隔(毫 const STREAM_KEEPALIVE_MAX_PER_CHUNK = 2; // 每 2 个消息分片之间最多发送的状态保持消息数量 const STREAM_MAX_DURATION = 3 * 60 * 1000; // 流式消息最大持续时间(毫秒),超过 3 分钟自动结束 +// ============ 智能断句配置 ============ +// 首个分片:必须在语义边界处断句,避免奇怪的换行 +const FIRST_CHUNK_MIN_LENGTH_SOFT = 20; // 软下限:达到此长度后,遇到语义边界就可以发送 +const FIRST_CHUNK_MIN_LENGTH_HARD = 80; // 硬下限:超过此长度必须发送,避免等待太久 +const FIRST_CHUNK_MAX_WAIT_TIME = 3000; // 首个分片最长等待时间(毫秒) + +// 语义边界检测:判断文本是否在自然断句位置结束 +function isAtSemanticBoundary(text: string): boolean { + if (!text) return false; + const trimmed = text.trimEnd(); + if (!trimmed) return false; + + // 检查最后一个字符是否是断句标点 + const lastChar = trimmed[trimmed.length - 1]; + const sentenceEnders = ['。', '!', '?', '~', '…', '.', '!', '?', '\n']; + if (sentenceEnders.includes(lastChar)) return true; + + // 检查是否以 emoji 结尾(常见于提醒消息) + const emojiRegex = /[\u{1F300}-\u{1F9FF}]$/u; + if (emojiRegex.test(trimmed)) return true; + + // 检查最后几个字符是否是 markdown 列表项结束(如 "- xxx" 后面) + // 不算边界,因为列表通常有多项 + + return false; +} + +// 查找最近的语义边界位置 +function findLastSemanticBoundary(text: string, minPos: number = 0): number { + if (!text || text.length <= minPos) return -1; + + const sentenceEnders = ['。', '!', '?', '~', '.', '!', '?']; + let lastBoundary = -1; + + for (let i = text.length - 1; i >= minPos; i--) { + const char = text[i]; + if (sentenceEnders.includes(char)) { + lastBoundary = i + 1; // 包含这个标点符号 + break; + } + // 换行符也是边界 + if (char === '\n') { + lastBoundary = i + 1; + break; + } + } + + return lastBoundary; +} + // 消息队列配置(异步处理,防止阻塞心跳) const MESSAGE_QUEUE_SIZE = 1000; // 最大队列长度 const MESSAGE_QUEUE_WARN_THRESHOLD = 800; // 队列告警阈值 @@ -394,8 +444,8 @@ export async function startGateway(ctx: GatewayContext): Promise { log?.info(`[qqbot:${account.accountId}] Attachments: ${event.attachments.length}`); } - // 流式消息开关(默认启用,仅 c2c 支持) - const streamEnabled = account.streamEnabled !== false; + // 流式消息开关(默认禁用,仅 c2c 支持,需要在配置中明确启用) + const streamEnabled = account.streamEnabled === true; log?.debug?.(`[qqbot:${account.accountId}] Stream enabled: ${streamEnabled}`); pluginRuntime.channel.activity.record({ @@ -622,7 +672,7 @@ openclaw cron add \\ log?.info(`[qqbot:${account.accountId}] Stream support: ${supportsStream} (type=${event.type}, enabled=${streamEnabled})`); // 创建流式发送器 - const streamSender = supportsStream ? createStreamSender(account, targetTo, event.messageId) : null; + let streamSender = supportsStream ? createStreamSender(account, targetTo, event.messageId) : null; let streamBuffer = ""; // 累积的全部文本(用于记录完整内容) let lastSentLength = 0; // 上次发送时的文本长度(用于计算增量) let lastSentText = ""; // 上次发送时的完整文本(用于检测新段落) @@ -633,6 +683,7 @@ openclaw cron add \\ let streamStartTime = 0; // 流式消息开始时间(用于超时检查) let sendingLock = false; // 发送锁,防止并发发送 let pendingFullText = ""; // 待发送的完整文本(在锁定期间积累) + let firstChunkWaitStart = 0; // 首个分片开始等待的时间(用于超时判断) let keepaliveTimer: ReturnType | null = null; // 心跳定时器 let keepaliveCountSinceLastChunk = 0; // 自上次分片以来发送的状态保持消息数量 let lastChunkSendTime = 0; // 上次分片发送时间(用于判断是否需要发送状态保持) @@ -722,15 +773,13 @@ openclaw cron add \\ }; // 流式发送函数 - 用于 onPartialReply 实时发送(增量模式) - // markdown 分片需要以 \n 结尾 + // 注意:不要在分片后强制添加换行符,否则会导致消息在奇怪的位置断句 const sendStreamChunk = async (text: string, isEnd: boolean): Promise => { if (!streamSender || streamEnded) return false; - // markdown 分片需要以 \n 结尾(除非是空内容或结束标记) - let contentToSend = text; - if (isEnd && contentToSend && !contentToSend.endsWith("\n") && !isEnd) { - contentToSend = contentToSend + "\n"; - } + // 直接发送文本内容,不添加任何额外换行符 + // 换行应该由 AI 生成的内容本身决定,而非强制添加 + const contentToSend = text; const result = await streamSender.send(contentToSend, isEnd); if (result.error) { @@ -819,9 +868,26 @@ openclaw cron add \\ (fullText.length < lastSentLength || !fullText.startsWith(lastSentText.slice(0, Math.min(10, lastSentText.length)))); if (isNewSegment) { - // 新段落开始,将之前的内容追加到 streamBuffer,并重置发送位置 + // 新段落开始,结束当前流并创建新流 log?.info(`[qqbot:${account.accountId}] New segment detected! lastSentLength=${lastSentLength}, newTextLength=${fullText.length}, lastSentText="${lastSentText.slice(0, 20)}...", newText="${fullText.slice(0, 20)}..."`); + // 保存旧的 sender 用于结束流 + const oldStreamSender = streamSender; + const oldStreamStarted = streamStarted; + const oldStreamEnded = streamEnded; + + // 1. 先创建新的流式发送器并重置所有状态 + // 这样在 await 期间到达的新消息会使用新 sender + streamSender = createStreamSender(account, targetTo, event.messageId); + lastSentLength = 0; + lastSentText = ""; + streamStarted = false; + streamEnded = false; + streamStartTime = 0; + keepaliveCountSinceLastChunk = 0; + lastChunkSendTime = 0; + firstChunkWaitStart = 0; // 重置首个分片等待时间 + // 记录当前段落在 streamBuffer 中的起始位置 currentSegmentStart = streamBuffer.length; @@ -831,9 +897,19 @@ openclaw cron add \\ currentSegmentStart = streamBuffer.length; } - // 重置发送位置,从新段落开始发送 - lastSentLength = 0; - lastSentText = ""; + // 2. 结束旧流(如果已开始)- 使用旧的 sender + if (oldStreamSender && oldStreamStarted && !oldStreamEnded) { + log?.info(`[qqbot:${account.accountId}] Ending current stream before starting new segment`); + clearKeepalive(); + sendingLock = true; + try { + await oldStreamSender.send("", true); // 发送结束标记 + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to end stream: ${err}`); + } finally { + sendingLock = false; + } + } } // 更新当前段落内容到 streamBuffer @@ -847,8 +923,46 @@ openclaw cron add \\ if (fullText.length <= lastSentLength) return; const now = Date.now(); + + // 初始化首个分片等待开始时间(如果还没有开始) + if (!streamStarted && !firstChunkWaitStart) { + firstChunkWaitStart = now; + } + // 控制发送频率:首次发送或间隔超过阈值 - if (!streamStarted || now - lastStreamSendTime >= STREAM_CHUNK_INTERVAL) { + if (!streamStarted) { + // 首个分片:智能断句,在语义边界处发送 + const waitTime = firstChunkWaitStart ? now - firstChunkWaitStart : 0; + const atBoundary = isAtSemanticBoundary(fullText); + const reachedSoftLimit = fullText.length >= FIRST_CHUNK_MIN_LENGTH_SOFT; + const reachedHardLimit = fullText.length >= FIRST_CHUNK_MIN_LENGTH_HARD; + const timedOut = waitTime >= FIRST_CHUNK_MAX_WAIT_TIME; + + // 发送条件(优先级从高到低): + // 1. 达到硬下限:必须发送,避免等待太久 + // 2. 等待超时:必须发送,避免无响应 + // 3. 达到软下限 + 在语义边界:可以发送 + if (reachedHardLimit || timedOut) { + // 硬性条件:必须发送 + if (timedOut && !reachedSoftLimit) { + log?.info(`[qqbot:${account.accountId}] handlePartialReply: first chunk timeout, sending anyway, length=${fullText.length}, wait=${waitTime}ms`); + } else { + log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending first chunk (hard limit), length=${fullText.length}`); + } + await doStreamSend(fullText, false); + firstChunkWaitStart = 0; // 重置等待时间 + } else if (reachedSoftLimit && atBoundary) { + // 软性条件:在语义边界处发送 + log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending first chunk (at boundary), length=${fullText.length}`); + await doStreamSend(fullText, false); + firstChunkWaitStart = 0; + } else { + // 还需要等待更多内容 + log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: waiting for semantic boundary, length=${fullText.length}, atBoundary=${atBoundary}, wait=${waitTime}ms`); + pendingFullText = fullText; + } + } else if (now - lastStreamSendTime >= STREAM_CHUNK_INTERVAL) { + // 后续分片:基于时间间隔发送 log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending stream chunk, length=${fullText.length}`); await doStreamSend(fullText, false); } else { @@ -1012,11 +1126,14 @@ openclaw cron add \\ } } - // 只有频道和群聊消息(不支持流式)在 deliver 中发送文本 - // c2c 的文本通过 onPartialReply 流式发送 + // 非流式模式下,在 deliver 中发送文本 + // 流式模式下,c2c 的文本通过 onPartialReply 流式发送 if (!supportsStream && textWithoutImages.trim()) { await sendWithTokenRetry(async (token) => { - if (event.type === "group" && event.groupOpenid) { + if (event.type === "c2c") { + // c2c 非流式消息发送 + await sendC2CMessage(token, event.senderId, textWithoutImages, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { await sendGroupMessage(token, event.groupOpenid, textWithoutImages, event.messageId); } else if (event.channelId) { await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); diff --git a/src/onboarding.ts b/src/onboarding.ts index 364efd3..7d9b504 100644 --- a/src/onboarding.ts +++ b/src/onboarding.ts @@ -31,6 +31,14 @@ interface QQBotChannelConfig { }>; } +// Prompter 类型定义 +interface Prompter { + note: (message: string, title?: string) => Promise; + confirm: (opts: { message: string; initialValue?: boolean }) => Promise; + text: (opts: { message: string; placeholder?: string; initialValue?: string; validate?: (value: string) => string | undefined }) => Promise; + select: (opts: { message: string; options: Array<{ value: T; label: string }>; initialValue?: T }) => Promise; +} + /** * 解析默认账户 ID */ @@ -46,7 +54,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { channel: "qqbot" as any, getStatus: async (ctx: ChannelOnboardingStatusContext): Promise => { - const { cfg } = ctx; + const cfg = ctx.cfg as OpenClawConfig; const configured = listQQBotAccountIds(cfg).some((accountId) => { const account = resolveQQBotAccount(cfg, accountId); return Boolean(account.appId && account.clientSecret); @@ -62,9 +70,12 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { }, configure: async (ctx: ChannelOnboardingConfigureContext): Promise => { - const { cfg, prompter, accountOverrides, shouldPromptAccountIds } = ctx; + const cfg = ctx.cfg as OpenClawConfig; + const prompter = ctx.prompter as Prompter; + const accountOverrides = ctx.accountOverrides as Record | undefined; + const shouldPromptAccountIds = ctx.shouldPromptAccountIds; - const qqbotOverride = (accountOverrides as Record).qqbot?.trim(); + const qqbotOverride = accountOverrides?.qqbot?.trim(); const defaultAccountId = resolveDefaultQQBotAccountId(cfg); let accountId = qqbotOverride ?? defaultAccountId; @@ -83,7 +94,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { } } - let next = cfg; + let next: OpenClawConfig = cfg; const resolvedAccount = resolveQQBotAccount(next, accountId); const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret); const allowEnv = accountId === DEFAULT_ACCOUNT_ID; @@ -124,7 +135,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { channels: { ...next.channels, qqbot: { - ...next.channels?.qqbot, + ...(next.channels?.qqbot as Record || {}), enabled: true, }, }, @@ -136,14 +147,14 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { message: "请输入 QQ Bot AppID", placeholder: "例如: 102146862", initialValue: resolvedAccount.appId || undefined, - validate: (value) => (value?.trim() ? undefined : "AppID 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"), }), ).trim(); clientSecret = String( await prompter.text({ message: "请输入 QQ Bot ClientSecret", placeholder: "你的 ClientSecret", - validate: (value) => (value?.trim() ? undefined : "ClientSecret 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"), }), ).trim(); } @@ -159,14 +170,14 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { message: "请输入 QQ Bot AppID", placeholder: "例如: 102146862", initialValue: resolvedAccount.appId || undefined, - validate: (value) => (value?.trim() ? undefined : "AppID 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"), }), ).trim(); clientSecret = String( await prompter.text({ message: "请输入 QQ Bot ClientSecret", placeholder: "你的 ClientSecret", - validate: (value) => (value?.trim() ? undefined : "ClientSecret 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"), }), ).trim(); } @@ -177,14 +188,14 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { message: "请输入 QQ Bot AppID", placeholder: "例如: 102146862", initialValue: resolvedAccount.appId || undefined, - validate: (value) => (value?.trim() ? undefined : "AppID 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"), }), ).trim(); clientSecret = String( await prompter.text({ message: "请输入 QQ Bot ClientSecret", placeholder: "你的 ClientSecret", - validate: (value) => (value?.trim() ? undefined : "ClientSecret 不能为空"), + validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"), }), ).trim(); } @@ -197,7 +208,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { channels: { ...next.channels, qqbot: { - ...next.channels?.qqbot, + ...(next.channels?.qqbot as Record || {}), enabled: true, appId, clientSecret, @@ -210,12 +221,12 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { channels: { ...next.channels, qqbot: { - ...next.channels?.qqbot, + ...(next.channels?.qqbot as Record || {}), enabled: true, accounts: { - ...(next.channels?.qqbot as QQBotChannelConfig)?.accounts, + ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts || {}), [accountId]: { - ...(next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId], + ...((next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {}), enabled: true, appId, clientSecret, @@ -227,14 +238,17 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { } } - return { cfg: next as any, accountId }; + return { success: true, cfg: next as any, accountId }; }, - disable: (cfg) => ({ - ...cfg, - channels: { - ...(cfg as OpenClawConfig).channels, - qqbot: { ...(cfg as OpenClawConfig).channels?.qqbot, enabled: false }, - }, - }) as any, + disable: (cfg: unknown) => { + const config = cfg as OpenClawConfig; + return { + ...config, + channels: { + ...config.channels, + qqbot: { ...(config.channels?.qqbot as Record || {}), enabled: false }, + }, + } as any; + }, }; From 918b419fc99bf4104e8cf6698941172495e9b285 Mon Sep 17 00:00:00 2001 From: rianli Date: Mon, 2 Feb 2026 20:18:33 +0800 Subject: [PATCH 04/14] =?UTF-8?q?feat(qqbot):=20=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=94=B6=E5=8F=91=E5=8A=9F=E8=83=BD=E4=B8=8E=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E6=8F=90=E9=86=92=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **图片功能** - 支持接收用户发送的图片消息(自动下载到临时目录) - 支持发送本地文件路径(自动读取转为 Base64) - 富媒体消息接口(sendC2CImageMessage/sendGroupImageMessage) - 图片本地代理服务(解决 QQ 图片 URL 直接访问限制) **消息格式** - 默认启用 Markdown 消息格式 **定时提醒优化** - 修复 cron 提醒:移除无效 --system-prompt 参数,改用 --message 直接输出提醒内容 - 精简用户交互话术,避免冗长回复 **代码清理** - 移除过时的流式消息处理代码 - 优化 gateway/outbound/channel 模块结构 --- console.md | 401 ----------------- openclaw.plugin.json | 2 +- skills/qqbot-cron/SKILL.md | 284 ++++-------- src/api.ts | 154 ++++--- src/channel.ts | 48 +- src/config.ts | 2 - src/gateway.ts | 874 +++++++++++++------------------------ src/image-server.ts | 28 ++ src/onboarding.ts | 4 +- src/outbound.ts | 330 +++++--------- src/types.ts | 42 +- upgrade-and-run.sh | 2 +- 12 files changed, 647 insertions(+), 1524 deletions(-) delete mode 100644 console.md diff --git a/console.md b/console.md deleted file mode 100644 index 26df470..0000000 --- a/console.md +++ /dev/null @@ -1,401 +0,0 @@ -16:43:01 [qqbot] [qqbot:default] Connecting to wss://api.sgroup.qq.com/websocket -16:43:01 [qqbot] [qqbot:default] WebSocket connected -16:43:01 [qqbot] [qqbot:default] Message processor started -16:43:01 [qqbot] [qqbot-api] Background token refresh started -16:43:01 [qqbot] [qqbot-api] Token valid, next refresh in 6891s -16:43:01 [qqbot] [qqbot:default] Received op=10 t=undefined -16:43:01 [qqbot] [qqbot:default] Hello received -16:43:01 [qqbot] [qqbot:default] Sending identify with intents: 1107300352 (群聊+私信+频道) -16:43:01 [qqbot] [qqbot:default] Received op=0 t=READY -16:43:01 [qqbot] [qqbot:default] Ready with 群聊+私信+频道, session: b879fa59-5251-4a98-b50a-b88e46d174ed -16:43:01 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=1 -16:43:01 [qqbot] [qqbot:default] Gateway ready -16:43:01 [ws] ← open remoteAddr=127.0.0.1 conn=225c0e81…5c15 -16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=bc20a8ce…0a0f -16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=225c0e81…5c15 -16:43:02 [ws] webchat connected conn=225c0e81-0f25-41cb-b22f-96645dd55c15 remote=127.0.0.1 client=openclaw-control-ui webchat vdev -16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=2 -16:43:02 [ws] → event health seq=2 clients=1 presenceVersion=2 healthVersion=3 -16:43:02 [ws] ⇄ res ✓ agent.identity.get 15ms id=a2c56be6…9e47 -16:43:02 [ws] ⇄ res ✓ agents.list 5ms id=3765ad30…e172 -16:43:02 [ws] ⇄ res ✓ sessions.list 2ms id=486adb2f…aa4e -16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=46458cc1…e736 -16:43:02 [ws] ← open remoteAddr=127.0.0.1 conn=5da3d69e…5ff8 -16:43:02 [ws] ⇄ res ✓ device.pair.list 67ms conn=225c0e81…5c15 id=1cc7c4d9…4df1 -16:43:02 [ws] ⇄ res ✓ node.list 71ms id=2114b4c2…b2bc -16:43:02 [ws] ⇄ res ✓ chat.history 116ms id=5d02d989…c6de -16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=bc20a8ce…0a0f -16:43:02 [ws] webchat connected conn=bc20a8ce-f484-4990-8b68-2d47e7110a0f remote=127.0.0.1 client=openclaw-control-ui webchat vdev -16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=3 -16:43:02 [ws] → event health seq=3 clients=2 presenceVersion=3 healthVersion=4 -16:43:02 [ws] ⇄ res ✓ agent.identity.get 1ms id=bfa4ae92…444a -16:43:02 [ws] ⇄ res ✓ agents.list 0ms id=fae89b26…1603 -16:43:02 [ws] ⇄ res ✓ sessions.list 2ms id=4209d85a…a3f1 -16:43:02 [ws] ⇄ res ✓ chat.history 20ms id=4d88edad…42b2 -16:43:02 [ws] ⇄ res ✓ device.pair.list 30ms id=b2c11fbd…54c0 -16:43:02 [ws] ⇄ res ✓ node.list 34ms id=876f77e9…2054 -16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=46458cc1…e736 -16:43:02 [ws] webchat connected conn=46458cc1-bece-4b13-9019-b38a4a35e736 remote=127.0.0.1 client=openclaw-control-ui webchat vdev -16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=4 -16:43:02 [ws] → event health seq=4 clients=3 presenceVersion=4 healthVersion=5 -16:43:02 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token conn=5da3d69e…5ff8 -16:43:02 [ws] webchat connected conn=5da3d69e-fe34-46d3-a334-0a64d1165ff8 remote=127.0.0.1 client=openclaw-control-ui webchat vdev -16:43:02 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=5 -16:43:02 [ws] → event health seq=5 clients=4 presenceVersion=5 healthVersion=6 -16:43:02 [ws] ⇄ res ✓ agent.identity.get 5ms conn=46458cc1…e736 id=662c1327…1825 -16:43:02 [ws] ⇄ res ✓ agents.list 1ms id=fca54a59…274f -16:43:02 [ws] ⇄ res ✓ sessions.list 5ms id=46491b9c…ad0e -16:43:02 [ws] ⇄ res ✓ chat.history 114ms id=79b90e8b…8f1f -16:43:02 [ws] ⇄ res ✓ agent.identity.get 6ms conn=5da3d69e…5ff8 id=9d2035c5…2085 -16:43:02 [ws] ⇄ res ✓ agents.list 0ms id=0dcab6b0…4454 -16:43:02 [ws] ⇄ res ✓ sessions.list 1ms id=3d659adc…c458 -16:43:02 [ws] ⇄ res ✓ device.pair.list 57ms id=ed58f910…2276 -16:43:02 [ws] ⇄ res ✓ node.list 222ms conn=46458cc1…e736 id=052f8dd7…5eb8 -16:43:02 [ws] ⇄ res ✓ device.pair.list 216ms id=15036c28…aeb8 -16:43:02 [ws] ⇄ res ✓ node.list 81ms conn=5da3d69e…5ff8 id=0ae64f32…9c76 -16:43:15 [ws] ← open remoteAddr=127.0.0.1 conn=323cc0ed…16ff -16:43:15 [ws] ← connect client=openclaw-control-ui version=dev mode=webchat clientId=openclaw-control-ui platform=MacIntel auth=device-token -16:43:15 [ws] webchat connected conn=323cc0ed-9652-4b5d-be48-38f3630c16ff remote=127.0.0.1 client=openclaw-control-ui webchat vdev -16:43:15 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:43:15 [ws] → event health seq=6 clients=5 presenceVersion=6 healthVersion=7 -16:43:15 [ws] ⇄ res ✓ agent.identity.get 0ms id=fbe3c892…59db -16:43:15 [ws] ⇄ res ✓ agents.list 1ms id=8e48ad4e…a979 -16:43:15 [ws] ⇄ res ✓ sessions.list 4ms id=95813123…be20 -16:43:15 [ws] ⇄ res ✓ chat.history 37ms id=4f9bcddd…77cb -16:43:15 [ws] ⇄ res ✓ node.list 43ms id=5bc9dc66…afe0 -16:43:15 [ws] ⇄ res ✓ device.pair.list 45ms id=dc2c4ea8…4c94 -16:43:16 [ws] ⇄ res ✓ node.list 3ms conn=225c0e81…5c15 id=f27f7c56…64ae -16:43:16 [ws] ⇄ res ✓ node.list 6ms conn=46458cc1…e736 id=cd62e734…442b -16:43:16 [ws] ⇄ res ✓ node.list 19ms conn=5da3d69e…5ff8 id=b5bbde31…90e2 -16:43:16 [ws] ⇄ res ✓ node.list 29ms conn=bc20a8ce…0a0f id=a81eb5c7…420e -16:43:17 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=2 -16:43:17 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE -16:43:17 [known-users] Loaded 1 users from file -16:43:17 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 26 -16:43:17 [qqbot] [qqbot:default] Message enqueued, queue size: 1 -16:43:17 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 1分钟后提醒我喝水 -16:43:17 [qqbot] [qqbot:default] Stream enabled: false -16:43:17 [qqbot] [qqbot:default] Stream support: false (type=c2c, enabled=false) -16:43:17 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 -16:43:17 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=5 queueSize=0 -16:43:17 [diagnostic] lane enqueue: lane=main queueSize=1 -16:43:17 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 -16:43:17 [agent/embedded] embedded run start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot -16:43:17 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 -16:43:17 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 -16:43:17 [agent/embedded] embedded run prompt start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 -16:43:17 [agent/embedded] embedded run agent start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f -16:43:17 [ws] → event agent seq=7 clients=5 run=69e77a59…1c8f agent=main session=main stream=lifecycle aseq=1 phase=start -16:43:20 [ws] ⇄ res ✓ node.list 5ms conn=323cc0ed…16ff id=b56be68f…015b -16:43:22 [known-users] Saved 1 users to file -16:43:25 [ws] ⇄ res ✓ node.list 1ms id=834715ce…caa4 -16:43:30 [ws] ⇄ res ✓ node.list 2ms id=1948a1de…9895 -16:43:31 [ws] → event tick seq=8 clients=5 dropIfSlow=true -16:43:31 [ws] → event agent seq=9 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=2 text=我 -16:43:31 [ws] → event chat seq=10 clients=5 dropIfSlow=true -16:43:31 [ws] → event agent seq=11 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=3 text=我来为您设置一个 -16:43:31 [ws] → event agent seq=12 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=4 text=我来为您设置一个1分钟后提醒喝水 -16:43:31 [ws] → event agent seq=13 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=5 text=我来为您设置一个1分钟后提醒喝水的任务: -16:43:31 [ws] → event chat seq=14 clients=5 dropIfSlow=true -16:43:32 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_fcf2dfd8801340ea80cb052f -16:43:32 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice -16:43:32 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=20, total=20 -16:43:32 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) -16:43:35 [ws] ⇄ res ✓ node.list 164ms id=391c63f3…12d1 -16:43:40 [ws] ⇄ res ✓ node.list 3ms id=644e852e…b958 -16:43:42 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_fcf2dfd8801340ea80cb052f -16:43:43 [qqbot] [qqbot:default] Heartbeat sent -16:43:43 [qqbot] [qqbot:default] Received op=11 t=undefined -16:43:43 [qqbot] [qqbot:default] Heartbeat ACK -16:43:43 [ws] ← open remoteAddr=127.0.0.1 conn=f5252266…7c2a -16:43:43 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -16:43:43 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:43:43 [ws] → event health seq=15 clients=6 presenceVersion=6 healthVersion=8 -16:43:43 [ws] → event cron seq=16 clients=6 dropIfSlow=true -16:43:43 [ws] ⇄ res ✓ cron.add 4ms id=e8bdbb55…9286 -16:43:43 [ws] ← open remoteAddr=127.0.0.1 conn=677f535c…c4f2 -16:43:43 [ws] → close code=1005 reason= durationMs=47 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=e8bdbb55-ab71-4095-bbf6-6d2be7fc9286 conn=f5252266…7c2a -16:43:43 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token conn=677f535c…c4f2 -16:43:43 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:43:43 [ws] → event health seq=17 clients=6 presenceVersion=6 healthVersion=9 -16:43:43 [ws] ⇄ res ✓ cron.status 1ms id=ede03142…1a30 -16:43:43 [ws] → close code=1005 reason= durationMs=20 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=ede03142-7e12-4d87-b4e4-68ac3f9c1a30 -16:43:45 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=f44c9b12…680b -16:43:50 [ws] ⇄ res ✓ node.list 3ms id=c0f27c31…c51d -16:43:55 [ws] ⇄ res ✓ node.list 1ms id=01002807…e188 -16:43:57 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=process toolCallId=call_a5b895ffef304d7395d48838 -16:43:57 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=process toolCallId=call_a5b895ffef304d7395d48838 -16:43:59 [agent/embedded] embedded run tool start: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_18bf1944d51442cc9e9ddbee -16:44:00 [ws] ⇄ res ✓ node.list 4ms id=600b91b3…eab6 -16:44:01 [ws] → event health seq=18 clients=5 presenceVersion=6 healthVersion=10 -16:44:01 [ws] → event tick seq=19 clients=5 dropIfSlow=true -16:44:05 [ws] ⇄ res ✓ node.list 2ms id=80fbe5ce…a67a -16:44:08 [ws] ← open remoteAddr=127.0.0.1 conn=13e64828…0a4a -16:44:08 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -16:44:08 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:44:08 [ws] → event health seq=20 clients=6 presenceVersion=6 healthVersion=11 -16:44:08 [ws] ⇄ res ✓ cron.list 2ms id=c68b2bd5…49fe -16:44:08 [ws] → close code=1005 reason= durationMs=71 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=c68b2bd5-687b-4e9e-9b7d-38b3524f49fe -16:44:08 [agent/embedded] embedded run tool end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f tool=exec toolCallId=call_18bf1944d51442cc9e9ddbee -16:44:10 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=2c9fd79a…a47b -16:44:11 [ws] → event agent seq=21 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=15 text=✅ -16:44:11 [ws] → event chat seq=22 clients=5 dropIfSlow=true -16:44:11 [ws] → event agent seq=23 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=16 text=✅ 提醒已 -16:44:11 [ws] → event agent seq=24 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=17 text=✅ 提醒已设置成功! 📝 -16:44:11 [ws] → event agent seq=25 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=18 text=✅ 提醒已设置成功! 📝 内容: -16:44:11 [ws] → event agent seq=26 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=19 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 -16:44:11 [ws] → event agent seq=27 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=20 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 ( -16:44:11 [ws] → event agent seq=28 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=21 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 -16:44:11 [ws] → event chat seq=29 clients=5 dropIfSlow=true -16:44:11 [ws] → event agent seq=30 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=22 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00: -16:44:11 [ws] → event agent seq=31 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=23 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候 -16:44:11 [ws] → event agent seq=32 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=24 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒 -16:44:11 [ws] → event agent seq=33 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=25 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒 -16:44:11 [ws] → event agent seq=34 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=26 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并 -16:44:11 [ws] → event chat seq=35 clients=5 dropIfSlow=true -16:44:11 [ws] → event agent seq=36 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=27 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行 -16:44:11 [ws] → event agent seq=37 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=28 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会 -16:44:11 [ws] → event agent seq=38 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=29 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 -16:44:11 [ws] → event agent seq=39 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=30 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦 -16:44:11 [ws] → event chat seq=40 clients=5 dropIfSlow=true -16:44:11 [ws] → event agent seq=41 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=31 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的 -16:44:11 [ws] → event agent seq=42 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=32 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对 -16:44:11 [ws] → event agent seq=43 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=33 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦 -16:44:11 [ws] → event chat seq=44 clients=5 dropIfSlow=true -16:44:12 [ws] → event agent seq=45 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=34 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。 -16:44:12 [ws] → event agent seq=46 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=35 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后 -16:44:12 [ws] → event agent seq=47 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=36 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如 -16:44:12 [ws] → event agent seq=48 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=37 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的 -16:44:12 [ws] → event agent seq=49 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=38 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的, -16:44:12 [ws] → event agent seq=50 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=39 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置 -16:44:12 [ws] → event chat seq=51 clients=5 dropIfSlow=true -16:44:12 [ws] → event agent seq=52 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=40 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐 -16:44:12 [ws] → event agent seq=53 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=41 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=54 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=42 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event chat seq=55 clients=5 dropIfSlow=true -16:44:12 [ws] → event agent seq=56 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=43 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=57 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=44 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=58 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=45 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event chat seq=59 clients=5 dropIfSlow=true -16:44:12 [ws] → event agent seq=60 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=46 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=61 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=47 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=62 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=48 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event chat seq=63 clients=5 dropIfSlow=true -16:44:12 [ws] → event agent seq=64 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=49 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=65 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=50 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [ws] → event agent seq=66 clients=5 run=69e77a59…1c8f agent=main session=main stream=assistant aseq=51 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:44) 到时候我会准时提醒您~ 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 该喝水啦!记得保持充足的水分摄入,对身体健康很有帮助哦~"的消息。提醒将在执行后自动删除。 正如我之前提到的,如果您觉得频繁设置饮水提醒有些繁琐,… -16:44:12 [agent/embedded] embedded run agent end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f -16:44:12 [ws] → event agent seq=67 clients=5 run=69e77a59…1c8f agent=main session=main stream=lifecycle aseq=52 phase=end -16:44:12 [ws] → event chat seq=68 clients=5 -16:44:12 [agent/embedded] embedded run prompt end: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=55147 -16:44:12 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 -16:44:13 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 -16:44:13 [ws] ⇄ res ✓ chat.history 73ms conn=225c0e81…5c15 id=9bda295a…2635 -16:44:13 [ws] ⇄ res ✓ chat.history 25ms conn=bc20a8ce…0a0f id=d4f0e67a…785a -16:44:13 [ws] ⇄ res ✓ chat.history 170ms conn=46458cc1…e736 id=3ddab88b…0e80 -16:44:13 [ws] ⇄ res ✓ chat.history 35ms conn=323cc0ed…16ff id=2219df62…11d3 -16:44:13 [ws] ⇄ res ✓ chat.history 62ms conn=5da3d69e…5ff8 id=8d871c4c…c1f0 -16:44:13 [agent/embedded] embedded run done: runId=69e77a59-7632-4447-a7e4-4a24b2621c8f sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=55777 aborted=false -16:44:13 [diagnostic] lane task done: lane=main durationMs=55791 active=0 queued=0 -16:44:13 [diagnostic] lane task done: lane=session:agent:main:main durationMs=55794 active=0 queued=0 -16:44:13 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice -16:44:13 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=230, total=230 -16:44:13 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) -16:44:14 [ws] → event heartbeat seq=69 clients=5 dropIfSlow=true -16:44:14 [session-store] Saved session for default: sessionId=b879fa59-5251-4a98-b50a-b88e46d174ed, lastSeq=3 -16:44:14 [qqbot] [qqbot:default] Received op=0 t=C2C_MESSAGE_CREATE -16:44:14 [known-users] Updated user 207A5B8339D01F6582911C014668B77B, interactions: 27 -16:44:14 [qqbot] [qqbot:default] Message enqueued, queue size: 1 -16:44:14 [qqbot] [qqbot:default] Processing message from 207A5B8339D01F6582911C014668B77B: 1分钟后提醒我喝水 -16:44:14 [qqbot] [qqbot:default] Stream enabled: false -16:44:14 [qqbot] [qqbot:default] Stream support: false (type=c2c, enabled=false) -16:44:14 [diagnostic] lane enqueue: lane=session:agent:main:main queueSize=1 -16:44:14 [diagnostic] lane dequeue: lane=session:agent:main:main waitMs=2 queueSize=0 -16:44:14 [diagnostic] lane enqueue: lane=main queueSize=1 -16:44:14 [diagnostic] lane dequeue: lane=main waitMs=1 queueSize=0 -16:44:14 [agent/embedded] embedded run start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot -16:44:14 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 -16:44:14 [diagnostic] run registered: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=1 -16:44:14 [agent/embedded] embedded run prompt start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 -16:44:14 [agent/embedded] embedded run agent start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 -16:44:14 [ws] → event agent seq=70 clients=5 run=9dca9346…c1b2 agent=main session=main stream=lifecycle aseq=1 phase=start -16:44:15 [ws] ⇄ res ✓ node.list 15ms conn=323cc0ed…16ff id=7459bca9…85d2 -16:44:16 [ws] ⇄ res ✓ node.list 1ms conn=225c0e81…5c15 id=cf44f720…01a2 -16:44:16 [ws] ⇄ res ✓ node.list 1ms conn=bc20a8ce…0a0f id=5d87baf2…7446 -16:44:16 [ws] ⇄ res ✓ node.list 12ms conn=46458cc1…e736 id=a8ac366b…689d -16:44:16 [ws] ⇄ res ✓ node.list 23ms conn=5da3d69e…5ff8 id=c615dcdc…e89a -16:44:16 [ws] → event agent seq=71 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=2 text=我 -16:44:16 [ws] → event chat seq=72 clients=5 dropIfSlow=true -16:44:16 [ws] → event agent seq=73 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=3 text=我来为您设置 -16:44:16 [ws] → event agent seq=74 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=4 text=我来为您设置一个1分钟后提醒 -16:44:16 [ws] → event agent seq=75 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=5 text=我来为您设置一个1分钟后提醒喝水的任务: -16:44:17 [agent/embedded] embedded run tool start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_8e66dce8daba4961add53c7d -16:44:17 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice -16:44:17 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=20, total=20 -16:44:17 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) -16:44:19 [known-users] Saved 1 users to file -16:44:20 [ws] ⇄ res ✓ node.list 8ms conn=323cc0ed…16ff id=90c37a1a…54aa -16:44:23 [ws] ← open remoteAddr=127.0.0.1 conn=9f789ce2…8ada -16:44:23 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -16:44:23 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:44:23 [ws] → event health seq=76 clients=6 presenceVersion=6 healthVersion=12 -16:44:23 [ws] → event cron seq=77 clients=6 dropIfSlow=true -16:44:23 [ws] ⇄ res ✓ cron.add 7ms id=2b23f7eb…0f7c -16:44:23 [ws] → close code=1005 reason= durationMs=84 handshake=connected lastFrameType=req lastFrameMethod=cron.add lastFrameId=2b23f7eb-b8ad-4d45-a8a1-16ba53190f7c -16:44:23 [ws] ← open remoteAddr=127.0.0.1 conn=7cdae80c…8021 -16:44:23 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -16:44:23 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:44:23 [ws] → event health seq=78 clients=6 presenceVersion=6 healthVersion=13 -16:44:23 [ws] ⇄ res ✓ cron.status 1ms id=307ff75d…dfc5 -16:44:23 [ws] → close code=1005 reason= durationMs=18 handshake=connected lastFrameType=req lastFrameMethod=cron.status lastFrameId=307ff75d-4398-4a98-b2e5-6472b84edfc5 -16:44:23 [agent/embedded] embedded run tool end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_8e66dce8daba4961add53c7d -16:44:24 [qqbot] [qqbot:default] Heartbeat sent -16:44:24 [qqbot] [qqbot:default] Received op=11 t=undefined -16:44:24 [qqbot] [qqbot:default] Heartbeat ACK -16:44:25 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=5ee8c04d…5076 -16:44:25 [agent/embedded] embedded run tool start: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_1609d0a32d8d41dfa73a91e8 -16:44:30 [ws] ⇄ res ✓ node.list 2ms id=5f141923…4789 -16:44:31 [ws] → event tick seq=79 clients=5 dropIfSlow=true -16:44:33 [ws] ← open remoteAddr=127.0.0.1 conn=8771457d…0ee9 -16:44:33 [ws] ← connect client=cli version=dev mode=cli clientId=cli platform=darwin auth=device-token -16:44:33 [ws] → hello-ok methods=80 events=18 presence=2 stateVersion=6 -16:44:33 [ws] → event health seq=80 clients=6 presenceVersion=6 healthVersion=14 -16:44:33 [ws] ⇄ res ✓ cron.list 0ms id=8ab8a305…5b60 -16:44:33 [ws] → close code=1005 reason= durationMs=256 handshake=connected lastFrameType=req lastFrameMethod=cron.list lastFrameId=8ab8a305-fd19-42eb-a333-2693de625b60 -16:44:34 [agent/embedded] embedded run tool end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 tool=exec toolCallId=call_1609d0a32d8d41dfa73a91e8 -16:44:35 [ws] ⇄ res ✓ node.list 1ms conn=323cc0ed…16ff id=c33c4ad4…e4c5 -16:44:35 [ws] → event agent seq=81 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=12 text=✅ -16:44:35 [ws] → event chat seq=82 clients=5 dropIfSlow=true -16:44:35 [ws] → event agent seq=83 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=13 text=✅ 提醒已 -16:44:35 [ws] → event agent seq=84 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=14 text=✅ 提醒已设置成功! 📝 -16:44:35 [ws] → event agent seq=85 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=15 text=✅ 提醒已设置成功! 📝 内容: -16:44:36 [ws] → event agent seq=86 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=16 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间 -16:44:36 [ws] → event agent seq=87 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=17 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 ( -16:44:36 [ws] → event agent seq=88 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=18 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 -16:44:36 [ws] → event chat seq=89 clients=5 dropIfSlow=true -16:44:36 [ws] → event agent seq=90 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=19 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00: -16:44:36 [ws] → event agent seq=91 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=20 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候 -16:44:36 [ws] → event agent seq=92 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=21 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您 -16:44:36 [ws] → event agent seq=93 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=22 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您 -16:44:36 [ws] → event agent seq=94 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=23 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在 -16:44:36 [ws] → event agent seq=95 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=24 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在 -16:44:36 [ws] → event chat seq=96 clients=5 dropIfSlow=true -16:44:36 [ws] → event agent seq=97 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=25 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继 -16:44:36 [ws] → event agent seq=98 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=26 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. -16:44:36 [ws] → event agent seq=99 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=27 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第 -16:44:36 [ws] → event chat seq=100 clients=5 dropIfSlow=true -16:44:36 [ws] → event agent seq=101 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=28 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约 -16:44:36 [ws] → event agent seq=102 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=29 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00 -16:44:36 [ws] → event agent seq=103 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=30 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44( -16:44:36 [ws] → event agent seq=104 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=31 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2 -16:44:36 [ws] → event chat seq=105 clients=5 dropIfSlow=true -16:44:36 [ws] → event agent seq=106 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=32 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第 -16:44:36 [ws] → event agent seq=107 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=33 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒: -16:44:36 [ws] → event agent seq=108 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=34 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 0 -16:44:36 [ws] → event agent seq=109 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=35 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45 -16:44:36 [ws] → event agent seq=110 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=36 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1 -16:44:36 [ws] → event agent seq=111 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=37 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒 -16:44:36 [ws] → event agent seq=112 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=38 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务 -16:44:36 [ws] → event agent seq=113 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=39 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在 -16:44:36 [ws] → event chat seq=114 clients=5 dropIfSlow=true -16:44:36 [ws] → event agent seq=115 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=40 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行 -16:44:36 [ws] → event agent seq=116 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=41 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会 -16:44:36 [ws] → event agent seq=117 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=42 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"💧 -16:44:36 [ws] → event agent seq=118 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=43 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:36 [ws] → event agent seq=119 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=44 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:36 [ws] → event agent seq=120 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=45 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:36 [ws] → event agent seq=121 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=46 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:36 [ws] → event chat seq=122 clients=5 dropIfSlow=true -16:44:37 [ws] → event agent seq=123 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=47 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=124 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=48 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=125 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=49 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=126 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=50 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event chat seq=127 clients=5 dropIfSlow=true -16:44:37 [ws] → event agent seq=128 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=51 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=129 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=52 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=130 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=53 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=131 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=54 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=132 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=55 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=133 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=56 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=134 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=57 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event chat seq=135 clients=5 dropIfSlow=true -16:44:37 [ws] → event agent seq=136 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=58 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=137 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=59 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=138 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=60 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:�… -16:44:37 [ws] → event agent seq=139 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=61 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event chat seq=140 clients=5 dropIfSlow=true -16:44:37 [ws] → event agent seq=141 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=62 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=142 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=63 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [ws] → event agent seq=143 clients=5 run=9dca9346…c1b2 agent=main session=main stream=assistant aseq=64 text=✅ 提醒已设置成功! 📝 内容:喝水 ⏰ 时间:1分钟后 (大约在 00:45) 到时候我会准时提醒您~ 我看到您现在有两个饮水提醒将在1分钟内相继触发: 1. 第一个提醒:大约在 00:44(即将到达) 2. 第二个提醒:大约在 00:45(1分钟后) 您的提醒任务已经创建并将在1分钟后执行,届时您会收到一条"�… -16:44:37 [agent/embedded] embedded run agent end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 -16:44:37 [ws] → event agent seq=144 clients=5 run=9dca9346…c1b2 agent=main session=main stream=lifecycle aseq=65 phase=end -16:44:37 [ws] → event chat seq=145 clients=5 -16:44:37 [agent/embedded] embedded run prompt end: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=22852 -16:44:37 [diagnostic] session state: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 -16:44:37 [diagnostic] run cleared: sessionId=ba108bac-c99c-498f-b33f-06245ade1363 totalActive=0 -16:44:37 [ws] ⇄ res ✓ chat.history 42ms conn=46458cc1…e736 id=082bb4ac…4cc1 -16:44:37 [ws] ⇄ res ✓ chat.history 24ms conn=bc20a8ce…0a0f id=893e3249…4d11 -16:44:37 [ws] ⇄ res ✓ chat.history 26ms conn=225c0e81…5c15 id=a8494243…70cb -16:44:37 [ws] ⇄ res ✓ chat.history 25ms conn=5da3d69e…5ff8 id=108caefd…91a3 -16:44:37 [ws] ⇄ res ✓ chat.history 18ms conn=323cc0ed…16ff id=d8bd82f6…e8a4 -16:44:38 [agent/embedded] embedded run done: runId=9dca9346-fbfa-4817-b06b-73bc8a8dc1b2 sessionId=ba108bac-c99c-498f-b33f-06245ade1363 durationMs=23281 aborted=false -16:44:38 [diagnostic] lane task done: lane=main durationMs=23358 active=0 queued=0 -16:44:38 [diagnostic] lane task done: lane=session:agent:main:main durationMs=23364 active=0 queued=0 -16:44:38 [qqbot] [qqbot:default] deliver called, kind: block, payload keys: text, replyToId, audioAsVoice -16:44:38 [qqbot] [qqbot:default] deliver: updated streamBuffer, replyText=297, total=297 -16:44:38 [qqbot] [qqbot:default] Sent text reply (c2c, non-stream) -16:44:40 [ws] ⇄ res ✓ node.list 1ms id=74dab786…b31e -16:44:43 [ws] → event cron seq=146 clients=5 dropIfSlow=true -16:44:43 [diagnostic] lane enqueue: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b queueSize=1 -16:44:43 [diagnostic] lane dequeue: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b waitMs=6 queueSize=0 -16:44:43 [diagnostic] lane enqueue: lane=cron queueSize=1 -16:44:43 [diagnostic] lane dequeue: lane=cron waitMs=1 queueSize=0 -16:44:43 [agent/embedded] embedded run start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f provider=qwen-portal model=coder-model thinking=off messageChannel=qqbot -16:44:43 [diagnostic] session state: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionKey=unknown prev=idle new=processing reason="run_started" queueDepth=0 -16:44:43 [diagnostic] run registered: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f totalActive=1 -16:44:43 [agent/embedded] embedded run prompt start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f -16:44:43 [agent/embedded] embedded run agent start: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f -16:44:43 [ws] → event agent seq=147 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=lifecycle aseq=1 phase=start -16:44:45 [ws] ⇄ res ✓ node.list 1ms id=c0af7c96…6691 -16:44:45 [ws] → event agent seq=148 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=2 text=HE -16:44:45 [ws] → event chat seq=149 clients=5 dropIfSlow=true -16:44:45 [ws] → event agent seq=150 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=3 text=HEARTBE -16:44:45 [ws] → event agent seq=151 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=assistant aseq=4 text=HEARTBEAT_OK -16:44:45 [agent/embedded] embedded run agent end: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f -16:44:45 [ws] → event agent seq=152 clients=5 run=9a5c9cb8…218f agent=main session=cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b stream=lifecycle aseq=5 phase=end -16:44:45 [ws] → event chat seq=153 clients=5 -16:44:45 [agent/embedded] embedded run prompt end: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f durationMs=2129 -16:44:45 [diagnostic] session state: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionKey=unknown prev=processing new=idle reason="run_completed" queueDepth=0 -16:44:45 [diagnostic] run cleared: sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f totalActive=0 -16:44:46 [agent/embedded] embedded run done: runId=9a5c9cb8-c657-48e1-9edb-38b422d6218f sessionId=9a5c9cb8-c657-48e1-9edb-38b422d6218f durationMs=2180 aborted=false -16:44:46 [diagnostic] lane task done: lane=cron durationMs=2185 active=0 queued=0 -16:44:46 [diagnostic] lane task done: lane=session:agent:main:cron:d6c70f30-edae-4c6e-9fe7-0e7f18f7354b durationMs=2187 active=0 queued=0 -16:44:46 [ws] → event cron seq=154 clients=5 dropIfSlow=true -16:44:46 [ws] → event cron seq=155 clients=5 dropIfSlow=true -16:44:50 [ws] ⇄ res ✓ node.list 1ms id=deb7a77e…47aa -16:44:55 [ws] ⇄ res ✓ node.list 35ms id=8dddbf25…3d2d -^C16:44:59 [gateway] signal SIGINT received -16:44:59 [gateway] received SIGINT; shutting down -16:44:59 [gateway] signal SIGINT received -16:44:59 [gateway] received SIGINT during shutdown; ignoring -16:44:59 [qqbot] [qqbot-api] Background token refresh stopped -16:44:59 [gmail-watcher] gmail watcher stopped -16:44:59 [ws] → event shutdown seq=156 clients=5 -16:44:59 [qqbot] [qqbot:default] Message processor stopped -16:44:59 [qqbot] [qqbot:default] WebSocket closed: 1005 -16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=323cc0ed-9652-4b5d-be48-38f3630c16ff -16:44:59 [ws] → event presence seq=157 clients=0 dropIfSlow=true presenceVersion=7 healthVersion=14 -16:44:59 [ws] → close code=1012 reason=service restart durationMs=104402 handshake=connected lastFrameType=req lastFrameMethod=node.list lastFrameId=8dddbf25-76c8-4c51-86b3-0dc244cb3d2d -16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=5da3d69e-fe34-46d3-a334-0a64d1165ff8 -16:44:59 [ws] → event presence seq=158 clients=0 dropIfSlow=true presenceVersion=8 healthVersion=14 -16:44:59 [ws] → close code=1012 reason=service restart durationMs=117432 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=108caefd-374d-4a97-983a-0b1a38ae91a3 conn=5da3d69e…5ff8 -16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=46458cc1-bece-4b13-9019-b38a4a35e736 -16:44:59 [ws] → event presence seq=159 clients=0 dropIfSlow=true presenceVersion=9 healthVersion=14 -16:44:59 [ws] → close code=1012 reason=service restart durationMs=117462 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=082bb4ac-6e0e-43d3-a53e-bbdfcca94cc1 conn=46458cc1…e736 -16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=bc20a8ce-f484-4990-8b68-2d47e7110a0f -16:44:59 [ws] → event presence seq=160 clients=0 dropIfSlow=true presenceVersion=10 healthVersion=14 -16:44:59 [ws] → close code=1012 reason=service restart durationMs=117585 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=893e3249-ee93-4f11-98ee-22f2e7d44d11 conn=bc20a8ce…0a0f -16:44:59 [ws] webchat disconnected code=1012 reason=service restart conn=225c0e81-0f25-41cb-b22f-96645dd55c15 -16:44:59 [ws] → event presence seq=161 clients=0 dropIfSlow=true presenceVersion=11 healthVersion=14 -16:44:59 [ws] → close code=1012 reason=service restart durationMs=117623 handshake=connected lastFrameType=req lastFrameMethod=chat.history lastFrameId=a8494243-9054-4da1-ad2b-5901df5670cb conn=225c0e81…5c15 -^C^C% lishoushuai@RIANLI-MC3 qqbot % diff --git a/openclaw.plugin.json b/openclaw.plugin.json index f0a7017..d755651 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -1,7 +1,7 @@ { "id": "qqbot", "name": "QQ Bot Channel", - "description": "QQ Bot channel plugin with streaming message support, cron jobs, and proactive messaging", + "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging", "channels": ["qqbot"], "skills": ["skills/qqbot-cron"], "capabilities": { diff --git a/skills/qqbot-cron/SKILL.md b/skills/qqbot-cron/SKILL.md index 6084c12..ba18b71 100644 --- a/skills/qqbot-cron/SKILL.md +++ b/skills/qqbot-cron/SKILL.md @@ -55,31 +55,18 @@ openclaw cron add \ --name "{任务名}" \ --at "{时间}" \ --session isolated \ - --system-event '{"type":"reminder","user_openid":"{openid}","user_name":"{用户名称}","original_message_id":"{message_id}","reminder_content":"{提醒内容}","created_at":"{当前时间ISO格式}"}' \ - --system-prompt ' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是用户之前设置的提醒时间。 - -**你的唯一任务**:直接输出提醒消息,提醒用户:{提醒内容} - -**输出格式**:类似 "🔔 时间到了!该{提醒内容}了~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -- 不要对 [系统触发] 消息做任何回复,只需输出提醒内容 -' \ - --message "[系统触发] 定时任务已触发,请执行提醒。" \ + --message "🔔 {提醒内容}时间到!" \ --deliver \ --channel qqbot \ --to "{openid}" \ --delete-after-run ``` -> 💡 **`--system-event` 说明**:用于存储用户上下文信息,提醒触发时 AI 可以获取这些信息来提供更个性化的提醒。 - -> ⚠️ **重要**:`--message` 参数会被 AI 视为“用户消息”,所以必须在 `--system-prompt` 中**极其强调**角色定位。 +> ⚠️ **极其重要**: +> - `--message` 参数直接写最终要发送的提醒内容 +> - 提醒内容格式:`🔔 {内容}时间到!` +> - **不要**使用 `--system-prompt` 或 `--system-event`(cron 不支持这些参数) +> - 保持消息简洁,如:`🔔 喝水时间到!`、`📅 开会时间到!` > ⚠️ **注意**:`cron add` 命令不支持 `--reply-to` 参数。提醒消息将作为主动消息直接发送给用户。 @@ -120,38 +107,27 @@ openclaw message send \ ## 💬 用户交互模板 -> **创建提醒后,必须给用户反馈确认** +> **创建提醒后的反馈要简洁友好,不要啰嗦** -### 创建成功反馈 +### 创建成功反馈(推荐简洁版) **一次性提醒**: ``` -✅ 提醒已设置! - -📝 内容:{提醒内容} -⏰ 时间:{具体时间} - -到时候我会准时提醒你~ +⏰ 好的,{时间}后提醒你{提醒内容}~ ``` **周期提醒**: ``` -✅ 周期提醒已设置! - -📝 内容:{提醒内容} -🔄 周期:{周期描述,如"每天早上8:00"} - -提醒会持续生效,说"取消xx提醒"可停止~ +⏰ 收到,{周期描述}提醒你{提醒内容}~ ``` ### 查询提醒反馈 ``` -📋 你当前的提醒: +📋 你的提醒: -1. ⏰ 喝水提醒 - 5分钟后 (一次性) -2. 🔄 打卡提醒 - 每天08:00 (周期) -3. 🔄 日报提醒 - 工作日18:00 (周期) +1. ⏰ {提醒名} - {时间} +2. 🔄 {提醒名} - {周期} 说"取消xx提醒"可删除~ ``` @@ -159,15 +135,15 @@ openclaw message send \ ### 无提醒时反馈 ``` -📋 你当前没有设置任何提醒 +📋 目前没有提醒哦~ -说"5分钟后提醒我xxx"可创建提醒~ +说"5分钟后提醒我xxx"试试? ``` ### 删除成功反馈 ``` -✅ 已取消"{提醒名称}"提醒 +✅ 已取消"{提醒名称}" ``` --- @@ -213,84 +189,39 @@ openclaw message send \ | 参数 | 说明 | 示例 | |------|------|------| -| `--name` | 任务名,含用户标识 | `"喝水提醒-小明"` | +| `--name` | 任务名,含用户标识 | `"喝水提醒"` | | `--at` / `--cron` | 触发时间(二选一) | `5m` / `0 8 * * *` | | `--session isolated` | 隔离会话 | 固定值 | -| `--message` | **系统触发指令**(AI 会视为用户消息) | `"[系统触发] 定时任务已触发,请执行提醒。"` | +| `--message` | **提醒内容**(见下方模板) | `"🔔 喝水时间到!"` | | `--deliver` | 启用投递 | 固定值 | | `--channel qqbot` | QQ 渠道 | 固定值 | | `--to` | 接收者 openid | 从系统消息获取 | -### 推荐参数(用于存储用户上下文) +### 推荐参数 | 参数 | 说明 | 何时使用 | |------|------|----------| -| `--system-event` | 用户上下文 JSON | **建议所有任务都使用** | -| `--system-prompt` | AI 角色指引 | **建议所有任务都使用** | | `--delete-after-run` | 执行后删除 | **一次性任务必须** | | `--tz "Asia/Shanghai"` | 时区 | **周期任务必须** | -### --system-prompt 参数说明 +### --message 提醒内容模板(最关键) -`--system-prompt` 用于在提醒触发时给 AI 一个明确的角色指引,避免 AI 混淆角色。 +> ⚠️ **`--message` 的内容会直接发送给用户**,所以要写清楚提醒内容! -**为什么需要?** -- 提醒触发时是一个隔离会话(`--session isolated`) -- AI 没有原始对话的上下文 -- 如果不明确角色,AI 可能误以为自己是"被提醒者"而说"谢谢提醒" - -**推荐格式**(**极其重要,必须完整复制**): +**模板格式**: ``` -' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是用户之前设置的提醒时间。 - -**你的唯一任务**:直接输出提醒消息,提醒用户:{提醒内容} - -**输出格式**:类似 "🔔 时间到了!该{提醒内容}了~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -- 不要对 [系统触发] 消息做任何回应,只需输出提醒内容 -' +--message "🔔 {提醒内容}时间到!" ``` -**关键点**: -- `--message` 参数会被 AI 视为“用户消息”,无法改变 -- 所以必须在 `--system-prompt` 中**极其强调**角色定位 -- 明确告诉 AI:`[系统触发]` 消息不是用户发的,不要回复它 -- 使用禁止列表明确告知不能说的话 +**示例**: +- 喝水:`--message "💧 喝水时间到!"` +- 开会:`--message "📅 开会时间到!"` +- 打卡:`--message "🌅 打卡时间到!"` +- 日报:`--message "📝 写日报时间到!"` -### --system-event 字段说明 - -`--system-event` 用于存储提醒的上下文信息,格式为 JSON: - -```json -{ - "type": "reminder", - "user_openid": "用户的openid", - "user_name": "用户名称(如有)", - "original_message_id": "创建提醒时的message_id", - "reminder_content": "提醒内容摘要", - "created_at": "创建时间ISO格式" -} -``` - -| 字段 | 说明 | 来源 | -|------|------|------| -| `type` | 事件类型,固定为 `"reminder"` | 固定值 | -| `user_openid` | 用户 openid | 从系统消息获取 | -| `user_name` | 用户名称 | 从系统消息获取(如有) | -| `original_message_id` | 创建时的消息 ID | 从系统消息获取 | -| `reminder_content` | 提醒内容摘要 | AI 根据用户请求生成 | -| `created_at` | 提醒创建时间 | 当前时间 ISO 格式 | - -> 💡 **为什么需要 `--system-event`?** -> - 提醒触发时,AI 可以获取完整的用户上下文 -> - 便于追踪提醒来源和调试 -> - 为未来可能的功能扩展预留信息 +**为什么这样写?** +- 消息内容会直接发送,不经过 AI 处理 +- 保持简洁,一目了然 --- @@ -303,24 +234,10 @@ openclaw message send \ **AI 执行**: ```bash openclaw cron add \ - --name "喝水提醒-用户" \ + --name "喝水提醒" \ --at "5m" \ --session isolated \ - --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"喝水","created_at":"2026-02-01T16:50:00+08:00"}' \ - --system-prompt ' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是用户之前设置的提醒时间。5分钟前用户设置了喝水提醒。 - -**你的唯一任务**:直接输出提醒消息,提醒用户喝水 - -**输出格式**:类似 "💧 时间到!该喝水啦~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -' \ - --message "[系统触发] 定时任务已触发,请执行提醒。" \ + --message "💧 喝水时间到!" \ --deliver \ --channel qqbot \ --to "{openid}" \ @@ -329,12 +246,12 @@ openclaw cron add \ **AI 回复**: ``` -✅ 提醒已设置! +⏰ 好的,5分钟后提醒你喝水~ +``` -📝 内容:喝水 -⏰ 时间:5分钟后 - -到时候我会准时提醒你~ +**5分钟后用户收到**: +``` +💧 喝水时间到! ``` --- @@ -346,25 +263,11 @@ openclaw cron add \ **AI 执行**: ```bash openclaw cron add \ - --name "打卡提醒-用户" \ + --name "打卡提醒" \ --cron "0 8 * * *" \ --tz "Asia/Shanghai" \ --session isolated \ - --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"打卡","created_at":"2026-02-01T16:50:00+08:00"}' \ - --system-prompt ' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是用户设置的每日早上8点打卡提醒时间。 - -**你的唯一任务**:直接输出提醒消息,提醒用户打卡 - -**输出格式**:类似 "🌅 早上好!该打卡了~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -' \ - --message "[系统触发] 定时任务已触发,请执行提醒。" \ + --message "🌅 打卡时间到!" \ --deliver \ --channel qqbot \ --to "{openid}" @@ -372,12 +275,7 @@ openclaw cron add \ **AI 回复**: ``` -✅ 周期提醒已设置! - -📝 内容:打卡 -🔄 周期:每天早上 8:00 - -提醒会持续生效,说"取消打卡提醒"可停止~ +⏰ 收到,每天早上8点提醒你打卡~ ``` > 💡 周期任务**不加** `--delete-after-run` @@ -391,58 +289,64 @@ openclaw cron add \ **AI 执行**: ```bash openclaw cron add \ - --name "日报提醒-用户" \ + --name "日报提醒" \ --cron "0 18 * * 1-5" \ --tz "Asia/Shanghai" \ --session isolated \ - --system-event '{"type":"reminder","user_openid":"{openid}","original_message_id":"{message_id}","reminder_content":"写日报","created_at":"2026-02-01T16:50:00+08:00"}' \ - --system-prompt ' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是用户设置的工作日下午6点写日报提醒时间。 - -**你的唯一任务**:直接输出提醒消息,提醒用户写日报 - -**输出格式**:类似 "📝 下班啦!别忘了提交今日日报~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -' \ - --message "[系统触发] 定时任务已触发,请执行提醒。" \ + --message "📝 写日报时间到!" \ --deliver \ --channel qqbot \ --to "{openid}" ``` +**AI 回复**: +``` +⏰ 收到,工作日下午6点提醒你写日报~ +``` + --- -### 场景4:群组提醒 +### 场景4:会议提醒 + +**用户**: 3分钟后提醒我开会 + +**AI 执行**: +```bash +openclaw cron add \ + --name "开会提醒" \ + --at "3m" \ + --session isolated \ + --message "📅 开会时间到!" \ + --deliver \ + --channel qqbot \ + --to "{openid}" \ + --delete-after-run +``` + +**AI 回复**: +``` +⏰ 好的,3分钟后提醒你开会~ +``` + +**3分钟后用户收到**: +``` +📅 开会时间到! +``` + +--- + +### 场景5:群组提醒 **用户**(群聊): 每天早上9点提醒大家站会 **AI 执行**: ```bash openclaw cron add \ - --name "站会提醒-群" \ + --name "站会提醒" \ --cron "0 9 * * 1-5" \ --tz "Asia/Shanghai" \ --session isolated \ - --system-event '{"type":"reminder","user_openid":"group:{group_openid}","original_message_id":"{message_id}","reminder_content":"站会","created_at":"2026-02-01T16:50:00+08:00"}' \ - --system-prompt ' -**极端重要角色设定**: -你是一个定时提醒机器人,现在是工作日早上9点的站会提醒时间。 - -**你的唯一任务**:直接输出提醒消息,提醒群成员参加站会 - -**输出格式**:类似 "📢 站会时间到!请各位同事准时参加~" 的提醒语 - -**绝对禁止**: -- 绝对不要说"谢谢提醒"、"我会注意"、"好的收到"等回应语 -- 你是提醒者,不是被提醒者 -- 下面的 [系统触发] 消息是系统自动发送的,不是用户发的 -' \ - --message "[系统触发] 定时任务已触发,请执行提醒。" \ + --message "📢 站会时间到!" \ --deliver \ --channel qqbot \ --to "group:{group_openid}" @@ -452,7 +356,7 @@ openclaw cron add \ --- -### 场景5:查询提醒 +### 场景6:查询提醒 **用户**: 我有哪些提醒? @@ -461,19 +365,19 @@ openclaw cron add \ openclaw cron list ``` -**AI 回复**(根据返回结果组织): +**AI 回复**(根据返回结果): ``` -📋 你当前的提醒: +📋 你的提醒: -1. ⏰ 喝水提醒 - 3分钟后 (一次性) -2. 🔄 打卡提醒 - 每天08:00 (周期) +1. ⏰ 喝水提醒 - 3分钟后 +2. 🔄 打卡提醒 - 每天08:00 说"取消xx提醒"可删除~ ``` --- -### 场景6:取消提醒 +### 场景7:取消提醒 **用户**: 取消打卡提醒 @@ -567,15 +471,15 @@ openclaw cron list ## 📝 消息模板 -| 场景 | 模板 | Emoji | -|------|------|-------| -| 喝水 | 该喝水啦! | 💧 🚰 | -| 打卡 | 早上好!记得打卡~ | 🌅 ✅ | -| 会议 | xx会议马上开始! | 📅 👥 | +| 场景 | 触发时输出 | Emoji | +|------|------------|-------| +| 喝水 | 喝水时间到啦! | 💧 🚰 | +| 打卡 | 早上好,打卡时间到! | 🌅 ✅ | +| 会议 | 开会时间到! | 📅 👥 | | 休息 | 该休息一下了~ | 😴 💤 | -| 日报 | 今日日报别忘了~ | 📝 ✍️ | -| 运动 | 该运动了! | 🏃 💪 | -| 吃药 | 记得按时吃药~ | 💊 🏥 | +| 日报 | 下班前别忘了写日报哦~ | 📝 ✍️ | +| 运动 | 运动时间到! | 🏃 💪 | +| 吃药 | 该吃药了~ | 💊 🏥 | | 生日 | 今天是xx的生日! | 🎂 🎉 | --- diff --git a/src/api.ts b/src/api.ts index 2f0534f..c9f18d6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,9 +1,7 @@ /** - * QQ Bot API 鉴权和请求封装(支持流式消息) + * QQ Bot API 鉴权和请求封装 */ -import { StreamState, type StreamConfig } from "./types.js"; - const API_BASE = "https://api.sgroup.qq.com"; const TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken"; @@ -12,10 +10,10 @@ let currentMarkdownSupport = false; /** * 初始化 API 配置 - * @param options.markdownSupport - 是否支持 markdown 消息 + * @param options.markdownSupport - 是否支持 markdown 消息(默认 false,需要机器人具备该权限才能启用) */ export function initApiConfig(options: { markdownSupport?: boolean }): void { - currentMarkdownSupport = options.markdownSupport === true; // 默认为 false + currentMarkdownSupport = options.markdownSupport === true; // 默认为 false,需要机器人具备 markdown 消息权限才能启用 } /** @@ -245,45 +243,35 @@ export async function getGatewayUrl(accessToken: string): Promise { return data.url; } -// ============ 流式消息发送接口 ============ +// ============ 消息发送接口 ============ /** - * 流式消息响应 + * 消息响应 */ -export interface StreamMessageResponse { +export interface MessageResponse { id: string; timestamp: number | string; - /** 流式消息ID,用于后续分片 */ - stream_id?: string; } /** - * 构建流式消息体 + * 构建消息体 * 根据 markdownSupport 配置决定消息格式: * - markdown 模式: { markdown: { content }, msg_type: 2 } * - 纯文本模式: { content, msg_type: 0 } */ -function buildStreamBody( +function buildMessageBody( content: string, msgId: string | undefined, - msgSeq: number, - stream?: StreamConfig + msgSeq: number ): Record { - // 流式 markdown 消息要求每个分片内容必须以换行符结尾 - // QQ API 错误码 40034017: "流式消息md分片需要\n结束" - let finalContent = content; - if (stream && currentMarkdownSupport && content && !content.endsWith("\n")) { - finalContent = content + "\n"; - } - const body: Record = currentMarkdownSupport ? { - markdown: { content: finalContent }, + markdown: { content }, msg_type: 2, msg_seq: msgSeq, } : { - content: finalContent, + content, msg_type: 0, msg_seq: msgSeq, }; @@ -292,29 +280,20 @@ function buildStreamBody( body.msg_id = msgId; } - if (stream) { - body.stream = { - state: stream.state, - index: stream.index, - ...(stream.id ? { id: stream.id } : {}), - }; - } - return body; } /** - * 发送 C2C 单聊消息(支持流式) + * 发送 C2C 单聊消息 */ export async function sendC2CMessage( accessToken: string, openid: string, content: string, - msgId?: string, - stream?: StreamConfig -): Promise { + msgId?: string +): Promise { const msgSeq = msgId ? getNextMsgSeq(msgId) : 1; - const body = buildStreamBody(content, msgId, msgSeq, stream); + const body = buildMessageBody(content, msgId, msgSeq); return apiRequest(accessToken, "POST", `/v2/users/${openid}/messages`, body); } @@ -358,17 +337,16 @@ export async function sendChannelMessage( } /** - * 发送群聊消息(支持流式) + * 发送群聊消息 */ export async function sendGroupMessage( accessToken: string, groupOpenid: string, content: string, - msgId?: string, - stream?: StreamConfig -): Promise { + msgId?: string +): Promise { const msgSeq = msgId ? getNextMsgSeq(msgId) : 1; - const body = buildStreamBody(content, msgId, msgSeq, stream); + const body = buildMessageBody(content, msgId, msgSeq); return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, body); } @@ -458,36 +436,64 @@ export interface UploadMediaResponse { /** * 上传富媒体文件到 C2C 单聊 + * @param url - 公网可访问的图片 URL(与 fileData 二选一) + * @param fileData - Base64 编码的文件内容(与 url 二选一) */ export async function uploadC2CMedia( accessToken: string, openid: string, fileType: MediaFileType, - url: string, + url?: string, + fileData?: string, srvSendMsg = false ): Promise { - return apiRequest(accessToken, "POST", `/v2/users/${openid}/files`, { + if (!url && !fileData) { + throw new Error("uploadC2CMedia: url or fileData is required"); + } + + const body: Record = { file_type: fileType, - url, srv_send_msg: srvSendMsg, - }); + }; + + if (url) { + body.url = url; + } else if (fileData) { + body.file_data = fileData; + } + + return apiRequest(accessToken, "POST", `/v2/users/${openid}/files`, body); } /** * 上传富媒体文件到群聊 + * @param url - 公网可访问的图片 URL(与 fileData 二选一) + * @param fileData - Base64 编码的文件内容(与 url 二选一) */ export async function uploadGroupMedia( accessToken: string, groupOpenid: string, fileType: MediaFileType, - url: string, + url?: string, + fileData?: string, srvSendMsg = false ): Promise { - return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/files`, { + if (!url && !fileData) { + throw new Error("uploadGroupMedia: url or fileData is required"); + } + + const body: Record = { file_type: fileType, - url, srv_send_msg: srvSendMsg, - }); + }; + + if (url) { + body.url = url; + } else if (fileData) { + body.file_data = fileData; + } + + return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/files`, body); } /** @@ -532,6 +538,9 @@ export async function sendGroupMediaMessage( /** * 发送带图片的 C2C 单聊消息(封装上传+发送) + * @param imageUrl - 图片来源,支持: + * - 公网 URL: https://example.com/image.png + * - Base64 Data URL: data:image/png;base64,xxxxx */ export async function sendC2CImageMessage( accessToken: string, @@ -540,14 +549,32 @@ export async function sendC2CImageMessage( msgId?: string, content?: string ): Promise<{ id: string; timestamp: number }> { - // 先上传图片获取 file_info - const uploadResult = await uploadC2CMedia(accessToken, openid, MediaFileType.IMAGE, imageUrl, false); - // 再发送富媒体消息 + let uploadResult: UploadMediaResponse; + + // 检查是否是 Base64 Data URL + if (imageUrl.startsWith("data:")) { + // 解析 Base64 Data URL: data:image/png;base64,xxxxx + const matches = imageUrl.match(/^data:([^;]+);base64,(.+)$/); + if (!matches) { + throw new Error("Invalid Base64 Data URL format"); + } + const base64Data = matches[2]; + // 使用 file_data 上传 + uploadResult = await uploadC2CMedia(accessToken, openid, MediaFileType.IMAGE, undefined, base64Data, false); + } else { + // 公网 URL,使用 url 参数上传 + uploadResult = await uploadC2CMedia(accessToken, openid, MediaFileType.IMAGE, imageUrl, undefined, false); + } + + // 发送富媒体消息 return sendC2CMediaMessage(accessToken, openid, uploadResult.file_info, msgId, content); } /** * 发送带图片的群聊消息(封装上传+发送) + * @param imageUrl - 图片来源,支持: + * - 公网 URL: https://example.com/image.png + * - Base64 Data URL: data:image/png;base64,xxxxx */ export async function sendGroupImageMessage( accessToken: string, @@ -556,9 +583,24 @@ export async function sendGroupImageMessage( msgId?: string, content?: string ): Promise<{ id: string; timestamp: string }> { - // 先上传图片获取 file_info - const uploadResult = await uploadGroupMedia(accessToken, groupOpenid, MediaFileType.IMAGE, imageUrl, false); - // 再发送富媒体消息 + let uploadResult: UploadMediaResponse; + + // 检查是否是 Base64 Data URL + if (imageUrl.startsWith("data:")) { + // 解析 Base64 Data URL: data:image/png;base64,xxxxx + const matches = imageUrl.match(/^data:([^;]+);base64,(.+)$/); + if (!matches) { + throw new Error("Invalid Base64 Data URL format"); + } + const base64Data = matches[2]; + // 使用 file_data 上传 + uploadResult = await uploadGroupMedia(accessToken, groupOpenid, MediaFileType.IMAGE, undefined, base64Data, false); + } else { + // 公网 URL,使用 url 参数上传 + uploadResult = await uploadGroupMedia(accessToken, groupOpenid, MediaFileType.IMAGE, imageUrl, undefined, false); + } + + // 发送富媒体消息 return sendGroupMediaMessage(accessToken, groupOpenid, uploadResult.file_info, msgId, content); } @@ -707,4 +749,4 @@ async function sleep(ms: number, signal?: AbortSignal): Promise { signal.addEventListener("abort", onAbort, { once: true }); } }); -} +} \ No newline at end of file diff --git a/src/channel.ts b/src/channel.ts index 4d355ab..2a2288a 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -8,15 +8,14 @@ import { import type { ResolvedQQBotAccount } from "./types.js"; import { DEFAULT_ACCOUNT_ID, listQQBotAccountIds, resolveQQBotAccount, applyQQBotAccountConfig, resolveDefaultQQBotAccountId } from "./config.js"; -import { sendText, sendMedia, createStreamSender, sendTextStream, StreamSender } from "./outbound.js"; +import { sendText, sendMedia } from "./outbound.js"; import { startGateway } from "./gateway.js"; import { qqbotOnboardingAdapter } from "./onboarding.js"; import { getQQBotRuntime } from "./runtime.js"; /** * 简单的文本分块函数 - * QQ Bot 使用流式消息时,不需要预先分块,而是在发送时逐步累积 - * 但框架可能调用此函数来预分块长文本 + * 用于预先分块长文本 */ function chunkText(text: string, limit: number): string[] { if (text.length <= limit) return [text]; @@ -52,10 +51,10 @@ export const qqbotPlugin: ChannelPlugin = { id: "qqbot", meta: { id: "qqbot", - label: "QQ Bot (Stream)", - selectionLabel: "QQ Bot (Stream)", + label: "QQ Bot", + selectionLabel: "QQ Bot", docsPath: "/docs/channels/qqbot", - blurb: "Connect to QQ via official QQ Bot API with streaming message support", + blurb: "Connect to QQ via official QQ Bot API", order: 50, }, capabilities: { @@ -67,7 +66,7 @@ export const qqbotPlugin: ChannelPlugin = { * blockStreaming: true 表示该 Channel 支持块流式 * 框架会收集流式响应,然后通过 deliver 回调发送 */ - blockStreaming: true, + blockStreaming: false, }, reload: { configPrefixes: ["channels.qqbot"] }, // CLI onboarding wizard @@ -180,7 +179,7 @@ export const qqbotPlugin: ChannelPlugin = { startAccount: async (ctx) => { const { account, abortSignal, log, cfg } = ctx; - log?.info(`[qqbot:${account.accountId}] Starting gateway (stream-enabled)`); + log?.info(`[qqbot:${account.accountId}] Starting gateway`); await startGateway({ account, @@ -282,36 +281,3 @@ export const qqbotPlugin: ChannelPlugin = { }), }, }; - -/** - * 导出流式消息工具函数,供外部使用 - * - * 使用示例: - * ```typescript - * import { createStreamSender } from "qqbot"; - * - * // 创建流式发送器 - * const sender = createStreamSender(account, "group:xxx", replyMsgId); - * - * // 发送第一个分片 (state=1, index=0, id="") - * await sender.send("Hello, ", false); - * - * // 发送中间分片 (state=1, index=1, id=从上次响应获取) - * await sender.send("Hello, this is ", false); - * - * // 发送最后分片并结束 (state=10, index=2) - * await sender.end("Hello, this is a streaming message!"); - * ``` - * - * 或使用 AsyncGenerator: - * ```typescript - * async function* generateText() { - * yield "Hello, "; - * yield "this is "; - * yield "a streaming message!"; - * } - * - * await sendTextStream(ctx, generateText()); - * ``` - */ -export { createStreamSender, sendTextStream, StreamSender }; diff --git a/src/config.ts b/src/config.ts index 38e3621..99678d2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -77,7 +77,6 @@ export function resolveQQBotAccount( systemPrompt: qqbot?.systemPrompt, imageServerBaseUrl: qqbot?.imageServerBaseUrl, markdownSupport: qqbot?.markdownSupport, - streamEnabled: qqbot?.streamEnabled, }; appId = qqbot?.appId ?? ""; } else { @@ -114,7 +113,6 @@ export function resolveQQBotAccount( systemPrompt: accountConfig.systemPrompt, imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL, markdownSupport: accountConfig.markdownSupport, - streamEnabled: accountConfig.streamEnabled, config: accountConfig, }; } diff --git a/src/gateway.ts b/src/gateway.ts index 306ca9d..1ebe187 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -1,13 +1,12 @@ import WebSocket from "ws"; import path from "node:path"; import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js"; -import { StreamState } from "./types.js"; import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh } from "./api.js"; import { loadSession, saveSession, clearSession, type SessionState } from "./session-store.js"; import { recordKnownUser, flushKnownUsers } from "./known-users.js"; import { getQQBotRuntime } from "./runtime.js"; -import { startImageServer, saveImage, saveImageFromPath, isImageServerRunning, downloadFile, type ImageServerConfig } from "./image-server.js"; -import { createStreamSender } from "./outbound.js"; +import { startImageServer, isImageServerRunning, downloadFile, type ImageServerConfig } from "./image-server.js"; +import { getImageSize, formatQQBotMarkdownImage, hasQQBotImageSize, DEFAULT_IMAGE_SIZE } from "./utils/image-size.js"; // QQ Bot intents - 按权限级别分组 const INTENTS = { @@ -54,64 +53,6 @@ const IMAGE_SERVER_PORT = parseInt(process.env.QQBOT_IMAGE_SERVER_PORT || "18765 // 使用绝对路径,确保文件保存和读取使用同一目录 const IMAGE_SERVER_DIR = process.env.QQBOT_IMAGE_SERVER_DIR || path.join(process.env.HOME || "/home/ubuntu", "clawd", "qqbot-images"); -// 流式消息配置 -const STREAM_CHUNK_INTERVAL = 500; // 流式消息分片间隔(毫秒) -const STREAM_MIN_CHUNK_SIZE = 10; // 最小分片大小(字符) -const STREAM_KEEPALIVE_FIRST_DELAY = 3000; // 首次状态保持延迟(毫秒),openclaw 3s 内未回复时发送 -const STREAM_KEEPALIVE_GAP = 10000; // 状态保持消息之间的间隔(毫秒) -const STREAM_KEEPALIVE_MAX_PER_CHUNK = 2; // 每 2 个消息分片之间最多发送的状态保持消息数量 -const STREAM_MAX_DURATION = 3 * 60 * 1000; // 流式消息最大持续时间(毫秒),超过 3 分钟自动结束 - -// ============ 智能断句配置 ============ -// 首个分片:必须在语义边界处断句,避免奇怪的换行 -const FIRST_CHUNK_MIN_LENGTH_SOFT = 20; // 软下限:达到此长度后,遇到语义边界就可以发送 -const FIRST_CHUNK_MIN_LENGTH_HARD = 80; // 硬下限:超过此长度必须发送,避免等待太久 -const FIRST_CHUNK_MAX_WAIT_TIME = 3000; // 首个分片最长等待时间(毫秒) - -// 语义边界检测:判断文本是否在自然断句位置结束 -function isAtSemanticBoundary(text: string): boolean { - if (!text) return false; - const trimmed = text.trimEnd(); - if (!trimmed) return false; - - // 检查最后一个字符是否是断句标点 - const lastChar = trimmed[trimmed.length - 1]; - const sentenceEnders = ['。', '!', '?', '~', '…', '.', '!', '?', '\n']; - if (sentenceEnders.includes(lastChar)) return true; - - // 检查是否以 emoji 结尾(常见于提醒消息) - const emojiRegex = /[\u{1F300}-\u{1F9FF}]$/u; - if (emojiRegex.test(trimmed)) return true; - - // 检查最后几个字符是否是 markdown 列表项结束(如 "- xxx" 后面) - // 不算边界,因为列表通常有多项 - - return false; -} - -// 查找最近的语义边界位置 -function findLastSemanticBoundary(text: string, minPos: number = 0): number { - if (!text || text.length <= minPos) return -1; - - const sentenceEnders = ['。', '!', '?', '~', '.', '!', '?']; - let lastBoundary = -1; - - for (let i = text.length - 1; i >= minPos; i--) { - const char = text[i]; - if (sentenceEnders.includes(char)) { - lastBoundary = i + 1; // 包含这个标点符号 - break; - } - // 换行符也是边界 - if (char === '\n') { - lastBoundary = i + 1; - break; - } - } - - return lastBoundary; -} - // 消息队列配置(异步处理,防止阻塞心跳) const MESSAGE_QUEUE_SIZE = 1000; // 最大队列长度 const MESSAGE_QUEUE_WARN_THRESHOLD = 800; // 队列告警阈值 @@ -181,6 +122,94 @@ function recordMessageReply(messageId: string): void { } } +// ============ 图片发送时的文本智能简化 ============ +// 当 AI 发送图片时,检测并移除冗余的解释性文字 + +/** + * 冗余文本模式 - 这些模式表示 AI 在"解释"而不是"回应" + * 通常出现在 AI 不确定图片是否发送成功时 + */ +const REDUNDANT_TEXT_PATTERNS = [ + // 中文冗余模式 + /让我总结一下[^\n]*/gi, + /目前的情况[是::][^\n]*/gi, + /由于[^\n]*(?:工具[集]?|插件|集成|API)[^\n]*(?:限制|问题)[^\n]*/gi, + /我已经[^\n]*(?:尝试|下载|保存)[^\n]*/gi, + /最实用的(?:方法|解决方案)[是::][^\n]*/gi, + /如果你希望我继续[^\n]*/gi, + /你可以[直接]?点击[^\n]*链接[^\n]*/gi, + /我注意到你重复[^\n]*/gi, + /我[已经]?多次尝试[^\n]*/gi, + /(?:已经|成功)?(?:保存|下载)到本地[^\n]*/gi, + /(?:直接)?(?:查看|访问)[该这]?(?:图片|文件|链接)[^\n]*/gi, + // 英文冗余模式 + /let me summarize[^\n]*/gi, + /i(?:'ve| have) tried[^\n]*(?:multiple|several)[^\n]*/gi, + /due to[^\n]*(?:tool|plugin|integration)[^\n]*limitation[^\n]*/gi, + /the most practical[^\n]*solution[^\n]*/gi, +]; + +/** + * 检查文本是否为纯冗余解释 + * 如果整个文本都是在解释发送过程,而不是描述图片内容,则返回 true + */ +function isEntirelyRedundantExplanation(text: string): boolean { + // 移除空行和空格 + const trimmed = text.trim(); + if (!trimmed) return true; + + // 检查是否包含"步骤列表"类的解释 + const hasStepList = /^\d+\.\s+/m.test(trimmed) && + (trimmed.includes("下载") || trimmed.includes("尝试") || trimmed.includes("发送")); + + // 检查是否主要由冗余模式组成 + let cleaned = trimmed; + for (const pattern of REDUNDANT_TEXT_PATTERNS) { + cleaned = cleaned.replace(pattern, ""); + } + + // 如果清理后只剩下很少的文字(主要是标点和连接词),认为整体都是冗余 + const cleanedWords = cleaned.replace(/[\s\n\r.,;:!?,。;:!?·…—""''()()【】[\]{}]+/g, "").trim(); + const significantContentRemaining = cleanedWords.length > 20; + + return hasStepList || !significantContentRemaining; +} + +/** + * 智能简化图片发送时的文本 + * 当检测到发送图片时,移除冗余的解释性文字 + * + * @param text 原始文本 + * @param hasImages 是否包含图片 + * @returns 简化后的文本 + */ +function simplifyTextForImageSend(text: string, hasImages: boolean): string { + if (!hasImages || !text) return text; + + const trimmed = text.trim(); + + // 如果整个文本都是冗余解释,替换为简短的成功提示 + if (isEntirelyRedundantExplanation(trimmed)) { + return "图片如上 ☝️"; + } + + // 否则,只移除明显的冗余段落 + let result = trimmed; + for (const pattern of REDUNDANT_TEXT_PATTERNS) { + result = result.replace(pattern, ""); + } + + // 清理多余的空行 + result = result.replace(/\n{3,}/g, "\n\n").trim(); + + // 如果清理后文本太短,恢复原文 + if (result.length < 10 && trimmed.length > 50) { + return "图片如上 ☝️"; + } + + return result || trimmed; +} + export interface GatewayContext { account: ResolvedQQBotAccount; abortSignal: AbortSignal; @@ -250,7 +279,7 @@ export async function startGateway(ctx: GatewayContext): Promise { initApiConfig({ markdownSupport: account.markdownSupport, }); - log?.info(`[qqbot:${account.accountId}] API config: markdownSupport=${account.markdownSupport !== false}`); + log?.info(`[qqbot:${account.accountId}] API config: markdownSupport=${account.markdownSupport === true}`); // 如果配置了公网 URL,启动图床服务器 let imageServerBaseUrl: string | null = null; @@ -444,10 +473,6 @@ export async function startGateway(ctx: GatewayContext): Promise { log?.info(`[qqbot:${account.accountId}] Attachments: ${event.attachments.length}`); } - // 流式消息开关(默认禁用,仅 c2c 支持,需要在配置中明确启用) - const streamEnabled = account.streamEnabled === true; - log?.debug?.(`[qqbot:${account.accountId}] Stream enabled: ${streamEnabled}`); - pluginRuntime.channel.activity.record({ channel: "qqbot", accountId: account.accountId, @@ -516,8 +541,7 @@ openclaw cron add \\ ⚠️ 重要注意事项: 1. --at 参数格式:相对时间用 \`5m\`、\`1h\` 等(不要加 + 号!);绝对时间用完整 ISO 格式 -2. 定时提醒消息不支持流式发送,命令中不要添加 --stream 参数 -3. --message 参数必须有实际内容,不能为空字符串`; +2. --message 参数必须有实际内容,不能为空字符串`; // 只有配置了图床公网地址,才告诉 AI 可以发送图片 if (imageServerBaseUrl) { @@ -661,315 +685,11 @@ openclaw cron add \\ }, responseTimeout); }); - // ============ 流式消息发送器 ============ + // ============ 消息发送目标 ============ // 确定发送目标 const targetTo = event.type === "c2c" ? event.senderId : event.type === "group" ? `group:${event.groupOpenid}` : `channel:${event.channelId}`; - - // 判断是否支持流式(仅 c2c 支持,群聊不支持流式,且需要开关启用) - const supportsStream = event.type === "c2c" && streamEnabled; - log?.info(`[qqbot:${account.accountId}] Stream support: ${supportsStream} (type=${event.type}, enabled=${streamEnabled})`); - - // 创建流式发送器 - let streamSender = supportsStream ? createStreamSender(account, targetTo, event.messageId) : null; - let streamBuffer = ""; // 累积的全部文本(用于记录完整内容) - let lastSentLength = 0; // 上次发送时的文本长度(用于计算增量) - let lastSentText = ""; // 上次发送时的完整文本(用于检测新段落) - let currentSegmentStart = 0; // 当前段落在 streamBuffer 中的起始位置 - let lastStreamSendTime = 0; // 上次流式发送时间 - let streamStarted = false; // 是否已开始流式发送 - let streamEnded = false; // 流式是否已结束 - let streamStartTime = 0; // 流式消息开始时间(用于超时检查) - let sendingLock = false; // 发送锁,防止并发发送 - let pendingFullText = ""; // 待发送的完整文本(在锁定期间积累) - let firstChunkWaitStart = 0; // 首个分片开始等待的时间(用于超时判断) - let keepaliveTimer: ReturnType | null = null; // 心跳定时器 - let keepaliveCountSinceLastChunk = 0; // 自上次分片以来发送的状态保持消息数量 - let lastChunkSendTime = 0; // 上次分片发送时间(用于判断是否需要发送状态保持) - - // 清理心跳定时器 - const clearKeepalive = () => { - if (keepaliveTimer) { - clearTimeout(keepaliveTimer); - keepaliveTimer = null; - } - }; - - // 重置心跳定时器(每次发送后调用) - // isContentChunk: 是否为内容分片(非状态保持消息) - const resetKeepalive = (isContentChunk: boolean = false) => { - clearKeepalive(); - - // 如果是内容分片,重置状态保持计数器和时间 - if (isContentChunk) { - keepaliveCountSinceLastChunk = 0; - lastChunkSendTime = Date.now(); - } - - if (streamSender && streamStarted && !streamEnded) { - // 计算下次状态保持消息的延迟时间 - // - 首次:3s(STREAM_KEEPALIVE_FIRST_DELAY) - // - 后续:10s(STREAM_KEEPALIVE_GAP) - const delay = keepaliveCountSinceLastChunk === 0 - ? STREAM_KEEPALIVE_FIRST_DELAY - : STREAM_KEEPALIVE_GAP; - - keepaliveTimer = setTimeout(async () => { - // 检查流式消息是否超时(超过 3 分钟自动结束) - const elapsed = Date.now() - streamStartTime; - if (elapsed >= STREAM_MAX_DURATION) { - log?.info(`[qqbot:${account.accountId}] Stream timeout after ${Math.round(elapsed / 1000)}s, auto ending stream`); - if (!streamEnded && !sendingLock) { - sendingLock = true; - try { - // 发送结束标记 - await streamSender!.send("", true); - streamEnded = true; - clearKeepalive(); - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Stream auto-end failed: ${err}`); - } finally { - sendingLock = false; - } - } - return; // 超时后不再继续心跳 - } - - // 检查是否已达到每2个分片之间的最大状态保持消息数量 - if (keepaliveCountSinceLastChunk >= STREAM_KEEPALIVE_MAX_PER_CHUNK) { - log?.debug?.(`[qqbot:${account.accountId}] Max keepalive reached (${keepaliveCountSinceLastChunk}/${STREAM_KEEPALIVE_MAX_PER_CHUNK}), waiting for next content chunk`); - // 不再发送状态保持,但继续监控超时 - resetKeepalive(false); - return; - } - - // 检查距上次分片是否超过 3s - const timeSinceLastChunk = Date.now() - lastChunkSendTime; - if (timeSinceLastChunk < STREAM_KEEPALIVE_FIRST_DELAY) { - // 还未到发送状态保持的时机,继续等待 - resetKeepalive(false); - return; - } - - // 发送状态保持消息 - if (!streamEnded && !sendingLock) { - log?.info(`[qqbot:${account.accountId}] Sending keepalive #${keepaliveCountSinceLastChunk + 1} (elapsed: ${Math.round(elapsed / 1000)}s, since chunk: ${Math.round(timeSinceLastChunk / 1000)}s)`); - sendingLock = true; - try { - // 发送空内容 - await streamSender!.send("", false); - lastStreamSendTime = Date.now(); - keepaliveCountSinceLastChunk++; - resetKeepalive(false); // 继续下一个状态保持(非内容分片) - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Keepalive failed: ${err}`); - } finally { - sendingLock = false; - } - } - }, delay); - } - }; - - // 流式发送函数 - 用于 onPartialReply 实时发送(增量模式) - // 注意:不要在分片后强制添加换行符,否则会导致消息在奇怪的位置断句 - const sendStreamChunk = async (text: string, isEnd: boolean): Promise => { - if (!streamSender || streamEnded) return false; - - // 直接发送文本内容,不添加任何额外换行符 - // 换行应该由 AI 生成的内容本身决定,而非强制添加 - const contentToSend = text; - - const result = await streamSender.send(contentToSend, isEnd); - if (result.error) { - log?.error(`[qqbot:${account.accountId}] Stream send error: ${result.error}`); - return false; - } else { - log?.debug?.(`[qqbot:${account.accountId}] Stream chunk sent, index: ${streamSender.getContext().index - 1}, isEnd: ${isEnd}, text: "${text.slice(0, 50)}..."`); - } - - if (isEnd) { - streamEnded = true; - clearKeepalive(); - } else { - // 发送成功后重置心跳,如果是有内容的分片则重置计数器 - const isContentChunk = text.length > 0; - resetKeepalive(isContentChunk); - } - return true; - }; - - // 执行一次流式发送(带锁保护) - const doStreamSend = async (fullText: string, forceEnd: boolean = false): Promise => { - // 如果正在发送,记录待发送的完整文本,稍后处理 - if (sendingLock) { - pendingFullText = fullText; - return; - } - - sendingLock = true; - try { - // 发送当前增量 - if (fullText.length > lastSentLength) { - const increment = fullText.slice(lastSentLength); - // 首次发送前,先设置流式状态和开始时间 - if (!streamStarted) { - streamStarted = true; - streamStartTime = Date.now(); - log?.info(`[qqbot:${account.accountId}] Stream started, max duration: ${STREAM_MAX_DURATION / 1000}s`); - } - const success = await sendStreamChunk(increment, forceEnd); - if (success) { - lastSentLength = fullText.length; - lastSentText = fullText; // 记录完整发送文本,用于检测新段落 - lastStreamSendTime = Date.now(); - log?.info(`[qqbot:${account.accountId}] Stream partial #${streamSender!.getContext().index}, increment: ${increment.length} chars, total: ${fullText.length} chars`); - } - } else if (forceEnd && !streamEnded) { - // 没有新内容但需要结束 - await sendStreamChunk("", true); - } - } finally { - sendingLock = false; - } - - // 处理在锁定期间积累的内容 - if (pendingFullText && pendingFullText.length > lastSentLength && !streamEnded) { - const pending = pendingFullText; - pendingFullText = ""; - // 递归发送积累的内容(不强制结束) - await doStreamSend(pending, false); - } - }; - - // onPartialReply 回调 - 实时接收 AI 生成的文本(payload.text 是累积的全文) - // 注意:agent 在一次对话中可能产生多个回复段落(如思考、工具调用后继续回复) - // 每个新段落的 text 会从头开始累积,需要检测并处理 - const handlePartialReply = async (payload: { text?: string }) => { - if (!streamSender || streamEnded) { - log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply skipped: streamSender=${!!streamSender}, streamEnded=${streamEnded}`); - return; - } - - const fullText = payload.text ?? ""; - if (!fullText) { - log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: empty text`); - return; - } - - hasResponse = true; - - // 检测是否是新段落: - // 1. lastSentText 不为空(说明已经发送过内容) - // 2. 当前文本不是以 lastSentText 开头(说明不是同一段落的增量) - // 3. 当前文本长度小于 lastSentLength(说明文本被重置了) - const isNewSegment = lastSentText.length > 0 && - (fullText.length < lastSentLength || !fullText.startsWith(lastSentText.slice(0, Math.min(10, lastSentText.length)))); - - if (isNewSegment) { - // 新段落开始,结束当前流并创建新流 - log?.info(`[qqbot:${account.accountId}] New segment detected! lastSentLength=${lastSentLength}, newTextLength=${fullText.length}, lastSentText="${lastSentText.slice(0, 20)}...", newText="${fullText.slice(0, 20)}..."`); - - // 保存旧的 sender 用于结束流 - const oldStreamSender = streamSender; - const oldStreamStarted = streamStarted; - const oldStreamEnded = streamEnded; - - // 1. 先创建新的流式发送器并重置所有状态 - // 这样在 await 期间到达的新消息会使用新 sender - streamSender = createStreamSender(account, targetTo, event.messageId); - lastSentLength = 0; - lastSentText = ""; - streamStarted = false; - streamEnded = false; - streamStartTime = 0; - keepaliveCountSinceLastChunk = 0; - lastChunkSendTime = 0; - firstChunkWaitStart = 0; // 重置首个分片等待时间 - - // 记录当前段落在 streamBuffer 中的起始位置 - currentSegmentStart = streamBuffer.length; - - // 追加换行分隔符(如果前面有内容且不以换行结尾) - if (streamBuffer.length > 0 && !streamBuffer.endsWith("\n")) { - streamBuffer += "\n\n"; - currentSegmentStart = streamBuffer.length; - } - - // 2. 结束旧流(如果已开始)- 使用旧的 sender - if (oldStreamSender && oldStreamStarted && !oldStreamEnded) { - log?.info(`[qqbot:${account.accountId}] Ending current stream before starting new segment`); - clearKeepalive(); - sendingLock = true; - try { - await oldStreamSender.send("", true); // 发送结束标记 - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to end stream: ${err}`); - } finally { - sendingLock = false; - } - } - } - - // 更新当前段落内容到 streamBuffer - // streamBuffer = 之前的段落内容 + 当前段落的完整内容 - const beforeCurrentSegment = streamBuffer.slice(0, currentSegmentStart); - streamBuffer = beforeCurrentSegment + fullText; - - log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: fullText.length=${fullText.length}, lastSentLength=${lastSentLength}, streamBuffer.length=${streamBuffer.length}, isNewSegment=${isNewSegment}`); - - // 如果没有新内容,跳过 - if (fullText.length <= lastSentLength) return; - - const now = Date.now(); - - // 初始化首个分片等待开始时间(如果还没有开始) - if (!streamStarted && !firstChunkWaitStart) { - firstChunkWaitStart = now; - } - - // 控制发送频率:首次发送或间隔超过阈值 - if (!streamStarted) { - // 首个分片:智能断句,在语义边界处发送 - const waitTime = firstChunkWaitStart ? now - firstChunkWaitStart : 0; - const atBoundary = isAtSemanticBoundary(fullText); - const reachedSoftLimit = fullText.length >= FIRST_CHUNK_MIN_LENGTH_SOFT; - const reachedHardLimit = fullText.length >= FIRST_CHUNK_MIN_LENGTH_HARD; - const timedOut = waitTime >= FIRST_CHUNK_MAX_WAIT_TIME; - - // 发送条件(优先级从高到低): - // 1. 达到硬下限:必须发送,避免等待太久 - // 2. 等待超时:必须发送,避免无响应 - // 3. 达到软下限 + 在语义边界:可以发送 - if (reachedHardLimit || timedOut) { - // 硬性条件:必须发送 - if (timedOut && !reachedSoftLimit) { - log?.info(`[qqbot:${account.accountId}] handlePartialReply: first chunk timeout, sending anyway, length=${fullText.length}, wait=${waitTime}ms`); - } else { - log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending first chunk (hard limit), length=${fullText.length}`); - } - await doStreamSend(fullText, false); - firstChunkWaitStart = 0; // 重置等待时间 - } else if (reachedSoftLimit && atBoundary) { - // 软性条件:在语义边界处发送 - log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending first chunk (at boundary), length=${fullText.length}`); - await doStreamSend(fullText, false); - firstChunkWaitStart = 0; - } else { - // 还需要等待更多内容 - log?.debug?.(`[qqbot:${account.accountId}] handlePartialReply: waiting for semantic boundary, length=${fullText.length}, atBoundary=${atBoundary}, wait=${waitTime}ms`); - pendingFullText = fullText; - } - } else if (now - lastStreamSendTime >= STREAM_CHUNK_INTERVAL) { - // 后续分片:基于时间间隔发送 - log?.info(`[qqbot:${account.accountId}] handlePartialReply: sending stream chunk, length=${fullText.length}`); - await doStreamSend(fullText, false); - } else { - // 不到发送时间,但记录待发送内容,确保最终会被发送 - pendingFullText = fullText; - } - }; const dispatchPromise = pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ ctx: ctxPayload, @@ -987,169 +707,244 @@ openclaw cron add \\ let replyText = payload.text ?? ""; - // 更新当前段落内容到 streamBuffer - // deliver 中的 replyText 是当前段落的完整文本 - if (replyText.length > 0) { - const beforeCurrentSegment = streamBuffer.slice(0, currentSegmentStart); - const newStreamBuffer = beforeCurrentSegment + replyText; - if (newStreamBuffer.length > streamBuffer.length) { - streamBuffer = newStreamBuffer; - log?.debug?.(`[qqbot:${account.accountId}] deliver: updated streamBuffer, replyText=${replyText.length}, total=${streamBuffer.length}`); - } - } - - // 收集所有图片路径 + // 收集所有图片(公网 URL 或 Base64 Data URL) + // 注意:本地文件路径由 OpenClaw 负责转换(通过对象存储上传或读取为 Base64) const imageUrls: string[] = []; - // 处理 mediaUrls 和 mediaUrl 字段(本地文件路径) - const mediaPaths: string[] = []; - if (payload.mediaUrls?.length) { - mediaPaths.push(...payload.mediaUrls); - } - if (payload.mediaUrl && !mediaPaths.includes(payload.mediaUrl)) { - mediaPaths.push(payload.mediaUrl); - } - - for (const localPath of mediaPaths) { - if (localPath && imageServerBaseUrl) { - try { - const savedUrl = saveImageFromPath(localPath); - if (savedUrl) { - imageUrls.push(savedUrl); - log?.info(`[qqbot:${account.accountId}] Saved media to server: ${localPath}`); + /** + * 检查并收集图片 URL + * 支持:公网 URL (http/https) 和 Base64 Data URL (data:image/...) + */ + const collectImageUrl = (url: string | undefined | null): boolean => { + if (!url) return false; + + const isHttpUrl = url.startsWith("http://") || url.startsWith("https://"); + const isDataUrl = url.startsWith("data:image/"); + + if (isHttpUrl || isDataUrl) { + if (!imageUrls.includes(url)) { + imageUrls.push(url); + if (isDataUrl) { + log?.info(`[qqbot:${account.accountId}] Collected Base64 image (length: ${url.length})`); } else { - log?.error(`[qqbot:${account.accountId}] Failed to save media (not found or not image): ${localPath}`); + log?.info(`[qqbot:${account.accountId}] Collected media URL: ${url.slice(0, 80)}...`); } - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to save media: ${err}`); } + return true; } + + // 检测本地文件路径 + const isLocalPath = url.startsWith("/") || + /^[a-zA-Z]:[\\/]/.test(url) || + url.startsWith("./") || + url.startsWith("../"); + + if (isLocalPath) { + log?.info(`[qqbot:${account.accountId}] Skipped local file path (OpenClaw should convert to Base64 or upload): ${url.slice(0, 80)}`); + } else { + log?.info(`[qqbot:${account.accountId}] Skipped unsupported media format: ${url.slice(0, 50)}`); + } + return false; + }; + + // 处理 mediaUrls 和 mediaUrl 字段 + if (payload.mediaUrls?.length) { + for (const url of payload.mediaUrls) { + collectImageUrl(url); + } + } + if (payload.mediaUrl) { + collectImageUrl(payload.mediaUrl); } - // 提取文本中的各种图片格式 - // 0. 提取 MEDIA: 前缀的本地文件路径 - const mediaPathRegex = /MEDIA:([^\s\n]+)/gi; - const mediaMatches = [...replyText.matchAll(mediaPathRegex)]; - for (const match of mediaMatches) { - const localPath = match[1]; - if (localPath && imageServerBaseUrl) { - try { - const savedUrl = saveImageFromPath(localPath); - if (savedUrl) { - imageUrls.push(savedUrl); - log?.info(`[qqbot:${account.accountId}] Saved local image to server: ${localPath}`); - } - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to save local image: ${err}`); - } - } - replyText = replyText.replace(match[0], "").trim(); - } - - // 0.5. 提取本地绝对文件路径 - const localPathRegex = /(\/[^\s\n]+?(?:\.(?:png|jpg|jpeg|gif|webp)|_(?:png|jpg|jpeg|gif|webp)(?:\s|$)))/gi; - const localPathMatches = [...replyText.matchAll(localPathRegex)]; - for (const match of localPathMatches) { - let localPath = match[1].trim(); - if (localPath && imageServerBaseUrl) { - localPath = localPath.replace(/_(?=(?:png|jpg|jpeg|gif|webp)$)/, "."); - try { - const savedUrl = saveImageFromPath(localPath); - if (savedUrl) { - imageUrls.push(savedUrl); - log?.info(`[qqbot:${account.accountId}] Saved local path image to server: ${localPath}`); - } - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to save local path image: ${err}`); - } - } - replyText = replyText.replace(match[0], "").trim(); - } - - // 1. 提取 base64 图片 - const base64ImageRegex = /!\[([^\]]*)\]\((data:image\/[^;]+;base64,[A-Za-z0-9+/=]+)\)|(? 0; - if (!hasImages && textWithoutImages) { - const originalText = textWithoutImages; - textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2"); - if (textWithoutImages !== originalText && textWithoutImages.trim()) { - textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)"; + // 2. 提取裸 URL 图片(仅在非 markdown 模式下移除) + const bareUrlRegex = /(?]+\.(?:png|jpg|jpeg|gif|webp)(?:\?[^\s"'<>]*)?)/gi; + const bareUrlMatches = [...replyText.matchAll(bareUrlRegex)]; + for (const match of bareUrlMatches) { + const url = match[1]; + if (url && !imageUrls.includes(url)) { + imageUrls.push(url); + log?.info(`[qqbot:${account.accountId}] Extracted bare image URL: ${url.slice(0, 80)}...`); } } - - try { - // 发送图片(如果有) - for (const imageUrl of imageUrls) { + + // 判断是否使用 markdown 模式 + const useMarkdown = account.markdownSupport === true; + log?.info(`[qqbot:${account.accountId}] Markdown mode: ${useMarkdown}, images: ${imageUrls.length}`); + + let textWithoutImages = replyText; + + // 🎯 智能简化文本:当发送图片时,移除冗余的解释性文字 + // 这解决了 AI 不确定图片是否发送成功而输出大量废话的问题 + if (imageUrls.length > 0) { + const originalLength = textWithoutImages.length; + textWithoutImages = simplifyTextForImageSend(textWithoutImages, true); + if (textWithoutImages.length !== originalLength) { + log?.info(`[qqbot:${account.accountId}] Simplified text for image send: ${originalLength} -> ${textWithoutImages.length} chars`); + } + } + + // 根据模式处理图片 + if (useMarkdown) { + // ============ Markdown 模式:使用 ![#宽px #高px](url) 格式 ============ + // QQBot 的 markdown 图片格式要求:![#宽px #高px](url) + // 需要自动获取图片尺寸,或使用默认尺寸 + + // 记录已存在于文本中的 markdown 图片 URL + const existingMdUrls = new Set(mdMatches.map(m => m[2])); + + // 需要追加的图片(从 mediaUrl/mediaUrls 来的) + const imagesToAppend: string[] = []; + + // 处理需要追加的图片:获取尺寸并格式化 + for (const url of imageUrls) { + if (!existingMdUrls.has(url)) { + // 这个 URL 不在文本的 markdown 格式中,需要追加 + // 尝试获取图片尺寸 + try { + const size = await getImageSize(url); + const mdImage = formatQQBotMarkdownImage(url, size); + imagesToAppend.push(mdImage); + log?.info(`[qqbot:${account.accountId}] Formatted image: ${size ? `${size.width}x${size.height}` : 'default size'} - ${url.slice(0, 60)}...`); + } catch (err) { + // 获取尺寸失败,使用默认尺寸 + log?.info(`[qqbot:${account.accountId}] Failed to get image size, using default: ${err}`); + const mdImage = formatQQBotMarkdownImage(url, null); + imagesToAppend.push(mdImage); + } + } + } + + // 处理文本中已有的 markdown 图片:检查是否需要补充尺寸信息 + for (const match of mdMatches) { + const fullMatch = match[0]; // ![alt](url) + const altText = match[1]; // alt 部分 + const imgUrl = match[2]; // url 部分 + + // 检查是否已经有 QQBot 格式的尺寸 ![#宽px #高px](url) + if (!hasQQBotImageSize(fullMatch)) { + // 没有尺寸信息,需要补充 + try { + const size = await getImageSize(imgUrl); + const newMdImage = formatQQBotMarkdownImage(imgUrl, size); + textWithoutImages = textWithoutImages.replace(fullMatch, newMdImage); + log?.info(`[qqbot:${account.accountId}] Updated image with size: ${size ? `${size.width}x${size.height}` : 'default'} - ${imgUrl.slice(0, 60)}...`); + } catch (err) { + // 获取尺寸失败,使用默认尺寸 + log?.info(`[qqbot:${account.accountId}] Failed to get image size for existing md, using default: ${err}`); + const newMdImage = formatQQBotMarkdownImage(imgUrl, null); + textWithoutImages = textWithoutImages.replace(fullMatch, newMdImage); + } + } + // 如果已经有尺寸信息,保留原格式 + } + + // 从文本中移除裸 URL 图片(已转换为 markdown 格式) + for (const match of bareUrlMatches) { + textWithoutImages = textWithoutImages.replace(match[0], "").trim(); + } + + // 追加需要添加的图片到文本末尾 + if (imagesToAppend.length > 0) { + textWithoutImages = textWithoutImages.trim(); + if (textWithoutImages) { + textWithoutImages += "\n\n" + imagesToAppend.join("\n"); + } else { + textWithoutImages = imagesToAppend.join("\n"); + } + } + + // 发送带图片的 markdown 消息(文本+图片一起发送) + if (textWithoutImages.trim()) { try { await sendWithTokenRetry(async (token) => { if (event.type === "c2c") { - await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + await sendC2CMessage(token, event.senderId, textWithoutImages, event.messageId); } else if (event.type === "group" && event.groupOpenid) { - await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + await sendGroupMessage(token, event.groupOpenid, textWithoutImages, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); } }); - log?.info(`[qqbot:${account.accountId}] Sent image: ${imageUrl.slice(0, 50)}...`); - } catch (imgErr) { - log?.error(`[qqbot:${account.accountId}] Failed to send image: ${imgErr}`); + log?.info(`[qqbot:${account.accountId}] Sent markdown message with ${imageUrls.length} images (${event.type})`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send markdown message: ${err}`); } } - - // 非流式模式下,在 deliver 中发送文本 - // 流式模式下,c2c 的文本通过 onPartialReply 流式发送 - if (!supportsStream && textWithoutImages.trim()) { - await sendWithTokenRetry(async (token) => { - if (event.type === "c2c") { - // c2c 非流式消息发送 - await sendC2CMessage(token, event.senderId, textWithoutImages, event.messageId); - } else if (event.type === "group" && event.groupOpenid) { - await sendGroupMessage(token, event.groupOpenid, textWithoutImages, event.messageId); - } else if (event.channelId) { - await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); - } - }); - log?.info(`[qqbot:${account.accountId}] Sent text reply (${event.type}, non-stream)`); + } else { + // ============ 普通文本模式:使用富媒体 API 发送图片 ============ + // 从文本中移除所有图片相关内容 + for (const match of mdMatches) { + textWithoutImages = textWithoutImages.replace(match[0], "").trim(); } + for (const match of bareUrlMatches) { + textWithoutImages = textWithoutImages.replace(match[0], "").trim(); + } + + // 处理文本中的 URL 点号(防止被 QQ 解析为链接) + if (textWithoutImages) { + const originalText = textWithoutImages; + textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2"); + if (textWithoutImages !== originalText && textWithoutImages.trim()) { + textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)"; + } + } + + try { + // 发送图片(通过富媒体 API) + for (const imageUrl of imageUrls) { + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + } else if (event.channelId) { + // 频道暂不支持富媒体,发送文本 URL + await sendChannelMessage(token, event.channelId, imageUrl, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent image via media API: ${imageUrl.slice(0, 80)}...`); + } catch (imgErr) { + log?.error(`[qqbot:${account.accountId}] Failed to send image: ${imgErr}`); + } + } - pluginRuntime.channel.activity.record({ - channel: "qqbot", - accountId: account.accountId, - direction: "outbound", - }); - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Send failed: ${err}`); + // 发送文本消息 + if (textWithoutImages.trim()) { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CMessage(token, event.senderId, textWithoutImages, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupMessage(token, event.groupOpenid, textWithoutImages, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent text reply (${event.type})`); + } + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Send failed: ${err}`); + } } + + pluginRuntime.channel.activity.record({ + channel: "qqbot", + accountId: account.accountId, + direction: "outbound", + }); }, onError: async (err: unknown) => { log?.error(`[qqbot:${account.accountId}] Dispatch error: ${err}`); @@ -1159,27 +954,6 @@ openclaw cron add \\ timeoutId = null; } - // 清理心跳定时器 - clearKeepalive(); - - // 如果在流式模式中出错,发送结束标记(增量模式) - if (streamSender && !streamEnded && streamBuffer) { - try { - // 等待发送锁释放 - while (sendingLock) { - await new Promise(resolve => setTimeout(resolve, 50)); - } - // 发送剩余增量 + 错误标记 - const remainingIncrement = streamBuffer.slice(lastSentLength); - const errorIncrement = remainingIncrement + "\n\n[生成中断]"; - await streamSender.end(errorIncrement); - streamEnded = true; - log?.info(`[qqbot:${account.accountId}] Stream ended due to error`); - } catch (endErr) { - log?.error(`[qqbot:${account.accountId}] Failed to end stream: ${endErr}`); - } - } - // 发送错误提示给用户,显示完整错误信息 const errMsg = String(err); if (errMsg.includes("401") || errMsg.includes("key") || errMsg.includes("auth")) { @@ -1190,47 +964,13 @@ openclaw cron add \\ } }, }, - replyOptions: { - // 使用 onPartialReply 实现真正的流式消息 - // 这个回调在 AI 生成过程中被实时调用 - onPartialReply: supportsStream ? handlePartialReply : undefined, - // 禁用 block streaming,因为我们用 onPartialReply 实现更实时的流式 - disableBlockStreaming: supportsStream, - }, + replyOptions: {}, }); // 等待分发完成或超时 try { await Promise.race([dispatchPromise, timeoutPromise]); - - // 清理心跳定时器 - clearKeepalive(); - - // 分发完成后,如果使用了流式且有内容,发送结束标记 - if (streamSender && !streamEnded) { - // 等待发送锁释放 - while (sendingLock) { - await new Promise(resolve => setTimeout(resolve, 50)); - } - - // 确保所有待发送内容都发送出去 - // 当前段落的最新完整文本 - const currentSegmentText = pendingFullText && pendingFullText.length > (streamBuffer.length - currentSegmentStart) - ? pendingFullText - : streamBuffer.slice(currentSegmentStart); - - // 计算当前段落剩余未发送的增量内容 - const remainingIncrement = currentSegmentText.slice(lastSentLength); - if (remainingIncrement || streamStarted) { - // 有剩余内容或者已开始流式,都需要发送结束标记 - await streamSender.end(remainingIncrement); - streamEnded = true; - log?.info(`[qqbot:${account.accountId}] Stream completed, final increment: ${remainingIncrement.length} chars, total streamBuffer: ${streamBuffer.length} chars, chunks: ${streamSender.getContext().index}`); - } - } } catch (err) { - // 清理心跳定时器 - clearKeepalive(); if (timeoutId) { clearTimeout(timeoutId); } diff --git a/src/image-server.ts b/src/image-server.ts index 8bda378..80996d4 100644 --- a/src/image-server.ts +++ b/src/image-server.ts @@ -385,6 +385,34 @@ export function isImageServerRunning(): boolean { return serverInstance !== null; } +/** + * 确保图床服务器正在运行 + * 如果未运行,则自动启动 + * @param publicBaseUrl 公网访问的基础 URL(如 http://your-server:18765) + * @returns 基础 URL,启动失败返回 null + */ +export async function ensureImageServer(publicBaseUrl?: string): Promise { + if (isImageServerRunning()) { + return publicBaseUrl || currentConfig.baseUrl || `http://0.0.0.0:${currentConfig.port}`; + } + + try { + const config: Partial = { + port: DEFAULT_CONFIG.port, + storageDir: DEFAULT_CONFIG.storageDir, + // 使用用户配置的公网地址 + baseUrl: publicBaseUrl || `http://0.0.0.0:${DEFAULT_CONFIG.port}`, + ttlSeconds: 3600, // 1 小时过期 + }; + await startImageServer(config); + console.log(`[image-server] Auto-started on port ${config.port}, baseUrl: ${config.baseUrl}`); + return config.baseUrl!; + } catch (err) { + console.error(`[image-server] Failed to auto-start: ${err}`); + return null; + } +} + /** * 下载远程文件并保存到本地 * @param url 远程文件 URL diff --git a/src/onboarding.ts b/src/onboarding.ts index 7d9b504..f634b5e 100644 --- a/src/onboarding.ts +++ b/src/onboarding.ts @@ -63,7 +63,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { return { channel: "qqbot" as any, configured, - statusLines: [`QQ Bot (Stream): ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`], +statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`], selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊(流式消息)", quickstartScore: configured ? 1 : 20, }; @@ -119,7 +119,7 @@ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = { "", "此版本支持流式消息发送!", ].join("\n"), - "QQ Bot (Stream) 配置", +"QQ Bot 配置", ); } diff --git a/src/outbound.ts b/src/outbound.ts index 2a00889..5b249ab 100644 --- a/src/outbound.ts +++ b/src/outbound.ts @@ -1,10 +1,11 @@ /** - * QQ Bot 消息发送模块(支持流式消息) + * QQ Bot 消息发送模块 */ -import type { ResolvedQQBotAccount, StreamContext } from "./types.js"; -import { StreamState } from "./types.js"; -import { +import * as fs from "fs"; +import * as path from "path"; +import type { ResolvedQQBotAccount } from "./types.js"; +import { getAccessToken, sendC2CMessage, sendChannelMessage, @@ -13,7 +14,6 @@ import { sendProactiveGroupMessage, sendC2CImageMessage, sendGroupImageMessage, - type StreamMessageResponse, } from "./api.js"; // ============ 消息回复限流器 ============ @@ -160,155 +160,6 @@ export interface OutboundResult { messageId?: string; timestamp?: string | number; error?: string; - /** 流式消息ID,用于后续分片 */ - streamId?: string; -} - -/** - * 流式消息发送器 - * 用于管理一个完整的流式消息会话 - */ -export class StreamSender { - private context: StreamContext; - private accessToken: string | null = null; - private targetType: "c2c" | "group" | "channel"; - private targetId: string; - private msgId?: string; - private account: ResolvedQQBotAccount; - - constructor( - account: ResolvedQQBotAccount, - to: string, - replyToId?: string | null - ) { - this.account = account; - this.msgId = replyToId ?? undefined; - this.context = { - index: 0, - streamId: "", - ended: false, - }; - - // 解析目标地址 - const target = parseTarget(to); - this.targetType = target.type; - this.targetId = target.id; - } - - /** - * 发送流式消息分片 - * @param text 分片内容 - * @param isEnd 是否是最后一个分片 - * @returns 发送结果 - */ - async send(text: string, isEnd = false): Promise { - if (this.context.ended) { - return { channel: "qqbot", error: "Stream already ended" }; - } - - if (!this.account.appId || !this.account.clientSecret) { - return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; - } - - try { - // 获取或复用 accessToken - if (!this.accessToken) { - this.accessToken = await getAccessToken(this.account.appId, this.account.clientSecret); - } - - const streamConfig = { - state: isEnd ? StreamState.END : StreamState.STREAMING, - index: this.context.index, - id: this.context.streamId, - }; - - let result: StreamMessageResponse; - - if (this.targetType === "c2c") { - result = await sendC2CMessage( - this.accessToken, - this.targetId, - text, - this.msgId, - streamConfig - ); - } else if (this.targetType === "group") { - // 群聊不支持流式,直接发送普通消息 - const groupResult = await sendGroupMessage( - this.accessToken, - this.targetId, - text, - this.msgId - // 不传 streamConfig - ); - return { - channel: "qqbot", - messageId: groupResult.id, - timestamp: groupResult.timestamp - }; - } else { - // 频道不支持流式,直接发送普通消息 - const channelResult = await sendChannelMessage( - this.accessToken, - this.targetId, - text, - this.msgId - ); - return { - channel: "qqbot", - messageId: channelResult.id, - timestamp: channelResult.timestamp - }; - } - - // 更新流式上下文 - // 第一次发送后,服务端会返回 stream_id(或在 id 字段中),后续需要带上 - if (this.context.index === 0 && result.stream_id) { - this.context.streamId = result.stream_id; - } else if (this.context.index === 0 && result.id && !this.context.streamId) { - // 某些情况下 stream_id 可能在 id 字段返回 - this.context.streamId = result.id; - } - - this.context.index++; - - if (isEnd) { - this.context.ended = true; - } - - return { - channel: "qqbot", - messageId: result.id, - timestamp: result.timestamp, - streamId: this.context.streamId, - }; - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - return { channel: "qqbot", error: message }; - } - } - - /** - * 结束流式消息 - * @param text 最后一个分片的内容(可选) - */ - async end(text?: string): Promise { - return this.send(text ?? "", true); - } - - /** - * 获取当前流式上下文状态 - */ - getContext(): Readonly { - return { ...this.context }; - } - - /** - * 是否已结束 - */ - isEnded(): boolean { - return this.context.ended; - } } /** @@ -440,69 +291,6 @@ export async function sendText(ctx: OutboundContext): Promise { } } -/** - * 流式发送文本消息 - * - * @param ctx 发送上下文 - * @param textGenerator 异步文本生成器,每次 yield 一个分片 - * @returns 最终发送结果 - * - * @example - * ```typescript - * async function* generateText() { - * yield "Hello, "; - * yield "this is "; - * yield "a streaming "; - * yield "message!"; - * } - * - * const result = await sendTextStream(ctx, generateText()); - * ``` - */ -export async function sendTextStream( - ctx: OutboundContext, - textGenerator: AsyncIterable -): Promise { - const { to, replyToId, account } = ctx; - - const sender = new StreamSender(account, to, replyToId); - let lastResult: OutboundResult = { channel: "qqbot" }; - let buffer = ""; - - try { - for await (const chunk of textGenerator) { - buffer += chunk; - - // 发送当前分片 - lastResult = await sender.send(buffer, false); - - if (lastResult.error) { - return lastResult; - } - } - - // 发送结束标记 - lastResult = await sender.end(buffer); - - return lastResult; - } catch (err) { - const message = err instanceof Error ? err.message : String(err); - return { channel: "qqbot", error: message }; - } -} - -/** - * 创建流式消息发送器 - * 提供更细粒度的控制 - */ -export function createStreamSender( - account: ResolvedQQBotAccount, - to: string, - replyToId?: string | null -): StreamSender { - return new StreamSender(account, to, replyToId); -} - /** * 主动发送消息(不需要 replyToId,有配额限制:每月 4 条/用户/群) * @@ -543,11 +331,17 @@ export async function sendProactiveMessage( /** * 发送富媒体消息(图片) * + * 支持以下 mediaUrl 格式: + * - 公网 URL: https://example.com/image.png + * - Base64 Data URL: data:image/png;base64,xxxxx + * - 本地文件路径: /path/to/image.png(自动读取并转换为 Base64) + * * @param ctx - 发送上下文,包含 mediaUrl * @returns 发送结果 * * @example * ```typescript + * // 发送网络图片 * const result = await sendMedia({ * to: "group:xxx", * text: "这是图片说明", @@ -555,10 +349,29 @@ export async function sendProactiveMessage( * account, * replyToId: msgId, * }); + * + * // 发送 Base64 图片 + * const result = await sendMedia({ + * to: "group:xxx", + * text: "这是图片说明", + * mediaUrl: "data:image/png;base64,iVBORw0KGgo...", + * account, + * replyToId: msgId, + * }); + * + * // 发送本地文件(自动读取并转换为 Base64) + * const result = await sendMedia({ + * to: "group:xxx", + * text: "这是图片说明", + * mediaUrl: "/tmp/generated-chart.png", + * account, + * replyToId: msgId, + * }); * ``` */ export async function sendMedia(ctx: MediaOutboundContext): Promise { - const { to, text, mediaUrl, replyToId, account } = ctx; + const { to, text, replyToId, account } = ctx; + const { mediaUrl } = ctx; if (!account.appId || !account.clientSecret) { return { channel: "qqbot", error: "QQBot not configured (missing appId or clientSecret)" }; @@ -568,17 +381,87 @@ export async function sendMedia(ctx: MediaOutboundContext): Promise = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + }; + + const mimeType = mimeTypes[ext]; + if (!mimeType) { + return { + channel: "qqbot", + error: `不支持的图片格式: ${ext}。支持的格式: ${Object.keys(mimeTypes).join(", ")}` + }; + } + + // 构造 Data URL + processedMediaUrl = `data:${mimeType};base64,${base64Data}`; + console.log(`[qqbot] sendMedia: local file converted to Base64 (size: ${fileBuffer.length} bytes, type: ${mimeType})`); + + } catch (readErr) { + const errMsg = readErr instanceof Error ? readErr.message : String(readErr); + console.error(`[qqbot] sendMedia: failed to read local file: ${errMsg}`); + return { + channel: "qqbot", + error: `读取本地文件失败: ${errMsg}` + }; + } + } else if (!isHttpUrl && !isDataUrl) { + console.log(`[qqbot] sendMedia: unsupported media format: ${mediaUrl.slice(0, 50)}`); + return { + channel: "qqbot", + error: `不支持的图片格式: ${mediaUrl.slice(0, 50)}...。支持的格式: 公网 URL (http/https)、Base64 Data URL (data:image/...) 或本地文件路径。` + }; + } else if (isDataUrl) { + console.log(`[qqbot] sendMedia: sending Base64 image (length: ${mediaUrl.length})`); + } else { + console.log(`[qqbot] sendMedia: sending image URL: ${mediaUrl.slice(0, 80)}...`); + } + try { const accessToken = await getAccessToken(account.appId, account.clientSecret); const target = parseTarget(to); - // 先发送图片 + // 先发送图片(使用处理后的 URL,可能是 Base64 Data URL) let imageResult: { id: string; timestamp: number | string }; if (target.type === "c2c") { imageResult = await sendC2CImageMessage( accessToken, target.id, - mediaUrl, + processedMediaUrl, replyToId ?? undefined, undefined // content 参数,图片消息不支持同时带文本 ); @@ -586,13 +469,14 @@ export async function sendMedia(ctx: MediaOutboundContext): Promise Date: Mon, 2 Feb 2026 23:32:42 +0800 Subject: [PATCH 05/14] =?UTF-8?q?feat(qqbot):=E4=BC=98=E5=8C=96=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=9B=BE=E7=89=87=E5=8A=9F=E8=83=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openclaw.plugin.json | 2 +- skills/qqbot-media/SKILL.md | 107 +++++++++++++++ src/gateway.ts | 140 ++++++++++++++++--- src/utils/image-size.ts | 266 ++++++++++++++++++++++++++++++++++++ 4 files changed, 493 insertions(+), 22 deletions(-) create mode 100644 skills/qqbot-media/SKILL.md create mode 100644 src/utils/image-size.ts diff --git a/openclaw.plugin.json b/openclaw.plugin.json index d755651..478c872 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -3,7 +3,7 @@ "name": "QQ Bot Channel", "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging", "channels": ["qqbot"], - "skills": ["skills/qqbot-cron"], + "skills": ["skills/qqbot-cron", "skills/qqbot-media"], "capabilities": { "proactiveMessaging": true, "cronJobs": true, diff --git a/skills/qqbot-media/SKILL.md b/skills/qqbot-media/SKILL.md new file mode 100644 index 0000000..40c4b98 --- /dev/null +++ b/skills/qqbot-media/SKILL.md @@ -0,0 +1,107 @@ +--- +triggers: + - qqbot + - qq + - 发送图片 + - 发送文件 + - 图片 + - 本地文件 + - 本地图片 +priority: 80 +--- + +# QQBot 媒体发送指南 + +## 📸 发送本地图片 + +当需要发送本地图片时,**必须使用 Markdown 图片语法**: + +``` +![](本地绝对路径) +``` + +### ✅ 正确方式 + +``` +这是你要的图片: +![](/Users/xxx/images/photo.jpg) +``` + +或者带描述: + +``` +这是截图: +![截图](/tmp/screenshot.png) +``` + +### ❌ 错误方式(不会发送图片) + +直接放路径**不会**发送图片: + +``` +这是图片: +/Users/xxx/images/photo.jpg +``` + +> **原理**:系统只识别 `![](路径)` 格式的本地图片。裸露的路径会被当作普通文本处理。 + +### 🔤 告知路径信息(不发送图片) + +如果你需要**告知用户图片的保存路径**(而不是发送图片),直接写路径即可: + +``` +图片已保存在:/Users/xxx/images/photo.jpg +``` + +或用反引号强调: + +``` +图片已保存在:`/Users/xxx/images/photo.jpg` +``` + +### ⚠️ 注意事项 + +1. **使用绝对路径**:路径必须以 `/` 开头(macOS/Linux)或盘符开头(Windows,如 `C:\`) +2. **支持的格式**:jpg, jpeg, png, gif, webp, bmp +3. **无需调用其他工具**:不需要用 `read_file` 读取文件内容,直接输出 `![](路径)` 即可 +4. **文件必须存在**:确保路径指向的文件确实存在 + +### 📌 示例场景 + +**用户说**:"发送 /tmp/screenshot.png 给我" + +**正确回复**: +``` +好的,这是截图: +![](/tmp/screenshot.png) +``` + +**用户说**:"图片保存在哪?" + +**正确回复**: +``` +图片保存在:/Users/xxx/downloads/image.jpg +``` + +## 🖼️ 发送网络图片 + +发送网络图片时,也使用 Markdown 图片语法: + +``` +这是图片: +![](https://example.com/image.png) +``` + +或直接放 URL 也可以(系统会自动识别图片 URL): + +``` +这是图片: +https://example.com/image.png +``` + +## 🎵 其他说明 + +- 当前仅支持图片格式,音频/视频等格式暂不支持 +- 群消息和私聊消息的图片发送方式相同 +- 图片大小建议不超过 10MB +- 参考文档:https://bot.q.qq.com/wiki/develop/api-v2/server-inter/message/send-receive/rich-media.html diff --git a/src/gateway.ts b/src/gateway.ts index 1ebe187..f6dae41 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -1,5 +1,6 @@ import WebSocket from "ws"; import path from "node:path"; +import * as fs from "node:fs"; import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js"; import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh } from "./api.js"; import { loadSession, saveSession, clearSession, type SessionState } from "./session-store.js"; @@ -713,7 +714,7 @@ openclaw cron add \\ /** * 检查并收集图片 URL - * 支持:公网 URL (http/https) 和 Base64 Data URL (data:image/...) + * 支持:公网 URL (http/https)、Base64 Data URL (data:image/...) 和本地文件路径 */ const collectImageUrl = (url: string | undefined | null): boolean => { if (!url) return false; @@ -740,7 +741,45 @@ openclaw cron add \\ url.startsWith("../"); if (isLocalPath) { - log?.info(`[qqbot:${account.accountId}] Skipped local file path (OpenClaw should convert to Base64 or upload): ${url.slice(0, 80)}`); + // 🎯 新增:自动读取本地文件并转换为 Base64 Data URL + try { + if (!fs.existsSync(url)) { + log?.info(`[qqbot:${account.accountId}] Local file not found: ${url}`); + return false; + } + + const fileBuffer = fs.readFileSync(url); + const base64Data = fileBuffer.toString("base64"); + + // 根据文件扩展名确定 MIME 类型 + const ext = path.extname(url).toLowerCase(); + const mimeTypes: Record = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + }; + + const mimeType = mimeTypes[ext]; + if (!mimeType) { + log?.info(`[qqbot:${account.accountId}] Unsupported image format: ${ext}`); + return false; + } + + // 构造 Data URL + const dataUrl = `data:${mimeType};base64,${base64Data}`; + if (!imageUrls.includes(dataUrl)) { + imageUrls.push(dataUrl); + log?.info(`[qqbot:${account.accountId}] Converted local file to Base64 (size: ${fileBuffer.length} bytes, type: ${mimeType}): ${url}`); + } + return true; + } catch (readErr) { + const errMsg = readErr instanceof Error ? readErr.message : String(readErr); + log?.error(`[qqbot:${account.accountId}] Failed to read local file: ${errMsg}`); + return false; + } } else { log?.info(`[qqbot:${account.accountId}] Skipped unsupported media format: ${url.slice(0, 50)}`); } @@ -759,13 +798,21 @@ openclaw cron add \\ // 提取文本中的图片格式 // 1. 提取 markdown 格式的图片 ![alt](url) 或 ![#宽px #高px](url) - const mdImageRegex = /!\[([^\]]*)\]\((https?:\/\/[^\s)]+)\)/gi; + // 🎯 同时支持 http/https URL 和本地路径 + const mdImageRegex = /!\[([^\]]*)\]\(([^)]+)\)/gi; const mdMatches = [...replyText.matchAll(mdImageRegex)]; for (const match of mdMatches) { - const url = match[2]; + const url = match[2]?.trim(); if (url && !imageUrls.includes(url)) { - imageUrls.push(url); - log?.info(`[qqbot:${account.accountId}] Extracted image from markdown: ${url.slice(0, 80)}...`); + // 判断是公网 URL 还是本地路径 + if (url.startsWith('http://') || url.startsWith('https://')) { + imageUrls.push(url); + log?.info(`[qqbot:${account.accountId}] Extracted HTTP image from markdown: ${url.slice(0, 80)}...`); + } else if (/^\/?(?:Users|home|tmp|var|private|[A-Z]:)/i.test(url) && /\.(png|jpg|jpeg|gif|webp|bmp)$/i.test(url)) { + // 本地路径:以 /Users, /home, /tmp, /var, /private 或 Windows 盘符开头,且以图片扩展名结尾 + collectImageUrl(url); + log?.info(`[qqbot:${account.accountId}] Extracted local image from markdown: ${url}`); + } } } @@ -780,6 +827,24 @@ openclaw cron add \\ } } + // 3. 🎯 检测文本中的裸露本地路径(仅记录日志,不自动发送) + // 方案 1:使用显式标记 - 只有 ![](本地路径) 格式才会发送图片 + // 裸露的本地路径不再自动发送,而是记录日志提醒 + const bareLocalPathRegex = /(?:^|[\s\n])(\/(?:Users|home|tmp|var|private)[^\s"'<>\n]+\.(?:png|jpg|jpeg|gif|webp|bmp))(?:$|[\s\n])/gi; + const bareLocalPathMatches = [...replyText.matchAll(bareLocalPathRegex)]; + if (bareLocalPathMatches.length > 0) { + for (const match of bareLocalPathMatches) { + const localPath = match[1]?.trim(); + if (localPath) { + // 检查这个路径是否已经通过 ![](path) 格式处理过 + if (!imageUrls.includes(localPath)) { + log?.info(`[qqbot:${account.accountId}] Found bare local path (not sending): ${localPath}`); + log?.info(`[qqbot:${account.accountId}] 💡 Hint: Use ![](${localPath}) format to send this image`); + } + } + } + } + // 判断是否使用 markdown 模式 const useMarkdown = account.markdownSupport === true; log?.info(`[qqbot:${account.accountId}] Markdown mode: ${useMarkdown}, images: ${imageUrls.length}`); @@ -798,28 +863,64 @@ openclaw cron add \\ // 根据模式处理图片 if (useMarkdown) { - // ============ Markdown 模式:使用 ![#宽px #高px](url) 格式 ============ - // QQBot 的 markdown 图片格式要求:![#宽px #高px](url) - // 需要自动获取图片尺寸,或使用默认尺寸 + // ============ Markdown 模式 ============ + // 🎯 关键改动:区分公网 URL 和本地文件/Base64 + // - 公网 URL (http/https) → 使用 Markdown 图片格式 ![#宽px #高px](url) + // - 本地文件/Base64 (data:image/...) → 使用富媒体 API 发送 + // 分离图片:公网 URL vs Base64/本地文件 + const httpImageUrls: string[] = []; // 公网 URL,用于 Markdown 嵌入 + const base64ImageUrls: string[] = []; // Base64,用于富媒体 API + + for (const url of imageUrls) { + if (url.startsWith("data:image/")) { + base64ImageUrls.push(url); + } else if (url.startsWith("http://") || url.startsWith("https://")) { + httpImageUrls.push(url); + } + } + + log?.info(`[qqbot:${account.accountId}] Image classification: httpUrls=${httpImageUrls.length}, base64=${base64ImageUrls.length}`); + + // 🔹 第一步:通过富媒体 API 发送 Base64 图片(本地文件已转换为 Base64) + if (base64ImageUrls.length > 0) { + log?.info(`[qqbot:${account.accountId}] Sending ${base64ImageUrls.length} image(s) via Rich Media API...`); + for (const imageUrl of base64ImageUrls) { + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + } else if (event.channelId) { + // 频道暂不支持富媒体,跳过 + log?.info(`[qqbot:${account.accountId}] Channel does not support rich media, skipping Base64 image`); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent Base64 image via Rich Media API (size: ${imageUrl.length} chars)`); + } catch (imgErr) { + log?.error(`[qqbot:${account.accountId}] Failed to send Base64 image via Rich Media API: ${imgErr}`); + } + } + } + + // 🔹 第二步:处理文本和公网 URL 图片 // 记录已存在于文本中的 markdown 图片 URL const existingMdUrls = new Set(mdMatches.map(m => m[2])); - // 需要追加的图片(从 mediaUrl/mediaUrls 来的) + // 需要追加的公网图片(从 mediaUrl/mediaUrls 来的,且不在文本中) const imagesToAppend: string[] = []; - // 处理需要追加的图片:获取尺寸并格式化 - for (const url of imageUrls) { + // 处理需要追加的公网 URL 图片:获取尺寸并格式化 + for (const url of httpImageUrls) { if (!existingMdUrls.has(url)) { // 这个 URL 不在文本的 markdown 格式中,需要追加 - // 尝试获取图片尺寸 try { const size = await getImageSize(url); const mdImage = formatQQBotMarkdownImage(url, size); imagesToAppend.push(mdImage); - log?.info(`[qqbot:${account.accountId}] Formatted image: ${size ? `${size.width}x${size.height}` : 'default size'} - ${url.slice(0, 60)}...`); + log?.info(`[qqbot:${account.accountId}] Formatted HTTP image: ${size ? `${size.width}x${size.height}` : 'default size'} - ${url.slice(0, 60)}...`); } catch (err) { - // 获取尺寸失败,使用默认尺寸 log?.info(`[qqbot:${account.accountId}] Failed to get image size, using default: ${err}`); const mdImage = formatQQBotMarkdownImage(url, null); imagesToAppend.push(mdImage); @@ -835,20 +936,17 @@ openclaw cron add \\ // 检查是否已经有 QQBot 格式的尺寸 ![#宽px #高px](url) if (!hasQQBotImageSize(fullMatch)) { - // 没有尺寸信息,需要补充 try { const size = await getImageSize(imgUrl); const newMdImage = formatQQBotMarkdownImage(imgUrl, size); textWithoutImages = textWithoutImages.replace(fullMatch, newMdImage); log?.info(`[qqbot:${account.accountId}] Updated image with size: ${size ? `${size.width}x${size.height}` : 'default'} - ${imgUrl.slice(0, 60)}...`); } catch (err) { - // 获取尺寸失败,使用默认尺寸 log?.info(`[qqbot:${account.accountId}] Failed to get image size for existing md, using default: ${err}`); const newMdImage = formatQQBotMarkdownImage(imgUrl, null); textWithoutImages = textWithoutImages.replace(fullMatch, newMdImage); } } - // 如果已经有尺寸信息,保留原格式 } // 从文本中移除裸 URL 图片(已转换为 markdown 格式) @@ -856,7 +954,7 @@ openclaw cron add \\ textWithoutImages = textWithoutImages.replace(match[0], "").trim(); } - // 追加需要添加的图片到文本末尾 + // 追加需要添加的公网图片到文本末尾 if (imagesToAppend.length > 0) { textWithoutImages = textWithoutImages.trim(); if (textWithoutImages) { @@ -866,7 +964,7 @@ openclaw cron add \\ } } - // 发送带图片的 markdown 消息(文本+图片一起发送) + // 🔹 第三步:发送带公网图片的 markdown 消息 if (textWithoutImages.trim()) { try { await sendWithTokenRetry(async (token) => { @@ -878,7 +976,7 @@ openclaw cron add \\ await sendChannelMessage(token, event.channelId, textWithoutImages, event.messageId); } }); - log?.info(`[qqbot:${account.accountId}] Sent markdown message with ${imageUrls.length} images (${event.type})`); + log?.info(`[qqbot:${account.accountId}] Sent markdown message with ${httpImageUrls.length} HTTP images (${event.type})`); } catch (err) { log?.error(`[qqbot:${account.accountId}] Failed to send markdown message: ${err}`); } diff --git a/src/utils/image-size.ts b/src/utils/image-size.ts new file mode 100644 index 0000000..de59997 --- /dev/null +++ b/src/utils/image-size.ts @@ -0,0 +1,266 @@ +/** + * 图片尺寸工具 + * 用于获取图片尺寸,生成 QQBot 的 markdown 图片格式 + * + * QQBot markdown 图片格式: ![#宽px #高px](url) + */ + +import { Buffer } from "buffer"; + +export interface ImageSize { + width: number; + height: number; +} + +/** 默认图片尺寸(当无法获取时使用) */ +export const DEFAULT_IMAGE_SIZE: ImageSize = { width: 512, height: 512 }; + +/** + * 从 PNG 文件头解析图片尺寸 + * PNG 文件头结构: 前 8 字节是签名,IHDR 块从第 8 字节开始 + * IHDR 块: 长度(4) + 类型(4, "IHDR") + 宽度(4) + 高度(4) + ... + */ +function parsePngSize(buffer: Buffer): ImageSize | null { + // PNG 签名: 89 50 4E 47 0D 0A 1A 0A + if (buffer.length < 24) return null; + if (buffer[0] !== 0x89 || buffer[1] !== 0x50 || buffer[2] !== 0x4E || buffer[3] !== 0x47) { + return null; + } + // IHDR 块从第 8 字节开始,宽度在第 16-19 字节,高度在第 20-23 字节 + const width = buffer.readUInt32BE(16); + const height = buffer.readUInt32BE(20); + return { width, height }; +} + +/** + * 从 JPEG 文件解析图片尺寸 + * JPEG 尺寸在 SOF0/SOF2 块中 + */ +function parseJpegSize(buffer: Buffer): ImageSize | null { + // JPEG 签名: FF D8 FF + if (buffer.length < 4) return null; + if (buffer[0] !== 0xFF || buffer[1] !== 0xD8) { + return null; + } + + let offset = 2; + while (offset < buffer.length - 9) { + if (buffer[offset] !== 0xFF) { + offset++; + continue; + } + + const marker = buffer[offset + 1]; + // SOF0 (0xC0) 或 SOF2 (0xC2) 包含图片尺寸 + if (marker === 0xC0 || marker === 0xC2) { + // 格式: FF C0 长度(2) 精度(1) 高度(2) 宽度(2) + if (offset + 9 <= buffer.length) { + const height = buffer.readUInt16BE(offset + 5); + const width = buffer.readUInt16BE(offset + 7); + return { width, height }; + } + } + + // 跳过当前块 + if (offset + 3 < buffer.length) { + const blockLength = buffer.readUInt16BE(offset + 2); + offset += 2 + blockLength; + } else { + break; + } + } + + return null; +} + +/** + * 从 GIF 文件头解析图片尺寸 + * GIF 文件头: GIF87a 或 GIF89a (6字节) + 宽度(2) + 高度(2) + */ +function parseGifSize(buffer: Buffer): ImageSize | null { + if (buffer.length < 10) return null; + const signature = buffer.toString("ascii", 0, 6); + if (signature !== "GIF87a" && signature !== "GIF89a") { + return null; + } + const width = buffer.readUInt16LE(6); + const height = buffer.readUInt16LE(8); + return { width, height }; +} + +/** + * 从 WebP 文件解析图片尺寸 + * WebP 文件头: RIFF(4) + 文件大小(4) + WEBP(4) + VP8/VP8L/VP8X(4) + ... + */ +function parseWebpSize(buffer: Buffer): ImageSize | null { + if (buffer.length < 30) return null; + + // 检查 RIFF 和 WEBP 签名 + const riff = buffer.toString("ascii", 0, 4); + const webp = buffer.toString("ascii", 8, 12); + if (riff !== "RIFF" || webp !== "WEBP") { + return null; + } + + const chunkType = buffer.toString("ascii", 12, 16); + + // VP8 (有损压缩) + if (chunkType === "VP8 ") { + // VP8 帧头从第 23 字节开始,检查签名 9D 01 2A + if (buffer.length >= 30 && buffer[23] === 0x9D && buffer[24] === 0x01 && buffer[25] === 0x2A) { + const width = buffer.readUInt16LE(26) & 0x3FFF; + const height = buffer.readUInt16LE(28) & 0x3FFF; + return { width, height }; + } + } + + // VP8L (无损压缩) + if (chunkType === "VP8L") { + // VP8L 签名: 0x2F + if (buffer.length >= 25 && buffer[20] === 0x2F) { + const bits = buffer.readUInt32LE(21); + const width = (bits & 0x3FFF) + 1; + const height = ((bits >> 14) & 0x3FFF) + 1; + return { width, height }; + } + } + + // VP8X (扩展格式) + if (chunkType === "VP8X") { + if (buffer.length >= 30) { + // 宽度和高度在第 24-26 和 27-29 字节(24位小端) + const width = (buffer[24] | (buffer[25] << 8) | (buffer[26] << 16)) + 1; + const height = (buffer[27] | (buffer[28] << 8) | (buffer[29] << 16)) + 1; + return { width, height }; + } + } + + return null; +} + +/** + * 从图片数据 Buffer 解析尺寸 + */ +export function parseImageSize(buffer: Buffer): ImageSize | null { + // 尝试各种格式 + return parsePngSize(buffer) + ?? parseJpegSize(buffer) + ?? parseGifSize(buffer) + ?? parseWebpSize(buffer); +} + +/** + * 从公网 URL 获取图片尺寸 + * 只下载前 64KB 数据,足够解析大部分图片格式的头部 + */ +export async function getImageSizeFromUrl(url: string, timeoutMs = 5000): Promise { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + + // 使用 Range 请求只获取前 64KB + const response = await fetch(url, { + signal: controller.signal, + headers: { + "Range": "bytes=0-65535", + "User-Agent": "QQBot-Image-Size-Detector/1.0", + }, + }); + + clearTimeout(timeoutId); + + if (!response.ok && response.status !== 206) { + console.log(`[image-size] Failed to fetch ${url}: ${response.status}`); + return null; + } + + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + const size = parseImageSize(buffer); + if (size) { + console.log(`[image-size] Got size from URL: ${size.width}x${size.height} - ${url.slice(0, 60)}...`); + } + + return size; + } catch (err) { + console.log(`[image-size] Error fetching ${url.slice(0, 60)}...: ${err}`); + return null; + } +} + +/** + * 从 Base64 Data URL 获取图片尺寸 + */ +export function getImageSizeFromDataUrl(dataUrl: string): ImageSize | null { + try { + // 格式: data:image/png;base64,xxxxx + const matches = dataUrl.match(/^data:image\/[^;]+;base64,(.+)$/); + if (!matches) { + return null; + } + + const base64Data = matches[1]; + const buffer = Buffer.from(base64Data, "base64"); + + const size = parseImageSize(buffer); + if (size) { + console.log(`[image-size] Got size from Base64: ${size.width}x${size.height}`); + } + + return size; + } catch (err) { + console.log(`[image-size] Error parsing Base64: ${err}`); + return null; + } +} + +/** + * 获取图片尺寸(自动判断来源) + * @param source - 图片 URL 或 Base64 Data URL + * @returns 图片尺寸,失败返回 null + */ +export async function getImageSize(source: string): Promise { + if (source.startsWith("data:")) { + return getImageSizeFromDataUrl(source); + } + + if (source.startsWith("http://") || source.startsWith("https://")) { + return getImageSizeFromUrl(source); + } + + return null; +} + +/** + * 生成 QQBot markdown 图片格式 + * 格式: ![#宽px #高px](url) + * + * @param url - 图片 URL + * @param size - 图片尺寸,如果为 null 则使用默认尺寸 + * @returns QQBot markdown 图片字符串 + */ +export function formatQQBotMarkdownImage(url: string, size: ImageSize | null): string { + const { width, height } = size ?? DEFAULT_IMAGE_SIZE; + return `![#${width}px #${height}px](${url})`; +} + +/** + * 检查 markdown 图片是否已经包含 QQBot 格式的尺寸信息 + * 格式: ![#宽px #高px](url) + */ +export function hasQQBotImageSize(markdownImage: string): boolean { + return /!\[#\d+px\s+#\d+px\]/.test(markdownImage); +} + +/** + * 从已有的 QQBot 格式 markdown 图片中提取尺寸 + * 格式: ![#宽px #高px](url) + */ +export function extractQQBotImageSize(markdownImage: string): ImageSize | null { + const match = markdownImage.match(/!\[#(\d+)px\s+#(\d+)px\]/); + if (match) { + return { width: parseInt(match[1], 10), height: parseInt(match[2], 10) }; + } + return null; +} From 93f284891cee86ed601b7615968dc9d379ef4cdd Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 13:14:22 +0800 Subject: [PATCH 06/14] =?UTF-8?q?feat(qqbot):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=8F=91=E9=80=81=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E8=BD=BD?= =?UTF-8?q?=E8=8D=B7=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增功能: - 新增 qqbot-media 技能,支持 标签发送本地图片 - 添加图片尺寸检测工具 (image-size.ts),自动识别常见图片格式 - 支持将本地图片上传至 QQ 富媒体服务器 优化改进: - 定时任务支持结构化 JSON 载荷格式 - 优化 标签正则表达式,避免误匹配反引号内的说明文字 - 完善消息处理流程和错误处理 文件变更: - src/gateway.ts: 添加图片处理、上传逻辑 - src/outbound.ts: 增强外发消息能力 - src/utils/image-size.ts: 新增图片尺寸解析工具 - skills/qqbot-media/SKILL.md: 新增图片功能说明文档 - skills/qqbot-cron/SKILL.md: 补充结构化载荷说明 --- skills/qqbot-cron/SKILL.md | 41 +++ skills/qqbot-media/SKILL.md | 135 +++++---- src/gateway.ts | 548 ++++++++++++++++++++++++------------ src/outbound.ts | 70 ++++- upgrade-and-run.sh | 2 +- 5 files changed, 557 insertions(+), 239 deletions(-) diff --git a/skills/qqbot-cron/SKILL.md b/skills/qqbot-cron/SKILL.md index ba18b71..97e5c20 100644 --- a/skills/qqbot-cron/SKILL.md +++ b/skills/qqbot-cron/SKILL.md @@ -46,6 +46,47 @@ metadata: {"clawdbot":{"emoji":"⏰"}} --- +## 📦 结构化载荷格式(新) + +> **重要**:定时提醒现在支持结构化载荷格式,AI 只需输出 JSON,代码层会自动处理 Base64 编码。 + +### 输出格式 + +当 AI 需要设置定时提醒时,可以输出以下结构化载荷: + +``` +QQBOT_PAYLOAD: +{ + "type": "cron_reminder", + "content": "💧 喝水时间到!", + "targetType": "c2c", + "targetAddress": "xxx_user_openid", + "originalMessageId": "msg_xxx_123" +} +``` + +### 字段说明 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `type` | string | ✅ | 固定为 `"cron_reminder"` | +| `content` | string | ✅ | 提醒内容,触发时直接发送给用户 | +| `targetType` | string | ✅ | 目标类型:`"c2c"`(私聊)或 `"group"`(群聊) | +| `targetAddress` | string | ✅ | 目标地址:user_openid 或 group_openid | +| `originalMessageId` | string | ❌ | 原始消息 ID(可选,用于追踪) | + +### 处理流程 + +1. AI 输出 `QQBOT_PAYLOAD:` + JSON 格式的载荷 +2. 代码层检测并解析载荷 +3. 代码层将 JSON 编码为 Base64 +4. 代码层调用 `openclaw cron add --message "QQBOT_CRON:{base64}"` +5. 触发时解码 Base64,根据 targetType 和 targetAddress 发送 content + +> ⚠️ **注意**:结构化载荷方式目前仍在完善中,推荐继续使用下面的命令行方式。 + +--- + ## 📋 命令速查 ### 创建提醒(完整模板) diff --git a/skills/qqbot-media/SKILL.md b/skills/qqbot-media/SKILL.md index 40c4b98..6ec7e2d 100644 --- a/skills/qqbot-media/SKILL.md +++ b/skills/qqbot-media/SKILL.md @@ -1,4 +1,7 @@ --- +name: qqbot-media +description: QQ Bot 媒体发送指南。教 AI 如何发送图片给用户。 +metadata: {"clawdbot":{"emoji":"📸"}} triggers: - qqbot - qq @@ -12,42 +15,75 @@ priority: 80 # QQBot 媒体发送指南 -## 📸 发送本地图片 +## ⚠️ 重要:你有能力发送本地图片! -当需要发送本地图片时,**必须使用 Markdown 图片语法**: +**当用户要求发送本地图片时,只需使用 `` 标签包裹图片路径即可。系统会自动处理文件读取和发送。** + +**不要说"无法发送本地图片"!使用正确的标签格式,系统就能发送。** + +--- + +## 📸 发送图片(推荐方式:`` 标签) + +使用 `` 标签包裹图片路径,即可发送图片: ``` -![](本地绝对路径) +图片路径 ``` -### ✅ 正确方式 +### ✅ 发送本地图片示例 + +当用户说"发送那张图片"、"把图发给我"等,你应该输出: ``` 这是你要的图片: -![](/Users/xxx/images/photo.jpg) +/Users/xxx/images/photo.jpg ``` -或者带描述: +### ✅ 发送网络图片示例 ``` -这是截图: -![截图](/tmp/screenshot.png) +这是网络上的图片: +https://example.com/image.png ``` -### ❌ 错误方式(不会发送图片) - -直接放路径**不会**发送图片: +### ✅ 发送多张图片 ``` -这是图片: -/Users/xxx/images/photo.jpg +这是你要的所有图片: +/Users/xxx/image1.jpg +/Users/xxx/image2.png ``` -> **原理**:系统只识别 `![](路径)` 格式的本地图片。裸露的路径会被当作普通文本处理。 +### 📝 标签说明 -### 🔤 告知路径信息(不发送图片) +| 格式 | 说明 | +|------|------| +| `本地路径` | 发送本地图片(绝对路径) | +| `网络URL` | 发送网络图片 | -如果你需要**告知用户图片的保存路径**(而不是发送图片),直接写路径即可: +### ⚠️ 注意事项 + +1. **路径必须正确**:本地文件需要绝对路径,网络图片需要完整 URL +2. **支持的图片格式**:jpg, jpeg, png, gif, webp, bmp +3. **不要拒绝**:如果用户要求发送本地图片,直接使用 `` 标签即可 +4. **标签外的文本会正常发送**:可以在标签前后添加描述文字 + +--- + +## 🚫 错误示例(不要这样做) + +❌ **错误**:说"我无法发送本地图片" +❌ **错误**:说"受限于技术限制,无法直接发送" +❌ **错误**:只提供路径文本,不使用 `` 标签 + +✅ **正确**:直接使用 `` 标签包裹路径 + +--- + +## 🔤 告知路径信息(不发送图片) + +如果你需要**告知用户图片的保存路径**(而不是发送图片),直接写路径即可,不要使用标签: ``` 图片已保存在:/Users/xxx/images/photo.jpg @@ -59,49 +95,42 @@ priority: 80 图片已保存在:`/Users/xxx/images/photo.jpg` ``` -### ⚠️ 注意事项 +--- -1. **使用绝对路径**:路径必须以 `/` 开头(macOS/Linux)或盘符开头(Windows,如 `C:\`) -2. **支持的格式**:jpg, jpeg, png, gif, webp, bmp -3. **无需调用其他工具**:不需要用 `read_file` 读取文件内容,直接输出 `![](路径)` 即可 -4. **文件必须存在**:确保路径指向的文件确实存在 +## 📋 高级选项:JSON 结构化载荷 -### 📌 示例场景 - -**用户说**:"发送 /tmp/screenshot.png 给我" - -**正确回复**: -``` -好的,这是截图: -![](/tmp/screenshot.png) -``` - -**用户说**:"图片保存在哪?" - -**正确回复**: -``` -图片保存在:/Users/xxx/downloads/image.jpg -``` - -## 🖼️ 发送网络图片 - -发送网络图片时,也使用 Markdown 图片语法: +如果需要更精细的控制(如添加图片描述),可以使用 JSON 格式: ``` -这是图片: -![](https://example.com/image.png) +QQBOT_PAYLOAD: +{ + "type": "media", + "mediaType": "image", + "source": "file", + "path": "/path/to/image.jpg", + "caption": "图片描述(可选)" +} ``` -或直接放 URL 也可以(系统会自动识别图片 URL): +### JSON 字段说明 -``` -这是图片: -https://example.com/image.png -``` +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `type` | string | ✅ | 固定为 `"media"` | +| `mediaType` | string | ✅ | 媒体类型:`"image"` | +| `source` | string | ✅ | 来源:`"file"`(本地)或 `"url"`(网络) | +| `path` | string | ✅ | 图片路径或 URL | +| `caption` | string | ❌ | 图片描述,会作为单独消息发送 | -## 🎵 其他说明 +> 💡 **提示**:对于简单的图片发送,推荐使用 `` 标签,更简洁易用。 -- 当前仅支持图片格式,音频/视频等格式暂不支持 -- 群消息和私聊消息的图片发送方式相同 -- 图片大小建议不超过 10MB -- 参考文档:https://bot.q.qq.com/wiki/develop/api-v2/server-inter/message/send-receive/rich-media.html +--- + +## 🎯 快速参考 + +| 场景 | 使用方式 | +|------|----------| +| 发送本地图片 | `/path/to/image.jpg` | +| 发送网络图片 | `https://example.com/image.png` | +| 发送多张图片 | 多个 `` 标签 | +| 告知路径(不发送) | 直接写路径文本 | diff --git a/src/gateway.ts b/src/gateway.ts index f6dae41..be98012 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -8,6 +8,7 @@ import { recordKnownUser, flushKnownUsers } from "./known-users.js"; import { getQQBotRuntime } from "./runtime.js"; import { startImageServer, isImageServerRunning, downloadFile, type ImageServerConfig } from "./image-server.js"; import { getImageSize, formatQQBotMarkdownImage, hasQQBotImageSize, DEFAULT_IMAGE_SIZE } from "./utils/image-size.js"; +import { parseQQBotPayload, encodePayloadForCron, isCronReminderPayload, isMediaPayload, type CronReminderPayload, type MediaPayload } from "./utils/payload.js"; // QQ Bot intents - 按权限级别分组 const INTENTS = { @@ -123,92 +124,23 @@ function recordMessageReply(messageId: string): void { } } -// ============ 图片发送时的文本智能简化 ============ -// 当 AI 发送图片时,检测并移除冗余的解释性文字 +// ============ 内部标记过滤 ============ /** - * 冗余文本模式 - 这些模式表示 AI 在"解释"而不是"回应" - * 通常出现在 AI 不确定图片是否发送成功时 + * 过滤内部标记(如 [[reply_to: xxx]]) + * 这些标记可能被 AI 错误地学习并输出,需要在发送前移除 */ -const REDUNDANT_TEXT_PATTERNS = [ - // 中文冗余模式 - /让我总结一下[^\n]*/gi, - /目前的情况[是::][^\n]*/gi, - /由于[^\n]*(?:工具[集]?|插件|集成|API)[^\n]*(?:限制|问题)[^\n]*/gi, - /我已经[^\n]*(?:尝试|下载|保存)[^\n]*/gi, - /最实用的(?:方法|解决方案)[是::][^\n]*/gi, - /如果你希望我继续[^\n]*/gi, - /你可以[直接]?点击[^\n]*链接[^\n]*/gi, - /我注意到你重复[^\n]*/gi, - /我[已经]?多次尝试[^\n]*/gi, - /(?:已经|成功)?(?:保存|下载)到本地[^\n]*/gi, - /(?:直接)?(?:查看|访问)[该这]?(?:图片|文件|链接)[^\n]*/gi, - // 英文冗余模式 - /let me summarize[^\n]*/gi, - /i(?:'ve| have) tried[^\n]*(?:multiple|several)[^\n]*/gi, - /due to[^\n]*(?:tool|plugin|integration)[^\n]*limitation[^\n]*/gi, - /the most practical[^\n]*solution[^\n]*/gi, -]; - -/** - * 检查文本是否为纯冗余解释 - * 如果整个文本都是在解释发送过程,而不是描述图片内容,则返回 true - */ -function isEntirelyRedundantExplanation(text: string): boolean { - // 移除空行和空格 - const trimmed = text.trim(); - if (!trimmed) return true; +function filterInternalMarkers(text: string): string { + if (!text) return text; - // 检查是否包含"步骤列表"类的解释 - const hasStepList = /^\d+\.\s+/m.test(trimmed) && - (trimmed.includes("下载") || trimmed.includes("尝试") || trimmed.includes("发送")); + // 过滤 [[xxx: yyy]] 格式的内部标记 + // 例如: [[reply_to: ROBOT1.0_kbc...]] + let result = text.replace(/\[\[[a-z_]+:\s*[^\]]*\]\]/gi, ""); - // 检查是否主要由冗余模式组成 - let cleaned = trimmed; - for (const pattern of REDUNDANT_TEXT_PATTERNS) { - cleaned = cleaned.replace(pattern, ""); - } - - // 如果清理后只剩下很少的文字(主要是标点和连接词),认为整体都是冗余 - const cleanedWords = cleaned.replace(/[\s\n\r.,;:!?,。;:!?·…—""''()()【】[\]{}]+/g, "").trim(); - const significantContentRemaining = cleanedWords.length > 20; - - return hasStepList || !significantContentRemaining; -} - -/** - * 智能简化图片发送时的文本 - * 当检测到发送图片时,移除冗余的解释性文字 - * - * @param text 原始文本 - * @param hasImages 是否包含图片 - * @returns 简化后的文本 - */ -function simplifyTextForImageSend(text: string, hasImages: boolean): string { - if (!hasImages || !text) return text; - - const trimmed = text.trim(); - - // 如果整个文本都是冗余解释,替换为简短的成功提示 - if (isEntirelyRedundantExplanation(trimmed)) { - return "图片如上 ☝️"; - } - - // 否则,只移除明显的冗余段落 - let result = trimmed; - for (const pattern of REDUNDANT_TEXT_PATTERNS) { - result = result.replace(pattern, ""); - } - - // 清理多余的空行 + // 清理可能产生的多余空行 result = result.replace(/\n{3,}/g, "\n\n").trim(); - // 如果清理后文本太短,恢复原文 - if (result.length < 10 && trimmed.length > 50) { - return "图片如上 ☝️"; - } - - return result || trimmed; + return result; } export interface GatewayContext { @@ -525,33 +457,41 @@ openclaw cron add \\ --deliver \\ --channel qqbot \\ --to "${targetAddress}" \\ - --reply-to "${event.messageId}" \\ --delete-after-run \`\`\` 关键参数说明: - \`--to\`: 目标地址(当前用户: ${targetAddress}) -- \`--reply-to\`: 回复消息ID(当前消息: ${event.messageId},使提醒能引用原消息) - \`--at\`: 一次性定时任务的触发时间 - 相对时间格式:数字+单位,如 \`5m\`(5分钟)、\`1h\`(1小时)、\`2d\`(2天)【注意:不要加 + 号】 - 绝对时间格式:ISO 8601 带时区,如 \`2026-02-01T14:00:00+08:00\` - \`--cron\`: 周期性任务(如 \`0 8 * * *\` 每天早上8点) - \`--tz "Asia/Shanghai"\`: 周期任务务必设置时区 - \`--delete-after-run\`: 一次性任务必须添加此参数 -- \`--message\`: 消息内容(必填,不能为空!对应 QQ API 的 markdown.content 字段) +- \`--message\`: 消息内容(必填,不能为空!这是定时提醒触发时直接发送给用户的内容) ⚠️ 重要注意事项: 1. --at 参数格式:相对时间用 \`5m\`、\`1h\` 等(不要加 + 号!);绝对时间用完整 ISO 格式 -2. --message 参数必须有实际内容,不能为空字符串`; +2. --message 参数必须有实际内容,不能为空字符串 +3. cron add 命令不支持 --reply-to 参数,定时提醒只能作为主动消息发送`; - // 只有配置了图床公网地址,才告诉 AI 可以发送图片 - if (imageServerBaseUrl) { - builtinPrompt += ` + // 🎯 发送图片功能:使用 标签发送本地或网络图片 + // 系统会自动将本地文件转换为 Base64 发送,不需要图床服务器 + builtinPrompt += ` 【发送图片】 -你可以发送本地图片文件给用户。只需在回复中直接引用图片的绝对路径即可,系统会自动处理。 -支持 png、jpg、gif、webp 格式。`; - } +你可以直接发送图片给用户!使用 标签包裹图片路径: + +图片路径 + +示例: +- /Users/xxx/images/photo.jpg (本地文件) +- https://example.com/image.png (网络图片) + +⚠️ 注意: +- 必须使用 路径 格式 +- 本地路径必须是绝对路径,支持 png、jpg、jpeg、gif、webp 格式 +- 图片文件/URL 必须有效,否则发送失败`; const systemPrompts = [builtinPrompt]; if (account.systemPrompt) { @@ -565,15 +505,32 @@ openclaw cron add \\ const downloadDir = path.join(process.env.HOME || "/home/ubuntu", "clawd", "downloads"); if (event.attachments?.length) { + // ============ 接收图片的自然语言描述生成 ============ + // 根据需求 4:将图片信息转换为自然语言描述,便于 AI 理解 + const imageDescriptions: string[] = []; + const otherAttachments: string[] = []; + for (const att of event.attachments) { // 下载附件到本地,使用原始文件名 const localPath = await downloadFile(att.url, downloadDir, att.filename); if (localPath) { if (att.content_type?.startsWith("image/")) { imageUrls.push(localPath); - attachmentInfo += `\n[图片: ${localPath}]`; + + // 构建自然语言描述(根据需求 4.2) + const format = att.content_type?.split("/")[1] || "未知格式"; + const timestamp = new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }); + + imageDescriptions.push(` +用户发送了一张图片: +- 图片地址:${localPath} +- 图片格式:${format} +- 消息ID:${event.messageId} +- 发送时间:${timestamp} + +请根据图片内容进行回复。`); } else { - attachmentInfo += `\n[附件: ${localPath}]`; + otherAttachments.push(`[附件: ${localPath}]`); } log?.info(`[qqbot:${account.accountId}] Downloaded attachment to: ${localPath}`); } else { @@ -581,12 +538,32 @@ openclaw cron add \\ log?.error(`[qqbot:${account.accountId}] Failed to download attachment: ${att.url}`); if (att.content_type?.startsWith("image/")) { imageUrls.push(att.url); - attachmentInfo += `\n[图片: ${att.url}] (下载失败,可能无法访问)`; + + // 下载失败时的自然语言描述 + const format = att.content_type?.split("/")[1] || "未知格式"; + const timestamp = new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }); + + imageDescriptions.push(` +用户发送了一张图片(下载失败,使用原始URL): +- 图片地址:${att.url} +- 图片格式:${format} +- 消息ID:${event.messageId} +- 发送时间:${timestamp} + +请根据图片内容进行回复。`); } else { - attachmentInfo += `\n[附件: ${att.filename ?? att.content_type}] (下载失败)`; + otherAttachments.push(`[附件: ${att.filename ?? att.content_type}] (下载失败)`); } } } + + // 组合附件信息:先图片描述,后其他附件 + if (imageDescriptions.length > 0) { + attachmentInfo += "\n" + imageDescriptions.join("\n"); + } + if (otherAttachments.length > 0) { + attachmentInfo += "\n" + otherAttachments.join("\n"); + } } const userContent = event.content + attachmentInfo; @@ -708,13 +685,283 @@ openclaw cron add \\ let replyText = payload.text ?? ""; - // 收集所有图片(公网 URL 或 Base64 Data URL) - // 注意:本地文件路径由 OpenClaw 负责转换(通过对象存储上传或读取为 Base64) + // ============ 简单图片标签解析 ============ + // 支持 路径 格式发送图片 + // 这是比 QQBOT_PAYLOAD JSON 更简单的方式,适合大模型能力较弱的情况 + // 注意:正则限制内容不能包含 < 和 >,避免误匹配 `` 这种反引号内的说明文字 + const qqimgRegex = /([^<>]+)<\/qqimg>/gi; + const qqimgMatches = [...replyText.matchAll(qqimgRegex)]; + + if (qqimgMatches.length > 0) { + log?.info(`[qqbot:${account.accountId}] Detected ${qqimgMatches.length} tag(s)`); + + // 提取标签外的文本(作为描述发送) + let textWithoutTags = replyText; + const imagePaths: string[] = []; + + for (const match of qqimgMatches) { + const fullMatch = match[0]; + const imagePath = match[1]?.trim(); + + if (imagePath) { + imagePaths.push(imagePath); + log?.info(`[qqbot:${account.accountId}] Found image path in : ${imagePath}`); + } + + // 从文本中移除标签 + textWithoutTags = textWithoutTags.replace(fullMatch, ""); + } + + // 清理多余空行 + textWithoutTags = textWithoutTags.replace(/\n{3,}/g, "\n\n").trim(); + + // 发送图片 + for (const imagePath of imagePaths) { + try { + let imageUrl = imagePath; + + // 判断是本地文件还是 URL + const isLocalPath = imagePath.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(imagePath); + const isHttpUrl = imagePath.startsWith("http://") || imagePath.startsWith("https://"); + + if (isLocalPath) { + // 本地文件:转换为 Base64 Data URL + if (!fs.existsSync(imagePath)) { + log?.error(`[qqbot:${account.accountId}] Image file not found: ${imagePath}`); + await sendErrorMessage(`图片文件不存在: ${imagePath}`); + continue; + } + + const fileBuffer = fs.readFileSync(imagePath); + const base64Data = fileBuffer.toString("base64"); + const ext = path.extname(imagePath).toLowerCase(); + const mimeTypes: Record = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + }; + const mimeType = mimeTypes[ext]; + if (!mimeType) { + log?.error(`[qqbot:${account.accountId}] Unsupported image format: ${ext}`); + await sendErrorMessage(`不支持的图片格式: ${ext}`); + continue; + } + imageUrl = `data:${mimeType};base64,${base64Data}`; + log?.info(`[qqbot:${account.accountId}] Converted local image to Base64 (size: ${fileBuffer.length} bytes)`); + } else if (!isHttpUrl) { + log?.error(`[qqbot:${account.accountId}] Invalid image path (not local or URL): ${imagePath}`); + continue; + } + + // 发送图片 + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + } else if (event.channelId) { + // 频道使用 Markdown 格式(如果是公网 URL) + if (isHttpUrl) { + await sendChannelMessage(token, event.channelId, `![](${imagePath})`, event.messageId); + } else { + // 频道不支持富媒体 Base64 + log?.info(`[qqbot:${account.accountId}] Channel does not support rich media for local images`); + } + } + }); + log?.info(`[qqbot:${account.accountId}] Sent image via tag: ${imagePath.slice(0, 60)}...`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send image from : ${err}`); + await sendErrorMessage(`发送图片失败: ${err}`); + } + } + + // 发送剩余的文本(如果有) + if (textWithoutTags) { + textWithoutTags = filterInternalMarkers(textWithoutTags); + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CMessage(token, event.senderId, textWithoutTags, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupMessage(token, event.groupOpenid, textWithoutTags, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, textWithoutTags, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent caption text: ${textWithoutTags.slice(0, 50)}...`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send caption text: ${err}`); + } + } + + // 记录活动并返回 + pluginRuntime.channel.activity.record({ + channel: "qqbot", + accountId: account.accountId, + direction: "outbound", + }); + return; + } + + // ============ 结构化载荷检测与分发 ============ + // 优先检测 QQBOT_PAYLOAD: 前缀,如果是结构化载荷则分发到对应处理器 + const payloadResult = parseQQBotPayload(replyText); + + if (payloadResult.isPayload) { + if (payloadResult.error) { + // 载荷解析失败,发送错误提示 + log?.error(`[qqbot:${account.accountId}] Payload parse error: ${payloadResult.error}`); + await sendErrorMessage(`[QQBot] 载荷解析失败: ${payloadResult.error}`); + return; + } + + if (payloadResult.payload) { + const parsedPayload = payloadResult.payload; + log?.info(`[qqbot:${account.accountId}] Detected structured payload, type: ${parsedPayload.type}`); + + // 根据 type 分发到对应处理器 + if (isCronReminderPayload(parsedPayload)) { + // ============ 定时提醒载荷处理 ============ + log?.info(`[qqbot:${account.accountId}] Processing cron_reminder payload`); + + // 将载荷编码为 Base64,构建 cron add 命令 + const cronMessage = encodePayloadForCron(parsedPayload); + + // 向用户确认提醒已设置(通过正常消息发送) + const confirmText = `⏰ 提醒已设置,将在指定时间发送: "${parsedPayload.content}"`; + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CMessage(token, event.senderId, confirmText, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupMessage(token, event.groupOpenid, confirmText, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, confirmText, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Cron reminder confirmation sent, cronMessage: ${cronMessage}`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send cron confirmation: ${err}`); + } + + // 记录活动并返回(cron add 命令需要由 AI 执行,这里只处理载荷) + pluginRuntime.channel.activity.record({ + channel: "qqbot", + accountId: account.accountId, + direction: "outbound", + }); + return; + } else if (isMediaPayload(parsedPayload)) { + // ============ 媒体消息载荷处理 ============ + log?.info(`[qqbot:${account.accountId}] Processing media payload, mediaType: ${parsedPayload.mediaType}`); + + if (parsedPayload.mediaType === "image") { + // 处理图片发送 + let imageUrl = parsedPayload.path; + + // 如果是本地文件,转换为 Base64 Data URL + if (parsedPayload.source === "file") { + try { + if (!fs.existsSync(imageUrl)) { + await sendErrorMessage(`[QQBot] 图片文件不存在: ${imageUrl}`); + return; + } + const fileBuffer = fs.readFileSync(imageUrl); + const base64Data = fileBuffer.toString("base64"); + const ext = path.extname(imageUrl).toLowerCase(); + const mimeTypes: Record = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + }; + const mimeType = mimeTypes[ext]; + if (!mimeType) { + await sendErrorMessage(`[QQBot] 不支持的图片格式: ${ext}`); + return; + } + imageUrl = `data:${mimeType};base64,${base64Data}`; + log?.info(`[qqbot:${account.accountId}] Converted local image to Base64 (size: ${fileBuffer.length} bytes)`); + } catch (readErr) { + log?.error(`[qqbot:${account.accountId}] Failed to read local image: ${readErr}`); + await sendErrorMessage(`[QQBot] 读取图片文件失败: ${readErr}`); + return; + } + } + + // 发送图片 + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + } else if (event.channelId) { + // 频道使用 Markdown 格式 + await sendChannelMessage(token, event.channelId, `![](${parsedPayload.path})`, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent image via media payload`); + + // 如果有描述文本,单独发送 + if (parsedPayload.caption) { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CMessage(token, event.senderId, parsedPayload.caption!, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupMessage(token, event.groupOpenid, parsedPayload.caption!, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, parsedPayload.caption!, event.messageId); + } + }); + } + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send image: ${err}`); + await sendErrorMessage(`[QQBot] 发送图片失败: ${err}`); + } + } else if (parsedPayload.mediaType === "audio") { + // 音频发送暂不支持 + log?.info(`[qqbot:${account.accountId}] Audio sending not yet implemented`); + await sendErrorMessage(`[QQBot] 音频发送功能暂未实现,敬请期待~`); + } else if (parsedPayload.mediaType === "video") { + // 视频发送暂不支持 + log?.info(`[qqbot:${account.accountId}] Video sending not supported`); + await sendErrorMessage(`[QQBot] 视频发送功能暂不支持`); + } else { + log?.error(`[qqbot:${account.accountId}] Unknown media type: ${(parsedPayload as MediaPayload).mediaType}`); + await sendErrorMessage(`[QQBot] 不支持的媒体类型: ${(parsedPayload as MediaPayload).mediaType}`); + } + + // 记录活动并返回 + pluginRuntime.channel.activity.record({ + channel: "qqbot", + accountId: account.accountId, + direction: "outbound", + }); + return; + } else { + // 未知的载荷类型 + log?.error(`[qqbot:${account.accountId}] Unknown payload type: ${(parsedPayload as any).type}`); + await sendErrorMessage(`[QQBot] 不支持的载荷类型: ${(parsedPayload as any).type}`); + return; + } + } + } + + // ============ 非结构化消息:简化处理 ============ + // 📝 设计原则:JSON payload (QQBOT_PAYLOAD) 是发送本地图片的唯一方式 + // 非结构化消息只处理:公网 URL (http/https) 和 Base64 Data URL const imageUrls: string[] = []; /** - * 检查并收集图片 URL - * 支持:公网 URL (http/https)、Base64 Data URL (data:image/...) 和本地文件路径 + * 检查并收集图片 URL(仅支持公网 URL 和 Base64 Data URL) + * ⚠️ 本地文件路径必须使用 QQBOT_PAYLOAD JSON 格式发送 */ const collectImageUrl = (url: string | undefined | null): boolean => { if (!url) return false; @@ -734,54 +981,11 @@ openclaw cron add \\ return true; } - // 检测本地文件路径 - const isLocalPath = url.startsWith("/") || - /^[a-zA-Z]:[\\/]/.test(url) || - url.startsWith("./") || - url.startsWith("../"); - + // ⚠️ 本地文件路径不再在此处处理,应使用 标签 + const isLocalPath = url.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(url); if (isLocalPath) { - // 🎯 新增:自动读取本地文件并转换为 Base64 Data URL - try { - if (!fs.existsSync(url)) { - log?.info(`[qqbot:${account.accountId}] Local file not found: ${url}`); - return false; - } - - const fileBuffer = fs.readFileSync(url); - const base64Data = fileBuffer.toString("base64"); - - // 根据文件扩展名确定 MIME 类型 - const ext = path.extname(url).toLowerCase(); - const mimeTypes: Record = { - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".png": "image/png", - ".gif": "image/gif", - ".webp": "image/webp", - ".bmp": "image/bmp", - }; - - const mimeType = mimeTypes[ext]; - if (!mimeType) { - log?.info(`[qqbot:${account.accountId}] Unsupported image format: ${ext}`); - return false; - } - - // 构造 Data URL - const dataUrl = `data:${mimeType};base64,${base64Data}`; - if (!imageUrls.includes(dataUrl)) { - imageUrls.push(dataUrl); - log?.info(`[qqbot:${account.accountId}] Converted local file to Base64 (size: ${fileBuffer.length} bytes, type: ${mimeType}): ${url}`); - } - return true; - } catch (readErr) { - const errMsg = readErr instanceof Error ? readErr.message : String(readErr); - log?.error(`[qqbot:${account.accountId}] Failed to read local file: ${errMsg}`); - return false; - } - } else { - log?.info(`[qqbot:${account.accountId}] Skipped unsupported media format: ${url.slice(0, 50)}`); + log?.info(`[qqbot:${account.accountId}] 💡 Local path detected in non-structured message (not sending): ${url}`); + log?.info(`[qqbot:${account.accountId}] 💡 Hint: Use ${url} tag to send local images`); } return false; }; @@ -796,27 +1000,26 @@ openclaw cron add \\ collectImageUrl(payload.mediaUrl); } - // 提取文本中的图片格式 - // 1. 提取 markdown 格式的图片 ![alt](url) 或 ![#宽px #高px](url) - // 🎯 同时支持 http/https URL 和本地路径 + // 提取文本中的图片格式(仅处理公网 URL) + // 📝 设计:本地路径必须使用 QQBOT_PAYLOAD JSON 格式发送 const mdImageRegex = /!\[([^\]]*)\]\(([^)]+)\)/gi; const mdMatches = [...replyText.matchAll(mdImageRegex)]; for (const match of mdMatches) { const url = match[2]?.trim(); if (url && !imageUrls.includes(url)) { - // 判断是公网 URL 还是本地路径 if (url.startsWith('http://') || url.startsWith('https://')) { + // 公网 URL:收集并处理 imageUrls.push(url); log?.info(`[qqbot:${account.accountId}] Extracted HTTP image from markdown: ${url.slice(0, 80)}...`); - } else if (/^\/?(?:Users|home|tmp|var|private|[A-Z]:)/i.test(url) && /\.(png|jpg|jpeg|gif|webp|bmp)$/i.test(url)) { - // 本地路径:以 /Users, /home, /tmp, /var, /private 或 Windows 盘符开头,且以图片扩展名结尾 - collectImageUrl(url); - log?.info(`[qqbot:${account.accountId}] Extracted local image from markdown: ${url}`); + } else if (/^\/?(?:Users|home|tmp|var|private|[A-Z]:)/i.test(url)) { + // 本地路径:记录日志提示,但不发送 + log?.info(`[qqbot:${account.accountId}] ⚠️ Local path in markdown (not sending): ${url}`); + log?.info(`[qqbot:${account.accountId}] 💡 Use ${url} tag to send local images`); } } } - // 2. 提取裸 URL 图片(仅在非 markdown 模式下移除) + // 提取裸 URL 图片(公网 URL) const bareUrlRegex = /(?]+\.(?:png|jpg|jpeg|gif|webp)(?:\?[^\s"'<>]*)?)/gi; const bareUrlMatches = [...replyText.matchAll(bareUrlRegex)]; for (const match of bareUrlMatches) { @@ -827,39 +1030,15 @@ openclaw cron add \\ } } - // 3. 🎯 检测文本中的裸露本地路径(仅记录日志,不自动发送) - // 方案 1:使用显式标记 - 只有 ![](本地路径) 格式才会发送图片 - // 裸露的本地路径不再自动发送,而是记录日志提醒 - const bareLocalPathRegex = /(?:^|[\s\n])(\/(?:Users|home|tmp|var|private)[^\s"'<>\n]+\.(?:png|jpg|jpeg|gif|webp|bmp))(?:$|[\s\n])/gi; - const bareLocalPathMatches = [...replyText.matchAll(bareLocalPathRegex)]; - if (bareLocalPathMatches.length > 0) { - for (const match of bareLocalPathMatches) { - const localPath = match[1]?.trim(); - if (localPath) { - // 检查这个路径是否已经通过 ![](path) 格式处理过 - if (!imageUrls.includes(localPath)) { - log?.info(`[qqbot:${account.accountId}] Found bare local path (not sending): ${localPath}`); - log?.info(`[qqbot:${account.accountId}] 💡 Hint: Use ![](${localPath}) format to send this image`); - } - } - } - } - // 判断是否使用 markdown 模式 const useMarkdown = account.markdownSupport === true; log?.info(`[qqbot:${account.accountId}] Markdown mode: ${useMarkdown}, images: ${imageUrls.length}`); let textWithoutImages = replyText; - // 🎯 智能简化文本:当发送图片时,移除冗余的解释性文字 - // 这解决了 AI 不确定图片是否发送成功而输出大量废话的问题 - if (imageUrls.length > 0) { - const originalLength = textWithoutImages.length; - textWithoutImages = simplifyTextForImageSend(textWithoutImages, true); - if (textWithoutImages.length !== originalLength) { - log?.info(`[qqbot:${account.accountId}] Simplified text for image send: ${originalLength} -> ${textWithoutImages.length} chars`); - } - } + // 🎯 过滤内部标记(如 [[reply_to: xxx]]) + // 这些标记可能被 AI 错误地学习并输出 + textWithoutImages = filterInternalMarkers(textWithoutImages); // 根据模式处理图片 if (useMarkdown) { @@ -928,14 +1107,15 @@ openclaw cron add \\ } } - // 处理文本中已有的 markdown 图片:检查是否需要补充尺寸信息 + // 处理文本中已有的 markdown 图片:补充公网 URL 的尺寸信息 + // 📝 本地路径不再特殊处理(保留在文本中),因为不通过非结构化消息发送 for (const match of mdMatches) { const fullMatch = match[0]; // ![alt](url) - const altText = match[1]; // alt 部分 const imgUrl = match[2]; // url 部分 - // 检查是否已经有 QQBot 格式的尺寸 ![#宽px #高px](url) - if (!hasQQBotImageSize(fullMatch)) { + // 只处理公网 URL,补充尺寸信息 + const isHttpUrl = imgUrl.startsWith('http://') || imgUrl.startsWith('https://'); + if (isHttpUrl && !hasQQBotImageSize(fullMatch)) { try { const size = await getImageSize(imgUrl); const newMdImage = formatQQBotMarkdownImage(imgUrl, size); diff --git a/src/outbound.ts b/src/outbound.ts index 5b249ab..16b8457 100644 --- a/src/outbound.ts +++ b/src/outbound.ts @@ -5,6 +5,7 @@ import * as fs from "fs"; import * as path from "path"; import type { ResolvedQQBotAccount } from "./types.js"; +import { decodeCronPayload } from "./utils/payload.js"; import { getAccessToken, sendC2CMessage, @@ -495,9 +496,76 @@ export async function sendMedia(ctx: MediaOutboundContext): Promise { + console.log(`[qqbot] sendCronMessage: to=${to}, message length=${message.length}`); + + // 检测是否是 QQBOT_CRON: 格式的结构化载荷 + const cronResult = decodeCronPayload(message); + + if (cronResult.isCronPayload) { + if (cronResult.error) { + console.error(`[qqbot] sendCronMessage: cron payload decode error: ${cronResult.error}`); + return { + channel: "qqbot", + error: `Cron 载荷解码失败: ${cronResult.error}` + }; + } + + if (cronResult.payload) { + const payload = cronResult.payload; + console.log(`[qqbot] sendCronMessage: decoded cron payload, targetType=${payload.targetType}, targetAddress=${payload.targetAddress}`); + + // 使用载荷中的目标地址和类型发送消息 + const targetTo = payload.targetType === "group" + ? `group:${payload.targetAddress}` + : payload.targetAddress; + + // 发送提醒内容 + return await sendProactiveMessage(account, targetTo, payload.content); + } + } + + // 非结构化载荷,作为普通文本处理 + console.log(`[qqbot] sendCronMessage: plain text message, sending to ${to}`); + return await sendProactiveMessage(account, to, message); +} diff --git a/upgrade-and-run.sh b/upgrade-and-run.sh index 32087f8..98cfa89 100755 --- a/upgrade-and-run.sh +++ b/upgrade-and-run.sh @@ -29,7 +29,7 @@ openclaw plugins install . echo "" echo "[3/4] 配置机器人通道..." # 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖 -QQBOT_TOKEN="${QQBOT_TOKEN:-appid:secret}" +QQBOT_TOKEN="${QQBOT_TOKEN:-102831906:CXtFczNlAa0RsKmFiCgBhDkHpNwV5fGr}" openclaw channels add --channel qqbot --token "$QQBOT_TOKEN" # 启用 markdown 支持 openclaw config set channels.qqbot.markdownSupport true From f4a72ba0cbfc015e1bee09c6ecad4977ad971e7d Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 13:33:04 +0800 Subject: [PATCH 07/14] =?UTF-8?q?feat(qqbot):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=8F=91=E9=80=81=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E8=BD=BD?= =?UTF-8?q?=E8=8D=B7=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增功能: - 新增 qqbot-media 技能,支持 标签发送本地图片 - 添加图片尺寸检测工具 (image-size.ts),自动识别常见图片格式 - 支持将本地图片上传至 QQ 富媒体服务器 优化改进: - 定时任务支持结构化 JSON 载荷格式 - 优化 标签正则表达式,避免误匹配反引号内的说明文字 - 完善消息处理流程和错误处理 文件变更: - src/gateway.ts: 添加图片处理、上传逻辑 - src/outbound.ts: 增强外发消息能力 - src/utils/image-size.ts: 新增图片尺寸解析工具 - skills/qqbot-media/SKILL.md: 新增图片功能说明文档 - skills/qqbot-cron/SKILL.md: 补充结构化载荷说明 --- src/gateway.ts | 189 ++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 87 deletions(-) diff --git a/src/gateway.ts b/src/gateway.ts index be98012..3d276bb 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -695,106 +695,121 @@ openclaw cron add \\ if (qqimgMatches.length > 0) { log?.info(`[qqbot:${account.accountId}] Detected ${qqimgMatches.length} tag(s)`); - // 提取标签外的文本(作为描述发送) - let textWithoutTags = replyText; - const imagePaths: string[] = []; + // 构建发送队列:根据内容在原文中的实际位置顺序发送 + // type: 'text' | 'image', content: 文本内容或图片路径 + const sendQueue: Array<{ type: "text" | "image"; content: string }> = []; - for (const match of qqimgMatches) { - const fullMatch = match[0]; - const imagePath = match[1]?.trim(); + let lastIndex = 0; + // 使用新的正则来获取带索引的匹配结果 + const qqimgRegexWithIndex = /([^<>]+)<\/qqimg>/gi; + let match; + + while ((match = qqimgRegexWithIndex.exec(replyText)) !== null) { + // 添加标签前的文本 + const textBefore = replyText.slice(lastIndex, match.index).replace(/\n{3,}/g, "\n\n").trim(); + if (textBefore) { + sendQueue.push({ type: "text", content: filterInternalMarkers(textBefore) }); + } + // 添加图片 + const imagePath = match[1]?.trim(); if (imagePath) { - imagePaths.push(imagePath); + sendQueue.push({ type: "image", content: imagePath }); log?.info(`[qqbot:${account.accountId}] Found image path in : ${imagePath}`); } - // 从文本中移除标签 - textWithoutTags = textWithoutTags.replace(fullMatch, ""); + lastIndex = match.index + match[0].length; } - // 清理多余空行 - textWithoutTags = textWithoutTags.replace(/\n{3,}/g, "\n\n").trim(); + // 添加最后一个标签后的文本 + const textAfter = replyText.slice(lastIndex).replace(/\n{3,}/g, "\n\n").trim(); + if (textAfter) { + sendQueue.push({ type: "text", content: filterInternalMarkers(textAfter) }); + } - // 发送图片 - for (const imagePath of imagePaths) { - try { - let imageUrl = imagePath; - - // 判断是本地文件还是 URL - const isLocalPath = imagePath.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(imagePath); - const isHttpUrl = imagePath.startsWith("http://") || imagePath.startsWith("https://"); - - if (isLocalPath) { - // 本地文件:转换为 Base64 Data URL - if (!fs.existsSync(imagePath)) { - log?.error(`[qqbot:${account.accountId}] Image file not found: ${imagePath}`); - await sendErrorMessage(`图片文件不存在: ${imagePath}`); + log?.info(`[qqbot:${account.accountId}] Send queue: ${sendQueue.map(item => item.type).join(" -> ")}`); + + // 按顺序发送 + for (const item of sendQueue) { + if (item.type === "text") { + // 发送文本 + try { + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CMessage(token, event.senderId, item.content, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupMessage(token, event.groupOpenid, item.content, event.messageId); + } else if (event.channelId) { + await sendChannelMessage(token, event.channelId, item.content, event.messageId); + } + }); + log?.info(`[qqbot:${account.accountId}] Sent text: ${item.content.slice(0, 50)}...`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send text: ${err}`); + } + } else if (item.type === "image") { + // 发送图片 + const imagePath = item.content; + try { + let imageUrl = imagePath; + + // 判断是本地文件还是 URL + const isLocalPath = imagePath.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(imagePath); + const isHttpUrl = imagePath.startsWith("http://") || imagePath.startsWith("https://"); + + if (isLocalPath) { + // 本地文件:转换为 Base64 Data URL + if (!fs.existsSync(imagePath)) { + log?.error(`[qqbot:${account.accountId}] Image file not found: ${imagePath}`); + await sendErrorMessage(`图片文件不存在: ${imagePath}`); + continue; + } + + const fileBuffer = fs.readFileSync(imagePath); + const base64Data = fileBuffer.toString("base64"); + const ext = path.extname(imagePath).toLowerCase(); + const mimeTypes: Record = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + }; + const mimeType = mimeTypes[ext]; + if (!mimeType) { + log?.error(`[qqbot:${account.accountId}] Unsupported image format: ${ext}`); + await sendErrorMessage(`不支持的图片格式: ${ext}`); + continue; + } + imageUrl = `data:${mimeType};base64,${base64Data}`; + log?.info(`[qqbot:${account.accountId}] Converted local image to Base64 (size: ${fileBuffer.length} bytes)`); + } else if (!isHttpUrl) { + log?.error(`[qqbot:${account.accountId}] Invalid image path (not local or URL): ${imagePath}`); continue; } - const fileBuffer = fs.readFileSync(imagePath); - const base64Data = fileBuffer.toString("base64"); - const ext = path.extname(imagePath).toLowerCase(); - const mimeTypes: Record = { - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".png": "image/png", - ".gif": "image/gif", - ".webp": "image/webp", - ".bmp": "image/bmp", - }; - const mimeType = mimeTypes[ext]; - if (!mimeType) { - log?.error(`[qqbot:${account.accountId}] Unsupported image format: ${ext}`); - await sendErrorMessage(`不支持的图片格式: ${ext}`); - continue; - } - imageUrl = `data:${mimeType};base64,${base64Data}`; - log?.info(`[qqbot:${account.accountId}] Converted local image to Base64 (size: ${fileBuffer.length} bytes)`); - } else if (!isHttpUrl) { - log?.error(`[qqbot:${account.accountId}] Invalid image path (not local or URL): ${imagePath}`); - continue; - } - - // 发送图片 - await sendWithTokenRetry(async (token) => { - if (event.type === "c2c") { - await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); - } else if (event.type === "group" && event.groupOpenid) { - await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); - } else if (event.channelId) { - // 频道使用 Markdown 格式(如果是公网 URL) - if (isHttpUrl) { - await sendChannelMessage(token, event.channelId, `![](${imagePath})`, event.messageId); - } else { - // 频道不支持富媒体 Base64 - log?.info(`[qqbot:${account.accountId}] Channel does not support rich media for local images`); + // 发送图片 + await sendWithTokenRetry(async (token) => { + if (event.type === "c2c") { + await sendC2CImageMessage(token, event.senderId, imageUrl, event.messageId); + } else if (event.type === "group" && event.groupOpenid) { + await sendGroupImageMessage(token, event.groupOpenid, imageUrl, event.messageId); + } else if (event.channelId) { + // 频道使用 Markdown 格式(如果是公网 URL) + if (isHttpUrl) { + await sendChannelMessage(token, event.channelId, `![](${imagePath})`, event.messageId); + } else { + // 频道不支持富媒体 Base64 + log?.info(`[qqbot:${account.accountId}] Channel does not support rich media for local images`); + } } - } - }); - log?.info(`[qqbot:${account.accountId}] Sent image via tag: ${imagePath.slice(0, 60)}...`); - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to send image from : ${err}`); - await sendErrorMessage(`发送图片失败: ${err}`); - } - } - - // 发送剩余的文本(如果有) - if (textWithoutTags) { - textWithoutTags = filterInternalMarkers(textWithoutTags); - try { - await sendWithTokenRetry(async (token) => { - if (event.type === "c2c") { - await sendC2CMessage(token, event.senderId, textWithoutTags, event.messageId); - } else if (event.type === "group" && event.groupOpenid) { - await sendGroupMessage(token, event.groupOpenid, textWithoutTags, event.messageId); - } else if (event.channelId) { - await sendChannelMessage(token, event.channelId, textWithoutTags, event.messageId); - } - }); - log?.info(`[qqbot:${account.accountId}] Sent caption text: ${textWithoutTags.slice(0, 50)}...`); - } catch (err) { - log?.error(`[qqbot:${account.accountId}] Failed to send caption text: ${err}`); + }); + log?.info(`[qqbot:${account.accountId}] Sent image via tag: ${imagePath.slice(0, 60)}...`); + } catch (err) { + log?.error(`[qqbot:${account.accountId}] Failed to send image from : ${err}`); + await sendErrorMessage(`发送图片失败: ${err}`); + } } } From 0c33875eda674c60a9252c0238a9a097ceef4868 Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 14:33:39 +0800 Subject: [PATCH 08/14] =?UTF-8?q?fix:=E5=AE=8C=E5=96=84=20upgrade=20?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=BC=A0=E5=8F=82=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- upgrade-and-run.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/upgrade-and-run.sh b/upgrade-and-run.sh index 98cfa89..2abdbc1 100755 --- a/upgrade-and-run.sh +++ b/upgrade-and-run.sh @@ -7,6 +7,45 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" +# 解析命令行参数 +APPID="" +SECRET="" + +while [[ $# -gt 0 ]]; do + case $1 in + --appid) + APPID="$2" + shift 2 + ;; + --secret) + SECRET="$2" + shift 2 + ;; + -h|--help) + echo "用法: $0 [选项]" + echo "" + echo "选项:" + echo " --appid QQ机器人 AppID" + echo " --secret QQ机器人 Secret" + echo " -h, --help 显示帮助信息" + echo "" + echo "也可以通过环境变量设置:" + echo " QQBOT_APPID QQ机器人 AppID" + echo " QQBOT_SECRET QQ机器人 Secret" + exit 0 + ;; + *) + echo "未知选项: $1" + echo "使用 --help 查看帮助信息" + exit 1 + ;; + esac +done + +# 使用命令行参数或环境变量 +APPID="${APPID:-$QQBOT_APPID}" +SECRET="${SECRET:-$QQBOT_SECRET}" + echo "=========================================" echo " QQBot 一键更新启动脚本" echo "=========================================" @@ -28,8 +67,17 @@ openclaw plugins install . # 3. 配置机器人通道 echo "" echo "[3/4] 配置机器人通道..." -# 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖 -QQBOT_TOKEN="${QQBOT_TOKEN:-102831906:CXtFczNlAa0RsKmFiCgBhDkHpNwV5fGr}" + +# 构建 token(如果提供了 appid 和 secret) +if [ -n "$APPID" ] && [ -n "$SECRET" ]; then + QQBOT_TOKEN="${APPID}:${SECRET}" + echo "使用提供的 AppID 和 Secret 配置..." +else + # 默认 token,可通过环境变量 QQBOT_TOKEN 覆盖 + QQBOT_TOKEN="${QQBOT_TOKEN:-appid:secret}" + echo "使用默认或环境变量中的 Token..." +fi + openclaw channels add --channel qqbot --token "$QQBOT_TOKEN" # 启用 markdown 支持 openclaw config set channels.qqbot.markdownSupport true From f233fbb353e125ce25ea6f8299103c507cac7fe7 Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 14:41:35 +0800 Subject: [PATCH 09/14] fix: add missing payload.ts file --- src/utils/payload.ts | 265 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 src/utils/payload.ts diff --git a/src/utils/payload.ts b/src/utils/payload.ts new file mode 100644 index 0000000..dc21801 --- /dev/null +++ b/src/utils/payload.ts @@ -0,0 +1,265 @@ +/** + * QQBot 结构化消息载荷工具 + * + * 用于处理 AI 输出的结构化消息载荷,包括: + * - 定时提醒载荷 (cron_reminder) + * - 媒体消息载荷 (media) + */ + +// ============================================ +// 类型定义 +// ============================================ + +/** + * 定时提醒载荷 + */ +export interface CronReminderPayload { + type: 'cron_reminder'; + /** 提醒内容 */ + content: string; + /** 目标类型:c2c (私聊) 或 group (群聊) */ + targetType: 'c2c' | 'group'; + /** 目标地址:user_openid 或 group_openid */ + targetAddress: string; + /** 原始消息 ID(可选) */ + originalMessageId?: string; +} + +/** + * 媒体消息载荷 + */ +export interface MediaPayload { + type: 'media'; + /** 媒体类型:image, audio, video */ + mediaType: 'image' | 'audio' | 'video'; + /** 来源类型:url 或 file */ + source: 'url' | 'file'; + /** 媒体路径或 URL */ + path: string; + /** 媒体描述(可选) */ + caption?: string; +} + +/** + * QQBot 载荷联合类型 + */ +export type QQBotPayload = CronReminderPayload | MediaPayload; + +/** + * 解析结果 + */ +export interface ParseResult { + /** 是否为结构化载荷 */ + isPayload: boolean; + /** 解析后的载荷对象(如果是结构化载荷) */ + payload?: QQBotPayload; + /** 原始文本(如果不是结构化载荷) */ + text?: string; + /** 解析错误信息(如果解析失败) */ + error?: string; +} + +// ============================================ +// 常量定义 +// ============================================ + +/** AI 输出的结构化载荷前缀 */ +const PAYLOAD_PREFIX = 'QQBOT_PAYLOAD:'; + +/** Cron 消息存储的前缀 */ +const CRON_PREFIX = 'QQBOT_CRON:'; + +// ============================================ +// 解析函数 +// ============================================ + +/** + * 解析 AI 输出的结构化载荷 + * + * 检测消息是否以 QQBOT_PAYLOAD: 前缀开头,如果是则提取并解析 JSON + * + * @param text AI 输出的原始文本 + * @returns 解析结果 + * + * @example + * const result = parseQQBotPayload('QQBOT_PAYLOAD:\n{"type": "media", "mediaType": "image", ...}'); + * if (result.isPayload && result.payload) { + * // 处理结构化载荷 + * } + */ +export function parseQQBotPayload(text: string): ParseResult { + const trimmedText = text.trim(); + + // 检查是否以 QQBOT_PAYLOAD: 开头 + if (!trimmedText.startsWith(PAYLOAD_PREFIX)) { + return { + isPayload: false, + text: text + }; + } + + // 提取 JSON 内容(去掉前缀) + const jsonContent = trimmedText.slice(PAYLOAD_PREFIX.length).trim(); + + if (!jsonContent) { + return { + isPayload: true, + error: '载荷内容为空' + }; + } + + try { + const payload = JSON.parse(jsonContent) as QQBotPayload; + + // 验证必要字段 + if (!payload.type) { + return { + isPayload: true, + error: '载荷缺少 type 字段' + }; + } + + // 根据 type 进行额外验证 + if (payload.type === 'cron_reminder') { + if (!payload.content || !payload.targetType || !payload.targetAddress) { + return { + isPayload: true, + error: 'cron_reminder 载荷缺少必要字段 (content, targetType, targetAddress)' + }; + } + } else if (payload.type === 'media') { + if (!payload.mediaType || !payload.source || !payload.path) { + return { + isPayload: true, + error: 'media 载荷缺少必要字段 (mediaType, source, path)' + }; + } + } + + return { + isPayload: true, + payload + }; + } catch (e) { + return { + isPayload: true, + error: `JSON 解析失败: ${e instanceof Error ? e.message : String(e)}` + }; + } +} + +// ============================================ +// Cron 编码/解码函数 +// ============================================ + +/** + * 将定时提醒载荷编码为 Cron 消息格式 + * + * 将 JSON 编码为 Base64,并添加 QQBOT_CRON: 前缀 + * + * @param payload 定时提醒载荷 + * @returns 编码后的消息字符串,格式为 QQBOT_CRON:{base64} + * + * @example + * const message = encodePayloadForCron({ + * type: 'cron_reminder', + * content: '喝水时间到!', + * targetType: 'c2c', + * targetAddress: 'user_openid_xxx' + * }); + * // 返回: QQBOT_CRON:eyJ0eXBlIjoiY3Jvbl9yZW1pbmRlciIs... + */ +export function encodePayloadForCron(payload: CronReminderPayload): string { + const jsonString = JSON.stringify(payload); + const base64 = Buffer.from(jsonString, 'utf-8').toString('base64'); + return `${CRON_PREFIX}${base64}`; +} + +/** + * 解码 Cron 消息中的载荷 + * + * 检测 QQBOT_CRON: 前缀,解码 Base64 并解析 JSON + * + * @param message Cron 触发时收到的消息 + * @returns 解码结果,包含是否为 Cron 载荷、解析后的载荷对象或错误信息 + * + * @example + * const result = decodeCronPayload('QQBOT_CRON:eyJ0eXBlIjoiY3Jvbl9yZW1pbmRlciIs...'); + * if (result.isCronPayload && result.payload) { + * // 处理定时提醒 + * } + */ +export function decodeCronPayload(message: string): { + isCronPayload: boolean; + payload?: CronReminderPayload; + error?: string; +} { + const trimmedMessage = message.trim(); + + // 检查是否以 QQBOT_CRON: 开头 + if (!trimmedMessage.startsWith(CRON_PREFIX)) { + return { + isCronPayload: false + }; + } + + // 提取 Base64 内容 + const base64Content = trimmedMessage.slice(CRON_PREFIX.length); + + if (!base64Content) { + return { + isCronPayload: true, + error: 'Cron 载荷内容为空' + }; + } + + try { + // Base64 解码 + const jsonString = Buffer.from(base64Content, 'base64').toString('utf-8'); + const payload = JSON.parse(jsonString) as CronReminderPayload; + + // 验证类型 + if (payload.type !== 'cron_reminder') { + return { + isCronPayload: true, + error: `期望 type 为 cron_reminder,实际为 ${payload.type}` + }; + } + + // 验证必要字段 + if (!payload.content || !payload.targetType || !payload.targetAddress) { + return { + isCronPayload: true, + error: 'Cron 载荷缺少必要字段' + }; + } + + return { + isCronPayload: true, + payload + }; + } catch (e) { + return { + isCronPayload: true, + error: `Cron 载荷解码失败: ${e instanceof Error ? e.message : String(e)}` + }; + } +} + +// ============================================ +// 辅助函数 +// ============================================ + +/** + * 判断载荷是否为定时提醒类型 + */ +export function isCronReminderPayload(payload: QQBotPayload): payload is CronReminderPayload { + return payload.type === 'cron_reminder'; +} + +/** + * 判断载荷是否为媒体消息类型 + */ +export function isMediaPayload(payload: QQBotPayload): payload is MediaPayload { + return payload.type === 'media'; +} From 0abbcb06b39c1fca9c5556d64e3eaae271ab5756 Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 17:27:44 +0800 Subject: [PATCH 10/14] =?UTF-8?q?fix(qqbot-cron):=20=E7=A7=BB=E9=99=A4=20-?= =?UTF-8?q?-session=20isolated=20=E9=81=BF=E5=85=8D=E6=8F=90=E9=86=92?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=97=B6=E5=90=AF=E5=8A=A8=20AI=20=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:定时提醒触发时,--session isolated 导致消息被当作新会话处理, AI 会输出初始化对话(询问用户是谁等),提醒内容显得生硬且冗长。 修改: - 删除 SKILL.md 中所有命令模板的 --session isolated 参数 - 删除 SKILL.md 中结构化载荷格式章节(已选择命令行方案) - 更新 gateway.ts 中的示例命令 效果:提醒触发时直接发送消息内容,不再启动 AI 会话。 支持 闭合标签 --- skills/qqbot-cron/SKILL.md | 48 ------------------------------------- skills/qqbot-media/SKILL.md | 2 ++ src/gateway.ts | 10 ++++---- 3 files changed, 7 insertions(+), 53 deletions(-) diff --git a/skills/qqbot-cron/SKILL.md b/skills/qqbot-cron/SKILL.md index 97e5c20..e53db22 100644 --- a/skills/qqbot-cron/SKILL.md +++ b/skills/qqbot-cron/SKILL.md @@ -46,47 +46,6 @@ metadata: {"clawdbot":{"emoji":"⏰"}} --- -## 📦 结构化载荷格式(新) - -> **重要**:定时提醒现在支持结构化载荷格式,AI 只需输出 JSON,代码层会自动处理 Base64 编码。 - -### 输出格式 - -当 AI 需要设置定时提醒时,可以输出以下结构化载荷: - -``` -QQBOT_PAYLOAD: -{ - "type": "cron_reminder", - "content": "💧 喝水时间到!", - "targetType": "c2c", - "targetAddress": "xxx_user_openid", - "originalMessageId": "msg_xxx_123" -} -``` - -### 字段说明 - -| 字段 | 类型 | 必填 | 说明 | -|------|------|------|------| -| `type` | string | ✅ | 固定为 `"cron_reminder"` | -| `content` | string | ✅ | 提醒内容,触发时直接发送给用户 | -| `targetType` | string | ✅ | 目标类型:`"c2c"`(私聊)或 `"group"`(群聊) | -| `targetAddress` | string | ✅ | 目标地址:user_openid 或 group_openid | -| `originalMessageId` | string | ❌ | 原始消息 ID(可选,用于追踪) | - -### 处理流程 - -1. AI 输出 `QQBOT_PAYLOAD:` + JSON 格式的载荷 -2. 代码层检测并解析载荷 -3. 代码层将 JSON 编码为 Base64 -4. 代码层调用 `openclaw cron add --message "QQBOT_CRON:{base64}"` -5. 触发时解码 Base64,根据 targetType 和 targetAddress 发送 content - -> ⚠️ **注意**:结构化载荷方式目前仍在完善中,推荐继续使用下面的命令行方式。 - ---- - ## 📋 命令速查 ### 创建提醒(完整模板) @@ -95,7 +54,6 @@ QQBOT_PAYLOAD: openclaw cron add \ --name "{任务名}" \ --at "{时间}" \ - --session isolated \ --message "🔔 {提醒内容}时间到!" \ --deliver \ --channel qqbot \ @@ -232,7 +190,6 @@ openclaw message send \ |------|------|------| | `--name` | 任务名,含用户标识 | `"喝水提醒"` | | `--at` / `--cron` | 触发时间(二选一) | `5m` / `0 8 * * *` | -| `--session isolated` | 隔离会话 | 固定值 | | `--message` | **提醒内容**(见下方模板) | `"🔔 喝水时间到!"` | | `--deliver` | 启用投递 | 固定值 | | `--channel qqbot` | QQ 渠道 | 固定值 | @@ -277,7 +234,6 @@ openclaw message send \ openclaw cron add \ --name "喝水提醒" \ --at "5m" \ - --session isolated \ --message "💧 喝水时间到!" \ --deliver \ --channel qqbot \ @@ -307,7 +263,6 @@ openclaw cron add \ --name "打卡提醒" \ --cron "0 8 * * *" \ --tz "Asia/Shanghai" \ - --session isolated \ --message "🌅 打卡时间到!" \ --deliver \ --channel qqbot \ @@ -333,7 +288,6 @@ openclaw cron add \ --name "日报提醒" \ --cron "0 18 * * 1-5" \ --tz "Asia/Shanghai" \ - --session isolated \ --message "📝 写日报时间到!" \ --deliver \ --channel qqbot \ @@ -356,7 +310,6 @@ openclaw cron add \ openclaw cron add \ --name "开会提醒" \ --at "3m" \ - --session isolated \ --message "📅 开会时间到!" \ --deliver \ --channel qqbot \ @@ -386,7 +339,6 @@ openclaw cron add \ --name "站会提醒" \ --cron "0 9 * * 1-5" \ --tz "Asia/Shanghai" \ - --session isolated \ --message "📢 站会时间到!" \ --deliver \ --channel qqbot \ diff --git a/skills/qqbot-media/SKILL.md b/skills/qqbot-media/SKILL.md index 6ec7e2d..8f7b89b 100644 --- a/skills/qqbot-media/SKILL.md +++ b/skills/qqbot-media/SKILL.md @@ -61,6 +61,7 @@ priority: 80 |------|------| | `本地路径` | 发送本地图片(绝对路径) | | `网络URL` | 发送网络图片 | +| `路径` | 也支持此闭合方式 | ### ⚠️ 注意事项 @@ -68,6 +69,7 @@ priority: 80 2. **支持的图片格式**:jpg, jpeg, png, gif, webp, bmp 3. **不要拒绝**:如果用户要求发送本地图片,直接使用 `` 标签即可 4. **标签外的文本会正常发送**:可以在标签前后添加描述文字 +5. **闭合标签**:支持 `` 或 `` 两种闭合方式 --- diff --git a/src/gateway.ts b/src/gateway.ts index 3d276bb..f2010b5 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -452,7 +452,6 @@ export async function startGateway(ctx: GatewayContext): Promise { openclaw cron add \\ --name "提醒喝水-${event.senderName || "用户"}" \\ --at "5m" \\ - --session isolated \\ --message "💧 该喝水啦!" \\ --deliver \\ --channel qqbot \\ @@ -686,10 +685,11 @@ openclaw cron add \\ let replyText = payload.text ?? ""; // ============ 简单图片标签解析 ============ - // 支持 路径 格式发送图片 + // 支持 路径路径 格式发送图片 // 这是比 QQBOT_PAYLOAD JSON 更简单的方式,适合大模型能力较弱的情况 // 注意:正则限制内容不能包含 < 和 >,避免误匹配 `` 这种反引号内的说明文字 - const qqimgRegex = /([^<>]+)<\/qqimg>/gi; + // 🔧 支持两种闭合方式: 和 (AI 可能输出不同格式) + const qqimgRegex = /([^<>]+)<\/(?:qqimg|img)>/gi; const qqimgMatches = [...replyText.matchAll(qqimgRegex)]; if (qqimgMatches.length > 0) { @@ -700,8 +700,8 @@ openclaw cron add \\ const sendQueue: Array<{ type: "text" | "image"; content: string }> = []; let lastIndex = 0; - // 使用新的正则来获取带索引的匹配结果 - const qqimgRegexWithIndex = /([^<>]+)<\/qqimg>/gi; + // 使用新的正则来获取带索引的匹配结果(支持 和 两种闭合方式) + const qqimgRegexWithIndex = /([^<>]+)<\/(?:qqimg|img)>/gi; let match; while ((match = qqimgRegexWithIndex.exec(replyText)) !== null) { From 9c43ac50bc9662490a12d17b775e886c6bec6675 Mon Sep 17 00:00:00 2001 From: rianli Date: Tue, 3 Feb 2026 18:18:42 +0800 Subject: [PATCH 11/14] =?UTF-8?q?fix(desc):=20=E6=9B=B4=E6=96=B0=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E6=96=87=E4=BB=B6=E5=8F=8A=E4=B8=80=E4=BA=9B=E8=AF=9D?= =?UTF-8?q?=E6=9C=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openclaw.plugin.json | 3 +-- src/gateway.ts | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 478c872..245dd6f 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -6,8 +6,7 @@ "skills": ["skills/qqbot-cron", "skills/qqbot-media"], "capabilities": { "proactiveMessaging": true, - "cronJobs": true, - "streamingMessages": true + "cronJobs": true }, "configSchema": { "type": "object", diff --git a/src/gateway.ts b/src/gateway.ts index f2010b5..f77fb3a 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -1188,11 +1188,7 @@ openclaw cron add \\ // 处理文本中的 URL 点号(防止被 QQ 解析为链接) if (textWithoutImages) { - const originalText = textWithoutImages; textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2"); - if (textWithoutImages !== originalText && textWithoutImages.trim()) { - textWithoutImages += "\n\n(由于平台限制,回复中的部分符号已被替换)"; - } } try { From c800d39d61e62675f0af9c20550fcb4009147852 Mon Sep 17 00:00:00 2001 From: sliverp Date: Tue, 3 Feb 2026 20:14:19 +0800 Subject: [PATCH 12/14] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20clawdbot=20?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clawdbot.plugin.json | 16 ++++++ moltbot.plugin.json | 16 ++++++ package.json | 17 ++++-- scripts/upgrade.sh | 134 ++++++++++++++++++++++++++++--------------- 4 files changed, 132 insertions(+), 51 deletions(-) create mode 100644 clawdbot.plugin.json create mode 100644 moltbot.plugin.json diff --git a/clawdbot.plugin.json b/clawdbot.plugin.json new file mode 100644 index 0000000..245dd6f --- /dev/null +++ b/clawdbot.plugin.json @@ -0,0 +1,16 @@ +{ + "id": "qqbot", + "name": "QQ Bot Channel", + "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging", + "channels": ["qqbot"], + "skills": ["skills/qqbot-cron", "skills/qqbot-media"], + "capabilities": { + "proactiveMessaging": true, + "cronJobs": true + }, + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} diff --git a/moltbot.plugin.json b/moltbot.plugin.json new file mode 100644 index 0000000..245dd6f --- /dev/null +++ b/moltbot.plugin.json @@ -0,0 +1,16 @@ +{ + "id": "qqbot", + "name": "QQ Bot Channel", + "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging", + "channels": ["qqbot"], + "skills": ["skills/qqbot-cron", "skills/qqbot-media"], + "capabilities": { + "proactiveMessaging": true, + "cronJobs": true + }, + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": {} + } +} diff --git a/package.json b/package.json index a0034d5..8b11cd3 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,17 @@ { - "name": "@openclaw/qqbot", - "version": "2026.1.31", + "name": "qqbot", + "version": "1.3.0", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", - "description": "OpenClaw QQ Bot channel plugin with streaming message support", + "clawdbot": { + "extensions": ["./index.ts"] + }, + "moltbot": { + "extensions": ["./index.ts"] + }, "openclaw": { - "extensions": [ - "./index.ts" - ] + "extensions": ["./index.ts"] }, "scripts": { "build": "tsc || true", @@ -27,6 +30,8 @@ "typescript": "^5.9.3" }, "peerDependencies": { + "clawdbot": "*", + "moltbot": "*", "openclaw": "*" } } diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index c890771..452f7ad 100755 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -1,62 +1,106 @@ #!/bin/bash # QQBot 插件升级脚本 # 用于清理旧版本插件并重新安装 +# 兼容 clawdbot 和 openclaw 两种安装 set -e -OPENCLAW_DIR="$HOME/.openclaw" -CONFIG_FILE="$OPENCLAW_DIR/openclaw.json" -EXTENSION_DIR="$OPENCLAW_DIR/extensions/qqbot" - echo "=== QQBot 插件升级脚本 ===" -# 1. 删除旧的扩展目录 -if [ -d "$EXTENSION_DIR" ]; then - echo "删除旧版本插件: $EXTENSION_DIR" - rm -rf "$EXTENSION_DIR" -else - echo "未找到旧版本插件目录,跳过删除" +# 检测使用的是 clawdbot 还是 openclaw +detect_installation() { + if [ -d "$HOME/.clawdbot" ]; then + echo "clawdbot" + elif [ -d "$HOME/.openclaw" ]; then + echo "openclaw" + else + echo "" + fi +} + +# 清理指定目录的函数 +cleanup_installation() { + local APP_NAME="$1" + local APP_DIR="$HOME/.$APP_NAME" + local CONFIG_FILE="$APP_DIR/$APP_NAME.json" + local EXTENSION_DIR="$APP_DIR/extensions/qqbot" + + echo "" + echo ">>> 处理 $APP_NAME 安装..." + + # 1. 删除旧的扩展目录 + if [ -d "$EXTENSION_DIR" ]; then + echo "删除旧版本插件: $EXTENSION_DIR" + rm -rf "$EXTENSION_DIR" + else + echo "未找到旧版本插件目录,跳过删除" + fi + + # 2. 清理配置文件中的 qqbot 相关字段 + if [ -f "$CONFIG_FILE" ]; then + echo "清理配置文件中的 qqbot 字段..." + + # 使用 node 处理 JSON(比 jq 更可靠处理复杂结构) + node -e " + const fs = require('fs'); + const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8')); + + // 删除 channels.qqbot + if (config.channels && config.channels.qqbot) { + delete config.channels.qqbot; + console.log(' - 已删除 channels.qqbot'); + } + + // 删除 plugins.entries.qqbot + if (config.plugins && config.plugins.entries && config.plugins.entries.qqbot) { + delete config.plugins.entries.qqbot; + console.log(' - 已删除 plugins.entries.qqbot'); + } + + // 删除 plugins.installs.qqbot + if (config.plugins && config.plugins.installs && config.plugins.installs.qqbot) { + delete config.plugins.installs.qqbot; + console.log(' - 已删除 plugins.installs.qqbot'); + } + + fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2)); + console.log('配置文件已更新'); + " + else + echo "未找到配置文件: $CONFIG_FILE" + fi +} + +# 检测并处理所有可能的安装 +FOUND_INSTALLATION="" + +# 检查 clawdbot +if [ -d "$HOME/.clawdbot" ]; then + cleanup_installation "clawdbot" + FOUND_INSTALLATION="clawdbot" fi -# 2. 清理配置文件中的 qqbot 相关字段 -if [ -f "$CONFIG_FILE" ]; then - echo "清理配置文件中的 qqbot 字段..." - - # 使用 node 处理 JSON(比 jq 更可靠处理复杂结构) - node -e " - const fs = require('fs'); - const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8')); - - // 删除 channels.qqbot - if (config.channels && config.channels.qqbot) { - delete config.channels.qqbot; - console.log(' - 已删除 channels.qqbot'); - } - - // 删除 plugins.entries.qqbot - if (config.plugins && config.plugins.entries && config.plugins.entries.qqbot) { - delete config.plugins.entries.qqbot; - console.log(' - 已删除 plugins.entries.qqbot'); - } - - // 删除 plugins.installs.qqbot - if (config.plugins && config.plugins.installs && config.plugins.installs.qqbot) { - delete config.plugins.installs.qqbot; - console.log(' - 已删除 plugins.installs.qqbot'); - } - - fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2)); - console.log('配置文件已更新'); - " -else - echo "未找到配置文件: $CONFIG_FILE" +# 检查 openclaw +if [ -d "$HOME/.openclaw" ]; then + cleanup_installation "openclaw" + FOUND_INSTALLATION="openclaw" fi +# 如果都没找到 +if [ -z "$FOUND_INSTALLATION" ]; then + echo "未找到 clawdbot 或 openclaw 安装目录" + echo "请确认已安装 clawdbot 或 openclaw" + exit 1 +fi + +# 使用检测到的安装类型作为命令 +CMD="$FOUND_INSTALLATION" + echo "" echo "=== 清理完成 ===" echo "" echo "接下来请执行以下命令重新安装插件:" echo " cd /path/to/qqbot" -echo " openclaw plugins install ." -echo " openclaw channels add --channel qqbot --token \"AppID:AppSecret\"" -echo " openclaw gateway restart" \ No newline at end of file +echo " $CMD plugins install ." +echo " $CMD channels add --channel qqbot --token \"AppID:AppSecret\"" +echo " $CMD gateway restart" From 5cda5215833953614750271e7a6ee81851460a43 Mon Sep 17 00:00:00 2001 From: sliverp Date: Tue, 3 Feb 2026 21:17:17 +0800 Subject: [PATCH 13/14] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20QQ=20Bot=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index fd5ec4a..97185b2 100644 --- a/index.ts +++ b/index.ts @@ -6,8 +6,8 @@ import { setQQBotRuntime } from "./src/runtime.js"; const plugin = { id: "qqbot", - name: "QQ Bot (Stream)", - description: "QQ Bot channel plugin with streaming message support", + name: "QQ Bot", + description: "QQ Bot channel plugin", configSchema: emptyPluginConfigSchema(), register(api: OpenClawPluginApi) { setQQBotRuntime(api.runtime); From d36567a74438488f03e0840f89a5dcf017330eb5 Mon Sep 17 00:00:00 2001 From: sliverp Date: Tue, 3 Feb 2026 21:33:02 +0800 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gateway.ts b/src/gateway.ts index f77fb3a..a58ef77 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -1265,7 +1265,7 @@ openclaw cron add \\ } if (!hasResponse) { log?.error(`[qqbot:${account.accountId}] No response within timeout`); - await sendErrorMessage("[ClawdBot] QQ响应正常,但未收到clawdbot响应,请检查大模型是否正确配置"); + await sendErrorMessage("QQ已经收到了你的请求并转交给了Openclaw,任务可能比较复杂,正在处理中..."); } } } catch (err) {